mirror of
https://github.com/standardnotes/server
synced 2026-04-28 00:01:24 -04:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 84cbeaf3be | |||
| 578ce0e74e | |||
| 532be7c358 | |||
| d406272f07 | |||
| 9de3352885 | |||
| 8575d20f7b | |||
| 102d4b1e8a | |||
| 1a57c247b2 | |||
| dbb0e4a974 | |||
| 5c02435ee4 | |||
| 0a1e555b13 | |||
| be668d7d7a | |||
| 87e50ec941 | |||
| 6d7ca1b926 | |||
| 00bfaaa53d | |||
| f939caf2d9 | |||
| 0f3615ee65 | |||
| 567bcf26b5 | |||
| 9d49764b84 | |||
| 5c9f493b67 | |||
| 4fe8e9a79f | |||
| f975dd9697 | |||
| 10832f7001 | |||
| 86b050865f | |||
| 6f07aaf87a | |||
| 634e8bd2d0 | |||
| 6853dfbf66 | |||
| 136cf252a1 | |||
| cad28ebba5 | |||
| 460fdf9eaf | |||
| bec1b502ad | |||
| 70bbf11db5 | |||
| c00c7becae | |||
| 89dc6c19bf | |||
| 972a91d59f | |||
| 045358ddbf | |||
| c7217a92ba | |||
| 3da7a21cde | |||
| 351e18f638 | |||
| 4f2129c4e0 | |||
| d7a1c667dd | |||
| 4de0bfa36d | |||
| 0443de88ce | |||
| f830bac873 | |||
| 517ae5ded9 | |||
| 6062f85000 | |||
| e2205c3849 | |||
| 0b46eff16e | |||
| df67982bca | |||
| d44866b3c0 | |||
| 6ee18bffe6 | |||
| a881dd2d79 | |||
| b767e1f072 | |||
| e3cb1faba4 | |||
| 5c5f988055 |
+6
-1
@@ -17,6 +17,9 @@ SYNCING_SERVER_LOG_LEVEL=debug
|
||||
FILES_SERVER_LOG_LEVEL=debug
|
||||
REVISIONS_SERVER_LOG_LEVEL=debug
|
||||
API_GATEWAY_LOG_LEVEL=debug
|
||||
COOKIE_DOMAIN=localhost
|
||||
COOKIE_SECURE=false
|
||||
COOKIE_PARTITIONED=false
|
||||
|
||||
MYSQL_DATABASE=standard_notes_db
|
||||
MYSQL_USER=std_notes_user
|
||||
@@ -27,4 +30,6 @@ AUTH_JWT_SECRET=f95259c5e441f5a4646d76422cfb3df4c4488842901aa50b6c51b8be2e0040e9
|
||||
AUTH_SERVER_ENCRYPTION_SERVER_KEY=1087415dfde3093797f9a7ca93a49e7d7aa1861735eb0d32aae9c303b8c3d060
|
||||
VALET_TOKEN_SECRET=4b886819ebe1e908077c6cae96311b48a8416bd60cc91c03060e15bdf6b30d1f
|
||||
|
||||
SYNCING_SERVER_CONTENT_SIZE_TRANSFER_LIMIT=1000000
|
||||
SYNCING_SERVER_CONTENT_SIZE_TRANSFER_LIMIT=100000
|
||||
|
||||
HTTP_REQUEST_PAYLOAD_LIMIT_MEGABYTES=1
|
||||
|
||||
@@ -42,26 +42,26 @@ jobs:
|
||||
workspace_name: ${{ inputs.workspace_name }}
|
||||
secrets: inherit
|
||||
|
||||
deploy-web:
|
||||
if: ${{ inputs.deploy_web }}
|
||||
# deploy-web:
|
||||
# if: ${{ inputs.deploy_web }}
|
||||
|
||||
needs: publish
|
||||
# needs: publish
|
||||
|
||||
name: Deploy Web
|
||||
uses: standardnotes/server/.github/workflows/common-deploy.yml@main
|
||||
with:
|
||||
service_name: ${{ inputs.service_name }}
|
||||
docker_image: ${{ inputs.service_name }}:${{ github.sha }}
|
||||
secrets: inherit
|
||||
# name: Deploy Web
|
||||
# uses: standardnotes/server/.github/workflows/common-deploy.yml@main
|
||||
# with:
|
||||
# service_name: ${{ inputs.service_name }}
|
||||
# docker_image: ${{ inputs.service_name }}:${{ github.sha }}
|
||||
# secrets: inherit
|
||||
|
||||
deploy-worker:
|
||||
if: ${{ inputs.deploy_worker }}
|
||||
# deploy-worker:
|
||||
# if: ${{ inputs.deploy_worker }}
|
||||
|
||||
needs: publish
|
||||
# needs: publish
|
||||
|
||||
name: Deploy Worker
|
||||
uses: standardnotes/server/.github/workflows/common-deploy.yml@main
|
||||
with:
|
||||
service_name: ${{ inputs.service_name }}-worker
|
||||
docker_image: ${{ inputs.service_name }}:${{ github.sha }}
|
||||
secrets: inherit
|
||||
# name: Deploy Worker
|
||||
# uses: standardnotes/server/.github/workflows/common-deploy.yml@main
|
||||
# with:
|
||||
# service_name: ${{ inputs.service_name }}-worker
|
||||
# docker_image: ${{ inputs.service_name }}:${{ github.sha }}
|
||||
# secrets: inherit
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -70,7 +70,8 @@ jobs:
|
||||
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
|
||||
echo "REFRESH_TOKEN_AGE=10" >> packages/home-server/.env
|
||||
echo "REVISIONS_FREQUENCY=2" >> packages/home-server/.env
|
||||
echo "CONTENT_SIZE_TRANSFER_LIMIT=1000000" >> packages/home-server/.env
|
||||
echo "CONTENT_SIZE_TRANSFER_LIMIT=100000" >> packages/home-server/.env
|
||||
echo "HTTP_REQUEST_PAYLOAD_LIMIT_MEGABYTES=1" >> packages/home-server/.env
|
||||
echo "DB_HOST=localhost" >> packages/home-server/.env
|
||||
echo "DB_PORT=3306" >> packages/home-server/.env
|
||||
echo "DB_DATABASE=standardnotes" >> packages/home-server/.env
|
||||
@@ -93,11 +94,11 @@ jobs:
|
||||
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
|
||||
|
||||
- name: Run E2E Test Suite
|
||||
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html?suite=${{ inputs.suite }}
|
||||
run: yarn dlx mocha-headless-chrome --timeout 3600000 -a no-sandbox -a disable-setuid-sandbox -f http://localhost:9001/mocha/test.html?suite=${{ inputs.suite }}
|
||||
|
||||
- name: Archive failed run logs
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: home-server-failure-logs-${{ inputs.suite }}-${{ matrix.db_type }}-${{ matrix.cache_type }}
|
||||
retention-days: 5
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -57,11 +57,11 @@ jobs:
|
||||
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
|
||||
|
||||
- name: Run E2E Test Suite
|
||||
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html?suite=${{ inputs.suite }}
|
||||
run: yarn dlx mocha-headless-chrome --timeout 3600000 -a no-sandbox -a disable-setuid-sandbox -f http://localhost:9001/mocha/test.html?suite=${{ inputs.suite }}
|
||||
|
||||
- name: Archive failed run logs
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: self-hosted-failure-logs-${{ inputs.suite }}
|
||||
retention-days: 5
|
||||
|
||||
@@ -13,14 +13,14 @@ jobs:
|
||||
|
||||
- name: Cache build
|
||||
id: cache-build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
packages/**/dist
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -41,14 +41,14 @@ jobs:
|
||||
|
||||
- name: Cache build
|
||||
id: cache-build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
packages/**/dist
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -73,14 +73,14 @@ jobs:
|
||||
|
||||
- name: Cache build
|
||||
id: cache-build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
packages/**/dist
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
- name: Cache build
|
||||
id: cache-build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
packages/**/dist
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
|
||||
- name: Cache build
|
||||
id: cache-build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
packages/**/dist
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
|
||||
- name: Cache build
|
||||
id: cache-build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
packages/**/dist
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
|
||||
- name: Cache build
|
||||
id: cache-build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
packages/**/dist
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
git_commit_gpgsign: true
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
@@ -466,6 +466,54 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.490.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-client-sns-npm-3.490.0-2cd839225d-4d8875521c.zip/node_modules/@aws-sdk/client-sns/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/client-sns", "npm:3.490.0"],\
|
||||
["@aws-crypto/sha256-browser", "npm:3.0.0"],\
|
||||
["@aws-crypto/sha256-js", "npm:3.0.0"],\
|
||||
["@aws-sdk/client-sts", "npm:3.490.0"],\
|
||||
["@aws-sdk/core", "npm:3.490.0"],\
|
||||
["@aws-sdk/credential-provider-node", "npm:3.490.0"],\
|
||||
["@aws-sdk/middleware-host-header", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-logger", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-recursion-detection", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-signing", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-user-agent", "npm:3.489.0"],\
|
||||
["@aws-sdk/region-config-resolver", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-endpoints", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-user-agent-browser", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-user-agent-node", "virtual:26ec4a89785e0643103d1dd3b2a86d8c63d7fd76dbfb0e516f1dc429fef4581a7306b382504a8b85e8fb995888356d6341786deec607cb64b29957c728540295#npm:3.489.0"],\
|
||||
["@smithy/config-resolver", "npm:2.0.23"],\
|
||||
["@smithy/core", "npm:1.2.2"],\
|
||||
["@smithy/fetch-http-handler", "npm:2.3.2"],\
|
||||
["@smithy/hash-node", "npm:2.0.18"],\
|
||||
["@smithy/invalid-dependency", "npm:2.0.16"],\
|
||||
["@smithy/middleware-content-length", "npm:2.0.18"],\
|
||||
["@smithy/middleware-endpoint", "npm:2.3.0"],\
|
||||
["@smithy/middleware-retry", "npm:2.0.26"],\
|
||||
["@smithy/middleware-serde", "npm:2.0.16"],\
|
||||
["@smithy/middleware-stack", "npm:2.0.10"],\
|
||||
["@smithy/node-config-provider", "npm:2.1.9"],\
|
||||
["@smithy/node-http-handler", "npm:2.2.2"],\
|
||||
["@smithy/protocol-http", "npm:3.0.12"],\
|
||||
["@smithy/smithy-client", "npm:2.2.1"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["@smithy/url-parser", "npm:2.0.16"],\
|
||||
["@smithy/util-base64", "npm:2.0.1"],\
|
||||
["@smithy/util-body-length-browser", "npm:2.0.1"],\
|
||||
["@smithy/util-body-length-node", "npm:2.1.0"],\
|
||||
["@smithy/util-defaults-mode-browser", "npm:2.0.24"],\
|
||||
["@smithy/util-defaults-mode-node", "npm:2.0.32"],\
|
||||
["@smithy/util-endpoints", "npm:1.0.8"],\
|
||||
["@smithy/util-retry", "npm:2.0.9"],\
|
||||
["@smithy/util-utf8", "npm:2.0.2"],\
|
||||
["fast-xml-parser", "npm:4.2.5"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/client-sqs", [\
|
||||
@@ -607,6 +655,50 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.490.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-client-sso-npm-3.490.0-26ec4a8978-785147e3c2.zip/node_modules/@aws-sdk/client-sso/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/client-sso", "npm:3.490.0"],\
|
||||
["@aws-crypto/sha256-browser", "npm:3.0.0"],\
|
||||
["@aws-crypto/sha256-js", "npm:3.0.0"],\
|
||||
["@aws-sdk/core", "npm:3.490.0"],\
|
||||
["@aws-sdk/middleware-host-header", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-logger", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-recursion-detection", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-user-agent", "npm:3.489.0"],\
|
||||
["@aws-sdk/region-config-resolver", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-endpoints", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-user-agent-browser", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-user-agent-node", "virtual:26ec4a89785e0643103d1dd3b2a86d8c63d7fd76dbfb0e516f1dc429fef4581a7306b382504a8b85e8fb995888356d6341786deec607cb64b29957c728540295#npm:3.489.0"],\
|
||||
["@smithy/config-resolver", "npm:2.0.23"],\
|
||||
["@smithy/core", "npm:1.2.2"],\
|
||||
["@smithy/fetch-http-handler", "npm:2.3.2"],\
|
||||
["@smithy/hash-node", "npm:2.0.18"],\
|
||||
["@smithy/invalid-dependency", "npm:2.0.16"],\
|
||||
["@smithy/middleware-content-length", "npm:2.0.18"],\
|
||||
["@smithy/middleware-endpoint", "npm:2.3.0"],\
|
||||
["@smithy/middleware-retry", "npm:2.0.26"],\
|
||||
["@smithy/middleware-serde", "npm:2.0.16"],\
|
||||
["@smithy/middleware-stack", "npm:2.0.10"],\
|
||||
["@smithy/node-config-provider", "npm:2.1.9"],\
|
||||
["@smithy/node-http-handler", "npm:2.2.2"],\
|
||||
["@smithy/protocol-http", "npm:3.0.12"],\
|
||||
["@smithy/smithy-client", "npm:2.2.1"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["@smithy/url-parser", "npm:2.0.16"],\
|
||||
["@smithy/util-base64", "npm:2.0.1"],\
|
||||
["@smithy/util-body-length-browser", "npm:2.0.1"],\
|
||||
["@smithy/util-body-length-node", "npm:2.1.0"],\
|
||||
["@smithy/util-defaults-mode-browser", "npm:2.0.24"],\
|
||||
["@smithy/util-defaults-mode-node", "npm:2.0.32"],\
|
||||
["@smithy/util-endpoints", "npm:1.0.8"],\
|
||||
["@smithy/util-retry", "npm:2.0.9"],\
|
||||
["@smithy/util-utf8", "npm:2.0.2"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/client-sts", [\
|
||||
@@ -703,6 +795,53 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.490.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-client-sts-npm-3.490.0-f3cd7f7c70-19d1b98694.zip/node_modules/@aws-sdk/client-sts/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/client-sts", "npm:3.490.0"],\
|
||||
["@aws-crypto/sha256-browser", "npm:3.0.0"],\
|
||||
["@aws-crypto/sha256-js", "npm:3.0.0"],\
|
||||
["@aws-sdk/core", "npm:3.490.0"],\
|
||||
["@aws-sdk/credential-provider-node", "npm:3.490.0"],\
|
||||
["@aws-sdk/middleware-host-header", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-logger", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-recursion-detection", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-user-agent", "npm:3.489.0"],\
|
||||
["@aws-sdk/region-config-resolver", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-endpoints", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-user-agent-browser", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-user-agent-node", "virtual:26ec4a89785e0643103d1dd3b2a86d8c63d7fd76dbfb0e516f1dc429fef4581a7306b382504a8b85e8fb995888356d6341786deec607cb64b29957c728540295#npm:3.489.0"],\
|
||||
["@smithy/config-resolver", "npm:2.0.23"],\
|
||||
["@smithy/core", "npm:1.2.2"],\
|
||||
["@smithy/fetch-http-handler", "npm:2.3.2"],\
|
||||
["@smithy/hash-node", "npm:2.0.18"],\
|
||||
["@smithy/invalid-dependency", "npm:2.0.16"],\
|
||||
["@smithy/middleware-content-length", "npm:2.0.18"],\
|
||||
["@smithy/middleware-endpoint", "npm:2.3.0"],\
|
||||
["@smithy/middleware-retry", "npm:2.0.26"],\
|
||||
["@smithy/middleware-serde", "npm:2.0.16"],\
|
||||
["@smithy/middleware-stack", "npm:2.0.10"],\
|
||||
["@smithy/node-config-provider", "npm:2.1.9"],\
|
||||
["@smithy/node-http-handler", "npm:2.2.2"],\
|
||||
["@smithy/protocol-http", "npm:3.0.12"],\
|
||||
["@smithy/smithy-client", "npm:2.2.1"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["@smithy/url-parser", "npm:2.0.16"],\
|
||||
["@smithy/util-base64", "npm:2.0.1"],\
|
||||
["@smithy/util-body-length-browser", "npm:2.0.1"],\
|
||||
["@smithy/util-body-length-node", "npm:2.1.0"],\
|
||||
["@smithy/util-defaults-mode-browser", "npm:2.0.24"],\
|
||||
["@smithy/util-defaults-mode-node", "npm:2.0.32"],\
|
||||
["@smithy/util-endpoints", "npm:1.0.8"],\
|
||||
["@smithy/util-middleware", "npm:2.0.9"],\
|
||||
["@smithy/util-retry", "npm:2.0.9"],\
|
||||
["@smithy/util-utf8", "npm:2.0.2"],\
|
||||
["fast-xml-parser", "npm:4.2.5"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/core", [\
|
||||
@@ -731,6 +870,19 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.490.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-core-npm-3.490.0-3725a806be-3e81f37825.zip/node_modules/@aws-sdk/core/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/core", "npm:3.490.0"],\
|
||||
["@smithy/core", "npm:1.2.2"],\
|
||||
["@smithy/protocol-http", "npm:3.0.12"],\
|
||||
["@smithy/signature-v4", "npm:2.0.5"],\
|
||||
["@smithy/smithy-client", "npm:2.2.1"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/credential-provider-env", [\
|
||||
@@ -755,6 +907,17 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-env-npm-3.489.0-e86b20f5e4-95ab96ee49.zip/node_modules/@aws-sdk/credential-provider-env/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/credential-provider-env", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/credential-provider-ini", [\
|
||||
@@ -791,6 +954,23 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.490.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-ini-npm-3.490.0-51f9d0faff-4e4cd2633a.zip/node_modules/@aws-sdk/credential-provider-ini/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/credential-provider-ini", "npm:3.490.0"],\
|
||||
["@aws-sdk/credential-provider-env", "npm:3.489.0"],\
|
||||
["@aws-sdk/credential-provider-process", "npm:3.489.0"],\
|
||||
["@aws-sdk/credential-provider-sso", "npm:3.490.0"],\
|
||||
["@aws-sdk/credential-provider-web-identity", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/credential-provider-imds", "npm:2.0.5"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/credential-provider-node", [\
|
||||
@@ -829,6 +1009,24 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.490.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-node-npm-3.490.0-3cbe0ec5e6-2f8141c3e1.zip/node_modules/@aws-sdk/credential-provider-node/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/credential-provider-node", "npm:3.490.0"],\
|
||||
["@aws-sdk/credential-provider-env", "npm:3.489.0"],\
|
||||
["@aws-sdk/credential-provider-ini", "npm:3.490.0"],\
|
||||
["@aws-sdk/credential-provider-process", "npm:3.489.0"],\
|
||||
["@aws-sdk/credential-provider-sso", "npm:3.490.0"],\
|
||||
["@aws-sdk/credential-provider-web-identity", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/credential-provider-imds", "npm:2.0.5"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/credential-provider-process", [\
|
||||
@@ -855,6 +1053,18 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-process-npm-3.489.0-9370bfd061-42f4f5f21d.zip/node_modules/@aws-sdk/credential-provider-process/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/credential-provider-process", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/credential-provider-sso", [\
|
||||
@@ -885,6 +1095,20 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.490.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-sso-npm-3.490.0-9acb6828c0-ef2eff8fbc.zip/node_modules/@aws-sdk/credential-provider-sso/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/credential-provider-sso", "npm:3.490.0"],\
|
||||
["@aws-sdk/client-sso", "npm:3.490.0"],\
|
||||
["@aws-sdk/token-providers", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/credential-provider-web-identity", [\
|
||||
@@ -909,6 +1133,17 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-web-identity-npm-3.489.0-002a8c8ade-911bc3fd28.zip/node_modules/@aws-sdk/credential-provider-web-identity/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/credential-provider-web-identity", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/middleware-bucket-endpoint", [\
|
||||
@@ -979,6 +1214,17 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-middleware-host-header-npm-3.489.0-10c65ea2e3-3f80f71691.zip/node_modules/@aws-sdk/middleware-host-header/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/middleware-host-header", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/protocol-http", "npm:3.0.12"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/middleware-location-constraint", [\
|
||||
@@ -1013,6 +1259,16 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-middleware-logger-npm-3.489.0-ba04fd0161-0bbf9d08c7.zip/node_modules/@aws-sdk/middleware-logger/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/middleware-logger", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/middleware-recursion-detection", [\
|
||||
@@ -1037,6 +1293,17 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-middleware-recursion-detection-npm-3.489.0-2cefe5fc6b-91eb0b3b46.zip/node_modules/@aws-sdk/middleware-recursion-detection/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/middleware-recursion-detection", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/protocol-http", "npm:3.0.12"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/middleware-sdk-s3", [\
|
||||
@@ -1099,6 +1366,20 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-middleware-signing-npm-3.489.0-722d97a2fd-6fedba4569.zip/node_modules/@aws-sdk/middleware-signing/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/middleware-signing", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/protocol-http", "npm:3.0.12"],\
|
||||
["@smithy/signature-v4", "npm:2.0.5"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["@smithy/util-middleware", "npm:2.0.9"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/middleware-ssec", [\
|
||||
@@ -1137,6 +1418,18 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-middleware-user-agent-npm-3.489.0-4a9e57c5ff-51fc7a8a03.zip/node_modules/@aws-sdk/middleware-user-agent/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/middleware-user-agent", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-endpoints", "npm:3.489.0"],\
|
||||
["@smithy/protocol-http", "npm:3.0.12"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/region-config-resolver", [\
|
||||
@@ -1163,6 +1456,19 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-region-config-resolver-npm-3.489.0-0721047a4a-045a630c94.zip/node_modules/@aws-sdk/region-config-resolver/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/region-config-resolver", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/node-config-provider", "npm:2.1.9"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["@smithy/util-config-provider", "npm:2.1.0"],\
|
||||
["@smithy/util-middleware", "npm:2.0.9"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/signature-v4-multi-region", [\
|
||||
@@ -1268,6 +1574,50 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-token-providers-npm-3.489.0-05002406d6-ee7a20eff7.zip/node_modules/@aws-sdk/token-providers/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/token-providers", "npm:3.489.0"],\
|
||||
["@aws-crypto/sha256-browser", "npm:3.0.0"],\
|
||||
["@aws-crypto/sha256-js", "npm:3.0.0"],\
|
||||
["@aws-sdk/middleware-host-header", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-logger", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-recursion-detection", "npm:3.489.0"],\
|
||||
["@aws-sdk/middleware-user-agent", "npm:3.489.0"],\
|
||||
["@aws-sdk/region-config-resolver", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-endpoints", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-user-agent-browser", "npm:3.489.0"],\
|
||||
["@aws-sdk/util-user-agent-node", "virtual:26ec4a89785e0643103d1dd3b2a86d8c63d7fd76dbfb0e516f1dc429fef4581a7306b382504a8b85e8fb995888356d6341786deec607cb64b29957c728540295#npm:3.489.0"],\
|
||||
["@smithy/config-resolver", "npm:2.0.23"],\
|
||||
["@smithy/fetch-http-handler", "npm:2.3.2"],\
|
||||
["@smithy/hash-node", "npm:2.0.18"],\
|
||||
["@smithy/invalid-dependency", "npm:2.0.16"],\
|
||||
["@smithy/middleware-content-length", "npm:2.0.18"],\
|
||||
["@smithy/middleware-endpoint", "npm:2.3.0"],\
|
||||
["@smithy/middleware-retry", "npm:2.0.26"],\
|
||||
["@smithy/middleware-serde", "npm:2.0.16"],\
|
||||
["@smithy/middleware-stack", "npm:2.0.10"],\
|
||||
["@smithy/node-config-provider", "npm:2.1.9"],\
|
||||
["@smithy/node-http-handler", "npm:2.2.2"],\
|
||||
["@smithy/property-provider", "npm:2.0.5"],\
|
||||
["@smithy/protocol-http", "npm:3.0.12"],\
|
||||
["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
|
||||
["@smithy/smithy-client", "npm:2.2.1"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["@smithy/url-parser", "npm:2.0.16"],\
|
||||
["@smithy/util-base64", "npm:2.0.1"],\
|
||||
["@smithy/util-body-length-browser", "npm:2.0.1"],\
|
||||
["@smithy/util-body-length-node", "npm:2.1.0"],\
|
||||
["@smithy/util-defaults-mode-browser", "npm:2.0.24"],\
|
||||
["@smithy/util-defaults-mode-node", "npm:2.0.32"],\
|
||||
["@smithy/util-endpoints", "npm:1.0.8"],\
|
||||
["@smithy/util-retry", "npm:2.0.9"],\
|
||||
["@smithy/util-utf8", "npm:2.0.2"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/types", [\
|
||||
@@ -1296,6 +1646,15 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-types-npm-3.489.0-f0f748fbaa-48778dad14.zip/node_modules/@aws-sdk/types/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/util-arn-parser", [\
|
||||
@@ -1328,6 +1687,17 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-util-endpoints-npm-3.489.0-1f2dd7e944-68f921982f.zip/node_modules/@aws-sdk/util-endpoints/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/util-endpoints", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["@smithy/util-endpoints", "npm:1.0.8"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/util-locate-window", [\
|
||||
@@ -1362,6 +1732,17 @@ const RAW_RUNTIME_STATE =
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-util-user-agent-browser-npm-3.489.0-ffb94f7b1d-2bb414b8d8.zip/node_modules/@aws-sdk/util-user-agent-browser/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/util-user-agent-browser", "npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["bowser", "npm:2.11.0"],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@aws-sdk/util-user-agent-node", [\
|
||||
@@ -1379,6 +1760,30 @@ const RAW_RUNTIME_STATE =
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/cache/@aws-sdk-util-user-agent-node-npm-3.489.0-082349e8a9-95dc1e07b6.zip/node_modules/@aws-sdk/util-user-agent-node/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/util-user-agent-node", "npm:3.489.0"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:26ec4a89785e0643103d1dd3b2a86d8c63d7fd76dbfb0e516f1dc429fef4581a7306b382504a8b85e8fb995888356d6341786deec607cb64b29957c728540295#npm:3.489.0", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@aws-sdk-util-user-agent-node-virtual-73c334651c/0/cache/@aws-sdk-util-user-agent-node-npm-3.489.0-082349e8a9-95dc1e07b6.zip/node_modules/@aws-sdk/util-user-agent-node/",\
|
||||
"packageDependencies": [\
|
||||
["@aws-sdk/util-user-agent-node", "virtual:26ec4a89785e0643103d1dd3b2a86d8c63d7fd76dbfb0e516f1dc429fef4581a7306b382504a8b85e8fb995888356d6341786deec607cb64b29957c728540295#npm:3.489.0"],\
|
||||
["@aws-sdk/types", "npm:3.489.0"],\
|
||||
["@smithy/node-config-provider", "npm:2.1.9"],\
|
||||
["@smithy/types", "npm:2.8.0"],\
|
||||
["@types/aws-crt", null],\
|
||||
["aws-crt", null],\
|
||||
["tslib", "npm:2.5.2"]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
"@types/aws-crt",\
|
||||
"aws-crt"\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["virtual:5f6733bd23aee10dd05576af160f1b93e0bb4a20b288e9b818dc0b69bdb08ea1a09d5836816f02bdafc9c01487816ae339c6b680c2f7849dfe249436c5f2b499#npm:3.485.0", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@aws-sdk-util-user-agent-node-virtual-c26ab353dd/0/cache/@aws-sdk-util-user-agent-node-npm-3.485.0-7991a74cb3-e2805ef37b.zip/node_modules/@aws-sdk/util-user-agent-node/",\
|
||||
"packageDependencies": [\
|
||||
@@ -5951,7 +6356,7 @@ const RAW_RUNTIME_STATE =
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["mixpanel", "npm:0.17.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["mysql2", "npm:3.9.7"],\
|
||||
["prettier", "npm:3.0.3"],\
|
||||
["reflect-metadata", "npm:0.2.1"],\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||
@@ -5983,6 +6388,7 @@ const RAW_RUNTIME_STATE =
|
||||
"packageLocation": "./packages/api-gateway/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/api-gateway", "workspace:packages/api-gateway"],\
|
||||
["@aws-sdk/client-sns", "npm:3.490.0"],\
|
||||
["@grpc/grpc-js", "npm:1.9.13"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
@@ -5990,6 +6396,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/grpc", "workspace:packages/grpc"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/cookie-parser", "npm:1.4.6"],\
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/express", "npm:4.17.17"],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
@@ -6001,6 +6408,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@typescript-eslint/parser", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
|
||||
["agentkeepalive", "npm:4.5.0"],\
|
||||
["axios", "npm:1.6.1"],\
|
||||
["cookie-parser", "npm:1.4.6"],\
|
||||
["cors", "npm:2.8.5"],\
|
||||
["dotenv", "npm:16.1.3"],\
|
||||
["eslint", "npm:8.41.0"],\
|
||||
@@ -6051,6 +6459,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/bcryptjs", "npm:2.4.2"],\
|
||||
["@types/cookie-parser", "npm:1.4.6"],\
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/express", "npm:4.17.17"],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
@@ -6062,7 +6471,10 @@ const RAW_RUNTIME_STATE =
|
||||
["@types/uuid", "npm:9.0.3"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
|
||||
["@typescript-eslint/parser", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
|
||||
["agentkeepalive", "npm:4.5.0"],\
|
||||
["axios", "npm:1.6.7"],\
|
||||
["bcryptjs", "npm:2.4.3"],\
|
||||
["cookie-parser", "npm:1.4.6"],\
|
||||
["cors", "npm:2.8.5"],\
|
||||
["dayjs", "npm:1.11.7"],\
|
||||
["dotenv", "npm:16.1.3"],\
|
||||
@@ -6073,7 +6485,7 @@ const RAW_RUNTIME_STATE =
|
||||
["inversify-express-utils", "npm:6.4.3"],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["mysql2", "npm:3.9.7"],\
|
||||
["otplib", "npm:12.0.1"],\
|
||||
["prettier", "npm:3.0.3"],\
|
||||
["prettyjson", "npm:1.2.5"],\
|
||||
@@ -6283,10 +6695,12 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/files-server", "workspace:packages/files"],\
|
||||
["@standardnotes/revisions-server", "workspace:packages/revisions"],\
|
||||
["@standardnotes/syncing-server", "workspace:packages/syncing-server"],\
|
||||
["@types/cookie-parser", "npm:1.4.6"],\
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/express", "npm:4.17.17"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
|
||||
["@typescript-eslint/parser", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:6.5.0"],\
|
||||
["cookie-parser", "npm:1.4.6"],\
|
||||
["cors", "npm:2.8.5"],\
|
||||
["dotenv", "npm:16.1.3"],\
|
||||
["eslint", "npm:8.41.0"],\
|
||||
@@ -6384,7 +6798,7 @@ const RAW_RUNTIME_STATE =
|
||||
["inversify-express-utils", "npm:6.4.3"],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["mysql2", "npm:3.9.7"],\
|
||||
["prettier", "npm:3.0.3"],\
|
||||
["reflect-metadata", "npm:0.2.1"],\
|
||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||
@@ -6403,6 +6817,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/scheduler-server", "workspace:packages/scheduler"],\
|
||||
["@aws-sdk/client-sns", "npm:3.484.0"],\
|
||||
["@aws-sdk/client-sqs", "npm:3.484.0"],\
|
||||
["@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"],\
|
||||
@@ -6420,7 +6835,7 @@ const RAW_RUNTIME_STATE =
|
||||
["inversify", "npm:6.0.1"],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["mysql2", "npm:3.9.7"],\
|
||||
["prettier", "npm:3.0.3"],\
|
||||
["reflect-metadata", "npm:0.2.1"],\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||
@@ -6571,7 +6986,7 @@ const RAW_RUNTIME_STATE =
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["jsonwebtoken", "npm:9.0.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["mysql2", "npm:3.9.7"],\
|
||||
["prettier", "npm:3.0.3"],\
|
||||
["prettyjson", "npm:1.2.5"],\
|
||||
["reflect-metadata", "npm:0.2.1"],\
|
||||
@@ -6651,7 +7066,7 @@ const RAW_RUNTIME_STATE =
|
||||
["inversify-express-utils", "npm:6.4.3"],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["mysql2", "npm:3.9.7"],\
|
||||
["prettier", "npm:3.0.3"],\
|
||||
["reflect-metadata", "npm:0.2.1"],\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||
@@ -6831,6 +7246,16 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@types/cookie-parser", [\
|
||||
["npm:1.4.6", {\
|
||||
"packageLocation": "./.yarn/cache/@types-cookie-parser-npm-1.4.6-27287e1e43-b1bbb17bc4.zip/node_modules/@types/cookie-parser/",\
|
||||
"packageDependencies": [\
|
||||
["@types/cookie-parser", "npm:1.4.6"],\
|
||||
["@types/express", "npm:4.17.17"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@types/cors", [\
|
||||
["npm:2.8.13", {\
|
||||
"packageLocation": "./.yarn/cache/@types-cors-npm-2.8.13-4b8ac1068f-7ef197ea19.zip/node_modules/@types/cors/",\
|
||||
@@ -8091,6 +8516,16 @@ const RAW_RUNTIME_STATE =
|
||||
["proxy-from-env", "npm:1.1.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.6.7", {\
|
||||
"packageLocation": "./.yarn/cache/axios-npm-1.6.7-d7b9974d1b-a1932b089e.zip/node_modules/axios/",\
|
||||
"packageDependencies": [\
|
||||
["axios", "npm:1.6.7"],\
|
||||
["follow-redirects", "virtual:d7b9974d1bba76881cc57a280a16dd4914416a6fc4923c2efbb6328057412974da1e719cef1530b7a62b97d85d828f7e1d49b5f6de3b5b0854d49902ec87827c#npm:1.15.5"],\
|
||||
["form-data", "npm:4.0.0"],\
|
||||
["proxy-from-env", "npm:1.1.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["babel-jest", [\
|
||||
@@ -9202,6 +9637,13 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["cookie", [\
|
||||
["npm:0.4.1", {\
|
||||
"packageLocation": "./.yarn/cache/cookie-npm-0.4.1-cc5e2ebb42-0f2defd60a.zip/node_modules/cookie/",\
|
||||
"packageDependencies": [\
|
||||
["cookie", "npm:0.4.1"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:0.5.0", {\
|
||||
"packageLocation": "./.yarn/cache/cookie-npm-0.5.0-e2d58a161a-aae7911ddc.zip/node_modules/cookie/",\
|
||||
"packageDependencies": [\
|
||||
@@ -9210,6 +9652,17 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["cookie-parser", [\
|
||||
["npm:1.4.6", {\
|
||||
"packageLocation": "./.yarn/cache/cookie-parser-npm-1.4.6-a68f84d02a-1e5a63aa82.zip/node_modules/cookie-parser/",\
|
||||
"packageDependencies": [\
|
||||
["cookie-parser", "npm:1.4.6"],\
|
||||
["cookie", "npm:0.4.1"],\
|
||||
["cookie-signature", "npm:1.0.6"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["cookie-signature", [\
|
||||
["npm:1.0.6", {\
|
||||
"packageLocation": "./.yarn/cache/cookie-signature-npm-1.0.6-93f325f7f0-f4e1b0a98a.zip/node_modules/cookie-signature/",\
|
||||
@@ -10458,6 +10911,26 @@ const RAW_RUNTIME_STATE =
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["npm:1.15.5", {\
|
||||
"packageLocation": "./.yarn/cache/follow-redirects-npm-1.15.5-9d14db76ca-d467f13c1c.zip/node_modules/follow-redirects/",\
|
||||
"packageDependencies": [\
|
||||
["follow-redirects", "npm:1.15.5"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:d7b9974d1bba76881cc57a280a16dd4914416a6fc4923c2efbb6328057412974da1e719cef1530b7a62b97d85d828f7e1d49b5f6de3b5b0854d49902ec87827c#npm:1.15.5", {\
|
||||
"packageLocation": "./.yarn/__virtual__/follow-redirects-virtual-393395f3f6/0/cache/follow-redirects-npm-1.15.5-9d14db76ca-d467f13c1c.zip/node_modules/follow-redirects/",\
|
||||
"packageDependencies": [\
|
||||
["follow-redirects", "virtual:d7b9974d1bba76881cc57a280a16dd4914416a6fc4923c2efbb6328057412974da1e719cef1530b7a62b97d85d828f7e1d49b5f6de3b5b0854d49902ec87827c#npm:1.15.5"],\
|
||||
["@types/debug", null],\
|
||||
["debug", null]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
"@types/debug",\
|
||||
"debug"\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["virtual:ffaff76449f02e83712a7d24e03c564489516739c78ebeffb0fbcdb3893ad9a0e48504f9acfa70fe6f16debe9c8dabde3679d63bf648278ea98a5ff38cf77a9e#npm:1.15.2", {\
|
||||
"packageLocation": "./.yarn/__virtual__/follow-redirects-virtual-c2d5794c26/0/cache/follow-redirects-npm-1.15.2-1ec1dd82be-8be0d39919.zip/node_modules/follow-redirects/",\
|
||||
"packageDependencies": [\
|
||||
@@ -13377,10 +13850,10 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["mysql2", [\
|
||||
["npm:3.3.3", {\
|
||||
"packageLocation": "./.yarn/cache/mysql2-npm-3.3.3-d2fe8cf512-4bf7ace8f1.zip/node_modules/mysql2/",\
|
||||
["npm:3.9.7", {\
|
||||
"packageLocation": "./.yarn/cache/mysql2-npm-3.9.7-8fe89e50ac-7f43b17cc0.zip/node_modules/mysql2/",\
|
||||
"packageDependencies": [\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["mysql2", "npm:3.9.7"],\
|
||||
["denque", "npm:2.1.0"],\
|
||||
["generate-function", "npm:2.3.1"],\
|
||||
["iconv-lite", "npm:0.6.3"],\
|
||||
@@ -16430,7 +16903,7 @@ const RAW_RUNTIME_STATE =
|
||||
["mkdirp", "npm:2.1.6"],\
|
||||
["mongodb", null],\
|
||||
["mssql", null],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["mysql2", "npm:3.9.7"],\
|
||||
["oracledb", null],\
|
||||
["pg", null],\
|
||||
["pg-native", null],\
|
||||
@@ -16522,7 +16995,7 @@ const RAW_RUNTIME_STATE =
|
||||
["mkdirp", "npm:2.1.6"],\
|
||||
["mongodb", null],\
|
||||
["mssql", null],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["mysql2", "npm:3.9.7"],\
|
||||
["oracledb", null],\
|
||||
["pg", null],\
|
||||
["pg-native", null],\
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
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
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -54,7 +54,6 @@ services:
|
||||
ports:
|
||||
- 3306
|
||||
restart: unless-stopped
|
||||
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
|
||||
volumes:
|
||||
- ./data/mysql:/var/lib/mysql
|
||||
- ./data/import:/docker-entrypoint-initdb.d
|
||||
|
||||
@@ -39,7 +39,6 @@ services:
|
||||
expose:
|
||||
- 3306
|
||||
restart: unless-stopped
|
||||
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
|
||||
volumes:
|
||||
- ./data/mysql:/var/lib/mysql
|
||||
- ./data/import:/docker-entrypoint-initdb.d
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@
|
||||
"publish": "lerna publish from-git --yes --no-verify-access --loglevel verbose",
|
||||
"postversion": "./scripts/push-tags-one-by-one.sh",
|
||||
"e2e": "yarn build && PORT=3123 yarn workspace @standardnotes/home-server start",
|
||||
"start": "yarn workspace @standardnotes/home-server run build && yarn workspace @standardnotes/home-server start"
|
||||
"start": "yarn build && yarn workspace @standardnotes/home-server start"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.0.2",
|
||||
@@ -39,7 +39,7 @@
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"packageManager": "yarn@4.0.2",
|
||||
"packageManager": "yarn@4.1.0",
|
||||
"dependenciesMeta": {
|
||||
"grpc-tools@1.12.4": {
|
||||
"unplugged": true
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.34.17](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.34.16...@standardnotes/analytics@2.34.17) (2024-06-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.34.16](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.34.15...@standardnotes/analytics@2.34.16) (2024-01-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.34.15](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.34.14...@standardnotes/analytics@2.34.15) (2024-01-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.34.14](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.34.13...@standardnotes/analytics@2.34.14) (2024-01-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -10,6 +10,12 @@ RUN corepack enable
|
||||
|
||||
COPY ./ /workspace
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
RUN yarn install --immutable
|
||||
|
||||
RUN yarn build
|
||||
|
||||
WORKDIR /workspace/packages/analytics
|
||||
|
||||
ENTRYPOINT [ "/workspace/packages/analytics/docker/entrypoint.sh" ]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.34.14",
|
||||
"version": "2.34.17",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -24,7 +24,7 @@
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --ext .ts --fix",
|
||||
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
|
||||
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=2",
|
||||
"worker": "yarn node dist/bin/worker.js",
|
||||
"report": "yarn node dist/bin/report.js",
|
||||
"setup:env": "cp .env.sample .env",
|
||||
@@ -57,7 +57,7 @@
|
||||
"inversify": "^6.0.1",
|
||||
"ioredis": "^5.2.4",
|
||||
"mixpanel": "^0.17.0",
|
||||
"mysql2": "^3.0.1",
|
||||
"mysql2": "^3.9.7",
|
||||
"reflect-metadata": "^0.2.1",
|
||||
"typeorm": "^0.3.17",
|
||||
"winston": "^3.8.1"
|
||||
|
||||
@@ -5,6 +5,8 @@ import { AnalyticsActivity } from '../Analytics/AnalyticsActivity'
|
||||
import { StatisticMeasureName } from '../Statistics/StatisticMeasureName'
|
||||
import { Period } from '../Time/Period'
|
||||
|
||||
import { safeHtml } from '@standardnotes/common'
|
||||
|
||||
const countActiveUsers = (measureName: string, data: any): { yesterday: number; last30Days: number } => {
|
||||
const totalActiveUsersLast30DaysIncludingToday = data.statisticsOverTime.find(
|
||||
(a: { name: string; period: number }) => a.name === measureName && a.period === 27,
|
||||
@@ -567,7 +569,7 @@ export const html = (data: any, timer: TimerInterface) => {
|
||||
const totalActivePlusUsers = countActiveUsers(StatisticMeasureName.NAMES.ActivePlusUsers, data)
|
||||
const totalActiveProUsers = countActiveUsers(StatisticMeasureName.NAMES.ActiveProUsers, data)
|
||||
|
||||
return ` <div>
|
||||
return safeHtml` <div>
|
||||
<p>Hello,</p>
|
||||
<p>
|
||||
<strong>Here are some statistics from yesterday:</strong>
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.92.1](https://github.com/standardnotes/server/compare/@standardnotes/api-gateway@1.91.0...@standardnotes/api-gateway@1.92.1) (2024-06-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api-gateway:** bump version ([102d4b1](https://github.com/standardnotes/server/commit/102d4b1e8ab000fc97d01c621654b6fc65e37d32))
|
||||
|
||||
## [1.90.1](https://github.com/standardnotes/server/compare/@standardnotes/api-gateway@1.90.0...@standardnotes/api-gateway@1.90.1) (2024-01-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
# [1.90.0](https://github.com/standardnotes/server/compare/@standardnotes/api-gateway@1.89.20...@standardnotes/api-gateway@1.90.0) (2024-01-18)
|
||||
|
||||
### Features
|
||||
|
||||
* add content sizes fixing upon grpc resource exhausted error ([#1029](https://github.com/standardnotes/server/issues/1029)) ([634e8bd](https://github.com/standardnotes/server/commit/634e8bd2d0f055abbda1150587ab9a444281e600))
|
||||
|
||||
## [1.89.20](https://github.com/standardnotes/server/compare/@standardnotes/api-gateway@1.89.19...@standardnotes/api-gateway@1.89.20) (2024-01-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api-gateway:** add codetag metadata to error logs ([136cf25](https://github.com/standardnotes/server/commit/136cf252a134efe7d99f79d8622c43dbebbb5ac8))
|
||||
|
||||
## [1.89.19](https://github.com/standardnotes/server/compare/@standardnotes/api-gateway@1.89.18...@standardnotes/api-gateway@1.89.19) (2024-01-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add dedicated http code response upon a request with too large payload ([#1019](https://github.com/standardnotes/server/issues/1019)) ([6062f85](https://github.com/standardnotes/server/commit/6062f850000477983315d2d9b7c913956f755ebb))
|
||||
|
||||
## [1.89.18](https://github.com/standardnotes/server/compare/@standardnotes/api-gateway@1.89.17...@standardnotes/api-gateway@1.89.18) (2024-01-08)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.89.17](https://github.com/standardnotes/server/compare/@standardnotes/api-gateway@1.89.16...@standardnotes/api-gateway@1.89.17) (2024-01-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -10,6 +10,12 @@ RUN corepack enable
|
||||
|
||||
COPY ./ /workspace
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
RUN yarn install --immutable
|
||||
|
||||
RUN yarn build
|
||||
|
||||
WORKDIR /workspace/packages/api-gateway
|
||||
|
||||
ENTRYPOINT [ "/workspace/packages/api-gateway/docker/entrypoint.sh" ]
|
||||
|
||||
@@ -27,6 +27,7 @@ import '../src/Controller/v2/RevisionsControllerV2'
|
||||
|
||||
import helmet from 'helmet'
|
||||
import * as cors from 'cors'
|
||||
import * as cookieParser from 'cookie-parser'
|
||||
import { text, json, Request, Response, NextFunction } from 'express'
|
||||
import * as winston from 'winston'
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
@@ -36,15 +37,35 @@ import { InversifyExpressServer } from 'inversify-express-utils'
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import { TYPES } from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { ResponseLocals } from '../src/Controller/ResponseLocals'
|
||||
|
||||
const container = new ContainerConfigLoader()
|
||||
void container.load().then((container) => {
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
const requestPayloadLimit = env.get('HTTP_REQUEST_PAYLOAD_LIMIT_MEGABYTES', true)
|
||||
? `${+env.get('HTTP_REQUEST_PAYLOAD_LIMIT_MEGABYTES', true)}mb`
|
||||
: '50mb'
|
||||
|
||||
const logger: winston.Logger = container.get(TYPES.ApiGateway_Logger)
|
||||
|
||||
const server = new InversifyExpressServer(container)
|
||||
|
||||
server.setConfig((app) => {
|
||||
app.use((request: Request, _response: Response, next: NextFunction) => {
|
||||
if (request.hostname.includes('standardnotes.org')) {
|
||||
logger.warn('Request is using deprecated domain', {
|
||||
origin: request.headers.origin,
|
||||
method: request.method,
|
||||
url: request.url,
|
||||
snjs: request.headers['x-snjs-version'],
|
||||
application: request.headers['x-application-version'],
|
||||
})
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
app.use((_request: Request, response: Response, next: NextFunction) => {
|
||||
response.setHeader('X-API-Gateway-Version', container.get(TYPES.ApiGateway_VERSION))
|
||||
next()
|
||||
@@ -72,13 +93,57 @@ void container.load().then((container) => {
|
||||
}),
|
||||
)
|
||||
|
||||
app.use(json({ limit: '50mb' }))
|
||||
app.use(cookieParser())
|
||||
|
||||
app.use(json({ limit: requestPayloadLimit }))
|
||||
app.use(
|
||||
text({
|
||||
type: ['text/plain', 'application/x-www-form-urlencoded', 'application/x-www-form-urlencoded; charset=utf-8'],
|
||||
}),
|
||||
)
|
||||
app.use(cors())
|
||||
const corsAllowedOrigins = container.get<string[]>(TYPES.ApiGateway_CORS_ALLOWED_ORIGINS)
|
||||
app.use(
|
||||
cors({
|
||||
credentials: true,
|
||||
exposedHeaders: ['x-captcha-required'],
|
||||
origin: (requestOrigin: string | undefined, callback: (err: Error | null, origin?: string[]) => void) => {
|
||||
const originStrictModeEnabled = env.get('CORS_ORIGIN_STRICT_MODE_ENABLED', true)
|
||||
? env.get('CORS_ORIGIN_STRICT_MODE_ENABLED', true) === 'true'
|
||||
: false
|
||||
|
||||
if (!originStrictModeEnabled) {
|
||||
callback(null, [requestOrigin as string])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const requstOriginIsNotFilled = !requestOrigin || requestOrigin === 'null'
|
||||
const requestOriginatesFromTheDesktopApp = requestOrigin?.startsWith('file://')
|
||||
const requestOriginatesFromClipperForFirefox = requestOrigin?.startsWith('moz-extension://')
|
||||
const requestOriginatesFromSelfHostedAppOnHttpPort = requestOrigin === 'http://localhost'
|
||||
const requestOriginatesFromSelfHostedAppOnCustomPort = requestOrigin?.match(/http:\/\/localhost:\d+/) !== null
|
||||
const requestOriginatesFromSelfHostedApp =
|
||||
requestOriginatesFromSelfHostedAppOnHttpPort || requestOriginatesFromSelfHostedAppOnCustomPort
|
||||
|
||||
const requestIsWhitelisted =
|
||||
corsAllowedOrigins.length === 0 ||
|
||||
requstOriginIsNotFilled ||
|
||||
requestOriginatesFromTheDesktopApp ||
|
||||
requestOriginatesFromClipperForFirefox ||
|
||||
requestOriginatesFromSelfHostedApp
|
||||
|
||||
if (requestIsWhitelisted) {
|
||||
callback(null, [requestOrigin as string])
|
||||
} else {
|
||||
if (corsAllowedOrigins.includes(requestOrigin)) {
|
||||
callback(null, [requestOrigin])
|
||||
} else {
|
||||
callback(new Error('Not allowed by CORS', { cause: 'origin not allowed' }))
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
)
|
||||
app.use(
|
||||
robots({
|
||||
UserAgent: '*',
|
||||
@@ -87,16 +152,18 @@ void container.load().then((container) => {
|
||||
)
|
||||
})
|
||||
|
||||
const logger: winston.Logger = container.get(TYPES.ApiGateway_Logger)
|
||||
|
||||
server.setErrorConfig((app) => {
|
||||
app.use((error: Record<string, unknown>, request: Request, response: Response, _next: NextFunction) => {
|
||||
const locals = response.locals as ResponseLocals
|
||||
|
||||
logger.error(`${error.stack}`, {
|
||||
origin: request.headers.origin,
|
||||
codeTag: 'server.ts',
|
||||
method: request.method,
|
||||
url: request.url,
|
||||
snjs: request.headers['x-snjs-version'],
|
||||
application: request.headers['x-application-version'],
|
||||
userId: response.locals.user ? response.locals.user.uuid : undefined,
|
||||
userId: locals.user ? locals.user.uuid : undefined,
|
||||
})
|
||||
logger.debug(
|
||||
`[URL: |${request.method}| ${request.url}][SNJS: ${request.headers['x-snjs-version']}][Application: ${
|
||||
@@ -104,6 +171,16 @@ void container.load().then((container) => {
|
||||
}] Request body: ${JSON.stringify(request.body)}`,
|
||||
)
|
||||
|
||||
if ('type' in error && error.type === 'entity.too.large') {
|
||||
response.status(413).send({
|
||||
error: {
|
||||
message: 'The request payload is too large.',
|
||||
},
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
response.status(500).send({
|
||||
error: {
|
||||
message:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.89.17",
|
||||
"version": "1.92.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -31,6 +31,7 @@
|
||||
"start": "yarn node dist/bin/server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sns": "^3.490.0",
|
||||
"@grpc/grpc-js": "^1.9.13",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
@@ -40,6 +41,7 @@
|
||||
"@standardnotes/time": "workspace:*",
|
||||
"agentkeepalive": "^4.5.0",
|
||||
"axios": "^1.6.1",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "2.8.5",
|
||||
"dotenv": "^16.0.1",
|
||||
"express": "^4.18.2",
|
||||
@@ -54,6 +56,7 @@
|
||||
"winston": "^3.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cookie-parser": "^1",
|
||||
"@types/cors": "^2.8.9",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/ioredis": "^5.0.0",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as winston from 'winston'
|
||||
import * as AgentKeepAlive from 'agentkeepalive'
|
||||
import * as grpc from '@grpc/grpc-js'
|
||||
import { SNSClient, SNSClientConfig } from '@aws-sdk/client-sns'
|
||||
import axios, { AxiosInstance } from 'axios'
|
||||
import Redis from 'ioredis'
|
||||
import { Container } from 'inversify'
|
||||
@@ -29,6 +30,10 @@ import { SyncResponseHttpRepresentation } from '../Mapping/Sync/Http/SyncRespons
|
||||
import { SyncRequestGRPCMapper } from '../Mapping/Sync/GRPC/SyncRequestGRPCMapper'
|
||||
import { SyncResponseGRPCMapper } from '../Mapping/Sync/GRPC/SyncResponseGRPCMapper'
|
||||
import { GRPCWebSocketAuthMiddleware } from '../Controller/GRPCWebSocketAuthMiddleware'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { SNSDomainEventPublisher } from '@standardnotes/domain-events-infra'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { DomainEventFactory } from '../Event/DomainEventFactory'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
async load(configuration?: {
|
||||
@@ -51,6 +56,34 @@ export class ContainerConfigLoader {
|
||||
.bind<boolean>(TYPES.ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER_OR_SELF_HOSTING)
|
||||
.toConstantValue(isConfiguredForHomeServerOrSelfHosting)
|
||||
|
||||
if (!isConfiguredForHomeServerOrSelfHosting) {
|
||||
const snsConfig: SNSClientConfig = {
|
||||
region: env.get('SNS_AWS_REGION', true),
|
||||
}
|
||||
if (env.get('SNS_ENDPOINT', true)) {
|
||||
snsConfig.endpoint = env.get('SNS_ENDPOINT', true)
|
||||
}
|
||||
if (env.get('SNS_ACCESS_KEY_ID', true) && env.get('SNS_SECRET_ACCESS_KEY', true)) {
|
||||
snsConfig.credentials = {
|
||||
accessKeyId: env.get('SNS_ACCESS_KEY_ID', true),
|
||||
secretAccessKey: env.get('SNS_SECRET_ACCESS_KEY', true),
|
||||
}
|
||||
}
|
||||
const snsClient = new SNSClient(snsConfig)
|
||||
container.bind<SNSClient>(TYPES.ApiGateway_SNS).toConstantValue(snsClient)
|
||||
|
||||
container.bind(TYPES.ApiGateway_SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN', true))
|
||||
|
||||
container
|
||||
.bind<DomainEventPublisherInterface>(TYPES.ApiGateway_DomainEventPublisher)
|
||||
.toConstantValue(
|
||||
new SNSDomainEventPublisher(
|
||||
container.get(TYPES.ApiGateway_SNS),
|
||||
container.get(TYPES.ApiGateway_SNS_TOPIC_ARN),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const winstonFormatters = [winston.format.splat(), winston.format.json()]
|
||||
|
||||
let logger: winston.Logger
|
||||
@@ -109,6 +142,10 @@ export class ContainerConfigLoader {
|
||||
.bind(TYPES.ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL)
|
||||
.toConstantValue(+env.get('CROSS_SERVICE_TOKEN_CACHE_TTL', true))
|
||||
container.bind(TYPES.ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER).toConstantValue(isConfiguredForHomeServer)
|
||||
container
|
||||
.bind<string[]>(TYPES.ApiGateway_CORS_ALLOWED_ORIGINS)
|
||||
.toConstantValue(env.get('CORS_ALLOWED_ORIGINS', true) ? env.get('CORS_ALLOWED_ORIGINS', true).split(',') : [])
|
||||
container.bind<string>(TYPES.ApiGateway_CAPTCHA_UI_URL).toConstantValue(env.get('CAPTCHA_UI_URL', true))
|
||||
|
||||
// Middleware
|
||||
container
|
||||
@@ -124,14 +161,14 @@ export class ContainerConfigLoader {
|
||||
// Services
|
||||
container.bind<TimerInterface>(TYPES.ApiGateway_Timer).toConstantValue(new Timer())
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
if (isConfiguredForInMemoryCache) {
|
||||
container
|
||||
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
|
||||
.toConstantValue(new InMemoryCrossServiceTokenCache(container.get(TYPES.ApiGateway_Timer)))
|
||||
} else {
|
||||
container
|
||||
.bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
|
||||
.to(RedisCrossServiceTokenCache)
|
||||
.toConstantValue(new RedisCrossServiceTokenCache(container.get(TYPES.ApiGateway_Redis)))
|
||||
}
|
||||
container
|
||||
.bind<EndpointResolverInterface>(TYPES.ApiGateway_EndpointResolver)
|
||||
@@ -192,6 +229,10 @@ export class ContainerConfigLoader {
|
||||
.bind<MapperInterface<SyncResponse, SyncResponseHttpRepresentation>>(TYPES.Mapper_SyncResponseGRPCMapper)
|
||||
.toConstantValue(new SyncResponseGRPCMapper())
|
||||
|
||||
container
|
||||
.bind<DomainEventFactoryInterface>(TYPES.ApiGateway_DomainEventFactory)
|
||||
.toConstantValue(new DomainEventFactory(container.get<TimerInterface>(TYPES.ApiGateway_Timer)))
|
||||
|
||||
container
|
||||
.bind<GRPCSyncingServerServiceProxy>(TYPES.ApiGateway_GRPCSyncingServerServiceProxy)
|
||||
.toConstantValue(
|
||||
@@ -202,6 +243,10 @@ export class ContainerConfigLoader {
|
||||
TYPES.Mapper_SyncResponseGRPCMapper,
|
||||
),
|
||||
container.get<winston.Logger>(TYPES.ApiGateway_Logger),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.ApiGateway_DomainEventFactory),
|
||||
isConfiguredForHomeServerOrSelfHosting
|
||||
? undefined
|
||||
: container.get<DomainEventPublisherInterface>(TYPES.ApiGateway_DomainEventPublisher),
|
||||
),
|
||||
)
|
||||
container
|
||||
|
||||
@@ -2,7 +2,12 @@ export const TYPES = {
|
||||
ApiGateway_Logger: Symbol.for('ApiGateway_Logger'),
|
||||
ApiGateway_Redis: Symbol.for('ApiGateway_Redis'),
|
||||
ApiGateway_HTTPClient: Symbol.for('ApiGateway_HTTPClient'),
|
||||
ApiGateway_SNS: Symbol.for('ApiGateway_SNS'),
|
||||
ApiGateway_DomainEventPublisher: Symbol.for('ApiGateway_DomainEventPublisher'),
|
||||
// env vars
|
||||
ApiGateway_CORS_ALLOWED_ORIGINS: Symbol.for('ApiGateway_CORS_ALLOWED_ORIGINS'),
|
||||
ApiGateway_SNS_TOPIC_ARN: Symbol.for('ApiGateway_SNS_TOPIC_ARN'),
|
||||
ApiGateway_SNS_AWS_REGION: Symbol.for('ApiGateway_SNS_AWS_REGION'),
|
||||
ApiGateway_SYNCING_SERVER_JS_URL: Symbol.for('ApiGateway_SYNCING_SERVER_JS_URL'),
|
||||
ApiGateway_AUTH_SERVER_URL: Symbol.for('ApiGateway_AUTH_SERVER_URL'),
|
||||
ApiGateway_AUTH_SERVER_GRPC_URL: Symbol.for('ApiGateway_AUTH_SERVER_GRPC_URL'),
|
||||
@@ -20,6 +25,7 @@ export const TYPES = {
|
||||
ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER_OR_SELF_HOSTING: Symbol.for(
|
||||
'ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER_OR_SELF_HOSTING',
|
||||
),
|
||||
ApiGateway_CAPTCHA_UI_URL: Symbol.for('ApiGateway_CAPTCHA_UI_URL'),
|
||||
// Middleware
|
||||
ApiGateway_RequiredCrossServiceTokenMiddleware: Symbol.for('ApiGateway_RequiredCrossServiceTokenMiddleware'),
|
||||
ApiGateway_OptionalCrossServiceTokenMiddleware: Symbol.for('ApiGateway_OptionalCrossServiceTokenMiddleware'),
|
||||
@@ -29,6 +35,7 @@ export const TYPES = {
|
||||
Mapper_SyncRequestGRPCMapper: Symbol.for('Mapper_SyncRequestGRPCMapper'),
|
||||
Mapper_SyncResponseGRPCMapper: Symbol.for('Mapper_SyncResponseGRPCMapper'),
|
||||
// Services
|
||||
ApiGateway_DomainEventFactory: Symbol.for('ApiGateway_DomainEventFactory'),
|
||||
ApiGateway_GRPCSyncingServerServiceProxy: Symbol.for('ApiGateway_GRPCSyncingServerServiceProxy'),
|
||||
ApiGateway_ServiceProxy: Symbol.for('ApiGateway_ServiceProxy'),
|
||||
ApiGateway_CrossServiceTokenCache: Symbol.for('ApiGateway_CrossServiceTokenCache'),
|
||||
|
||||
@@ -8,6 +8,8 @@ import { Logger } from 'winston'
|
||||
|
||||
import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface'
|
||||
import { ServiceProxyInterface } from '../Service/Proxy/ServiceProxyInterface'
|
||||
import { ResponseLocals } from './ResponseLocals'
|
||||
import { RoleName } from '@standardnotes/domain-core'
|
||||
|
||||
export abstract class AuthMiddleware extends BaseMiddleware {
|
||||
constructor(
|
||||
@@ -40,9 +42,33 @@ export abstract class AuthMiddleware extends BaseMiddleware {
|
||||
}
|
||||
|
||||
if (crossServiceToken === null) {
|
||||
const cookiesFromHeaders = new Map<string, string[]>()
|
||||
request.headers.cookie?.split(';').forEach((cookie) => {
|
||||
const parts = cookie.split('=')
|
||||
if (parts.length === 2) {
|
||||
const existingCookies = cookiesFromHeaders.get(parts[0].trim())
|
||||
if (existingCookies) {
|
||||
existingCookies.push(parts[1].trim())
|
||||
cookiesFromHeaders.set(parts[0].trim(), existingCookies)
|
||||
} else {
|
||||
cookiesFromHeaders.set(parts[0].trim(), [parts[1].trim()])
|
||||
}
|
||||
}
|
||||
})
|
||||
const authResponse = await this.serviceProxy.validateSession({
|
||||
authorization: authHeaderValue,
|
||||
sharedVaultOwnerContext: sharedVaultOwnerContextHeaderValue,
|
||||
headers: {
|
||||
authorization: authHeaderValue.replace('Bearer ', ''),
|
||||
sharedVaultOwnerContext: sharedVaultOwnerContextHeaderValue,
|
||||
},
|
||||
requestMetadata: {
|
||||
snjs: request.headers['x-snjs-version'] as string,
|
||||
application: request.headers['x-application-version'] as string,
|
||||
url: request.url,
|
||||
method: request.method,
|
||||
userAgent: request.headers['user-agent'],
|
||||
secChUa: request.headers['sec-ch-ua'] as string,
|
||||
},
|
||||
cookies: cookiesFromHeaders,
|
||||
})
|
||||
|
||||
if (!this.handleSessionValidationResponse(authResponse, response, next)) {
|
||||
@@ -55,33 +81,27 @@ export abstract class AuthMiddleware extends BaseMiddleware {
|
||||
crossServiceTokenFetchedFromCache = false
|
||||
}
|
||||
|
||||
response.locals.authToken = crossServiceToken
|
||||
|
||||
const decodedToken = <CrossServiceTokenData>(
|
||||
verify(response.locals.authToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||
)
|
||||
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||
|
||||
if (this.crossServiceTokenCacheTTL && !crossServiceTokenFetchedFromCache) {
|
||||
await this.crossServiceTokenCache.set({
|
||||
key: cacheKey,
|
||||
encodedCrossServiceToken: response.locals.authToken,
|
||||
encodedCrossServiceToken: crossServiceToken,
|
||||
expiresAtInSeconds: this.getCrossServiceTokenCacheExpireTimestamp(decodedToken),
|
||||
userUuid: decodedToken.user.uuid,
|
||||
})
|
||||
}
|
||||
|
||||
response.locals.user = decodedToken.user
|
||||
response.locals.session = decodedToken.session
|
||||
response.locals.roles = decodedToken.roles
|
||||
response.locals.sharedVaultOwnerContext = decodedToken.shared_vault_owner_context
|
||||
response.locals.readOnlyAccess = decodedToken.session?.readonly_access ?? false
|
||||
if (response.locals.readOnlyAccess) {
|
||||
this.logger.debug('User operates on read-only access', {
|
||||
codeTag: 'AuthMiddleware',
|
||||
userId: response.locals.user.uuid,
|
||||
})
|
||||
}
|
||||
response.locals.belongsToSharedVaults = decodedToken.belongs_to_shared_vaults ?? []
|
||||
Object.assign(response.locals, {
|
||||
authToken: crossServiceToken,
|
||||
user: decodedToken.user,
|
||||
session: decodedToken.session,
|
||||
roles: decodedToken.roles,
|
||||
sharedVaultOwnerContext: decodedToken.shared_vault_owner_context,
|
||||
readOnlyAccess: decodedToken.session?.readonly_access ?? false,
|
||||
isFreeUser: decodedToken.roles.length === 1 && decodedToken.roles[0].name === RoleName.NAMES.CoreUser,
|
||||
belongsToSharedVaults: decodedToken.belongs_to_shared_vaults ?? [],
|
||||
} as ResponseLocals)
|
||||
} catch (error) {
|
||||
let detailedErrorMessage = (error as Error).message
|
||||
if (error instanceof AxiosError) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { verify } from 'jsonwebtoken'
|
||||
import { Logger } from 'winston'
|
||||
import { ConnectionValidationResponse, IAuthClient, WebsocketConnectionAuthorizationHeader } from '@standardnotes/grpc'
|
||||
import { RoleName } from '@standardnotes/domain-core'
|
||||
import { ResponseLocals } from './ResponseLocals'
|
||||
|
||||
export class GRPCWebSocketAuthMiddleware extends BaseMiddleware {
|
||||
constructor(
|
||||
@@ -90,15 +91,17 @@ export class GRPCWebSocketAuthMiddleware extends BaseMiddleware {
|
||||
|
||||
const crossServiceToken = authResponse.data.authToken as string
|
||||
|
||||
response.locals.authToken = crossServiceToken
|
||||
|
||||
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||
|
||||
response.locals.user = decodedToken.user
|
||||
response.locals.session = decodedToken.session
|
||||
response.locals.roles = decodedToken.roles
|
||||
response.locals.isFreeUser =
|
||||
decodedToken.roles.length === 1 && decodedToken.roles[0].name === RoleName.NAMES.CoreUser
|
||||
Object.assign(response.locals, {
|
||||
authToken: crossServiceToken,
|
||||
user: decodedToken.user,
|
||||
session: decodedToken.session,
|
||||
roles: decodedToken.roles,
|
||||
isFreeUser: decodedToken.roles.length === 1 && decodedToken.roles[0].name === RoleName.NAMES.CoreUser,
|
||||
readOnlyAccess: decodedToken.session?.readonly_access ?? false,
|
||||
hasContentLimit: decodedToken.hasContentLimit,
|
||||
} as ResponseLocals)
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Could not pass the request to websocket connection validation on underlying service: ${
|
||||
|
||||
@@ -20,8 +20,6 @@ export class LegacyController extends BaseHttpController {
|
||||
['DELETE:/session', 'DELETE:session'],
|
||||
['DELETE:/session/all', 'DELETE:session/all'],
|
||||
['POST:/session/refresh', 'POST:session/refresh'],
|
||||
['POST:/auth/sign_in', 'POST:auth/sign_in'],
|
||||
['GET:/auth/params', 'GET:auth/params'],
|
||||
])
|
||||
|
||||
this.PARAMETRIZED_AUTH_ROUTES = new Map([
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface OfflineResponseLocals {
|
||||
offlineAuthToken: string
|
||||
userEmail: string
|
||||
featuresToken: string
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Role } from '@standardnotes/security'
|
||||
|
||||
export interface ResponseLocals {
|
||||
authToken: string
|
||||
user: {
|
||||
uuid: string
|
||||
email: string
|
||||
}
|
||||
roles: Array<Role>
|
||||
session?: {
|
||||
uuid: string
|
||||
api_version: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
device_info: string
|
||||
readonly_access: boolean
|
||||
access_expiration: string
|
||||
refresh_expiration: string
|
||||
}
|
||||
readOnlyAccess: boolean
|
||||
isFreeUser: boolean
|
||||
belongsToSharedVaults?: Array<{
|
||||
shared_vault_uuid: string
|
||||
permission: string
|
||||
}>
|
||||
sharedVaultOwnerContext?: {
|
||||
upload_bytes_limit: number
|
||||
}
|
||||
hasContentLimit: boolean
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { TokenAuthenticationMethod } from './TokenAuthenticationMethod'
|
||||
|
||||
export interface SubscriptionResponseLocals {
|
||||
tokenAuthenticationMethod: TokenAuthenticationMethod
|
||||
}
|
||||
@@ -7,6 +7,9 @@ import { AxiosError, AxiosInstance, AxiosResponse } from 'axios'
|
||||
import { Logger } from 'winston'
|
||||
import { TYPES } from '../Bootstrap/Types'
|
||||
import { TokenAuthenticationMethod } from './TokenAuthenticationMethod'
|
||||
import { ResponseLocals } from './ResponseLocals'
|
||||
import { OfflineResponseLocals } from './OfflineResponseLocals'
|
||||
import { SubscriptionResponseLocals } from './SubscriptionResponseLocals'
|
||||
|
||||
@injectable()
|
||||
export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||
@@ -34,13 +37,16 @@ export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||
return
|
||||
}
|
||||
|
||||
response.locals.tokenAuthenticationMethod = email
|
||||
? TokenAuthenticationMethod.OfflineSubscriptionToken
|
||||
: TokenAuthenticationMethod.SubscriptionToken
|
||||
const locals = {
|
||||
tokenAuthenticationMethod: email
|
||||
? TokenAuthenticationMethod.OfflineSubscriptionToken
|
||||
: TokenAuthenticationMethod.SubscriptionToken,
|
||||
} as SubscriptionResponseLocals
|
||||
Object.assign(response.locals, locals)
|
||||
|
||||
try {
|
||||
const url =
|
||||
response.locals.tokenAuthenticationMethod == TokenAuthenticationMethod.OfflineSubscriptionToken
|
||||
locals.tokenAuthenticationMethod == TokenAuthenticationMethod.OfflineSubscriptionToken
|
||||
? `${this.authServerUrl}/offline/subscription-tokens/${subscriptionToken}/validate`
|
||||
: `${this.authServerUrl}/subscription-tokens/${subscriptionToken}/validate`
|
||||
|
||||
@@ -65,7 +71,7 @@ export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||
return
|
||||
}
|
||||
|
||||
if (response.locals.tokenAuthenticationMethod == TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
||||
if (locals.tokenAuthenticationMethod == TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
||||
this.handleOfflineAuthTokenValidationResponse(response, authResponse)
|
||||
|
||||
return next()
|
||||
@@ -101,24 +107,26 @@ export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
|
||||
}
|
||||
|
||||
private handleOfflineAuthTokenValidationResponse(response: Response, authResponse: AxiosResponse) {
|
||||
response.locals.offlineAuthToken = authResponse.data.authToken
|
||||
|
||||
const decodedToken = <OfflineUserTokenData>(
|
||||
verify(authResponse.data.authToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||
)
|
||||
|
||||
response.locals.offlineUserEmail = decodedToken.userEmail
|
||||
response.locals.offlineFeaturesToken = decodedToken.featuresToken
|
||||
Object.assign(response.locals, {
|
||||
offlineAuthToken: authResponse.data.authToken,
|
||||
userEmail: decodedToken.userEmail,
|
||||
featuresToken: decodedToken.featuresToken,
|
||||
} as OfflineResponseLocals)
|
||||
}
|
||||
|
||||
private handleAuthTokenValidationResponse(response: Response, authResponse: AxiosResponse) {
|
||||
response.locals.authToken = authResponse.data.authToken
|
||||
|
||||
const decodedToken = <CrossServiceTokenData>(
|
||||
verify(authResponse.data.authToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||
)
|
||||
|
||||
response.locals.user = decodedToken.user
|
||||
response.locals.roles = decodedToken.roles
|
||||
Object.assign(response.locals, {
|
||||
authToken: authResponse.data.authToken,
|
||||
user: decodedToken.user,
|
||||
roles: decodedToken.roles,
|
||||
} as ResponseLocals)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AxiosError, AxiosInstance } from 'axios'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { TYPES } from '../Bootstrap/Types'
|
||||
import { ResponseLocals } from './ResponseLocals'
|
||||
|
||||
@injectable()
|
||||
export class WebSocketAuthMiddleware extends BaseMiddleware {
|
||||
@@ -55,13 +56,14 @@ export class WebSocketAuthMiddleware extends BaseMiddleware {
|
||||
|
||||
const crossServiceToken = authResponse.data.authToken
|
||||
|
||||
response.locals.authToken = crossServiceToken
|
||||
|
||||
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||
|
||||
response.locals.user = decodedToken.user
|
||||
response.locals.session = decodedToken.session
|
||||
response.locals.roles = decodedToken.roles
|
||||
Object.assign(response.locals, {
|
||||
authToken: crossServiceToken,
|
||||
user: decodedToken.user,
|
||||
session: decodedToken.session,
|
||||
roles: decodedToken.roles,
|
||||
} as ResponseLocals)
|
||||
} catch (error) {
|
||||
const errorMessage = (error as AxiosError).isAxiosError
|
||||
? JSON.stringify((error as AxiosError).response?.data)
|
||||
|
||||
@@ -4,12 +4,14 @@ import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-exp
|
||||
import { TYPES } from '../../Bootstrap/Types'
|
||||
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
|
||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||
import { JsonResult } from 'inversify-express-utils/lib/results'
|
||||
|
||||
@controller('/v1')
|
||||
export class ActionsController extends BaseHttpController {
|
||||
constructor(
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private serviceProxy: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_CAPTCHA_UI_URL) private captchaUIUrl: string,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -19,7 +21,7 @@ export class ActionsController extends BaseHttpController {
|
||||
await this.serviceProxy.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/sign_in'),
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/pkce_sign_in'),
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
@@ -29,7 +31,7 @@ export class ActionsController extends BaseHttpController {
|
||||
await this.serviceProxy.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'auth/params'),
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/pkce_params'),
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
@@ -83,4 +85,11 @@ export class ActionsController extends BaseHttpController {
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/meta')
|
||||
async serverMetadata(): Promise<JsonResult> {
|
||||
return this.json({
|
||||
captchaUIUrl: this.captchaUIUrl,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
controller,
|
||||
httpDelete,
|
||||
httpGet,
|
||||
httpPatch,
|
||||
httpPost,
|
||||
httpPut,
|
||||
results,
|
||||
@@ -16,6 +15,8 @@ import { TYPES } from '../../Bootstrap/Types'
|
||||
import { ServiceProxyInterface } from '../../Service/Proxy/ServiceProxyInterface'
|
||||
import { TokenAuthenticationMethod } from '../TokenAuthenticationMethod'
|
||||
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||
import { ResponseLocals } from '../ResponseLocals'
|
||||
import { SubscriptionResponseLocals } from '../SubscriptionResponseLocals'
|
||||
|
||||
@controller('/v1/users')
|
||||
export class UsersController extends BaseHttpController {
|
||||
@@ -37,16 +38,6 @@ export class UsersController extends BaseHttpController {
|
||||
await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-activation-code', request.body)
|
||||
}
|
||||
|
||||
@httpPatch('/:userId', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async updateUser(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('PATCH', 'users/:userId', request.params.userId),
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
|
||||
@httpPut('/:userUuid/password', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async changePassword(request: Request, response: Response): Promise<void> {
|
||||
this.logger.debug(
|
||||
@@ -84,7 +75,7 @@ export class UsersController extends BaseHttpController {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'auth/params'),
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/pkce_params'),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -140,6 +131,20 @@ export class UsersController extends BaseHttpController {
|
||||
)
|
||||
}
|
||||
|
||||
@httpPut('/:userUuid/subscription-settings', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async putSubscriptionSetting(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||
'PUT',
|
||||
'users/:userUuid/subscription-settings',
|
||||
request.params.userUuid,
|
||||
),
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/:userUuid/settings/:settingName', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async getSetting(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
@@ -214,7 +219,9 @@ export class UsersController extends BaseHttpController {
|
||||
|
||||
@httpGet('/subscription', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
|
||||
async getSubscriptionBySubscriptionToken(request: Request, response: Response): Promise<void> {
|
||||
if (response.locals.tokenAuthenticationMethod === TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
||||
const locals = response.locals as SubscriptionResponseLocals & ResponseLocals
|
||||
|
||||
if (locals.tokenAuthenticationMethod === TokenAuthenticationMethod.OfflineSubscriptionToken) {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
@@ -227,11 +234,7 @@ export class UsersController extends BaseHttpController {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||
'GET',
|
||||
'users/:userUuid/subscription',
|
||||
response.locals.user.uuid,
|
||||
),
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'users/:userUuid/subscription', locals.user.uuid),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
|
||||
import { ContentSizesFixRequestedEvent, DomainEventService } from '@standardnotes/domain-events'
|
||||
|
||||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
constructor(private timer: TimerInterface) {}
|
||||
|
||||
createContentSizesFixRequestedEvent(dto: { userUuid: string }): ContentSizesFixRequestedEvent {
|
||||
return {
|
||||
type: 'CONTENT_SIZES_FIX_REQUESTED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: dto.userUuid,
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.Auth,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ContentSizesFixRequestedEvent } from '@standardnotes/domain-events'
|
||||
|
||||
export interface DomainEventFactoryInterface {
|
||||
createContentSizesFixRequestedEvent(dto: { userUuid: string }): ContentSizesFixRequestedEvent
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
import { inject, injectable } from 'inversify'
|
||||
import * as IORedis from 'ioredis'
|
||||
import { TYPES } from '../../Bootstrap/Types'
|
||||
|
||||
import { CrossServiceTokenCacheInterface } from '../../Service/Cache/CrossServiceTokenCacheInterface'
|
||||
|
||||
@injectable()
|
||||
export class RedisCrossServiceTokenCache implements CrossServiceTokenCacheInterface {
|
||||
private readonly PREFIX = 'cst'
|
||||
private readonly USER_CST_PREFIX = 'user-cst'
|
||||
|
||||
constructor(@inject(TYPES.ApiGateway_Redis) private redisClient: IORedis.Redis) {}
|
||||
constructor(private redisClient: IORedis.Redis) {}
|
||||
|
||||
async set(dto: {
|
||||
key: string
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Request, Response } from 'express'
|
||||
import { ServiceContainerInterface, ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
||||
import { ResponseLocals } from '../../Controller/ResponseLocals'
|
||||
|
||||
export class DirectCallServiceProxy implements ServiceProxyInterface {
|
||||
constructor(
|
||||
@@ -9,23 +10,44 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
|
||||
private filesServerUrl: string,
|
||||
) {}
|
||||
|
||||
async validateSession(
|
||||
async validateSession(dto: {
|
||||
headers: {
|
||||
authorization: string
|
||||
sharedVaultOwnerContext?: string
|
||||
},
|
||||
_retryAttempt?: number,
|
||||
): Promise<{ status: number; data: unknown; headers: { contentType: string } }> {
|
||||
}
|
||||
cookies?: Map<string, string[]>
|
||||
snjs?: string
|
||||
application?: string
|
||||
retryAttempt?: number
|
||||
}): Promise<{
|
||||
status: number
|
||||
data: unknown
|
||||
headers: {
|
||||
contentType: string
|
||||
}
|
||||
}> {
|
||||
const authService = this.serviceContainer.get(ServiceIdentifier.create(ServiceIdentifier.NAMES.Auth).getValue())
|
||||
if (!authService) {
|
||||
throw new Error('Auth service not found')
|
||||
}
|
||||
|
||||
let stringOfCookies = ''
|
||||
for (const cookieName of dto.cookies?.keys() ?? []) {
|
||||
for (const cookieValue of dto.cookies?.get(cookieName) as string[]) {
|
||||
stringOfCookies += `${cookieName}=${cookieValue}; `
|
||||
}
|
||||
}
|
||||
|
||||
const serviceResponse = (await authService.handleRequest(
|
||||
{
|
||||
body: {
|
||||
authTokenFromHeaders: dto.headers.authorization,
|
||||
sharedVaultOwnerContext: dto.headers.sharedVaultOwnerContext,
|
||||
},
|
||||
headers: {
|
||||
authorization: headers.authorization,
|
||||
'x-shared-vault-owner-context': headers.sharedVaultOwnerContext,
|
||||
'x-snjs-version': dto.snjs,
|
||||
'x-application-version': dto.application,
|
||||
cookie: stringOfCookies.trim(),
|
||||
},
|
||||
} as never,
|
||||
{} as never,
|
||||
@@ -134,11 +156,13 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
|
||||
response: Response,
|
||||
serviceResponse: { statusCode: number; json: Record<string, unknown> },
|
||||
): void {
|
||||
const locals = response.locals as ResponseLocals
|
||||
|
||||
void response.status(serviceResponse.statusCode).send({
|
||||
meta: {
|
||||
auth: {
|
||||
userUuid: response.locals.user?.uuid,
|
||||
roles: response.locals.roles,
|
||||
userUuid: locals.user?.uuid,
|
||||
roles: locals.roles,
|
||||
},
|
||||
server: {
|
||||
filesServerUrl: this.filesServerUrl,
|
||||
|
||||
@@ -8,6 +8,8 @@ import { TYPES } from '../../Bootstrap/Types'
|
||||
import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCacheInterface'
|
||||
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { ResponseLocals } from '../../Controller/ResponseLocals'
|
||||
import { OfflineResponseLocals } from '../../Controller/OfflineResponseLocals'
|
||||
|
||||
@injectable()
|
||||
export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
@@ -26,20 +28,51 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
@inject(TYPES.ApiGateway_Timer) private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async validateSession(
|
||||
async validateSession(dto: {
|
||||
headers: {
|
||||
authorization: string
|
||||
sharedVaultOwnerContext?: string
|
||||
},
|
||||
retryAttempt?: number,
|
||||
): Promise<{ status: number; data: unknown; headers: { contentType: string } }> {
|
||||
}
|
||||
requestMetadata: {
|
||||
url: string
|
||||
method: string
|
||||
snjs?: string
|
||||
application?: string
|
||||
userAgent?: string
|
||||
secChUa?: string
|
||||
}
|
||||
cookies?: Map<string, string[]>
|
||||
retryAttempt?: number
|
||||
}): Promise<{
|
||||
status: number
|
||||
data: unknown
|
||||
headers: {
|
||||
contentType: string
|
||||
}
|
||||
}> {
|
||||
try {
|
||||
let stringOfCookies = ''
|
||||
for (const cookieName of dto.cookies?.keys() ?? []) {
|
||||
for (const cookieValue of dto.cookies?.get(cookieName) as string[]) {
|
||||
stringOfCookies += `${cookieName}=${cookieValue}; `
|
||||
}
|
||||
}
|
||||
|
||||
const authResponse = await this.httpClient.request({
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: headers.authorization,
|
||||
Accept: 'application/json',
|
||||
'x-shared-vault-owner-context': headers.sharedVaultOwnerContext,
|
||||
Cookie: stringOfCookies.trim(),
|
||||
'x-snjs-version': dto.requestMetadata.snjs,
|
||||
'x-application-version': dto.requestMetadata.application,
|
||||
'x-origin-user-agent': dto.requestMetadata.userAgent,
|
||||
'x-origin-sec-ch-ua': dto.requestMetadata.secChUa,
|
||||
'x-origin-url': dto.requestMetadata.url,
|
||||
'x-origin-method': dto.requestMetadata.method,
|
||||
},
|
||||
data: {
|
||||
authTokenFromHeaders: dto.headers.authorization,
|
||||
sharedVaultOwnerContext: dto.headers.sharedVaultOwnerContext,
|
||||
},
|
||||
validateStatus: (status: number) => {
|
||||
return status >= 200 && status < 500
|
||||
@@ -56,13 +89,18 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
}
|
||||
} catch (error) {
|
||||
const requestDidNotMakeIt = this.requestTimedOutOrDidNotReachDestination(error as Record<string, unknown>)
|
||||
const tooManyRetryAttempts = retryAttempt && retryAttempt > 2
|
||||
const tooManyRetryAttempts = dto.retryAttempt && dto.retryAttempt > 2
|
||||
if (!tooManyRetryAttempts && requestDidNotMakeIt) {
|
||||
await this.timer.sleep(50)
|
||||
|
||||
const nextRetryAttempt = retryAttempt ? retryAttempt + 1 : 1
|
||||
const nextRetryAttempt = dto.retryAttempt ? dto.retryAttempt + 1 : 1
|
||||
|
||||
return this.validateSession(headers, nextRetryAttempt)
|
||||
return this.validateSession({
|
||||
headers: dto.headers,
|
||||
cookies: dto.cookies,
|
||||
requestMetadata: dto.requestMetadata,
|
||||
retryAttempt: nextRetryAttempt,
|
||||
})
|
||||
}
|
||||
|
||||
throw error
|
||||
@@ -176,21 +214,32 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<AxiosResponse | undefined> {
|
||||
const locals = response.locals as ResponseLocals | OfflineResponseLocals
|
||||
|
||||
try {
|
||||
const headers: Record<string, string> = {}
|
||||
for (const headerName of Object.keys(request.headers)) {
|
||||
headers[headerName] = request.headers[headerName] as string
|
||||
}
|
||||
|
||||
headers['x-origin-url'] = request.url
|
||||
headers['x-origin-method'] = request.method
|
||||
headers['x-snjs-version'] = request.headers['x-snjs-version'] as string
|
||||
headers['x-application-version'] = request.headers['x-application-version'] as string
|
||||
headers['x-origin-user-agent'] = request.headers['user-agent'] as string
|
||||
headers['x-origin-sec-ch-ua'] = request.headers['sec-ch-ua'] as string
|
||||
|
||||
delete headers.host
|
||||
delete headers['content-length']
|
||||
|
||||
if (response.locals.authToken) {
|
||||
headers['X-Auth-Token'] = response.locals.authToken
|
||||
headers.cookie = request.headers.cookie as string
|
||||
|
||||
if ('authToken' in locals && locals.authToken) {
|
||||
headers['X-Auth-Token'] = locals.authToken
|
||||
}
|
||||
|
||||
if (response.locals.offlineAuthToken) {
|
||||
headers['X-Auth-Offline-Token'] = response.locals.offlineAuthToken
|
||||
if ('offlineAuthToken' in locals && locals.offlineAuthToken) {
|
||||
headers['X-Auth-Offline-Token'] = locals.offlineAuthToken
|
||||
}
|
||||
|
||||
const serviceResponse = await this.httpClient.request({
|
||||
@@ -222,7 +271,7 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
this.logger.error(
|
||||
`Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${detailedErrorMessage}`,
|
||||
{
|
||||
userId: response.locals.user ? response.locals.user.uuid : undefined,
|
||||
userId: (locals as ResponseLocals).user ? (locals as ResponseLocals).user.uuid : undefined,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -257,6 +306,7 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void> {
|
||||
const locals = response.locals as ResponseLocals
|
||||
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
|
||||
|
||||
if (!serviceResponse) {
|
||||
@@ -274,8 +324,8 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
response.status(serviceResponse.status).send({
|
||||
meta: {
|
||||
auth: {
|
||||
userUuid: response.locals.user?.uuid,
|
||||
roles: response.locals.roles,
|
||||
userUuid: locals.user?.uuid,
|
||||
roles: locals.roles,
|
||||
},
|
||||
server: {
|
||||
filesServerUrl: this.filesServerUrl,
|
||||
@@ -335,13 +385,11 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
|
||||
private applyResponseHeaders(serviceResponse: AxiosResponse, response: Response): void {
|
||||
const returnedHeadersFromUnderlyingService = [
|
||||
'access-control-allow-methods',
|
||||
'access-control-allow-origin',
|
||||
'access-control-expose-headers',
|
||||
'authorization',
|
||||
'content-type',
|
||||
'x-ssjs-version',
|
||||
'x-auth-version',
|
||||
'authorization',
|
||||
'set-cookie',
|
||||
'access-control-expose-headers',
|
||||
'x-captcha-required',
|
||||
]
|
||||
|
||||
returnedHeadersFromUnderlyingService.map((headerName) => {
|
||||
|
||||
@@ -49,13 +49,22 @@ export interface ServiceProxyInterface {
|
||||
endpointOrMethodIdentifier: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void>
|
||||
validateSession(
|
||||
validateSession(dto: {
|
||||
headers: {
|
||||
authorization: string
|
||||
sharedVaultOwnerContext?: string
|
||||
},
|
||||
retryAttempt?: number,
|
||||
): Promise<{
|
||||
}
|
||||
requestMetadata: {
|
||||
url: string
|
||||
method: string
|
||||
snjs?: string
|
||||
application?: string
|
||||
userAgent?: string
|
||||
secChUa?: string
|
||||
}
|
||||
cookies?: Map<string, string[]>
|
||||
retryAttempt?: number
|
||||
}): Promise<{
|
||||
status: number
|
||||
data: unknown
|
||||
headers: {
|
||||
|
||||
@@ -7,8 +7,6 @@ export class EndpointResolver implements EndpointResolverInterface {
|
||||
// Auth Middleware
|
||||
['[POST]:sessions/validate', 'auth.sessions.validate'],
|
||||
// Actions Controller
|
||||
['[POST]:auth/sign_in', 'auth.signIn'],
|
||||
['[GET]:auth/params', 'auth.params'],
|
||||
['[POST]:auth/sign_out', 'auth.signOut'],
|
||||
['[POST]:auth/recovery/codes', 'auth.generateRecoveryCodes'],
|
||||
['[POST]:auth/recovery/login', 'auth.signInWithRecoveryCodes'],
|
||||
@@ -40,7 +38,6 @@ export class EndpointResolver implements EndpointResolverInterface {
|
||||
// Tokens Controller
|
||||
['[POST]:subscription-tokens', 'auth.subscription-tokens.create'],
|
||||
// Users Controller
|
||||
['[PATCH]:users/:userId', 'auth.users.update'],
|
||||
['[PUT]:users/:userUuid/attributes/credentials', 'auth.users.updateCredentials'],
|
||||
['[DELETE]:users/:userUuid', 'auth.users.delete'],
|
||||
['[POST]:listed', 'auth.users.createListedAccount'],
|
||||
@@ -49,6 +46,7 @@ export class EndpointResolver implements EndpointResolverInterface {
|
||||
['[PUT]:users/:userUuid/settings', 'auth.users.updateSetting'],
|
||||
['[GET]:users/:userUuid/settings/:settingName', 'auth.users.getSetting'],
|
||||
['[DELETE]:users/:userUuid/settings/:settingName', 'auth.users.deleteSetting'],
|
||||
['[PUT]:users/:userUuid/subscription-settings', 'auth.users.updateSubscriptionSetting'],
|
||||
['[GET]:users/:userUuid/subscription-settings/:subscriptionSettingName', 'auth.users.getSubscriptionSetting'],
|
||||
['[GET]:users/:userUuid/features', 'auth.users.getFeatures'],
|
||||
['[GET]:users/:userUuid/subscription', 'auth.users.getSubscription'],
|
||||
|
||||
@@ -2,13 +2,15 @@ import { AxiosInstance, AxiosError, AxiosResponse, Method } from 'axios'
|
||||
import { Request, Response } from 'express'
|
||||
import { Logger } from 'winston'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { IAuthClient, AuthorizationHeader, SessionValidationResponse } from '@standardnotes/grpc'
|
||||
import { Cookie, IAuthClient, RequestValidationOptions, SessionValidationResponse } from '@standardnotes/grpc'
|
||||
import * as grpc from '@grpc/grpc-js'
|
||||
|
||||
import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCacheInterface'
|
||||
import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
|
||||
import { GRPCSyncingServerServiceProxy } from './GRPCSyncingServerServiceProxy'
|
||||
import { Status } from '@grpc/grpc-js/build/src/constants'
|
||||
import { ResponseLocals } from '../../Controller/ResponseLocals'
|
||||
import { OfflineResponseLocals } from '../../Controller/OfflineResponseLocals'
|
||||
|
||||
export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
constructor(
|
||||
@@ -28,23 +30,56 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
private gRPCSyncingServerServiceProxy: GRPCSyncingServerServiceProxy,
|
||||
) {}
|
||||
|
||||
async validateSession(
|
||||
async validateSession(dto: {
|
||||
headers: {
|
||||
authorization: string
|
||||
sharedVaultOwnerContext?: string
|
||||
},
|
||||
retryAttempt?: number,
|
||||
): Promise<{ status: number; data: unknown; headers: { contentType: string } }> {
|
||||
}
|
||||
requestMetadata: {
|
||||
url: string
|
||||
method: string
|
||||
snjs?: string
|
||||
application?: string
|
||||
userAgent?: string
|
||||
secChUa?: string
|
||||
}
|
||||
cookies?: Map<string, string[]>
|
||||
retryAttempt?: number
|
||||
}): Promise<{
|
||||
status: number
|
||||
data: unknown
|
||||
headers: {
|
||||
contentType: string
|
||||
}
|
||||
}> {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
try {
|
||||
const request = new AuthorizationHeader()
|
||||
request.setBearerToken(headers.authorization)
|
||||
const request = new RequestValidationOptions()
|
||||
request.setBearerToken(dto.headers.authorization)
|
||||
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-shared-vault-owner-context', headers.sharedVaultOwnerContext ?? '')
|
||||
for (const cookieName of dto.cookies?.keys() ?? []) {
|
||||
for (const cookieValue of dto.cookies?.get(cookieName) as string[]) {
|
||||
const cookie = new Cookie()
|
||||
cookie.setName(cookieName)
|
||||
cookie.setValue(cookieValue)
|
||||
|
||||
request.addCookie(cookie)
|
||||
}
|
||||
}
|
||||
if (dto.headers.sharedVaultOwnerContext) {
|
||||
request.setSharedVaultOwnerContext(dto.headers.sharedVaultOwnerContext)
|
||||
}
|
||||
|
||||
this.logger.debug('[GRPCServiceProxy] Validating session via gRPC')
|
||||
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-snjs-version', dto.requestMetadata.snjs as string)
|
||||
metadata.set('x-application-version', dto.requestMetadata.application as string)
|
||||
metadata.set('x-origin-user-agent', dto.requestMetadata.userAgent as string)
|
||||
metadata.set('x-origin-sec-ch-ua', dto.requestMetadata.secChUa as string)
|
||||
metadata.set('x-origin-url', dto.requestMetadata.url)
|
||||
metadata.set('x-origin-method', dto.requestMetadata.method)
|
||||
|
||||
this.authClient.validate(
|
||||
request,
|
||||
metadata,
|
||||
@@ -88,8 +123,8 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
try {
|
||||
const result = await promise
|
||||
|
||||
if (retryAttempt) {
|
||||
this.logger.debug(`Request to Auth Server succeeded after ${retryAttempt} retries`)
|
||||
if (dto.retryAttempt) {
|
||||
this.logger.info(`Request to Auth Server succeeded after ${dto.retryAttempt} retries`)
|
||||
}
|
||||
|
||||
return result as { status: number; data: unknown; headers: { contentType: string } }
|
||||
@@ -97,15 +132,20 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
const requestDidNotMakeIt =
|
||||
'code' in (error as Record<string, unknown>) && (error as Record<string, unknown>).code === Status.UNAVAILABLE
|
||||
|
||||
const tooManyRetryAttempts = retryAttempt && retryAttempt > 2
|
||||
const tooManyRetryAttempts = dto.retryAttempt && dto.retryAttempt > 2
|
||||
if (!tooManyRetryAttempts && requestDidNotMakeIt) {
|
||||
await this.timer.sleep(50)
|
||||
|
||||
const nextRetryAttempt = retryAttempt ? retryAttempt + 1 : 1
|
||||
const nextRetryAttempt = dto.retryAttempt ? dto.retryAttempt + 1 : 1
|
||||
|
||||
this.logger.debug(`Retrying request to Auth Server for the ${nextRetryAttempt} time`)
|
||||
this.logger.warn(`Retrying request to Auth Server for the ${nextRetryAttempt} time`)
|
||||
|
||||
return this.validateSession(headers, nextRetryAttempt)
|
||||
return this.validateSession({
|
||||
headers: dto.headers,
|
||||
cookies: dto.cookies,
|
||||
requestMetadata: dto.requestMetadata,
|
||||
retryAttempt: nextRetryAttempt,
|
||||
})
|
||||
}
|
||||
|
||||
throw error
|
||||
@@ -135,13 +175,15 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
response: Response,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void> {
|
||||
const locals = response.locals as ResponseLocals
|
||||
|
||||
const result = await this.gRPCSyncingServerServiceProxy.sync(request, response, payload)
|
||||
|
||||
response.status(result.status).send({
|
||||
meta: {
|
||||
auth: {
|
||||
userUuid: response.locals.user?.uuid,
|
||||
roles: response.locals.roles,
|
||||
userUuid: locals.user?.uuid,
|
||||
roles: locals.roles,
|
||||
},
|
||||
server: {
|
||||
filesServerUrl: this.filesServerUrl,
|
||||
@@ -250,6 +292,8 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
payload?: Record<string, unknown> | string,
|
||||
retryAttempt?: number,
|
||||
): Promise<AxiosResponse | undefined> {
|
||||
const locals = response.locals as ResponseLocals | OfflineResponseLocals
|
||||
|
||||
try {
|
||||
const headers: Record<string, string> = {}
|
||||
for (const headerName of Object.keys(request.headers)) {
|
||||
@@ -259,12 +303,14 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
delete headers.host
|
||||
delete headers['content-length']
|
||||
|
||||
if (response.locals.authToken) {
|
||||
headers['X-Auth-Token'] = response.locals.authToken
|
||||
headers.cookie = request.headers.cookie as string
|
||||
|
||||
if ('authToken' in locals && locals.authToken) {
|
||||
headers['X-Auth-Token'] = locals.authToken
|
||||
}
|
||||
|
||||
if (response.locals.offlineAuthToken) {
|
||||
headers['X-Auth-Offline-Token'] = response.locals.offlineAuthToken
|
||||
if ('offlineAuthToken' in locals && locals.offlineAuthToken) {
|
||||
headers['X-Auth-Offline-Token'] = locals.offlineAuthToken
|
||||
}
|
||||
|
||||
const serviceResponse = await this.httpClient.request({
|
||||
@@ -314,7 +360,7 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
? `Request to ${serverUrl}/${endpoint} timed out after ${retryAttempt} retries`
|
||||
: `Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${detailedErrorMessage}`,
|
||||
{
|
||||
userId: response.locals.user ? response.locals.user.uuid : undefined,
|
||||
userId: (locals as ResponseLocals).user ? (locals as ResponseLocals).user.uuid : undefined,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -349,6 +395,8 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
endpoint: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<void> {
|
||||
const locals = response.locals as ResponseLocals
|
||||
|
||||
const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
|
||||
|
||||
if (!serviceResponse) {
|
||||
@@ -366,8 +414,8 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
response.status(serviceResponse.status).send({
|
||||
meta: {
|
||||
auth: {
|
||||
userUuid: response.locals.user?.uuid,
|
||||
roles: response.locals.roles,
|
||||
userUuid: locals.user?.uuid,
|
||||
roles: locals.roles,
|
||||
},
|
||||
server: {
|
||||
filesServerUrl: this.filesServerUrl,
|
||||
@@ -427,13 +475,11 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
|
||||
|
||||
private applyResponseHeaders(serviceResponse: AxiosResponse, response: Response): void {
|
||||
const returnedHeadersFromUnderlyingService = [
|
||||
'access-control-allow-methods',
|
||||
'access-control-allow-origin',
|
||||
'access-control-expose-headers',
|
||||
'authorization',
|
||||
'content-type',
|
||||
'x-ssjs-version',
|
||||
'x-auth-version',
|
||||
'authorization',
|
||||
'set-cookie',
|
||||
'access-control-expose-headers',
|
||||
'x-captcha-required',
|
||||
]
|
||||
|
||||
returnedHeadersFromUnderlyingService.map((headerName) => {
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { ISyncingClient, SyncRequest, SyncResponse } from '@standardnotes/grpc'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
import { Metadata } from '@grpc/grpc-js'
|
||||
|
||||
import { SyncResponseHttpRepresentation } from '../../Mapping/Sync/Http/SyncResponseHttpRepresentation'
|
||||
import { Status } from '@grpc/grpc-js/build/src/constants'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { SyncResponseHttpRepresentation } from '../../Mapping/Sync/Http/SyncResponseHttpRepresentation'
|
||||
import { ResponseLocals } from '../../Controller/ResponseLocals'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
|
||||
export class GRPCSyncingServerServiceProxy {
|
||||
constructor(
|
||||
private syncingClient: ISyncingClient,
|
||||
private syncRequestGRPCMapper: MapperInterface<Record<string, unknown>, SyncRequest>,
|
||||
private syncResponseGRPCMapper: MapperInterface<SyncResponse, SyncResponseHttpRepresentation>,
|
||||
private logger: Logger,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private domainEventPublisher?: DomainEventPublisherInterface,
|
||||
) {}
|
||||
|
||||
async sync(
|
||||
@@ -20,24 +25,27 @@ export class GRPCSyncingServerServiceProxy {
|
||||
response: Response,
|
||||
payload?: Record<string, unknown> | string,
|
||||
): Promise<{ status: number; data: unknown }> {
|
||||
const locals = response.locals as ResponseLocals
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const syncRequest = this.syncRequestGRPCMapper.toProjection(payload as Record<string, unknown>)
|
||||
|
||||
const metadata = new Metadata()
|
||||
metadata.set('x-user-uuid', response.locals.user.uuid)
|
||||
metadata.set('x-user-uuid', locals.user.uuid)
|
||||
metadata.set('x-snjs-version', request.headers['x-snjs-version'] as string)
|
||||
metadata.set('x-read-only-access', response.locals.readOnlyAccess ? 'true' : 'false')
|
||||
if (response.locals.readOnlyAccess) {
|
||||
metadata.set('x-read-only-access', locals.readOnlyAccess ? 'true' : 'false')
|
||||
if (locals.readOnlyAccess) {
|
||||
this.logger.debug('Syncing with read-only access', {
|
||||
codeTag: 'GRPCSyncingServerServiceProxy',
|
||||
userId: response.locals.user.uuid,
|
||||
userId: locals.user.uuid,
|
||||
})
|
||||
}
|
||||
if (response.locals.session) {
|
||||
metadata.set('x-session-uuid', response.locals.session.uuid)
|
||||
if (locals.session) {
|
||||
metadata.set('x-session-uuid', locals.session.uuid)
|
||||
}
|
||||
metadata.set('x-is-free-user', response.locals.isFreeUser ? 'true' : 'false')
|
||||
metadata.set('x-is-free-user', locals.isFreeUser ? 'true' : 'false')
|
||||
metadata.set('x-has-content-limit', locals.hasContentLimit ? 'true' : 'false')
|
||||
|
||||
this.syncingClient.syncItems(syncRequest, metadata, (error, syncResponse) => {
|
||||
if (error) {
|
||||
@@ -52,10 +60,16 @@ export class GRPCSyncingServerServiceProxy {
|
||||
if (error.code === Status.INTERNAL) {
|
||||
this.logger.error(`Internal gRPC error: ${error.message}. Payload: ${JSON.stringify(payload)}`, {
|
||||
codeTag: 'GRPCSyncingServerServiceProxy',
|
||||
userId: response.locals.user.uuid,
|
||||
userId: locals.user.uuid,
|
||||
})
|
||||
}
|
||||
|
||||
if (error.code === Status.RESOURCE_EXHAUSTED && this.domainEventPublisher !== undefined) {
|
||||
void this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createContentSizesFixRequestedEvent({ userUuid: locals.user.uuid }),
|
||||
)
|
||||
}
|
||||
|
||||
return reject(error)
|
||||
}
|
||||
|
||||
@@ -68,7 +82,7 @@ export class GRPCSyncingServerServiceProxy {
|
||||
) {
|
||||
this.logger.error(`Internal gRPC error: ${JSON.stringify(error)}. Payload: ${JSON.stringify(payload)}`, {
|
||||
codeTag: 'GRPCSyncingServerServiceProxy.catch',
|
||||
userId: response.locals.user.uuid,
|
||||
userId: locals.user.uuid,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,11 @@ CACHE_TYPE=redis
|
||||
|
||||
DISABLE_USER_REGISTRATION=false
|
||||
|
||||
COOKIE_DOMAIN=
|
||||
COOKIE_SAME_SITE=
|
||||
COOKIE_SECURE=
|
||||
COOKIE_PARTITIONED=
|
||||
|
||||
ACCESS_TOKEN_AGE=5184000
|
||||
REFRESH_TOKEN_AGE=31556926
|
||||
|
||||
@@ -49,6 +54,10 @@ VALET_TOKEN_TTL=
|
||||
|
||||
WEB_SOCKET_CONNECTION_TOKEN_SECRET=
|
||||
|
||||
# Human verfication
|
||||
CAPTCHA_SERVER_URL=
|
||||
CAPTCHA_UI_URL=
|
||||
|
||||
# (Optional) U2F Setup
|
||||
U2F_RELYING_PARTY_ID=
|
||||
U2F_RELYING_PARTY_NAME=
|
||||
|
||||
@@ -3,6 +3,57 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.178.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.178.3...@standardnotes/auth-server@1.178.5) (2024-06-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bump versions on packages ([8575d20](https://github.com/standardnotes/server/commit/8575d20f7b79f5220da7cced0041ae12b72e1e49))
|
||||
|
||||
# [1.178.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.177.20...@standardnotes/auth-server@1.178.0) (2024-01-19)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add script for fixing subscriptions with missing id state ([#1030](https://github.com/standardnotes/server/issues/1030)) ([86b0508](https://github.com/standardnotes/server/commit/86b050865f8090ed33d5ce05528ff0e1e23657ef))
|
||||
|
||||
## [1.177.20](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.177.19...@standardnotes/auth-server@1.177.20) (2024-01-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.177.19](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.177.18...@standardnotes/auth-server@1.177.19) (2024-01-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** add server daily email backup permission for all versions of core user role ([#1028](https://github.com/standardnotes/server/issues/1028)) ([460fdf9](https://github.com/standardnotes/server/commit/460fdf9eafe2db629637ba481f2b135ed21560b9))
|
||||
|
||||
## [1.177.18](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.177.17...@standardnotes/auth-server@1.177.18) (2024-01-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** add more logs to syncing subscription ([c7217a9](https://github.com/standardnotes/server/commit/c7217a92ba89d8b5f4963a832aa7561dd146ca0d))
|
||||
* **auth:** add renewal for shared offline subscriptions ([045358d](https://github.com/standardnotes/server/commit/045358ddbf300996a23bba8d6945b1d7b5f6e862))
|
||||
|
||||
## [1.177.17](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.177.16...@standardnotes/auth-server@1.177.17) (2024-01-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** add debug logs for subscription sync requested event ([351e18f](https://github.com/standardnotes/server/commit/351e18f6389c2dbaa2107e6549be9928c2e8834f))
|
||||
|
||||
## [1.177.16](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.177.15...@standardnotes/auth-server@1.177.16) (2024-01-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** update shared subscriptions upon subscription sync ([#1022](https://github.com/standardnotes/server/issues/1022)) ([d7a1c66](https://github.com/standardnotes/server/commit/d7a1c667dd62dacc1ef15f2a4f408dc07045fcad))
|
||||
|
||||
## [1.177.15](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.177.14...@standardnotes/auth-server@1.177.15) (2024-01-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** check for user agent persisting on session during a session refresh ([#1016](https://github.com/standardnotes/server/issues/1016)) ([0b46eff](https://github.com/standardnotes/server/commit/0b46eff16ea0c32cac91ead04474303500359f4f))
|
||||
|
||||
## [1.177.14](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.177.13...@standardnotes/auth-server@1.177.14) (2024-01-08)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.177.13](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.177.12...@standardnotes/auth-server@1.177.13) (2024-01-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
@@ -10,6 +10,12 @@ RUN corepack enable
|
||||
|
||||
COPY ./ /workspace
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
RUN yarn install --immutable
|
||||
|
||||
RUN yarn build
|
||||
|
||||
WORKDIR /workspace/packages/auth
|
||||
|
||||
ENTRYPOINT [ "/workspace/packages/auth/docker/entrypoint.sh" ]
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { UserSubscriptionRepositoryInterface } from '../src/Domain/Subscription/UserSubscriptionRepositoryInterface'
|
||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
const fixSubscriptions = async (
|
||||
userRepository: UserRepositoryInterface,
|
||||
userSubscriptionRepository: UserSubscriptionRepositoryInterface,
|
||||
domainEventFactory: DomainEventFactoryInterface,
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
): Promise<void> => {
|
||||
const subscriptions = await userSubscriptionRepository.findBySubscriptionId(0)
|
||||
|
||||
for (const subscription of subscriptions) {
|
||||
const userUuidOrError = Uuid.create(subscription.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
continue
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const user = await userRepository.findOneByUuid(userUuid)
|
||||
if (!user) {
|
||||
continue
|
||||
}
|
||||
|
||||
await domainEventPublisher.publish(
|
||||
domainEventFactory.createSubscriptionStateRequestedEvent({
|
||||
userEmail: user.email,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const container = new ContainerConfigLoader('worker')
|
||||
void container.load().then((container) => {
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
const logger: Logger = container.get(TYPES.Auth_Logger)
|
||||
|
||||
logger.info('Starting to fix subscriptions with missing subscriptionId ...')
|
||||
|
||||
const userRepository = container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository)
|
||||
const userSubscriptionRepository = container.get<UserSubscriptionRepositoryInterface>(
|
||||
TYPES.Auth_UserSubscriptionRepository,
|
||||
)
|
||||
const domainEventFactory = container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory)
|
||||
const domainEventPublisher = container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher)
|
||||
|
||||
Promise.resolve(
|
||||
fixSubscriptions(userRepository, userSubscriptionRepository, domainEventFactory, domainEventPublisher),
|
||||
)
|
||||
.then(() => {
|
||||
logger.info('Finished fixing subscriptions with missing subscriptionId.')
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Failed to fix subscriptions with missing subscriptionId.', {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
})
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
@@ -20,6 +20,7 @@ import '../src/Infra/InversifyExpressUtils/AnnotatedHealthCheckController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedFeaturesController'
|
||||
|
||||
import * as cors from 'cors'
|
||||
import * as cookieParser from 'cookie-parser'
|
||||
import * as grpc from '@grpc/grpc-js'
|
||||
import { urlencoded, json, Request, Response, NextFunction } from 'express'
|
||||
import * as winston from 'winston'
|
||||
@@ -35,6 +36,7 @@ import { AuthService } from '@standardnotes/grpc'
|
||||
import { AuthenticateRequest } from '../src/Domain/UseCase/AuthenticateRequest'
|
||||
import { CreateCrossServiceToken } from '../src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken'
|
||||
import { TokenDecoderInterface, WebSocketConnectionTokenData } from '@standardnotes/security'
|
||||
import { ResponseLocals } from '../src/Infra/InversifyExpressUtils/ResponseLocals'
|
||||
|
||||
const container = new ContainerConfigLoader()
|
||||
void container.load().then((container) => {
|
||||
@@ -52,6 +54,7 @@ void container.load().then((container) => {
|
||||
})
|
||||
app.use(json())
|
||||
app.use(urlencoded({ extended: true }))
|
||||
app.use(cookieParser())
|
||||
app.use(cors())
|
||||
})
|
||||
|
||||
@@ -59,12 +62,13 @@ void container.load().then((container) => {
|
||||
|
||||
server.setErrorConfig((app) => {
|
||||
app.use((error: Record<string, unknown>, request: Request, response: Response, _next: NextFunction) => {
|
||||
const locals = response.locals as ResponseLocals
|
||||
logger.error(`${error.stack}`, {
|
||||
method: request.method,
|
||||
url: request.url,
|
||||
snjs: request.headers['x-snjs-version'],
|
||||
application: request.headers['x-application-version'],
|
||||
userId: response.locals.user ? response.locals.user.uuid : undefined,
|
||||
userId: locals.user ? locals.user.uuid : undefined,
|
||||
})
|
||||
|
||||
response.status(500).send({
|
||||
|
||||
@@ -9,28 +9,23 @@ import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||
import { SettingRepositoryInterface } from '../src/Domain/Setting/SettingRepositoryInterface'
|
||||
import { MuteFailedBackupsEmailsOption } from '@standardnotes/settings'
|
||||
import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface'
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
|
||||
import { GetUserKeyParams } from '../src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams'
|
||||
import { Email, SettingName } from '@standardnotes/domain-core'
|
||||
import { Email } from '@standardnotes/domain-core'
|
||||
|
||||
const inputArgs = process.argv.slice(2)
|
||||
const backupEmail = inputArgs[0]
|
||||
|
||||
const requestBackups = async (
|
||||
userRepository: UserRepositoryInterface,
|
||||
settingRepository: SettingRepositoryInterface,
|
||||
roleService: RoleServiceInterface,
|
||||
domainEventFactory: DomainEventFactoryInterface,
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
getUserKeyParamsUseCase: GetUserKeyParams,
|
||||
): Promise<void> => {
|
||||
const permissionName = PermissionName.DailyEmailBackup
|
||||
const muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails
|
||||
const muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted
|
||||
|
||||
const emailOrError = Email.create(backupEmail)
|
||||
if (emailOrError.isFailed()) {
|
||||
@@ -48,24 +43,13 @@ const requestBackups = async (
|
||||
throw new Error(`User ${backupEmail} is not permitted for email backups`)
|
||||
}
|
||||
|
||||
let userHasEmailsMuted = false
|
||||
const emailsMutedSetting = await settingRepository.findOneByNameAndUserUuid(muteEmailsSettingName, user.uuid)
|
||||
if (emailsMutedSetting !== null && emailsMutedSetting.props.value !== null) {
|
||||
userHasEmailsMuted = emailsMutedSetting.props.value === muteEmailsSettingValue
|
||||
}
|
||||
|
||||
const keyParamsResponse = await getUserKeyParamsUseCase.execute({
|
||||
userUuid: user.uuid,
|
||||
authenticated: false,
|
||||
})
|
||||
|
||||
await domainEventPublisher.publish(
|
||||
domainEventFactory.createEmailBackupRequestedEvent(
|
||||
user.uuid,
|
||||
emailsMutedSetting?.id.toString() as string,
|
||||
userHasEmailsMuted,
|
||||
keyParamsResponse.keyParams,
|
||||
),
|
||||
domainEventFactory.createEmailBackupRequestedEvent(user.uuid, keyParamsResponse.keyParams),
|
||||
)
|
||||
|
||||
return
|
||||
@@ -82,7 +66,6 @@ void container.load().then((container) => {
|
||||
|
||||
logger.info(`Starting email backup requesting for ${backupEmail} ...`)
|
||||
|
||||
const settingRepository: SettingRepositoryInterface = container.get(TYPES.Auth_SettingRepository)
|
||||
const userRepository: UserRepositoryInterface = container.get(TYPES.Auth_UserRepository)
|
||||
const roleService: RoleServiceInterface = container.get(TYPES.Auth_RoleService)
|
||||
const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.Auth_DomainEventFactory)
|
||||
@@ -90,14 +73,7 @@ void container.load().then((container) => {
|
||||
const getUserKeyParamsUseCase: GetUserKeyParams = container.get(TYPES.Auth_GetUserKeyParams)
|
||||
|
||||
Promise.resolve(
|
||||
requestBackups(
|
||||
userRepository,
|
||||
settingRepository,
|
||||
roleService,
|
||||
domainEventFactory,
|
||||
domainEventPublisher,
|
||||
getUserKeyParamsUseCase,
|
||||
),
|
||||
requestBackups(userRepository, roleService, domainEventFactory, domainEventPublisher, getUserKeyParamsUseCase),
|
||||
)
|
||||
.then(() => {
|
||||
logger.info(`Email backup requesting complete for ${backupEmail}`)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
|
||||
const pnp = require(path.normalize(path.resolve(__dirname, '../../..', '.pnp.cjs'))).setup()
|
||||
|
||||
const index = require(path.normalize(path.resolve(__dirname, '../dist/bin/fix_subscriptions.js')))
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true })
|
||||
|
||||
exports.default = index
|
||||
@@ -42,6 +42,10 @@ case "$COMMAND" in
|
||||
exec node docker/entrypoint-fix-roles.js
|
||||
;;
|
||||
|
||||
'fix-subscriptions' )
|
||||
exec node docker/entrypoint-fix-subscriptions.js
|
||||
;;
|
||||
|
||||
'delete-accounts' )
|
||||
FILE_NAME=$1 && shift 1
|
||||
MODE=$1 && shift 1
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class EnableEmailBackupsForAll1705493201352 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// Core User v1 Permissions
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `role_permissions` (role_uuid, permission_uuid) VALUES \
|
||||
("bde42e26-628c-44e6-9d76-21b08954b0bf", "eb0575a2-6e26-49e3-9501-f2e75d7dbda3") \
|
||||
',
|
||||
)
|
||||
// Core User v2 Permissions
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `role_permissions` (role_uuid, permission_uuid) VALUES \
|
||||
("23bf88ca-bee1-4a4c-adf0-b7a48749eea7", "eb0575a2-6e26-49e3-9501-f2e75d7dbda3") \
|
||||
',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class UserRolesContentLimit1707759514236 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `permissions` (uuid, name) VALUES ("f8b4ced2-6a59-49f8-9ade-416a5f5ffc61", "server:content-limit")',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `roles` (uuid, name, version) VALUES ("ab2e15c9-9252-43f3-829c-6f0af3315791", "CORE_USER", 4)',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `role_permissions` (permission_uuid, role_uuid) VALUES \
|
||||
("b04a7670-934e-4ab1-b8a3-0f27ff159511", "ab2e15c9-9252-43f3-829c-6f0af3315791"), \
|
||||
("eb0575a2-6e26-49e3-9501-f2e75d7dbda3", "ab2e15c9-9252-43f3-829c-6f0af3315791"), \
|
||||
("f8b4ced2-6a59-49f8-9ade-416a5f5ffc61", "ab2e15c9-9252-43f3-829c-6f0af3315791") \
|
||||
',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DELETE FROM `role_permissions` WHERE role_uuid="ab2e15c9-9252-43f3-829c-6f0af3315791"')
|
||||
await queryRunner.query('DELETE FROM `permissions` WHERE uuid="f8b4ced2-6a59-49f8-9ade-416a5f5ffc61"')
|
||||
await queryRunner.query('DELETE FROM `roles` WHERE uuid="ab2e15c9-9252-43f3-829c-6f0af3315791"')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSessionVersion1707813542369 implements MigrationInterface {
|
||||
name = 'AddSessionVersion1707813542369'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `sessions` ADD `version` smallint NULL DEFAULT 1')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `sessions` DROP COLUMN `version`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSessionPrivateIdentifier1709133001993 implements MigrationInterface {
|
||||
name = 'AddSessionPrivateIdentifier1709133001993'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `sessions` ADD `private_identifier` varchar(36) NULL COMMENT 'Used to identify a session without exposing the UUID in client-side cookies.'",
|
||||
)
|
||||
await queryRunner.query('CREATE INDEX `index_sessions_on_private_identifier` ON `sessions` (`private_identifier`)')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `index_sessions_on_private_identifier` ON `sessions`')
|
||||
await queryRunner.query('ALTER TABLE `sessions` DROP COLUMN `private_identifier`')
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddRevokedSessionPrivateIdentifier1709206805226 implements MigrationInterface {
|
||||
name = 'AddRevokedSessionPrivateIdentifier1709206805226'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
"ALTER TABLE `revoked_sessions` ADD `private_identifier` varchar(36) NULL COMMENT 'Used to identify a session without exposing the UUID in client-side cookies.'",
|
||||
)
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX `index_revoked_sessions_on_private_identifier` ON `revoked_sessions` (`private_identifier`)',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `index_revoked_sessions_on_private_identifier` ON `revoked_sessions`')
|
||||
await queryRunner.query('ALTER TABLE `revoked_sessions` DROP COLUMN `private_identifier`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddApplicationAndSnjsToSessions1710236132439 implements MigrationInterface {
|
||||
name = 'AddApplicationAndSnjsToSessions1710236132439'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `sessions` ADD `application` varchar(255) NULL')
|
||||
await queryRunner.query('ALTER TABLE `sessions` ADD `snjs` varchar(255) NULL')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `sessions` DROP COLUMN `snjs`')
|
||||
await queryRunner.query('ALTER TABLE `sessions` DROP COLUMN `application`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class EnableEmailBackupsForAll1705493490376 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// Core User v1 Permissions
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `role_permissions` (role_uuid, permission_uuid) VALUES \
|
||||
("bde42e26-628c-44e6-9d76-21b08954b0bf", "eb0575a2-6e26-49e3-9501-f2e75d7dbda3") \
|
||||
',
|
||||
)
|
||||
// Core User v2 Permissions
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `role_permissions` (role_uuid, permission_uuid) VALUES \
|
||||
("23bf88ca-bee1-4a4c-adf0-b7a48749eea7", "eb0575a2-6e26-49e3-9501-f2e75d7dbda3") \
|
||||
',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class UserRolesContentLimit1707759514236 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `permissions` (uuid, name) VALUES ("f8b4ced2-6a59-49f8-9ade-416a5f5ffc61", "server:content-limit")',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `roles` (uuid, name, version) VALUES ("ab2e15c9-9252-43f3-829c-6f0af3315791", "CORE_USER", 4)',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO `role_permissions` (permission_uuid, role_uuid) VALUES \
|
||||
("b04a7670-934e-4ab1-b8a3-0f27ff159511", "ab2e15c9-9252-43f3-829c-6f0af3315791"), \
|
||||
("eb0575a2-6e26-49e3-9501-f2e75d7dbda3", "ab2e15c9-9252-43f3-829c-6f0af3315791"), \
|
||||
("f8b4ced2-6a59-49f8-9ade-416a5f5ffc61", "ab2e15c9-9252-43f3-829c-6f0af3315791") \
|
||||
',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DELETE FROM `role_permissions` WHERE role_uuid="ab2e15c9-9252-43f3-829c-6f0af3315791"')
|
||||
await queryRunner.query('DELETE FROM `permissions` WHERE uuid="f8b4ced2-6a59-49f8-9ade-416a5f5ffc61"')
|
||||
await queryRunner.query('DELETE FROM `roles` WHERE uuid="ab2e15c9-9252-43f3-829c-6f0af3315791"')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSessionVersion1707813542369 implements MigrationInterface {
|
||||
name = 'AddSessionVersion1707813542369'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `sessions` ADD `version` smallint NULL DEFAULT 1')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `sessions` DROP COLUMN `version`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSessionPrivateIdentifier1709133169237 implements MigrationInterface {
|
||||
name = 'AddSessionPrivateIdentifier1709133169237'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_sessions_on_updated_at"')
|
||||
await queryRunner.query('DROP INDEX "index_sessions_on_user_uuid"')
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "temporary_sessions" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(255), "hashed_access_token" varchar(255) NOT NULL, "hashed_refresh_token" varchar(255) NOT NULL, "access_expiration" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "refresh_expiration" datetime NOT NULL, "api_version" varchar(255), "user_agent" text, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, "readonly_access" tinyint NOT NULL DEFAULT (0), "version" smallint DEFAULT (1), "private_identifier" varchar(36))',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO "temporary_sessions"("uuid", "user_uuid", "hashed_access_token", "hashed_refresh_token", "access_expiration", "refresh_expiration", "api_version", "user_agent", "created_at", "updated_at", "readonly_access", "version") SELECT "uuid", "user_uuid", "hashed_access_token", "hashed_refresh_token", "access_expiration", "refresh_expiration", "api_version", "user_agent", "created_at", "updated_at", "readonly_access", "version" FROM "sessions"',
|
||||
)
|
||||
await queryRunner.query('DROP TABLE "sessions"')
|
||||
await queryRunner.query('ALTER TABLE "temporary_sessions" RENAME TO "sessions"')
|
||||
await queryRunner.query('CREATE INDEX "index_sessions_on_updated_at" ON "sessions" ("updated_at") ')
|
||||
await queryRunner.query('CREATE INDEX "index_sessions_on_user_uuid" ON "sessions" ("user_uuid") ')
|
||||
await queryRunner.query('CREATE INDEX "index_sessions_on_private_identifier" ON "sessions" ("private_identifier") ')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_sessions_on_private_identifier"')
|
||||
await queryRunner.query('DROP INDEX "index_sessions_on_user_uuid"')
|
||||
await queryRunner.query('DROP INDEX "index_sessions_on_updated_at"')
|
||||
await queryRunner.query('ALTER TABLE "sessions" RENAME TO "temporary_sessions"')
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "sessions" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(255), "hashed_access_token" varchar(255) NOT NULL, "hashed_refresh_token" varchar(255) NOT NULL, "access_expiration" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "refresh_expiration" datetime NOT NULL, "api_version" varchar(255), "user_agent" text, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL, "readonly_access" tinyint NOT NULL DEFAULT (0), "version" smallint DEFAULT (1))',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO "sessions"("uuid", "user_uuid", "hashed_access_token", "hashed_refresh_token", "access_expiration", "refresh_expiration", "api_version", "user_agent", "created_at", "updated_at", "readonly_access", "version") SELECT "uuid", "user_uuid", "hashed_access_token", "hashed_refresh_token", "access_expiration", "refresh_expiration", "api_version", "user_agent", "created_at", "updated_at", "readonly_access", "version" FROM "temporary_sessions"',
|
||||
)
|
||||
await queryRunner.query('DROP TABLE "temporary_sessions"')
|
||||
await queryRunner.query('CREATE INDEX "index_sessions_on_user_uuid" ON "sessions" ("user_uuid") ')
|
||||
await queryRunner.query('CREATE INDEX "index_sessions_on_updated_at" ON "sessions" ("updated_at") ')
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddRevokedSessionPrivateIdentifier1709208455658 implements MigrationInterface {
|
||||
name = 'AddRevokedSessionPrivateIdentifier1709208455658'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_revoked_sessions_on_user_uuid"')
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "temporary_revoked_sessions" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(36) NOT NULL, "received" tinyint NOT NULL DEFAULT (0), "created_at" datetime NOT NULL, "received_at" datetime, "user_agent" text, "api_version" varchar(255), "private_identifier" varchar(36), CONSTRAINT "FK_edaf18faca67e682be39b5ecae5" FOREIGN KEY ("user_uuid") REFERENCES "users" ("uuid") ON DELETE CASCADE ON UPDATE NO ACTION)',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO "temporary_revoked_sessions"("uuid", "user_uuid", "received", "created_at", "received_at", "user_agent", "api_version") SELECT "uuid", "user_uuid", "received", "created_at", "received_at", "user_agent", "api_version" FROM "revoked_sessions"',
|
||||
)
|
||||
await queryRunner.query('DROP TABLE "revoked_sessions"')
|
||||
await queryRunner.query('ALTER TABLE "temporary_revoked_sessions" RENAME TO "revoked_sessions"')
|
||||
await queryRunner.query('CREATE INDEX "index_revoked_sessions_on_user_uuid" ON "revoked_sessions" ("user_uuid") ')
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "index_revoked_sessions_on_private_identifier" ON "revoked_sessions" ("private_identifier") ',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_revoked_sessions_on_user_uuid"')
|
||||
await queryRunner.query('ALTER TABLE "revoked_sessions" RENAME TO "temporary_revoked_sessions"')
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "revoked_sessions" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(36) NOT NULL, "received" tinyint NOT NULL DEFAULT (0), "created_at" datetime NOT NULL, "received_at" datetime, "user_agent" text, "api_version" varchar(255), CONSTRAINT "FK_edaf18faca67e682be39b5ecae5" FOREIGN KEY ("user_uuid") REFERENCES "users" ("uuid") ON DELETE CASCADE ON UPDATE NO ACTION)',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO "revoked_sessions"("uuid", "user_uuid", "received", "created_at", "received_at", "user_agent", "api_version") SELECT "uuid", "user_uuid", "received", "created_at", "received_at", "user_agent", "api_version" FROM "temporary_revoked_sessions"',
|
||||
)
|
||||
await queryRunner.query('DROP TABLE "temporary_revoked_sessions"')
|
||||
await queryRunner.query('CREATE INDEX "index_revoked_sessions_on_user_uuid" ON "revoked_sessions" ("user_uuid") ')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddApplicationAndSnjsToSessions1710236132439 implements MigrationInterface {
|
||||
name = 'AddApplicationAndSnjsToSessions1710236132439'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `sessions` ADD `application` varchar(255) NULL')
|
||||
await queryRunner.query('ALTER TABLE `sessions` ADD `snjs` varchar(255) NULL')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `sessions` DROP COLUMN `snjs`')
|
||||
await queryRunner.query('ALTER TABLE `sessions` DROP COLUMN `application`')
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.177.13",
|
||||
"version": "1.178.5",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
"description": "Auth Server",
|
||||
"description": "Auth Server for SN",
|
||||
"main": "dist/src/index.js",
|
||||
"typings": "dist/src/index.d.ts",
|
||||
"author": "Karol Sójko <karol@standardnotes.com>",
|
||||
@@ -24,8 +24,7 @@
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --fix --ext .ts",
|
||||
"pretest": "yarn lint && yarn build",
|
||||
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
|
||||
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=2",
|
||||
"start": "yarn node dist/bin/server.js",
|
||||
"worker": "yarn node dist/bin/worker.js",
|
||||
"cleanup": "yarn node dist/bin/cleanup.js",
|
||||
@@ -60,7 +59,10 @@
|
||||
"@standardnotes/sncrypto-common": "^1.13.4",
|
||||
"@standardnotes/sncrypto-node": "workspace:*",
|
||||
"@standardnotes/time": "workspace:*",
|
||||
"agentkeepalive": "^4.5.0",
|
||||
"axios": "^1.6.7",
|
||||
"bcryptjs": "2.4.3",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "2.8.5",
|
||||
"dayjs": "^1.11.6",
|
||||
"dotenv": "^16.0.1",
|
||||
@@ -68,7 +70,7 @@
|
||||
"inversify": "^6.0.1",
|
||||
"inversify-express-utils": "^6.4.3",
|
||||
"ioredis": "^5.2.4",
|
||||
"mysql2": "^3.0.1",
|
||||
"mysql2": "^3.9.7",
|
||||
"otplib": "12.0.1",
|
||||
"prettyjson": "^1.2.5",
|
||||
"reflect-metadata": "^0.2.1",
|
||||
@@ -80,6 +82,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/cookie-parser": "^1",
|
||||
"@types/cors": "^2.8.9",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/ioredis": "^5.0.0",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import * as winston from 'winston'
|
||||
import * as AgentKeepAlive from 'agentkeepalive'
|
||||
import Redis from 'ioredis'
|
||||
import { SNSClient, SNSClientConfig } from '@aws-sdk/client-sns'
|
||||
import axios, { AxiosInstance } from 'axios'
|
||||
import { SQSClient, SQSClientConfig } from '@aws-sdk/client-sqs'
|
||||
import { S3Client } from '@aws-sdk/client-s3'
|
||||
import { Container } from 'inversify'
|
||||
@@ -36,13 +38,11 @@ import { AuthResponseFactoryResolver } from '../Domain/Auth/AuthResponseFactoryR
|
||||
import { ClearLoginAttempts } from '../Domain/UseCase/ClearLoginAttempts'
|
||||
import { IncreaseLoginAttempts } from '../Domain/UseCase/IncreaseLoginAttempts'
|
||||
import { GetUserKeyParams } from '../Domain/UseCase/GetUserKeyParams/GetUserKeyParams'
|
||||
import { UpdateUser } from '../Domain/UseCase/UpdateUser'
|
||||
import { RedisEphemeralSessionRepository } from '../Infra/Redis/RedisEphemeralSessionRepository'
|
||||
import { GetActiveSessionsForUser } from '../Domain/UseCase/GetActiveSessionsForUser'
|
||||
import { DeleteOtherSessionsForUser } from '../Domain/UseCase/DeleteOtherSessionsForUser'
|
||||
import { DeleteSessionForUser } from '../Domain/UseCase/DeleteSessionForUser'
|
||||
import { Register } from '../Domain/UseCase/Register'
|
||||
import { LockRepository } from '../Infra/Redis/LockRepository'
|
||||
import { TypeORMRevokedSessionRepository } from '../Infra/TypeORM/TypeORMRevokedSessionRepository'
|
||||
import { AuthenticationMethodResolver } from '../Domain/Auth/AuthenticationMethodResolver'
|
||||
import { RevokedSession } from '../Domain/Session/RevokedSession'
|
||||
@@ -284,6 +284,21 @@ import { AccountDeletionVerificationPassedEventHandler } from '../Domain/Handler
|
||||
import { RenewSharedSubscriptions } from '../Domain/UseCase/RenewSharedSubscriptions/RenewSharedSubscriptions'
|
||||
import { FixStorageQuotaForUser } from '../Domain/UseCase/FixStorageQuotaForUser/FixStorageQuotaForUser'
|
||||
import { FileQuotaRecalculatedEventHandler } from '../Domain/Handler/FileQuotaRecalculatedEventHandler'
|
||||
import { SessionServiceInterface } from '../Domain/Session/SessionServiceInterface'
|
||||
import { SubscriptionStateFetchedEventHandler } from '../Domain/Handler/SubscriptionStateFetchedEventHandler'
|
||||
import { CaptchaServerInterface } from '../Domain/HumanVerification/CaptchaServerInterface'
|
||||
import { VerifyHumanInteraction } from '../Domain/UseCase/VerifyHumanInteraction/VerifyHumanInteraction'
|
||||
import { HttpCaptchaServer } from '../Infra/Http/HumanVerification/HttpCaptchaServer'
|
||||
import { CookieFactoryInterface } from '../Domain/Auth/Cookies/CookieFactoryInterface'
|
||||
import { CookieFactory } from '../Domain/Auth/Cookies/CookieFactory'
|
||||
import { RedisLockRepository } from '../Infra/Redis/RedisLockRepository'
|
||||
import { DeleteSessionByToken } from '../Domain/UseCase/DeleteSessionByToken/DeleteSessionByToken'
|
||||
import { GetSessionFromToken } from '../Domain/UseCase/GetSessionFromToken/GetSessionFromToken'
|
||||
import { CooldownSessionTokens } from '../Domain/UseCase/CooldownSessionTokens/CooldownSessionTokens'
|
||||
import { SessionTokensCooldownRepositoryInterface } from '../Domain/Session/SessionTokensCooldownRepositoryInterface'
|
||||
import { RedisSessionTokensCooldownRepository } from '../Infra/Redis/RedisSessionTokensCooldownRepository'
|
||||
import { InMemorySessionTokensCooldownRepository } from '../Infra/InMemory/InMemorySessionTokensCooldownRepository'
|
||||
import { GetCooldownSessionTokens } from '../Domain/UseCase/GetCooldownSessionTokens/GetCooldownSessionTokens'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
constructor(private mode: 'server' | 'worker' = 'server') {}
|
||||
@@ -328,6 +343,8 @@ export class ContainerConfigLoader {
|
||||
const isConfiguredForSelfHosting = env.get('MODE', true) === 'self-hosted'
|
||||
const isConfiguredForHomeServerOrSelfHosting = isConfiguredForHomeServer || isConfiguredForSelfHosting
|
||||
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
|
||||
const captchaServerUrl = env.get('CAPTCHA_SERVER_URL', true)
|
||||
const captchaUIUrl = env.get('CAPTCHA_UI_URL', true)
|
||||
|
||||
container
|
||||
.bind<boolean>(TYPES.Auth_IS_CONFIGURED_FOR_HOME_SERVER_OR_SELF_HOSTING)
|
||||
@@ -595,9 +612,17 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind(TYPES.Auth_MAX_LOGIN_ATTEMPTS)
|
||||
.toConstantValue(env.get('MAX_LOGIN_ATTEMPTS', true) ? +env.get('MAX_LOGIN_ATTEMPTS', true) : 6)
|
||||
container
|
||||
.bind(TYPES.Auth_MAX_CAPTCHA_LOGIN_ATTEMPTS)
|
||||
.toConstantValue(env.get('MAX_CAPTCHA_LOGIN_ATTEMPTS', true) ? +env.get('MAX_CAPTCHA_LOGIN_ATTEMPTS', true) : 6)
|
||||
container
|
||||
.bind(TYPES.Auth_FAILED_LOGIN_LOCKOUT)
|
||||
.toConstantValue(env.get('FAILED_LOGIN_LOCKOUT', true) ? +env.get('FAILED_LOGIN_LOCKOUT', true) : 3600)
|
||||
container
|
||||
.bind(TYPES.Auth_FAILED_LOGIN_CAPTCHA_LOCKOUT)
|
||||
.toConstantValue(
|
||||
env.get('FAILED_LOGIN_CAPTCHA_LOCKOUT', true) ? +env.get('FAILED_LOGIN_CAPTCHA_LOCKOUT', true) : 86400,
|
||||
)
|
||||
container.bind(TYPES.Auth_PSEUDO_KEY_PARAMS_KEY).toConstantValue(env.get('PSEUDO_KEY_PARAMS_KEY'))
|
||||
container
|
||||
.bind(TYPES.Auth_EPHEMERAL_SESSION_AGE)
|
||||
@@ -631,6 +656,10 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind(TYPES.Auth_READONLY_USERS)
|
||||
.toConstantValue(env.get('READONLY_USERS', true) ? env.get('READONLY_USERS', true).split(',') : [])
|
||||
container.bind(TYPES.Auth_CAPTCHA_SERVER_URL).toConstantValue(captchaServerUrl)
|
||||
container.bind(TYPES.Auth_CAPTCHA_UI_URL).toConstantValue(captchaUIUrl)
|
||||
container.bind<boolean>(TYPES.Auth_HUMAN_VERIFICATION_ENABLED).toConstantValue(!!captchaServerUrl && !!captchaUIUrl)
|
||||
container.bind<boolean>(TYPES.Auth_FORCE_LEGACY_SESSIONS).toConstantValue(env.get('E2E_TESTING', true) === 'true')
|
||||
|
||||
if (isConfiguredForInMemoryCache) {
|
||||
container
|
||||
@@ -650,6 +679,7 @@ export class ContainerConfigLoader {
|
||||
container.get(TYPES.Auth_Timer),
|
||||
container.get(TYPES.Auth_MAX_LOGIN_ATTEMPTS),
|
||||
container.get(TYPES.Auth_FAILED_LOGIN_LOCKOUT),
|
||||
container.get(TYPES.Auth_FAILED_LOGIN_CAPTCHA_LOCKOUT),
|
||||
),
|
||||
)
|
||||
container
|
||||
@@ -677,9 +707,21 @@ export class ContainerConfigLoader {
|
||||
container.get(TYPES.Auth_Timer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SessionTokensCooldownRepositoryInterface>(TYPES.Auth_SessionTokensCooldownRepository)
|
||||
.toConstantValue(new InMemorySessionTokensCooldownRepository())
|
||||
} else {
|
||||
container.bind<PKCERepositoryInterface>(TYPES.Auth_PKCERepository).to(RedisPKCERepository)
|
||||
container.bind<LockRepositoryInterface>(TYPES.Auth_LockRepository).to(LockRepository)
|
||||
container
|
||||
.bind<LockRepositoryInterface>(TYPES.Auth_LockRepository)
|
||||
.toConstantValue(
|
||||
new RedisLockRepository(
|
||||
container.get<Redis>(TYPES.Auth_Redis),
|
||||
container.get<number>(TYPES.Auth_MAX_LOGIN_ATTEMPTS),
|
||||
container.get<number>(TYPES.Auth_FAILED_LOGIN_LOCKOUT),
|
||||
container.get<number>(TYPES.Auth_FAILED_LOGIN_CAPTCHA_LOCKOUT),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<EphemeralSessionRepositoryInterface>(TYPES.Auth_EphemeralSessionRepository)
|
||||
.to(RedisEphemeralSessionRepository)
|
||||
@@ -689,6 +731,9 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<SubscriptionTokenRepositoryInterface>(TYPES.Auth_SubscriptionTokenRepository)
|
||||
.to(RedisSubscriptionTokenRepository)
|
||||
container
|
||||
.bind<SessionTokensCooldownRepositoryInterface>(TYPES.Auth_SessionTokensCooldownRepository)
|
||||
.toConstantValue(new RedisSessionTokensCooldownRepository(container.get<Redis>(TYPES.Auth_Redis)))
|
||||
}
|
||||
|
||||
container
|
||||
@@ -738,6 +783,41 @@ export class ContainerConfigLoader {
|
||||
container.get<UserSubscriptionRepositoryInterface>(TYPES.Auth_UserSubscriptionRepository),
|
||||
container.get<string[]>(TYPES.Auth_READONLY_USERS),
|
||||
container.get<GetSetting>(TYPES.Auth_GetSetting),
|
||||
container.get<boolean>(TYPES.Auth_FORCE_LEGACY_SESSIONS),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<GetCooldownSessionTokens>(TYPES.Auth_GetCooldownSessionTokens)
|
||||
.toConstantValue(
|
||||
new GetCooldownSessionTokens(
|
||||
container.get<SessionTokensCooldownRepositoryInterface>(TYPES.Auth_SessionTokensCooldownRepository),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<GetSessionFromToken>(TYPES.Auth_GetSessionFromToken)
|
||||
.toConstantValue(
|
||||
new GetSessionFromToken(
|
||||
container.get<SessionRepositoryInterface>(TYPES.Auth_SessionRepository),
|
||||
container.get<EphemeralSessionRepositoryInterface>(TYPES.Auth_EphemeralSessionRepository),
|
||||
container.get<GetCooldownSessionTokens>(TYPES.Auth_GetCooldownSessionTokens),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<DeleteSessionByToken>(TYPES.Auth_DeleteSessionByToken)
|
||||
.toConstantValue(
|
||||
new DeleteSessionByToken(
|
||||
container.get<GetSessionFromToken>(TYPES.Auth_GetSessionFromToken),
|
||||
container.get<SessionRepositoryInterface>(TYPES.Auth_SessionRepository),
|
||||
container.get<EphemeralSessionRepositoryInterface>(TYPES.Auth_EphemeralSessionRepository),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<CooldownSessionTokens>(TYPES.Auth_CooldownSessionTokens)
|
||||
.toConstantValue(
|
||||
new CooldownSessionTokens(
|
||||
env.get('COOLDOWN_SESSION_TOKENS_TTL', true) ? +env.get('COOLDOWN_SESSION_TOKENS_TTL', true) : 120,
|
||||
container.get<SessionTokensCooldownRepositoryInterface>(TYPES.Auth_SessionTokensCooldownRepository),
|
||||
),
|
||||
)
|
||||
container.bind<AuthResponseFactory20161215>(TYPES.Auth_AuthResponseFactory20161215).to(AuthResponseFactory20161215)
|
||||
@@ -778,7 +858,16 @@ export class ContainerConfigLoader {
|
||||
.toConstantValue(new TokenEncoder<ValetTokenData>(container.get(TYPES.Auth_VALET_TOKEN_SECRET)))
|
||||
container
|
||||
.bind<AuthenticationMethodResolver>(TYPES.Auth_AuthenticationMethodResolver)
|
||||
.to(AuthenticationMethodResolver)
|
||||
.toConstantValue(
|
||||
new AuthenticationMethodResolver(
|
||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||
container.get<SessionServiceInterface>(TYPES.Auth_SessionService),
|
||||
container.get<TokenDecoderInterface<SessionTokenData>>(TYPES.Auth_SessionTokenDecoder),
|
||||
container.get<TokenDecoderInterface<SessionTokenData>>(TYPES.Auth_FallbackSessionTokenDecoder),
|
||||
container.get<GetSessionFromToken>(TYPES.Auth_GetSessionFromToken),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container.bind<DomainEventFactory>(TYPES.Auth_DomainEventFactory).to(DomainEventFactory)
|
||||
container
|
||||
.bind<SettingsAssociationServiceInterface>(TYPES.Auth_SettingsAssociationService)
|
||||
@@ -817,6 +906,43 @@ export class ContainerConfigLoader {
|
||||
.bind<SelectorInterface<boolean>>(TYPES.Auth_BooleanSelector)
|
||||
.toConstantValue(new DeterministicSelector<boolean>())
|
||||
|
||||
const httpAgentKeepAliveTimeout = env.get('HTTP_AGENT_KEEP_ALIVE_TIMEOUT', true)
|
||||
? +env.get('HTTP_AGENT_KEEP_ALIVE_TIMEOUT', true)
|
||||
: 4_000
|
||||
|
||||
container.bind<AxiosInstance>(TYPES.Auth_HTTPClient).toConstantValue(
|
||||
axios.create({
|
||||
httpAgent: new AgentKeepAlive({
|
||||
keepAlive: true,
|
||||
timeout: 2 * httpAgentKeepAliveTimeout,
|
||||
freeSocketTimeout: httpAgentKeepAliveTimeout,
|
||||
}),
|
||||
}),
|
||||
)
|
||||
|
||||
container
|
||||
.bind<CaptchaServerInterface>(TYPES.Auth_CaptchaServer)
|
||||
.toConstantValue(
|
||||
new HttpCaptchaServer(
|
||||
container.get(TYPES.Auth_Logger),
|
||||
container.get(TYPES.Auth_HTTPClient),
|
||||
container.get(TYPES.Auth_CAPTCHA_SERVER_URL),
|
||||
),
|
||||
)
|
||||
|
||||
container
|
||||
.bind<CookieFactoryInterface>(TYPES.Auth_CookieFactory)
|
||||
.toConstantValue(
|
||||
new CookieFactory(
|
||||
['None', 'Lax', 'Strict'].includes(env.get('COOKIE_SAME_SITE', true))
|
||||
? (env.get('COOKIE_SAME_SITE', true) as 'None' | 'Lax' | 'Strict')
|
||||
: 'None',
|
||||
env.get('COOKIE_DOMAIN', true) ?? 'standardnotes.com',
|
||||
env.get('COOKIE_SECURE', true) ? env.get('COOKIE_SECURE', true) === 'true' : true,
|
||||
env.get('COOKIE_PARTITIONED', true) ? env.get('COOKIE_PARTITIONED', true) === 'true' : true,
|
||||
),
|
||||
)
|
||||
|
||||
// Middleware
|
||||
container.bind<SessionMiddleware>(TYPES.Auth_SessionMiddleware).to(SessionMiddleware)
|
||||
container.bind<LockMiddleware>(TYPES.Auth_LockMiddleware).to(LockMiddleware)
|
||||
@@ -951,6 +1077,7 @@ export class ContainerConfigLoader {
|
||||
new SetSubscriptionSettingValue(
|
||||
container.get<SubscriptionSettingRepositoryInterface>(TYPES.Auth_SubscriptionSettingRepository),
|
||||
container.get<GetSubscriptionSetting>(TYPES.Auth_GetSubscriptionSetting),
|
||||
container.get<SettingsAssociationServiceInterface>(TYPES.Auth_SettingsAssociationService),
|
||||
container.get<TimerInterface>(TYPES.Auth_Timer),
|
||||
),
|
||||
)
|
||||
@@ -986,8 +1113,45 @@ export class ContainerConfigLoader {
|
||||
.toConstantValue(new CleanupExpiredSessions(container.get(TYPES.Auth_SessionRepository)))
|
||||
container.bind<AuthenticateUser>(TYPES.Auth_AuthenticateUser).to(AuthenticateUser)
|
||||
container.bind<AuthenticateRequest>(TYPES.Auth_AuthenticateRequest).to(AuthenticateRequest)
|
||||
container.bind<RefreshSessionToken>(TYPES.Auth_RefreshSessionToken).to(RefreshSessionToken)
|
||||
container.bind<SignIn>(TYPES.Auth_SignIn).to(SignIn)
|
||||
container
|
||||
.bind<RefreshSessionToken>(TYPES.Auth_RefreshSessionToken)
|
||||
.toConstantValue(
|
||||
new RefreshSessionToken(
|
||||
container.get<SessionServiceInterface>(TYPES.Auth_SessionService),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
|
||||
container.get<TimerInterface>(TYPES.Auth_Timer),
|
||||
container.get<GetSetting>(TYPES.Auth_GetSetting),
|
||||
container.get<CooldownSessionTokens>(TYPES.Auth_CooldownSessionTokens),
|
||||
container.get<GetSessionFromToken>(TYPES.Auth_GetSessionFromToken),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<VerifyHumanInteraction>(TYPES.Auth_VerifyHumanInteraction)
|
||||
.toConstantValue(
|
||||
new VerifyHumanInteraction(
|
||||
container.get(TYPES.Auth_HUMAN_VERIFICATION_ENABLED),
|
||||
container.get<CaptchaServerInterface>(TYPES.Auth_CaptchaServer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SignIn>(TYPES.Auth_SignIn)
|
||||
.toConstantValue(
|
||||
new SignIn(
|
||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||
container.get<AuthResponseFactoryResolverInterface>(TYPES.Auth_AuthResponseFactoryResolver),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
|
||||
container.get<SessionServiceInterface>(TYPES.Auth_SessionService),
|
||||
container.get<PKCERepositoryInterface>(TYPES.Auth_PKCERepository),
|
||||
container.get<CrypterInterface>(TYPES.Auth_Crypter),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
container.get<number>(TYPES.Auth_MAX_LOGIN_ATTEMPTS),
|
||||
container.get<LockRepositoryInterface>(TYPES.Auth_LockRepository),
|
||||
container.get<VerifyHumanInteraction>(TYPES.Auth_VerifyHumanInteraction),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<VerifyMFA>(TYPES.Auth_VerifyMFA)
|
||||
.toConstantValue(
|
||||
@@ -1004,8 +1168,24 @@ export class ContainerConfigLoader {
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container.bind<ClearLoginAttempts>(TYPES.Auth_ClearLoginAttempts).to(ClearLoginAttempts)
|
||||
container.bind<IncreaseLoginAttempts>(TYPES.Auth_IncreaseLoginAttempts).to(IncreaseLoginAttempts)
|
||||
container
|
||||
.bind<ClearLoginAttempts>(TYPES.Auth_ClearLoginAttempts)
|
||||
.toConstantValue(
|
||||
new ClearLoginAttempts(
|
||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||
container.get<LockRepositoryInterface>(TYPES.Auth_LockRepository),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<IncreaseLoginAttempts>(TYPES.Auth_IncreaseLoginAttempts)
|
||||
.toConstantValue(
|
||||
new IncreaseLoginAttempts(
|
||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||
container.get<LockRepositoryInterface>(TYPES.Auth_LockRepository),
|
||||
container.get<number>(TYPES.Auth_MAX_LOGIN_ATTEMPTS),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<GetUserKeyParamsRecovery>(TYPES.Auth_GetUserKeyParamsRecovery)
|
||||
.toConstantValue(
|
||||
@@ -1016,7 +1196,6 @@ export class ContainerConfigLoader {
|
||||
container.get<GetSetting>(TYPES.Auth_GetSetting),
|
||||
),
|
||||
)
|
||||
container.bind<UpdateUser>(TYPES.Auth_UpdateUser).to(UpdateUser)
|
||||
container
|
||||
.bind<ApplyDefaultSettings>(TYPES.Auth_ApplyDefaultSettings)
|
||||
.toConstantValue(
|
||||
@@ -1117,6 +1296,9 @@ export class ContainerConfigLoader {
|
||||
container.get<ClearLoginAttempts>(TYPES.Auth_ClearLoginAttempts),
|
||||
container.get<DeleteSetting>(TYPES.Auth_DeleteSetting),
|
||||
container.get<AuthenticatorRepositoryInterface>(TYPES.Auth_AuthenticatorRepository),
|
||||
container.get<number>(TYPES.Auth_MAX_LOGIN_ATTEMPTS),
|
||||
container.get<LockRepositoryInterface>(TYPES.Auth_LockRepository),
|
||||
container.get<VerifyHumanInteraction>(TYPES.Auth_VerifyHumanInteraction),
|
||||
),
|
||||
)
|
||||
container
|
||||
@@ -1249,7 +1431,6 @@ export class ContainerConfigLoader {
|
||||
.toConstantValue(
|
||||
new TriggerEmailBackupForUser(
|
||||
container.get<RoleServiceInterface>(TYPES.Auth_RoleService),
|
||||
container.get<GetSetting>(TYPES.Auth_GetSetting),
|
||||
container.get<GetUserKeyParams>(TYPES.Auth_GetUserKeyParams),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
|
||||
@@ -1324,15 +1505,9 @@ export class ContainerConfigLoader {
|
||||
.bind<AuthController>(TYPES.Auth_AuthController)
|
||||
.toConstantValue(
|
||||
new AuthController(
|
||||
container.get(TYPES.Auth_ClearLoginAttempts),
|
||||
container.get(TYPES.Auth_Register),
|
||||
container.get(TYPES.Auth_DomainEventPublisher),
|
||||
container.get(TYPES.Auth_DomainEventFactory),
|
||||
container.get(TYPES.Auth_SignInWithRecoveryCodes),
|
||||
container.get(TYPES.Auth_GetUserKeyParamsRecovery),
|
||||
container.get(TYPES.Auth_GenerateRecoveryCodes),
|
||||
container.get(TYPES.Auth_Logger),
|
||||
container.get(TYPES.Auth_SessionService),
|
||||
container.get<GetUserKeyParamsRecovery>(TYPES.Auth_GetUserKeyParamsRecovery),
|
||||
container.get<GenerateRecoveryCodes>(TYPES.Auth_GenerateRecoveryCodes),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
@@ -1409,6 +1584,7 @@ export class ContainerConfigLoader {
|
||||
container.get<SetSettingValue>(TYPES.Auth_SetSettingValue),
|
||||
container.get<OfflineSettingServiceInterface>(TYPES.Auth_OfflineSettingService),
|
||||
container.get<ContentDecoderInterface>(TYPES.Auth_ContenDecoder),
|
||||
container.get<RenewSharedSubscriptions>(TYPES.Auth_RenewSharedSubscriptions),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
@@ -1566,6 +1742,16 @@ export class ContainerConfigLoader {
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SubscriptionStateFetchedEventHandler>(TYPES.Auth_SubscriptionStateFetchedEventHandler)
|
||||
.toConstantValue(
|
||||
new SubscriptionStateFetchedEventHandler(
|
||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||
container.get<UserSubscriptionRepositoryInterface>(TYPES.Auth_UserSubscriptionRepository),
|
||||
container.get<OfflineUserSubscriptionRepositoryInterface>(TYPES.Auth_OfflineUserSubscriptionRepository),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.Auth_AccountDeletionRequestedEventHandler)],
|
||||
@@ -1607,6 +1793,7 @@ export class ContainerConfigLoader {
|
||||
'FILE_QUOTA_RECALCULATED',
|
||||
container.get<FileQuotaRecalculatedEventHandler>(TYPES.Auth_FileQuotaRecalculatedEventHandler),
|
||||
],
|
||||
['SUBSCRIPTION_STATE_FETCHED', container.get(TYPES.Auth_SubscriptionStateFetchedEventHandler)],
|
||||
])
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
@@ -1639,14 +1826,23 @@ export class ContainerConfigLoader {
|
||||
.bind<BaseAuthController>(TYPES.Auth_BaseAuthController)
|
||||
.toConstantValue(
|
||||
new BaseAuthController(
|
||||
container.get(TYPES.Auth_VerifyMFA),
|
||||
container.get(TYPES.Auth_SignIn),
|
||||
container.get(TYPES.Auth_GetUserKeyParams),
|
||||
container.get(TYPES.Auth_ClearLoginAttempts),
|
||||
container.get(TYPES.Auth_IncreaseLoginAttempts),
|
||||
container.get(TYPES.Auth_Logger),
|
||||
container.get(TYPES.Auth_AuthController),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
container.get<VerifyMFA>(TYPES.Auth_VerifyMFA),
|
||||
container.get<SignIn>(TYPES.Auth_SignIn),
|
||||
container.get<GetUserKeyParams>(TYPES.Auth_GetUserKeyParams),
|
||||
container.get<ClearLoginAttempts>(TYPES.Auth_ClearLoginAttempts),
|
||||
container.get<IncreaseLoginAttempts>(TYPES.Auth_IncreaseLoginAttempts),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
container.get<AuthController>(TYPES.Auth_AuthController),
|
||||
container.get<Register>(TYPES.Auth_Register),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
|
||||
container.get<SessionServiceInterface>(TYPES.Auth_SessionService),
|
||||
container.get<VerifyHumanInteraction>(TYPES.Auth_VerifyHumanInteraction),
|
||||
container.get<CookieFactoryInterface>(TYPES.Auth_CookieFactory),
|
||||
container.get<SignInWithRecoveryCodes>(TYPES.Auth_SignInWithRecoveryCodes),
|
||||
container.get<DeleteSessionByToken>(TYPES.Auth_DeleteSessionByToken),
|
||||
container.get<string>(TYPES.Auth_CAPTCHA_UI_URL),
|
||||
container.get<ControllerContainerInterface>(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1708,12 +1904,12 @@ export class ContainerConfigLoader {
|
||||
.bind<BaseUsersController>(TYPES.Auth_BaseUsersController)
|
||||
.toConstantValue(
|
||||
new BaseUsersController(
|
||||
container.get<UpdateUser>(TYPES.Auth_UpdateUser),
|
||||
container.get<DeleteAccount>(TYPES.Auth_DeleteAccount),
|
||||
container.get<GetUserSubscription>(TYPES.Auth_GetUserSubscription),
|
||||
container.get<ClearLoginAttempts>(TYPES.Auth_ClearLoginAttempts),
|
||||
container.get<IncreaseLoginAttempts>(TYPES.Auth_IncreaseLoginAttempts),
|
||||
container.get<ChangeCredentials>(TYPES.Auth_ChangeCredentials),
|
||||
container.get<CookieFactoryInterface>(TYPES.Auth_CookieFactory),
|
||||
container.get<ControllerContainerInterface>(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
@@ -1721,11 +1917,12 @@ export class ContainerConfigLoader {
|
||||
.bind<BaseAdminController>(TYPES.Auth_BaseAdminController)
|
||||
.toConstantValue(
|
||||
new BaseAdminController(
|
||||
container.get(TYPES.Auth_DeleteSetting),
|
||||
container.get(TYPES.Auth_UserRepository),
|
||||
container.get(TYPES.Auth_CreateSubscriptionToken),
|
||||
container.get(TYPES.Auth_CreateOfflineSubscriptionToken),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
container.get<DeleteSetting>(TYPES.Auth_DeleteSetting),
|
||||
container.get<GetSetting>(TYPES.Auth_GetSetting),
|
||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||
container.get<CreateSubscriptionToken>(TYPES.Auth_CreateSubscriptionToken),
|
||||
container.get<CreateOfflineSubscriptionToken>(TYPES.Auth_CreateOfflineSubscriptionToken),
|
||||
container.get<ControllerContainerInterface>(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
container
|
||||
@@ -1748,9 +1945,12 @@ export class ContainerConfigLoader {
|
||||
new BaseSubscriptionSettingsController(
|
||||
container.get<GetSubscriptionSetting>(TYPES.Auth_GetSubscriptionSetting),
|
||||
container.get<GetSharedOrRegularSubscriptionForUser>(TYPES.Auth_GetSharedOrRegularSubscriptionForUser),
|
||||
container.get<SetSubscriptionSettingValue>(TYPES.Auth_SetSubscriptionSettingValue),
|
||||
container.get<TriggerPostSettingUpdateActions>(TYPES.Auth_TriggerPostSettingUpdateActions),
|
||||
container.get<MapperInterface<SubscriptionSetting, SubscriptionSettingHttpRepresentation>>(
|
||||
TYPES.Auth_SubscriptionSettingHttpMapper,
|
||||
),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
container.get<ControllerContainerInterface>(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
@@ -1775,10 +1975,11 @@ export class ContainerConfigLoader {
|
||||
.bind<BaseSessionController>(TYPES.Auth_BaseSessionController)
|
||||
.toConstantValue(
|
||||
new BaseSessionController(
|
||||
container.get(TYPES.Auth_DeleteSessionForUser),
|
||||
container.get(TYPES.Auth_DeleteOtherSessionsForUser),
|
||||
container.get(TYPES.Auth_RefreshSessionToken),
|
||||
container.get(TYPES.Auth_ControllerContainer),
|
||||
container.get<DeleteSessionForUser>(TYPES.Auth_DeleteSessionForUser),
|
||||
container.get<DeleteOtherSessionsForUser>(TYPES.Auth_DeleteOtherSessionsForUser),
|
||||
container.get<RefreshSessionToken>(TYPES.Auth_RefreshSessionToken),
|
||||
container.get<CookieFactoryInterface>(TYPES.Auth_CookieFactory),
|
||||
container.get<ControllerContainerInterface>(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
container
|
||||
|
||||
@@ -34,6 +34,7 @@ const TYPES = {
|
||||
Auth_UserSubscriptionRepository: Symbol.for('Auth_UserSubscriptionRepository'),
|
||||
Auth_OfflineUserSubscriptionRepository: Symbol.for('Auth_OfflineUserSubscriptionRepository'),
|
||||
Auth_SubscriptionTokenRepository: Symbol.for('Auth_SubscriptionTokenRepository'),
|
||||
Auth_SessionTokensCooldownRepository: Symbol.for('Auth_SessionTokensCooldownRepository'),
|
||||
Auth_OfflineSubscriptionTokenRepository: Symbol.for('Auth_OfflineSubscriptionTokenRepository'),
|
||||
Auth_SharedSubscriptionInvitationRepository: Symbol.for('Auth_SharedSubscriptionInvitationRepository'),
|
||||
Auth_PKCERepository: Symbol.for('Auth_PKCERepository'),
|
||||
@@ -84,7 +85,9 @@ const TYPES = {
|
||||
Auth_REFRESH_TOKEN_AGE: Symbol.for('Auth_REFRESH_TOKEN_AGE'),
|
||||
Auth_EPHEMERAL_SESSION_AGE: Symbol.for('Auth_EPHEMERAL_SESSION_AGE'),
|
||||
Auth_MAX_LOGIN_ATTEMPTS: Symbol.for('Auth_MAX_LOGIN_ATTEMPTS'),
|
||||
Auth_MAX_CAPTCHA_LOGIN_ATTEMPTS: Symbol.for('Auth_MAX_CAPTCHA_LOGIN_ATTEMPTS'),
|
||||
Auth_FAILED_LOGIN_LOCKOUT: Symbol.for('Auth_FAILED_LOGIN_LOCKOUT'),
|
||||
Auth_FAILED_LOGIN_CAPTCHA_LOCKOUT: Symbol.for('Auth_FAILED_LOGIN_CAPTCHA_LOCKOUT'),
|
||||
Auth_PSEUDO_KEY_PARAMS_KEY: Symbol.for('Auth_PSEUDO_KEY_PARAMS_KEY'),
|
||||
Auth_REDIS_URL: Symbol.for('Auth_REDIS_URL'),
|
||||
Auth_DISABLE_USER_REGISTRATION: Symbol.for('Auth_DISABLE_USER_REGISTRATION'),
|
||||
@@ -100,6 +103,10 @@ const TYPES = {
|
||||
Auth_U2F_REQUIRE_USER_VERIFICATION: Symbol.for('Auth_U2F_REQUIRE_USER_VERIFICATION'),
|
||||
Auth_READONLY_USERS: Symbol.for('Auth_READONLY_USERS'),
|
||||
Auth_IS_CONFIGURED_FOR_HOME_SERVER_OR_SELF_HOSTING: Symbol.for('Auth_IS_CONFIGURED_FOR_HOME_SERVER_OR_SELF_HOSTING'),
|
||||
Auth_CAPTCHA_SERVER_URL: Symbol.for('Auth_CAPTCHA_SERVER_URL'),
|
||||
Auth_CAPTCHA_UI_URL: Symbol.for('Auth_CAPTCHA_UI_URL'),
|
||||
Auth_HUMAN_VERIFICATION_ENABLED: Symbol.for('Auth_HUMAN_VERIFICATION_ENABLED'),
|
||||
Auth_FORCE_LEGACY_SESSIONS: Symbol.for('Auth_FORCE_LEGACY_SESSIONS'),
|
||||
// use cases
|
||||
Auth_AuthenticateUser: Symbol.for('Auth_AuthenticateUser'),
|
||||
Auth_AuthenticateRequest: Symbol.for('Auth_AuthenticateRequest'),
|
||||
@@ -109,7 +116,6 @@ const TYPES = {
|
||||
Auth_ClearLoginAttempts: Symbol.for('Auth_ClearLoginAttempts'),
|
||||
Auth_IncreaseLoginAttempts: Symbol.for('Auth_IncreaseLoginAttempts'),
|
||||
Auth_GetUserKeyParams: Symbol.for('Auth_GetUserKeyParams'),
|
||||
Auth_UpdateUser: Symbol.for('Auth_UpdateUser'),
|
||||
Auth_Register: Symbol.for('Auth_Register'),
|
||||
Auth_GetActiveSessionsForUser: Symbol.for('Auth_GetActiveSessionsForUser'),
|
||||
Auth_DeleteOtherSessionsForUser: Symbol.for('Auth_DeleteOtherSessionsForUser'),
|
||||
@@ -158,6 +164,10 @@ const TYPES = {
|
||||
Auth_ApplyDefaultSettings: Symbol.for('Auth_ApplyDefaultSettings'),
|
||||
Auth_ActivatePremiumFeatures: Symbol.for('Auth_ActivatePremiumFeatures'),
|
||||
Auth_SignInWithRecoveryCodes: Symbol.for('Auth_SignInWithRecoveryCodes'),
|
||||
Auth_GetSessionFromToken: Symbol.for('Auth_GetSessionFromToken'),
|
||||
Auth_DeleteSessionByToken: Symbol.for('Auth_DeleteSessionByToken'),
|
||||
Auth_CooldownSessionTokens: Symbol.for('Auth_CooldownSessionTokens'),
|
||||
Auth_GetCooldownSessionTokens: Symbol.for('Auth_GetCooldownSessionTokens'),
|
||||
Auth_GetUserKeyParamsRecovery: Symbol.for('Auth_GetUserKeyParamsRecovery'),
|
||||
Auth_UpdateStorageQuotaUsedForUser: Symbol.for('Auth_UpdateStorageQuotaUsedForUser'),
|
||||
Auth_AddSharedVaultUser: Symbol.for('Auth_AddSharedVaultUser'),
|
||||
@@ -171,6 +181,7 @@ const TYPES = {
|
||||
Auth_DeleteAccountsFromCSVFile: Symbol.for('Auth_DeleteAccountsFromCSVFile'),
|
||||
Auth_RenewSharedSubscriptions: Symbol.for('Auth_RenewSharedSubscriptions'),
|
||||
Auth_FixStorageQuotaForUser: Symbol.for('Auth_FixStorageQuotaForUser'),
|
||||
Auth_VerifyHumanInteraction: Symbol.for('Auth_VerifyHumanInteraction'),
|
||||
// Handlers
|
||||
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
||||
Auth_AccountDeletionVerificationPassedEventHandler: Symbol.for('Auth_AccountDeletionVerificationPassedEventHandler'),
|
||||
@@ -205,7 +216,9 @@ const TYPES = {
|
||||
),
|
||||
Auth_UserInvitedToSharedVaultEventHandler: Symbol.for('Auth_UserInvitedToSharedVaultEventHandler'),
|
||||
Auth_FileQuotaRecalculatedEventHandler: Symbol.for('Auth_FileQuotaRecalculatedEventHandler'),
|
||||
Auth_SubscriptionStateFetchedEventHandler: Symbol.for('Auth_SubscriptionStateFetchedEventHandler'),
|
||||
// Services
|
||||
Auth_CookieFactory: Symbol.for('Auth_CookieFactory'),
|
||||
Auth_DeviceDetector: Symbol.for('Auth_DeviceDetector'),
|
||||
Auth_SessionService: Symbol.for('Auth_SessionService'),
|
||||
Auth_OfflineSettingService: Symbol.for('Auth_OfflineSettingService'),
|
||||
@@ -258,6 +271,8 @@ const TYPES = {
|
||||
Auth_BaseListedController: Symbol.for('Auth_BaseListedController'),
|
||||
Auth_BaseFeaturesController: Symbol.for('Auth_BaseFeaturesController'),
|
||||
Auth_CSVFileReader: Symbol.for('Auth_CSVFileReader'),
|
||||
Auth_CaptchaServer: Symbol.for('Auth_CaptchaServer'),
|
||||
Auth_HTTPClient: Symbol.for('Auth_HTTPClient'),
|
||||
}
|
||||
|
||||
export default TYPES
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { DomainEventInterface, DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
|
||||
import { AuthController } from './AuthController'
|
||||
import { ClearLoginAttempts } from '../Domain/UseCase/ClearLoginAttempts'
|
||||
import { User } from '../Domain/User/User'
|
||||
import { Register } from '../Domain/UseCase/Register'
|
||||
import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface'
|
||||
import { KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
|
||||
import { SignInWithRecoveryCodes } from '../Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes'
|
||||
import { GetUserKeyParamsRecovery } from '../Domain/UseCase/GetUserKeyParamsRecovery/GetUserKeyParamsRecovery'
|
||||
import { GenerateRecoveryCodes } from '../Domain/UseCase/GenerateRecoveryCodes/GenerateRecoveryCodes'
|
||||
import { Logger } from 'winston'
|
||||
import { SessionServiceInterface } from '../Domain/Session/SessionServiceInterface'
|
||||
import { ApiVersion } from '../Domain/Api/ApiVersion'
|
||||
|
||||
describe('AuthController', () => {
|
||||
let clearLoginAttempts: ClearLoginAttempts
|
||||
let register: Register
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
let event: DomainEventInterface
|
||||
let user: User
|
||||
let doSignInWithRecoveryCodes: SignInWithRecoveryCodes
|
||||
let getUserKeyParamsRecovery: GetUserKeyParamsRecovery
|
||||
let doGenerateRecoveryCodes: GenerateRecoveryCodes
|
||||
let logger: Logger
|
||||
let sessionService: SessionServiceInterface
|
||||
|
||||
const createController = () =>
|
||||
new AuthController(
|
||||
clearLoginAttempts,
|
||||
register,
|
||||
domainEventPublisher,
|
||||
domainEventFactory,
|
||||
doSignInWithRecoveryCodes,
|
||||
getUserKeyParamsRecovery,
|
||||
doGenerateRecoveryCodes,
|
||||
logger,
|
||||
sessionService,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
register = {} as jest.Mocked<Register>
|
||||
register.execute = jest.fn()
|
||||
|
||||
user = {} as jest.Mocked<User>
|
||||
user.email = 'test@test.te'
|
||||
|
||||
clearLoginAttempts = {} as jest.Mocked<ClearLoginAttempts>
|
||||
clearLoginAttempts.execute = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<DomainEventInterface>
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createUserRegisteredEvent = jest.fn().mockReturnValue(event)
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.debug = jest.fn()
|
||||
|
||||
sessionService = {} as jest.Mocked<SessionServiceInterface>
|
||||
sessionService.deleteSessionByToken = jest.fn().mockReturnValue('1-2-3')
|
||||
})
|
||||
|
||||
it('should register a user', async () => {
|
||||
register.execute = jest.fn().mockReturnValue({ success: true, authResponse: { user } })
|
||||
|
||||
const response = await createController().register({
|
||||
email: 'test@test.te',
|
||||
password: 'asdzxc',
|
||||
version: ProtocolVersion.V004,
|
||||
api: ApiVersion.v20200115,
|
||||
origination: KeyParamsOrigination.Registration,
|
||||
userAgent: 'Google Chrome',
|
||||
identifier: 'test@test.te',
|
||||
pw_nonce: '11',
|
||||
ephemeral: false,
|
||||
})
|
||||
|
||||
expect(register.execute).toHaveBeenCalledWith({
|
||||
apiVersion: '20200115',
|
||||
kpOrigination: 'registration',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
ephemeralSession: false,
|
||||
version: '004',
|
||||
email: 'test@test.te',
|
||||
password: 'asdzxc',
|
||||
pwNonce: '11',
|
||||
})
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledWith(event)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data).toEqual({ user: { email: 'test@test.te' } })
|
||||
})
|
||||
|
||||
it('should not register a user if request param is missing', async () => {
|
||||
const response = await createController().register({
|
||||
email: 'test@test.te',
|
||||
password: '',
|
||||
version: ProtocolVersion.V004,
|
||||
api: ApiVersion.v20200115,
|
||||
origination: KeyParamsOrigination.Registration,
|
||||
userAgent: 'Google Chrome',
|
||||
identifier: 'test@test.te',
|
||||
pw_nonce: '11',
|
||||
ephemeral: false,
|
||||
})
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
|
||||
expect(response.status).toEqual(400)
|
||||
})
|
||||
|
||||
it('should respond with error if registering a user fails', async () => {
|
||||
register.execute = jest.fn().mockReturnValue({ success: false, errorMessage: 'Something bad happened' })
|
||||
|
||||
const response = await createController().register({
|
||||
email: 'test@test.te',
|
||||
password: 'test',
|
||||
version: ProtocolVersion.V004,
|
||||
api: ApiVersion.v20200115,
|
||||
origination: KeyParamsOrigination.Registration,
|
||||
userAgent: 'Google Chrome',
|
||||
identifier: 'test@test.te',
|
||||
pw_nonce: '11',
|
||||
ephemeral: false,
|
||||
})
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
|
||||
expect(response.status).toEqual(400)
|
||||
})
|
||||
|
||||
it('should throw error on the delete user method as it is still a part of the payments server', async () => {
|
||||
let caughtError = null
|
||||
try {
|
||||
await createController().deleteAccount({} as never)
|
||||
} catch (error) {
|
||||
caughtError = error
|
||||
}
|
||||
|
||||
expect(caughtError).not.toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -1,42 +1,23 @@
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import {
|
||||
UserRegistrationRequestParams,
|
||||
UserServerInterface,
|
||||
UserDeletionResponseBody,
|
||||
UserRegistrationResponseBody,
|
||||
UserUpdateRequestParams,
|
||||
} from '@standardnotes/api'
|
||||
import { ErrorTag, HttpResponse, HttpStatusCode } from '@standardnotes/responses'
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import { UserDeletionResponseBody, UserUpdateRequestParams } from '@standardnotes/api'
|
||||
import { HttpResponse, HttpStatusCode } from '@standardnotes/responses'
|
||||
|
||||
import { ClearLoginAttempts } from '../Domain/UseCase/ClearLoginAttempts'
|
||||
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 { GetUserKeyParamsRecovery } from '../Domain/UseCase/GetUserKeyParamsRecovery/GetUserKeyParamsRecovery'
|
||||
import { RecoveryKeyParamsRequestParams } from '../Infra/Http/Request/RecoveryKeyParamsRequestParams'
|
||||
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 { Logger } from 'winston'
|
||||
import { SessionServiceInterface } from '../Domain/Session/SessionServiceInterface'
|
||||
import { ApiVersion } from '../Domain/Api/ApiVersion'
|
||||
import { UserUpdateResponse } from '@standardnotes/api/dist/Domain/Response/User/UserUpdateResponse'
|
||||
|
||||
export class AuthController implements UserServerInterface {
|
||||
/**
|
||||
* DEPRECATED: This controller is deprecated and will be removed in the future.
|
||||
*/
|
||||
export class AuthController {
|
||||
constructor(
|
||||
private clearLoginAttempts: ClearLoginAttempts,
|
||||
private registerUser: Register,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private doSignInWithRecoveryCodes: SignInWithRecoveryCodes,
|
||||
private getUserKeyParamsRecovery: GetUserKeyParamsRecovery,
|
||||
private doGenerateRecoveryCodes: GenerateRecoveryCodes,
|
||||
private logger: Logger,
|
||||
private sessionService: SessionServiceInterface,
|
||||
) {}
|
||||
|
||||
async update(_params: UserUpdateRequestParams): Promise<HttpResponse<UserUpdateResponse>> {
|
||||
@@ -47,57 +28,6 @@ export class AuthController implements UserServerInterface {
|
||||
throw new Error('This method is implemented on the payments server.')
|
||||
}
|
||||
|
||||
async register(params: UserRegistrationRequestParams): Promise<HttpResponse<UserRegistrationResponseBody>> {
|
||||
if (!params.email || !params.password) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
data: {
|
||||
error: {
|
||||
message: 'Please enter an email and a password to register.',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const registerResult = await this.registerUser.execute({
|
||||
email: params.email,
|
||||
password: params.password,
|
||||
updatedWithUserAgent: params.userAgent as string,
|
||||
apiVersion: params.api,
|
||||
ephemeralSession: params.ephemeral,
|
||||
pwNonce: params.pw_nonce,
|
||||
kpOrigination: params.origination,
|
||||
kpCreated: params.created,
|
||||
version: params.version,
|
||||
})
|
||||
|
||||
if (!registerResult.success) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
data: {
|
||||
error: {
|
||||
message: registerResult.errorMessage,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
await this.clearLoginAttempts.execute({ email: registerResult.authResponse.user.email as string })
|
||||
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createUserRegisteredEvent({
|
||||
userUuid: <string>registerResult.authResponse.user.uuid,
|
||||
email: <string>registerResult.authResponse.user.email,
|
||||
protocolVersion: (<string>registerResult.authResponse.user.protocolVersion) as ProtocolVersion,
|
||||
}),
|
||||
)
|
||||
|
||||
return {
|
||||
status: HttpStatusCode.Success,
|
||||
data: registerResult.authResponse,
|
||||
}
|
||||
}
|
||||
|
||||
async generateRecoveryCodes(
|
||||
params: GenerateRecoveryCodesRequestParams,
|
||||
): Promise<HttpResponse<GenerateRecoveryCodesResponseBody>> {
|
||||
@@ -124,62 +54,11 @@ export class AuthController implements UserServerInterface {
|
||||
}
|
||||
}
|
||||
|
||||
async signInWithRecoveryCodes(
|
||||
params: SignInWithRecoveryCodesRequestParams,
|
||||
): Promise<HttpResponse<SignInWithRecoveryCodesResponseBody>> {
|
||||
if (params.apiVersion !== ApiVersion.v20200115) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
data: {
|
||||
error: {
|
||||
message: 'Invalid API version.',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.doSignInWithRecoveryCodes.execute({
|
||||
userAgent: params.userAgent,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
codeVerifier: params.codeVerifier,
|
||||
recoveryCodes: params.recoveryCodes,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.debug(`Failed to sign in with recovery codes: ${result.getError()}`)
|
||||
|
||||
return {
|
||||
status: HttpStatusCode.Unauthorized,
|
||||
data: {
|
||||
error: {
|
||||
message: 'Invalid login credentials.',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: HttpStatusCode.Success,
|
||||
data: result.getValue(),
|
||||
}
|
||||
}
|
||||
|
||||
async recoveryKeyParams(
|
||||
params: RecoveryKeyParamsRequestParams,
|
||||
): Promise<HttpResponse<RecoveryKeyParamsResponseBody>> {
|
||||
if (params.apiVersion !== ApiVersion.v20200115) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
data: {
|
||||
error: {
|
||||
message: 'Invalid API version.',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.getUserKeyParamsRecovery.execute({
|
||||
apiVersion: params.apiVersion,
|
||||
username: params.username,
|
||||
codeChallenge: params.codeChallenge,
|
||||
recoveryCodes: params.recoveryCodes,
|
||||
@@ -205,33 +84,4 @@ export class AuthController implements UserServerInterface {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async signOut(params: Record<string, unknown>): Promise<HttpResponse> {
|
||||
if (params.readOnlyAccess) {
|
||||
return {
|
||||
status: HttpStatusCode.Unauthorized,
|
||||
data: {
|
||||
error: {
|
||||
tag: ErrorTag.ReadOnlyAccess,
|
||||
message: 'Session has read-only access.',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const userUuid = await this.sessionService.deleteSessionByToken(
|
||||
(params.authorizationHeader as string).replace('Bearer ', ''),
|
||||
)
|
||||
|
||||
let headers = undefined
|
||||
if (userUuid !== null) {
|
||||
headers = new Map([['x-invalidate-cache', userUuid]])
|
||||
}
|
||||
|
||||
return {
|
||||
status: HttpStatusCode.NoContent,
|
||||
data: {},
|
||||
headers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,10 @@ describe('SubscriptionInvitesController', () => {
|
||||
invitations: [],
|
||||
})
|
||||
|
||||
const result = await createController().listInvites({ api: ApiVersion.v20200115, inviterEmail: 'test@test.te' })
|
||||
const result = await createController().listInvites({
|
||||
api: ApiVersion.VERSIONS.v20200115,
|
||||
inviterEmail: 'test@test.te',
|
||||
})
|
||||
|
||||
expect(listSharedSubscriptionInvitations.execute).toHaveBeenCalledWith({
|
||||
inviterEmail: 'test@test.te',
|
||||
@@ -68,7 +71,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().cancelInvite({
|
||||
api: ApiVersion.v20200115,
|
||||
api: ApiVersion.VERSIONS.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
})
|
||||
@@ -87,7 +90,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().cancelInvite({
|
||||
api: ApiVersion.v20200115,
|
||||
api: ApiVersion.VERSIONS.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -100,7 +103,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().declineInvite({
|
||||
api: ApiVersion.v20200115,
|
||||
api: ApiVersion.VERSIONS.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -117,7 +120,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().declineInvite({
|
||||
api: ApiVersion.v20200115,
|
||||
api: ApiVersion.VERSIONS.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -134,7 +137,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().acceptInvite({
|
||||
api: ApiVersion.v20200115,
|
||||
api: ApiVersion.VERSIONS.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -151,7 +154,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().acceptInvite({
|
||||
api: ApiVersion.v20200115,
|
||||
api: ApiVersion.VERSIONS.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -168,7 +171,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().invite({
|
||||
api: ApiVersion.v20200115,
|
||||
api: ApiVersion.VERSIONS.v20200115,
|
||||
identifier: 'invitee@test.te',
|
||||
inviterUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
@@ -187,7 +190,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
|
||||
it('should not invite to user subscription if the identifier is missing in request', async () => {
|
||||
const result = await createController().invite({
|
||||
api: ApiVersion.v20200115,
|
||||
api: ApiVersion.VERSIONS.v20200115,
|
||||
identifier: '',
|
||||
inviterUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
@@ -205,7 +208,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().invite({
|
||||
api: ApiVersion.v20200115,
|
||||
api: ApiVersion.VERSIONS.v20200115,
|
||||
identifier: 'invitee@test.te',
|
||||
inviterUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { ApiVersion } from './ApiVersion'
|
||||
|
||||
describe('ApiVersion', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = ApiVersion.create(ApiVersion.VERSIONS.v20200115)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('20200115')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
for (const value of ['', undefined, null, 0, 'SOME_VERSION']) {
|
||||
const valueOrError = ApiVersion.create(value as string)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should tell if the version is supported for registration', () => {
|
||||
const version = ApiVersion.create(ApiVersion.VERSIONS.v20200115).getValue()
|
||||
|
||||
expect(version.isSupportedForRegistration()).toBeTruthy()
|
||||
|
||||
const version2 = ApiVersion.create(ApiVersion.VERSIONS.v20240226).getValue()
|
||||
|
||||
expect(version2.isSupportedForRegistration()).toBeTruthy()
|
||||
|
||||
const version3 = ApiVersion.create(ApiVersion.VERSIONS.v20161215).getValue()
|
||||
|
||||
expect(version3.isSupportedForRegistration()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should tell if the version is supported for recovery sign in', () => {
|
||||
const version = ApiVersion.create(ApiVersion.VERSIONS.v20200115).getValue()
|
||||
|
||||
expect(version.isSupportedForRecoverySignIn()).toBeTruthy()
|
||||
|
||||
const version2 = ApiVersion.create(ApiVersion.VERSIONS.v20240226).getValue()
|
||||
|
||||
expect(version2.isSupportedForRecoverySignIn()).toBeTruthy()
|
||||
|
||||
const version3 = ApiVersion.create(ApiVersion.VERSIONS.v20161215).getValue()
|
||||
|
||||
expect(version3.isSupportedForRecoverySignIn()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,37 @@
|
||||
export enum ApiVersion {
|
||||
v20161215 = '20161215',
|
||||
v20190520 = '20190520',
|
||||
v20200115 = '20200115',
|
||||
import { Result, ValueObject } from '@standardnotes/domain-core'
|
||||
|
||||
import { ApiVersionProps } from './ApiVersionProps'
|
||||
|
||||
export class ApiVersion extends ValueObject<ApiVersionProps> {
|
||||
static readonly VERSIONS = {
|
||||
v20161215: '20161215',
|
||||
v20190520: '20190520',
|
||||
v20200115: '20200115',
|
||||
v20240226: '20240226',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: ApiVersionProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(version: string): Result<ApiVersion> {
|
||||
const isValidVersion = Object.values(this.VERSIONS).includes(version)
|
||||
if (!isValidVersion) {
|
||||
return Result.fail(`Invalid api version: ${version}`)
|
||||
} else {
|
||||
return Result.ok(new ApiVersion({ value: version }))
|
||||
}
|
||||
}
|
||||
|
||||
isSupportedForRegistration(): boolean {
|
||||
return [ApiVersion.VERSIONS.v20200115, ApiVersion.VERSIONS.v20240226].includes(this.props.value)
|
||||
}
|
||||
|
||||
isSupportedForRecoverySignIn(): boolean {
|
||||
return [ApiVersion.VERSIONS.v20200115, ApiVersion.VERSIONS.v20240226].includes(this.props.value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface ApiVersionProps {
|
||||
value: string
|
||||
}
|
||||
@@ -3,6 +3,6 @@ import { KeyParamsData, SessionBody } from '@standardnotes/responses'
|
||||
import { AuthResponse } from './AuthResponse'
|
||||
|
||||
export interface AuthResponse20200115 extends AuthResponse {
|
||||
session: SessionBody
|
||||
key_params: KeyParamsData
|
||||
sessionBody: SessionBody
|
||||
keyParams: KeyParamsData
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Session } from '../Session/Session'
|
||||
import { AuthResponse20161215 } from './AuthResponse20161215'
|
||||
import { AuthResponse20200115 } from './AuthResponse20200115'
|
||||
|
||||
export interface AuthResponseCreationResult {
|
||||
response?: AuthResponse20200115
|
||||
legacyResponse?: AuthResponse20161215
|
||||
session?: Session
|
||||
cookies?: { accessToken: string; refreshToken: string }
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user