mirror of
https://github.com/standardnotes/server
synced 2026-04-23 03:01:42 -04:00
Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 547cdfd8ec | |||
| a0af8f0025 | |||
| c970b1ea68 | |||
| 4d1e2dec26 | |||
| 108408a944 | |||
| 18d07d431f | |||
| cbc024f67a | |||
| 55ec5970da | |||
| 58bdca6659 | |||
| ef49b0d3f8 | |||
| 9cb691e5ad | |||
| 04d09582d4 | |||
| 8f90dc172b | |||
| f759261919 | |||
| 2606f6d929 | |||
| c288e5d8dc | |||
| 4b76d4b71e | |||
| 72310130d2 | |||
| f9e51ef06e | |||
| 92a5eb0d98 | |||
| 77d2ea1a1f | |||
| 92f96ddb84 | |||
| 15a914e25e | |||
| 912a29d091 | |||
| b2c32ce70e | |||
| ed1a708c40 | |||
| e905128d45 | |||
| fd598f372a | |||
| 7a3946a9e2 | |||
| cbdd2584d0 | |||
| f3161c2712 | |||
| 148542dd5a | |||
| d2b2c339f2 | |||
| d2578c48f0 | |||
| fecfd54728 | |||
| 17e4162d3e | |||
| 742209d773 | |||
| 1fa4b7cf27 | |||
| 5dc5507039 | |||
| 3035a20b9f | |||
| 04b3bb034f | |||
| bf84be0136 | |||
| 890cf48749 | |||
| 2b3436c6ce | |||
| 4df8c3b2e5 | |||
| 25a2696c32 | |||
| 52f879f842 | |||
| 4f70fa156d | |||
| 38e77f04be | |||
| 060206ddd4 | |||
| 0bc0909386 | |||
| 667d528a8c | |||
| fa7fbe26e7 | |||
| ba422a29d0 | |||
| d220ec5bf7 | |||
| 7baf5492bc | |||
| d5a8409bb5 | |||
| f58f90667c | |||
| a388e1a802 | |||
| 8811d10a73 | |||
| c7a394cd1a | |||
| b7615a7f2e | |||
| 1ca70c1e50 | |||
| 253cbb1d0c | |||
| e38a16404c | |||
| f17a1f875c | |||
| 2237e0f5df | |||
| 0df471585f | |||
| 95aac1a7bf | |||
| c078bc958d | |||
| 49c27924ea | |||
| c9dd8e7338 | |||
| 5ef90cc75b | |||
| 063c61d96c | |||
| 0cb5e36b20 | |||
| 319bab5b34 | |||
| 90a4f2111f | |||
| 3aba202970 | |||
| c8974b7fa2 | |||
| 3654a19586 | |||
| 5f0929c1aa | |||
| c0fa83bce6 | |||
| c201ee42a0 | |||
| e6a4cc3098 | |||
| 39f2fe2ba1 | |||
| 72ce190996 | |||
| 527dd1b61b | |||
| af8feaadfe | |||
| 3164f76662 | |||
| d6e531d4b6 | |||
| af76878dad | |||
| 28cce39fe7 | |||
| a8b806af08 | |||
| fa0b0294b4 | |||
| 58ab410b0a | |||
| 51c8b20506 | |||
| 1e62a3760e | |||
| 2f569d4104 | |||
| f23e444ed0 | |||
| e6e9a32f03 | |||
| 8237df33a7 | |||
| 624b574013 | |||
| d762bc89d1 | |||
| f0cbec07b8 |
@@ -4,6 +4,7 @@ DB_USERNAME=std_notes_user
|
||||
DB_PASSWORD=changeme123
|
||||
DB_DATABASE=standard_notes_db
|
||||
DB_PORT=3306
|
||||
DB_SQLITE_DATABASE_PATH=standard_notes_db
|
||||
REDIS_PORT=6379
|
||||
REDIS_HOST=cache
|
||||
AUTH_SERVER_ACCESS_TOKEN_AGE=4
|
||||
|
||||
@@ -54,6 +54,11 @@ jobs:
|
||||
|
||||
e2e-home-server:
|
||||
name: (Home Server) E2E Test Suite
|
||||
strategy:
|
||||
matrix:
|
||||
db_type: [mysql, sqlite]
|
||||
cache_type: [redis, memory]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
@@ -61,6 +66,19 @@ jobs:
|
||||
image: standardnotes/snjs:${{ inputs.snjs_image_tag }}
|
||||
ports:
|
||||
- 9001:9001
|
||||
cache:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
db:
|
||||
image: mysql
|
||||
ports:
|
||||
- 3306:3306
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: standardnotes
|
||||
MYSQL_USER: standardnotes
|
||||
MYSQL_PASSWORD: standardnotes
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -90,6 +108,13 @@ jobs:
|
||||
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
|
||||
echo "REFRESH_TOKEN_AGE=7" >> packages/home-server/.env
|
||||
echo "REVISIONS_FREQUENCY=5" >> packages/home-server/.env
|
||||
echo "DB_HOST=db" >> packages/home-server/.env
|
||||
echo "DB_PORT=3306" >> packages/home-server/.env
|
||||
echo "DB_USERNAME=standardnotes" >> packages/home-server/.env
|
||||
echo "DB_PASSWORD=standardnotes" >> packages/home-server/.env
|
||||
echo "DB_TYPE=${{ matrix.db_type }}" >> packages/home-server/.env
|
||||
echo "REDIS_URL=redis://cache" >> packages/home-server/.env
|
||||
echo "CACHE_TYPE=${{ matrix.cache_type }}" >> packages/home-server/.env
|
||||
|
||||
- name: Run Server
|
||||
run: nohup yarn workspace @standardnotes/home-server start &
|
||||
|
||||
@@ -4560,17 +4560,16 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/api", [\
|
||||
["npm:1.26.10", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.26.10-f6165cafd3-3c3561aec8.zip/node_modules/@standardnotes/api/",\
|
||||
["npm:1.26.25", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.26.25-fbb86eb9b7-68a820bd36.zip/node_modules/@standardnotes/api/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/api", "npm:1.26.10"],\
|
||||
["@standardnotes/api", "npm:1.26.25"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/encryption", "npm:1.21.38"],\
|
||||
["@standardnotes/models", "npm:1.45.5"],\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/models", "npm:1.46.7"],\
|
||||
["@standardnotes/responses", "npm:1.13.26"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/utils", "npm:1.16.5"],\
|
||||
["@standardnotes/utils", "npm:1.17.4"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -4635,7 +4634,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||
["@simplewebauthn/server", "npm:7.2.0"],\
|
||||
["@simplewebauthn/typescript-types", "npm:7.0.0"],\
|
||||
["@standardnotes/api", "npm:1.26.10"],\
|
||||
["@standardnotes/api", "npm:1.26.25"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
@@ -4781,27 +4780,13 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "SOFT"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/encryption", [\
|
||||
["npm:1.21.38", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.21.38-d08c3d4766-1393840523.zip/node_modules/@standardnotes/encryption/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/encryption", "npm:1.21.38"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/models", "npm:1.45.5"],\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
||||
["@standardnotes/utils", "npm:1.16.5"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/event-store", [\
|
||||
["workspace:packages/event-store", {\
|
||||
"packageLocation": "./packages/event-store/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/event-store", "workspace:packages/event-store"],\
|
||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
@@ -4840,6 +4825,17 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.59.6", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.59.6-2bcea0cc35-2c855396f7.zip/node_modules/@standardnotes/features/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/features", "npm:1.59.6"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/files-server", [\
|
||||
@@ -4934,14 +4930,15 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/models", [\
|
||||
["npm:1.45.5", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.45.5-29326e959c-15f26c11b2.zip/node_modules/@standardnotes/models/",\
|
||||
["npm:1.46.7", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.46.7-ef9a3fc3ad-50589454f1.zip/node_modules/@standardnotes/models/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/models", "npm:1.45.5"],\
|
||||
["@standardnotes/models", "npm:1.46.7"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.59.5"],\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/utils", "npm:1.16.5"],\
|
||||
["@standardnotes/features", "npm:1.59.6"],\
|
||||
["@standardnotes/responses", "npm:1.13.26"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||
["@standardnotes/utils", "npm:1.17.4"],\
|
||||
["lodash", "npm:4.17.21"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -4976,6 +4973,17 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.13.26", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.13.26-cd12940788-6c5e3bf896.zip/node_modules/@standardnotes/responses/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/responses", "npm:1.13.26"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.59.6"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/revisions-server", [\
|
||||
@@ -4986,7 +4994,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@aws-sdk/client-s3", "npm:3.342.0"],\
|
||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||
["@standardnotes/api", "npm:1.26.10"],\
|
||||
["@standardnotes/api", "npm:1.26.25"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
@@ -5135,6 +5143,14 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.13.4", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-sncrypto-common-npm-1.13.4-3186513fa6-48e0e207f2.zip/node_modules/@standardnotes/sncrypto-common/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/sncrypto-node", [\
|
||||
@@ -5170,7 +5186,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@aws-sdk/client-sns", "npm:3.342.0"],\
|
||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||
["@standardnotes/api", "npm:1.26.10"],\
|
||||
["@standardnotes/api", "npm:1.26.25"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
@@ -5178,6 +5194,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/dotenv", "npm:8.2.0"],\
|
||||
@@ -5187,6 +5204,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@types/newrelic", "npm:9.14.0"],\
|
||||
["@types/node", "npm:20.2.5"],\
|
||||
["@types/prettyjson", "npm:0.0.30"],\
|
||||
["@types/semver", "npm:7.5.0"],\
|
||||
["@types/ua-parser-js", "npm:0.7.36"],\
|
||||
["@types/uuid", "npm:8.3.4"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.59.8"],\
|
||||
@@ -5209,6 +5227,7 @@ const RAW_RUNTIME_STATE =
|
||||
["prettier", "npm:2.8.8"],\
|
||||
["prettyjson", "npm:1.2.5"],\
|
||||
["reflect-metadata", "npm:0.1.13"],\
|
||||
["semver", "npm:7.5.1"],\
|
||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.16"],\
|
||||
@@ -5253,6 +5272,17 @@ const RAW_RUNTIME_STATE =
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.17.4", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.17.4-e5908cc204-7cb3fc838d.zip/node_modules/@standardnotes/utils/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/utils", "npm:1.17.4"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["dompurify", "npm:2.4.5"],\
|
||||
["lodash", "npm:4.17.21"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/websockets-server", [\
|
||||
@@ -5262,7 +5292,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/websockets-server", "workspace:packages/websockets"],\
|
||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||
["@standardnotes/api", "npm:1.26.10"],\
|
||||
["@standardnotes/api", "npm:1.26.25"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,50 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.24.9](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.8...@standardnotes/analytics@2.24.9) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.8](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.7...@standardnotes/analytics@2.24.8) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.6...@standardnotes/analytics@2.24.7) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.5...@standardnotes/analytics@2.24.6) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.4...@standardnotes/analytics@2.24.5) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.3...@standardnotes/analytics@2.24.4) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.2...@standardnotes/analytics@2.24.3) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.1...@standardnotes/analytics@2.24.2) (2023-06-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.0...@standardnotes/analytics@2.24.1) (2023-06-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/server/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
|
||||
|
||||
# [2.24.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.23.7...@standardnotes/analytics@2.24.0) (2023-06-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **home-server:** add overriding environment variables in underlying services ([#621](https://github.com/standardnotes/server/issues/621)) ([f0cbec0](https://github.com/standardnotes/server/commit/f0cbec07b87d60dfad92072944553f76e0bea164))
|
||||
|
||||
## [2.23.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.23.6...@standardnotes/analytics@2.23.7) (2023-06-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.23.7",
|
||||
"version": "2.24.9",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -88,9 +88,9 @@ export class ContainerConfigLoader {
|
||||
}
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: env.get('LOG_LEVEL') || 'info',
|
||||
level: env.get('LOG_LEVEL', true) || 'info',
|
||||
format: winston.format.combine(...winstonFormatters),
|
||||
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
|
||||
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
|
||||
})
|
||||
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
|
||||
|
||||
|
||||
@@ -1,24 +1,9 @@
|
||||
import { AbstractEnv } from '@standardnotes/domain-core'
|
||||
import { config, DotenvParseOutput } from 'dotenv'
|
||||
import { injectable } from 'inversify'
|
||||
|
||||
@injectable()
|
||||
export class Env {
|
||||
private env?: DotenvParseOutput
|
||||
|
||||
public load(): void {
|
||||
export class Env extends AbstractEnv {
|
||||
load(): void {
|
||||
const output = config()
|
||||
this.env = <DotenvParseOutput>output.parsed
|
||||
}
|
||||
|
||||
public get(key: string, optional = false): string {
|
||||
if (!this.env) {
|
||||
this.load()
|
||||
}
|
||||
|
||||
if (!process.env[key] && !optional) {
|
||||
throw new Error(`Environment variable ${key} not set`)
|
||||
}
|
||||
|
||||
return <string>process.env[key]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
MODE=microservice # microservice | home-server
|
||||
LOG_LEVEL=debug
|
||||
NODE_ENV=development
|
||||
VERSION=development
|
||||
|
||||
@@ -3,6 +3,78 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.65.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.5...@standardnotes/api-gateway@1.65.6) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.4...@standardnotes/api-gateway@1.65.5) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.3...@standardnotes/api-gateway@1.65.4) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.2...@standardnotes/api-gateway@1.65.3) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.1...@standardnotes/api-gateway@1.65.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.0...@standardnotes/api-gateway@1.65.1) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
# [1.65.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.64.3...@standardnotes/api-gateway@1.65.0) (2023-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
* shared vaults functionality in api-gateway,auth,files,common,security,domain-events. ([#629](https://github.com/standardnotes/api-gateway/issues/629)) ([fa7fbe2](https://github.com/standardnotes/api-gateway/commit/fa7fbe26e7b0707fc21d71e04af76870f5248baf))
|
||||
|
||||
## [1.64.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.64.2...@standardnotes/api-gateway@1.64.3) (2023-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add debug logs for invalid-auth responses ([d5a8409](https://github.com/standardnotes/api-gateway/commit/d5a8409bb5d35b9caf410a36ea0d5cb747129e8d))
|
||||
|
||||
## [1.64.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.64.1...@standardnotes/api-gateway@1.64.2) (2023-06-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **home-server:** add debug logs about container initalizations ([0df4715](https://github.com/standardnotes/api-gateway/commit/0df471585fd5b4626ec2972f3b9a3e33b2830e65))
|
||||
|
||||
## [1.64.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.64.0...@standardnotes/api-gateway@1.64.1) (2023-06-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api-gateway:** direct call service proxy to return 400 responses instead of throwing errors ([e6a4cc3](https://github.com/standardnotes/api-gateway/commit/e6a4cc3098bdf84fc9d48ed0d9098ebb52afb0e7))
|
||||
|
||||
# [1.64.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.63.2...@standardnotes/api-gateway@1.64.0) (2023-06-05)
|
||||
|
||||
### Features
|
||||
|
||||
* **home-server:** allow running the home server with a mysql and redis configuration ([#622](https://github.com/standardnotes/api-gateway/issues/622)) ([d6e531d](https://github.com/standardnotes/api-gateway/commit/d6e531d4b6c1c80a894f6d7ec93632595268dd64))
|
||||
|
||||
## [1.63.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.63.1...@standardnotes/api-gateway@1.63.2) (2023-06-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/api-gateway/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
|
||||
|
||||
## [1.63.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.63.0...@standardnotes/api-gateway@1.63.1) (2023-06-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **home-server:** add default for VERSION environment variable ([2f569d4](https://github.com/standardnotes/api-gateway/commit/2f569d41047a802eb72ef1a3618ffe4df28a709c))
|
||||
|
||||
# [1.63.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.62.4...@standardnotes/api-gateway@1.63.0) (2023-06-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **home-server:** add overriding environment variables in underlying services ([#621](https://github.com/standardnotes/api-gateway/issues/621)) ([f0cbec0](https://github.com/standardnotes/api-gateway/commit/f0cbec07b87d60dfad92072944553f76e0bea164))
|
||||
|
||||
## [1.62.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.62.3...@standardnotes/api-gateway@1.62.4) (2023-06-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -16,6 +16,8 @@ import '../src/Controller/v1/OfflineController'
|
||||
import '../src/Controller/v1/FilesController'
|
||||
import '../src/Controller/v1/SubscriptionInvitesController'
|
||||
import '../src/Controller/v1/AuthenticatorsController'
|
||||
import '../src/Controller/v1/AsymmetricMessagesController'
|
||||
import '../src/Controller/v1/SharedVaultsController'
|
||||
|
||||
import '../src/Controller/v2/PaymentsControllerV2'
|
||||
import '../src/Controller/v2/ActionsControllerV2'
|
||||
@@ -45,28 +47,29 @@ void container.load().then((container) => {
|
||||
response.setHeader('X-API-Gateway-Version', container.get(TYPES.VERSION))
|
||||
next()
|
||||
})
|
||||
/* eslint-disable */
|
||||
app.use(helmet({
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
defaultSrc: ["https: 'self'"],
|
||||
baseUri: ["'self'"],
|
||||
childSrc: ["*", "blob:"],
|
||||
connectSrc: ["*"],
|
||||
fontSrc: ["*", "'self'"],
|
||||
formAction: ["'self'"],
|
||||
frameAncestors: ["*", "*.standardnotes.org", "*.standardnotes.com"],
|
||||
frameSrc: ["*", "blob:"],
|
||||
imgSrc: ["'self'", "*", "data:"],
|
||||
manifestSrc: ["'self'"],
|
||||
mediaSrc: ["'self'"],
|
||||
objectSrc: ["'self'"],
|
||||
scriptSrc: ["'self'"],
|
||||
styleSrc: ["'self'"]
|
||||
}
|
||||
}
|
||||
}))
|
||||
/* eslint-enable */
|
||||
app.use(
|
||||
helmet({
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
defaultSrc: ["https: 'self'"],
|
||||
baseUri: ["'self'"],
|
||||
childSrc: ['*', 'blob:'],
|
||||
connectSrc: ['*'],
|
||||
fontSrc: ['*', "'self'"],
|
||||
formAction: ["'self'"],
|
||||
frameAncestors: ['*', '*.standardnotes.org', '*.standardnotes.com'],
|
||||
frameSrc: ['*', 'blob:'],
|
||||
imgSrc: ["'self'", '*', 'data:'],
|
||||
manifestSrc: ["'self'"],
|
||||
mediaSrc: ["'self'"],
|
||||
objectSrc: ["'self'"],
|
||||
scriptSrc: ["'self'"],
|
||||
styleSrc: ["'self'"],
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
app.use(json({ limit: '50mb' }))
|
||||
app.use(
|
||||
text({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.62.4",
|
||||
"version": "1.65.6",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -24,13 +24,18 @@ import { OptionalCrossServiceTokenMiddleware } from '../Controller/OptionalCross
|
||||
import { Transform } from 'stream'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
async load(configuration?: { serviceContainer?: ServiceContainerInterface; logger?: Transform }): Promise<Container> {
|
||||
const env: Env = new Env()
|
||||
async load(configuration?: {
|
||||
serviceContainer?: ServiceContainerInterface
|
||||
logger?: Transform
|
||||
environmentOverrides?: { [name: string]: string }
|
||||
}): Promise<Container> {
|
||||
const env: Env = new Env(configuration?.environmentOverrides)
|
||||
env.load()
|
||||
|
||||
const container = new Container()
|
||||
|
||||
const isConfiguredForHomeServer = env.get('CACHE_TYPE') === 'memory'
|
||||
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
|
||||
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
|
||||
|
||||
const winstonFormatters = [winston.format.splat(), winston.format.json()]
|
||||
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
|
||||
@@ -41,19 +46,20 @@ export class ContainerConfigLoader {
|
||||
winstonFormatters.push(newrelicWinstonFormatter())
|
||||
}
|
||||
|
||||
let logger: winston.Logger
|
||||
if (configuration?.logger) {
|
||||
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(configuration.logger as winston.Logger)
|
||||
logger = configuration.logger as winston.Logger
|
||||
} else {
|
||||
const logger = winston.createLogger({
|
||||
level: env.get('LOG_LEVEL') || 'info',
|
||||
logger = winston.createLogger({
|
||||
level: env.get('LOG_LEVEL', true) || 'info',
|
||||
format: winston.format.combine(...winstonFormatters),
|
||||
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
|
||||
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
|
||||
defaultMeta: { service: 'api-gateway' },
|
||||
})
|
||||
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
|
||||
}
|
||||
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
|
||||
|
||||
if (!isConfiguredForHomeServer) {
|
||||
if (!isConfiguredForInMemoryCache) {
|
||||
const redisUrl = env.get('REDIS_URL')
|
||||
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||
let redis
|
||||
@@ -79,7 +85,7 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind(TYPES.HTTP_CALL_TIMEOUT)
|
||||
.toConstantValue(env.get('HTTP_CALL_TIMEOUT', true) ? +env.get('HTTP_CALL_TIMEOUT', true) : 60_000)
|
||||
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION'))
|
||||
container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION', true) ?? 'development')
|
||||
container.bind(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL).toConstantValue(+env.get('CROSS_SERVICE_TOKEN_CACHE_TTL', true))
|
||||
|
||||
// Middleware
|
||||
@@ -120,6 +126,8 @@ export class ContainerConfigLoader {
|
||||
.bind<EndpointResolverInterface>(TYPES.EndpointResolver)
|
||||
.toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
|
||||
|
||||
logger.debug('Configuration complete')
|
||||
|
||||
return container
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,9 @@
|
||||
import { AbstractEnv } from '@standardnotes/domain-core'
|
||||
import { config, DotenvParseOutput } from 'dotenv'
|
||||
import { injectable } from 'inversify'
|
||||
|
||||
@injectable()
|
||||
export class Env {
|
||||
private env?: DotenvParseOutput
|
||||
|
||||
public load(): void {
|
||||
export class Env extends AbstractEnv {
|
||||
load(): void {
|
||||
const output = config()
|
||||
this.env = <DotenvParseOutput>output.parsed
|
||||
}
|
||||
|
||||
public get(key: string, optional = false): string {
|
||||
if (!this.env) {
|
||||
this.load()
|
||||
}
|
||||
|
||||
if (!process.env[key] && !optional) {
|
||||
throw new Error(`Environment variable ${key} not set`)
|
||||
}
|
||||
|
||||
return <string>process.env[key]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
import { ServiceContainerInterface, ServiceIdentifier, ServiceInterface } from '@standardnotes/domain-core'
|
||||
import {
|
||||
ServiceConfiguration,
|
||||
ServiceContainerInterface,
|
||||
ServiceIdentifier,
|
||||
ServiceInterface,
|
||||
} from '@standardnotes/domain-core'
|
||||
|
||||
import { ContainerConfigLoader } from './Container'
|
||||
import { Transform } from 'stream'
|
||||
|
||||
export class Service implements ServiceInterface {
|
||||
private logger: Transform | undefined
|
||||
|
||||
constructor(private serviceContainer: ServiceContainerInterface) {
|
||||
this.serviceContainer.register(this.getId(), this)
|
||||
}
|
||||
|
||||
setLogger(logger: Transform): void {
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
async handleRequest(_request: never, _response: never, _endpointOrMethodIdentifier: string): Promise<unknown> {
|
||||
throw new Error('Requests are handled via inversify-express at ApiGateway level')
|
||||
}
|
||||
|
||||
async getContainer(): Promise<unknown> {
|
||||
async getContainer(configuration?: ServiceConfiguration): Promise<unknown> {
|
||||
const config = new ContainerConfigLoader()
|
||||
|
||||
return config.load({
|
||||
serviceContainer: this.serviceContainer,
|
||||
logger: this.logger,
|
||||
logger: configuration?.logger,
|
||||
environmentOverrides: configuration?.environmentOverrides,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export abstract class AuthMiddleware extends BaseMiddleware {
|
||||
private crossServiceTokenCacheTTL: number,
|
||||
private crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||
private timer: TimerInterface,
|
||||
private logger: Logger,
|
||||
protected logger: Logger,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ export class RequiredCrossServiceTokenMiddleware extends AuthMiddleware {
|
||||
_next: NextFunction,
|
||||
): boolean {
|
||||
if (!authHeaderValue) {
|
||||
this.logger.debug('Missing auth header')
|
||||
|
||||
response.status(401).send({
|
||||
error: {
|
||||
tag: 'invalid-auth',
|
||||
|
||||
@@ -4,6 +4,7 @@ export * from './SubscriptionTokenAuthMiddleware'
|
||||
export * from './TokenAuthenticationMethod'
|
||||
export * from './WebSocketAuthMiddleware'
|
||||
export * from './v1/ActionsController'
|
||||
export * from './v1/AsymmetricMessagesController'
|
||||
export * from './v1/AuthenticatorsController'
|
||||
export * from './v1/FilesController'
|
||||
export * from './v1/InvoicesController'
|
||||
@@ -12,6 +13,7 @@ export * from './v1/OfflineController'
|
||||
export * from './v1/PaymentsController'
|
||||
export * from './v1/RevisionsController'
|
||||
export * from './v1/SessionsController'
|
||||
export * from './v1/SharedVaultsController'
|
||||
export * from './v1/SubscriptionInvitesController'
|
||||
export * from './v1/TokensController'
|
||||
export * from './v1/UsersController'
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import { BaseHttpController, controller, all } from 'inversify-express-utils'
|
||||
import { TYPES } from '../../Bootstrap/Types'
|
||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
|
||||
@controller('/v1/asymmetric-messages')
|
||||
export class AsymmetricMessagesController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface) {
|
||||
super()
|
||||
}
|
||||
|
||||
@all('*', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
async subscriptions(request: Request, response: Response): Promise<void> {
|
||||
await this.serviceProxy.callSyncingServer(request, response, request.path.replace('/v1/', ''), request.body)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import { BaseHttpController, controller, all } from 'inversify-express-utils'
|
||||
import { TYPES } from '../../Bootstrap/Types'
|
||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
|
||||
@controller('/v1/shared-vaults')
|
||||
export class SharedVaultsController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface) {
|
||||
super()
|
||||
}
|
||||
|
||||
@all('*', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
async subscriptions(request: Request, response: Response): Promise<void> {
|
||||
await this.serviceProxy.callSyncingServer(request, response, request.path.replace('/v1/', ''), request.body)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { ServiceContainerInterface, ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
import { ServiceProxyInterface } from '../Http/ServiceProxyInterface'
|
||||
import { ServiceContainerInterface, ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
export class DirectCallServiceProxy implements ServiceProxyInterface {
|
||||
constructor(private serviceContainer: ServiceContainerInterface, private filesServerUrl: string) {}
|
||||
@@ -34,8 +34,12 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
|
||||
}
|
||||
}
|
||||
|
||||
async callEmailServer(_request: Request, _response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
|
||||
throw new Error('Email server is not available.')
|
||||
async callEmailServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
|
||||
response.status(400).send({
|
||||
error: {
|
||||
message: 'Email server is not available.',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async callAuthServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
|
||||
@@ -54,10 +58,14 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
|
||||
|
||||
async callAuthServerWithLegacyFormat(
|
||||
_request: Request,
|
||||
_response: Response,
|
||||
response: Response,
|
||||
_endpointOrMethodIdentifier: string,
|
||||
): Promise<void> {
|
||||
throw new Error('Legacy auth endpoints are no longer available.')
|
||||
response.status(400).send({
|
||||
error: {
|
||||
message: 'Legacy auth endpoints are no longer available.',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async callRevisionsServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
|
||||
@@ -92,22 +100,30 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
|
||||
|
||||
async callLegacySyncingServer(
|
||||
_request: Request,
|
||||
_response: Response,
|
||||
response: Response,
|
||||
_endpointOrMethodIdentifier: string,
|
||||
): Promise<void> {
|
||||
throw new Error('Legacy syncing server endpoints are no longer available.')
|
||||
response.status(400).send({
|
||||
error: {
|
||||
message: 'Legacy syncing server endpoints are no longer available.',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async callPaymentsServer(_request: Request, _response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
|
||||
throw new Error('Payments server is not available.')
|
||||
async callPaymentsServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
|
||||
response.status(400).send({
|
||||
error: {
|
||||
message: 'Payments server is not available.',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async callWebSocketServer(
|
||||
_request: Request,
|
||||
_response: Response,
|
||||
_endpointOrMethodIdentifier: string,
|
||||
): Promise<void> {
|
||||
throw new Error('Websockets server is not available.')
|
||||
async callWebSocketServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
|
||||
response.status(400).send({
|
||||
error: {
|
||||
message: 'Websockets server is not available.',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
private sendDecoratedResponse(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
MODE=microservice # microservice | home-server
|
||||
LOG_LEVEL=debug
|
||||
NODE_ENV=development
|
||||
VERSION=development
|
||||
@@ -20,8 +21,10 @@ DB_USERNAME=auth
|
||||
DB_PASSWORD=changeme123
|
||||
DB_DATABASE=auth
|
||||
DB_DEBUG_LEVEL=all # "all" | "query" | "schema" | "error" | "warn" | "info" | "log" | "migration"
|
||||
DB_TYPE=mysql
|
||||
|
||||
REDIS_URL=redis://cache
|
||||
CACHE_TYPE=redis
|
||||
|
||||
DISABLE_USER_REGISTRATION=false
|
||||
|
||||
|
||||
@@ -3,6 +3,118 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.123.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.122.2...@standardnotes/auth-server@1.123.0) (2023-07-12)
|
||||
|
||||
### Features
|
||||
|
||||
* domain items ([#655](https://github.com/standardnotes/server/issues/655)) ([a0af8f0](https://github.com/standardnotes/server/commit/a0af8f00252e1219e58cb7e066c11a8e71692e9d))
|
||||
|
||||
## [1.122.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.122.1...@standardnotes/auth-server@1.122.2) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/server/issues/648)) ([c288e5d](https://github.com/standardnotes/server/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
## [1.122.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.122.0...@standardnotes/auth-server@1.122.1) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.122.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.121.0...@standardnotes/auth-server@1.122.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.121.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.120.2...@standardnotes/auth-server@1.121.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add notifications model ([#638](https://github.com/standardnotes/server/issues/638)) ([fecfd54](https://github.com/standardnotes/server/commit/fecfd5472824b5adae708db95d351e4ad65ee87b))
|
||||
|
||||
## [1.120.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.120.1...@standardnotes/auth-server@1.120.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.120.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.120.0...@standardnotes/auth-server@1.120.1) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.120.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.6...@standardnotes/auth-server@1.120.0) (2023-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
* shared vaults functionality in api-gateway,auth,files,common,security,domain-events. ([#629](https://github.com/standardnotes/server/issues/629)) ([fa7fbe2](https://github.com/standardnotes/server/commit/fa7fbe26e7b0707fc21d71e04af76870f5248baf))
|
||||
|
||||
## [1.119.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.5...@standardnotes/auth-server@1.119.6) (2023-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** add debug logs for authentication method resolver ([d220ec5](https://github.com/standardnotes/server/commit/d220ec5bf7509f9eb19dcda71c3667aaf388a35b))
|
||||
|
||||
## [1.119.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.4...@standardnotes/auth-server@1.119.5) (2023-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add debug logs for invalid-auth responses ([d5a8409](https://github.com/standardnotes/server/commit/d5a8409bb5d35b9caf410a36ea0d5cb747129e8d))
|
||||
|
||||
## [1.119.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.3...@standardnotes/auth-server@1.119.4) (2023-06-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.119.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.2...@standardnotes/auth-server@1.119.3) (2023-06-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **home-server:** add debug logs about container initalizations ([0df4715](https://github.com/standardnotes/server/commit/0df471585fd5b4626ec2972f3b9a3e33b2830e65))
|
||||
|
||||
## [1.119.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.1...@standardnotes/auth-server@1.119.2) (2023-06-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **home-server:** env var determining the sqlite database location ([#626](https://github.com/standardnotes/server/issues/626)) ([0cb5e36](https://github.com/standardnotes/server/commit/0cb5e36b20d9b095ea0edbcd877387e6c0069856))
|
||||
|
||||
## [1.119.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.119.0...@standardnotes/auth-server@1.119.1) (2023-06-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **home-server:** add default value for valet token ttl ([c201ee4](https://github.com/standardnotes/server/commit/c201ee42a00d9e5402afea2f2c5848a362c1529e))
|
||||
|
||||
# [1.119.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.118.0...@standardnotes/auth-server@1.119.0) (2023-06-09)
|
||||
|
||||
### Features
|
||||
|
||||
* **home-server:** add activating premium features ([#624](https://github.com/standardnotes/server/issues/624)) ([72ce190](https://github.com/standardnotes/server/commit/72ce1909960fbd2ec6a47b8dbdfbe53a4f10e776))
|
||||
|
||||
# [1.118.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.117.0...@standardnotes/auth-server@1.118.0) (2023-06-07)
|
||||
|
||||
### Features
|
||||
|
||||
* configurable path for uploads and db ([#623](https://github.com/standardnotes/server/issues/623)) ([af8feaa](https://github.com/standardnotes/server/commit/af8feaadfe2dd58baab4cca217d6307b4a221326))
|
||||
|
||||
# [1.117.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.116.2...@standardnotes/auth-server@1.117.0) (2023-06-05)
|
||||
|
||||
### Features
|
||||
|
||||
* **home-server:** allow running the home server with a mysql and redis configuration ([#622](https://github.com/standardnotes/server/issues/622)) ([d6e531d](https://github.com/standardnotes/server/commit/d6e531d4b6c1c80a894f6d7ec93632595268dd64))
|
||||
|
||||
## [1.116.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.116.1...@standardnotes/auth-server@1.116.2) (2023-06-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **home-server:** streaming logs ([a8b806a](https://github.com/standardnotes/server/commit/a8b806af084b3e3fe8707ff0cb041a74042ee049))
|
||||
|
||||
## [1.116.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.116.0...@standardnotes/auth-server@1.116.1) (2023-06-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* initializing data source with already configured environment ([624b574](https://github.com/standardnotes/server/commit/624b574013157e9e044d4a8ed53cadb7fcc567ae))
|
||||
|
||||
# [1.116.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.115.5...@standardnotes/auth-server@1.116.0) (2023-06-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **home-server:** add overriding environment variables in underlying services ([#621](https://github.com/standardnotes/server/issues/621)) ([f0cbec0](https://github.com/standardnotes/server/commit/f0cbec07b87d60dfad92072944553f76e0bea164))
|
||||
|
||||
## [1.115.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.115.4...@standardnotes/auth-server@1.115.5) (2023-06-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddNotifications1688540448427 implements MigrationInterface {
|
||||
name = 'AddNotifications1688540448427'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE `notifications` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `type` varchar(36) NOT NULL, `payload` text NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `index_notifications_on_user_uuid` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `index_notifications_on_user_uuid` ON `notifications`')
|
||||
await queryRunner.query('DROP TABLE `notifications`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class RemoveNotifications1688540448428 implements MigrationInterface {
|
||||
name = 'RemoveNotifications1688540448428'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `index_notifications_on_user_uuid` ON `notifications`')
|
||||
await queryRunner.query('DROP TABLE `notifications`')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE `notifications` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `type` varchar(36) NOT NULL, `payload` text NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `index_notifications_on_user_uuid` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddNotifications1688540623272 implements MigrationInterface {
|
||||
name = 'AddNotifications1688540623272'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "notifications" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(36) NOT NULL, "type" varchar(36) NOT NULL, "payload" text NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query('CREATE INDEX "index_notifications_on_user_uuid" ON "notifications" ("user_uuid") ')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_notifications_on_user_uuid"')
|
||||
await queryRunner.query('DROP TABLE "notifications"')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class RemoveNotifications1688540623273 implements MigrationInterface {
|
||||
name = 'RemoveNotifications1688540623273'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_notifications_on_user_uuid"')
|
||||
await queryRunner.query('DROP TABLE "notifications"')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "notifications" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(36) NOT NULL, "type" varchar(36) NOT NULL, "payload" text NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query('CREATE INDEX "index_notifications_on_user_uuid" ON "notifications" ("user_uuid") ')
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.115.5",
|
||||
"version": "1.123.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -32,7 +32,8 @@
|
||||
"weekly-backup:email": "yarn node dist/bin/backup.js email weekly",
|
||||
"content-recalculation": "yarn node dist/bin/content.js",
|
||||
"typeorm": "typeorm-ts-node-commonjs",
|
||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
|
||||
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'",
|
||||
"migrate": "yarn build && yarn typeorm migration:run -d dist/src/Bootstrap/DataSource.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sns": "^3.332.0",
|
||||
@@ -41,7 +42,7 @@
|
||||
"@cbor-extract/cbor-extract-linux-x64": "^2.1.1",
|
||||
"@simplewebauthn/server": "^7.2.0",
|
||||
"@simplewebauthn/typescript-types": "^7.0.0",
|
||||
"@standardnotes/api": "^1.25.3",
|
||||
"@standardnotes/api": "^1.26.25",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { Result, ServiceInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export interface AuthServiceInterface extends ServiceInterface {
|
||||
activatePremiumFeatures(username: string): Promise<Result<string>>
|
||||
}
|
||||
@@ -251,38 +251,23 @@ import { HomeServerValetTokenController } from '../Infra/InversifyExpressUtils/H
|
||||
import { HomeServerWebSocketsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerWebSocketsController'
|
||||
import { HomeServerSessionsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSessionsController'
|
||||
import { Transform } from 'stream'
|
||||
import { ActivatePremiumFeatures } from '../Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeatures'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
async load(configuration?: {
|
||||
controllerConatiner?: ControllerContainerInterface
|
||||
directCallDomainEventPublisher?: DirectCallDomainEventPublisher
|
||||
logger?: Transform
|
||||
environmentOverrides?: { [name: string]: string }
|
||||
}): Promise<Container> {
|
||||
const directCallDomainEventPublisher =
|
||||
configuration?.directCallDomainEventPublisher ?? new DirectCallDomainEventPublisher()
|
||||
|
||||
const env: Env = new Env()
|
||||
const env: Env = new Env(configuration?.environmentOverrides)
|
||||
env.load()
|
||||
|
||||
const container = new Container()
|
||||
|
||||
await AppDataSource.initialize()
|
||||
|
||||
const isConfiguredForHomeServer = env.get('DB_TYPE') === 'sqlite'
|
||||
|
||||
if (!isConfiguredForHomeServer) {
|
||||
const redisUrl = env.get('REDIS_URL')
|
||||
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||
let redis
|
||||
if (isRedisInClusterMode) {
|
||||
redis = new Redis.Cluster(redisUrl.split(','))
|
||||
} else {
|
||||
redis = new Redis(redisUrl)
|
||||
}
|
||||
|
||||
container.bind(TYPES.Auth_Redis).toConstantValue(redis)
|
||||
}
|
||||
|
||||
const winstonFormatters = [winston.format.splat(), winston.format.json()]
|
||||
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
|
||||
await import('newrelic')
|
||||
@@ -292,16 +277,38 @@ export class ContainerConfigLoader {
|
||||
winstonFormatters.push(newrelicWinstonFormatter())
|
||||
}
|
||||
|
||||
let logger: winston.Logger
|
||||
if (configuration?.logger) {
|
||||
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(configuration.logger as winston.Logger)
|
||||
logger = configuration.logger as winston.Logger
|
||||
} else {
|
||||
const logger = winston.createLogger({
|
||||
level: env.get('LOG_LEVEL') || 'info',
|
||||
logger = winston.createLogger({
|
||||
level: env.get('LOG_LEVEL', true) || 'info',
|
||||
format: winston.format.combine(...winstonFormatters),
|
||||
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL') || 'info' })],
|
||||
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
|
||||
defaultMeta: { service: 'auth' },
|
||||
})
|
||||
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(logger)
|
||||
}
|
||||
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(logger)
|
||||
|
||||
const appDataSource = new AppDataSource(env)
|
||||
await appDataSource.initialize()
|
||||
|
||||
logger.debug('Database initialized')
|
||||
|
||||
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
|
||||
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
|
||||
|
||||
if (!isConfiguredForInMemoryCache) {
|
||||
const redisUrl = env.get('REDIS_URL')
|
||||
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||
let redis
|
||||
if (isRedisInClusterMode) {
|
||||
redis = new Redis.Cluster(redisUrl.split(','))
|
||||
} else {
|
||||
redis = new Redis(redisUrl)
|
||||
}
|
||||
|
||||
container.bind(TYPES.Auth_Redis).toConstantValue(redis)
|
||||
}
|
||||
|
||||
container.bind<TimerInterface>(TYPES.Auth_Timer).toConstantValue(new Timer())
|
||||
@@ -358,42 +365,42 @@ export class ContainerConfigLoader {
|
||||
// ORM
|
||||
container
|
||||
.bind<Repository<OfflineSetting>>(TYPES.Auth_ORMOfflineSettingRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(OfflineSetting))
|
||||
.toConstantValue(appDataSource.getRepository(OfflineSetting))
|
||||
container
|
||||
.bind<Repository<OfflineUserSubscription>>(TYPES.Auth_ORMOfflineUserSubscriptionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(OfflineUserSubscription))
|
||||
.toConstantValue(appDataSource.getRepository(OfflineUserSubscription))
|
||||
container
|
||||
.bind<Repository<RevokedSession>>(TYPES.Auth_ORMRevokedSessionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(RevokedSession))
|
||||
container.bind<Repository<Role>>(TYPES.Auth_ORMRoleRepository).toConstantValue(AppDataSource.getRepository(Role))
|
||||
.toConstantValue(appDataSource.getRepository(RevokedSession))
|
||||
container.bind<Repository<Role>>(TYPES.Auth_ORMRoleRepository).toConstantValue(appDataSource.getRepository(Role))
|
||||
container
|
||||
.bind<Repository<Session>>(TYPES.Auth_ORMSessionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(Session))
|
||||
.toConstantValue(appDataSource.getRepository(Session))
|
||||
container
|
||||
.bind<Repository<Setting>>(TYPES.Auth_ORMSettingRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(Setting))
|
||||
.toConstantValue(appDataSource.getRepository(Setting))
|
||||
container
|
||||
.bind<Repository<SharedSubscriptionInvitation>>(TYPES.Auth_ORMSharedSubscriptionInvitationRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(SharedSubscriptionInvitation))
|
||||
.toConstantValue(appDataSource.getRepository(SharedSubscriptionInvitation))
|
||||
container
|
||||
.bind<Repository<SubscriptionSetting>>(TYPES.Auth_ORMSubscriptionSettingRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(SubscriptionSetting))
|
||||
container.bind<Repository<User>>(TYPES.Auth_ORMUserRepository).toConstantValue(AppDataSource.getRepository(User))
|
||||
.toConstantValue(appDataSource.getRepository(SubscriptionSetting))
|
||||
container.bind<Repository<User>>(TYPES.Auth_ORMUserRepository).toConstantValue(appDataSource.getRepository(User))
|
||||
container
|
||||
.bind<Repository<UserSubscription>>(TYPES.Auth_ORMUserSubscriptionRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(UserSubscription))
|
||||
.toConstantValue(appDataSource.getRepository(UserSubscription))
|
||||
container
|
||||
.bind<Repository<TypeORMSessionTrace>>(TYPES.Auth_ORMSessionTraceRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(TypeORMSessionTrace))
|
||||
.toConstantValue(appDataSource.getRepository(TypeORMSessionTrace))
|
||||
container
|
||||
.bind<Repository<TypeORMAuthenticator>>(TYPES.Auth_ORMAuthenticatorRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(TypeORMAuthenticator))
|
||||
.toConstantValue(appDataSource.getRepository(TypeORMAuthenticator))
|
||||
container
|
||||
.bind<Repository<TypeORMAuthenticatorChallenge>>(TYPES.Auth_ORMAuthenticatorChallengeRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(TypeORMAuthenticatorChallenge))
|
||||
.toConstantValue(appDataSource.getRepository(TypeORMAuthenticatorChallenge))
|
||||
container
|
||||
.bind<Repository<TypeORMCacheEntry>>(TYPES.Auth_ORMCacheEntryRepository)
|
||||
.toConstantValue(AppDataSource.getRepository(TypeORMCacheEntry))
|
||||
.toConstantValue(appDataSource.getRepository(TypeORMCacheEntry))
|
||||
|
||||
// Repositories
|
||||
container.bind<SessionRepositoryInterface>(TYPES.Auth_SessionRepository).to(TypeORMSessionRepository)
|
||||
@@ -486,7 +493,9 @@ export class ContainerConfigLoader {
|
||||
.bind(TYPES.Auth_AUTH_JWT_TTL)
|
||||
.toConstantValue(env.get('AUTH_JWT_TTL', true) ? +env.get('AUTH_JWT_TTL') : 60_000)
|
||||
container.bind(TYPES.Auth_VALET_TOKEN_SECRET).toConstantValue(env.get('VALET_TOKEN_SECRET', true))
|
||||
container.bind(TYPES.Auth_VALET_TOKEN_TTL).toConstantValue(+env.get('VALET_TOKEN_TTL', true))
|
||||
container
|
||||
.bind(TYPES.Auth_VALET_TOKEN_TTL)
|
||||
.toConstantValue(env.get('VALET_TOKEN_TTL', true) ? +env.get('VALET_TOKEN_TTL', true) : 7200)
|
||||
container
|
||||
.bind(TYPES.Auth_WEB_SOCKET_CONNECTION_TOKEN_SECRET)
|
||||
.toConstantValue(env.get('WEB_SOCKET_CONNECTION_TOKEN_SECRET', true))
|
||||
@@ -548,7 +557,16 @@ export class ContainerConfigLoader {
|
||||
.bind(TYPES.Auth_READONLY_USERS)
|
||||
.toConstantValue(env.get('READONLY_USERS', true) ? env.get('READONLY_USERS', true).split(',') : [])
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
if (isConfiguredForInMemoryCache) {
|
||||
container
|
||||
.bind<PKCERepositoryInterface>(TYPES.Auth_PKCERepository)
|
||||
.toConstantValue(
|
||||
new TypeORMPKCERepository(
|
||||
container.get(TYPES.Auth_CacheEntryRepository),
|
||||
container.get(TYPES.Auth_Logger),
|
||||
container.get(TYPES.Auth_Timer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<LockRepositoryInterface>(TYPES.Auth_LockRepository)
|
||||
.toConstantValue(
|
||||
@@ -576,15 +594,6 @@ export class ContainerConfigLoader {
|
||||
container.get(TYPES.Auth_Timer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<PKCERepositoryInterface>(TYPES.Auth_PKCERepository)
|
||||
.toConstantValue(
|
||||
new TypeORMPKCERepository(
|
||||
container.get(TYPES.Auth_CacheEntryRepository),
|
||||
container.get(TYPES.Auth_Logger),
|
||||
container.get(TYPES.Auth_Timer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SubscriptionTokenRepositoryInterface>(TYPES.Auth_SubscriptionTokenRepository)
|
||||
.toConstantValue(
|
||||
@@ -778,6 +787,16 @@ export class ContainerConfigLoader {
|
||||
container.get(TYPES.Auth_CryptoNode),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<ActivatePremiumFeatures>(TYPES.Auth_ActivatePremiumFeatures)
|
||||
.toConstantValue(
|
||||
new ActivatePremiumFeatures(
|
||||
container.get(TYPES.Auth_UserRepository),
|
||||
container.get(TYPES.Auth_UserSubscriptionRepository),
|
||||
container.get(TYPES.Auth_RoleService),
|
||||
container.get(TYPES.Auth_Timer),
|
||||
),
|
||||
)
|
||||
|
||||
container
|
||||
.bind<CleanupSessionTraces>(TYPES.Auth_CleanupSessionTraces)
|
||||
@@ -1185,6 +1204,8 @@ export class ContainerConfigLoader {
|
||||
)
|
||||
}
|
||||
|
||||
logger.debug('Configuration complete')
|
||||
|
||||
return container
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DataSource, LoggerOptions } from 'typeorm'
|
||||
import { DataSource, EntityTarget, LoggerOptions, ObjectLiteral, Repository } from 'typeorm'
|
||||
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
|
||||
import { Permission } from '../Domain/Permission/Permission'
|
||||
import { Role } from '../Domain/Role/Role'
|
||||
@@ -19,88 +19,106 @@ import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
|
||||
import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
export class AppDataSource {
|
||||
private _dataSource: DataSource | undefined
|
||||
|
||||
const isConfiguredForMySQL = env.get('DB_TYPE') === 'mysql'
|
||||
constructor(private env: Env) {}
|
||||
|
||||
const maxQueryExecutionTime = env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
? +env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
: 45_000
|
||||
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
|
||||
if (!this._dataSource) {
|
||||
throw new Error('DataSource not initialized')
|
||||
}
|
||||
|
||||
const commonDataSourceOptions = {
|
||||
maxQueryExecutionTime,
|
||||
entities: [
|
||||
User,
|
||||
UserSubscription,
|
||||
OfflineUserSubscription,
|
||||
Session,
|
||||
RevokedSession,
|
||||
Role,
|
||||
Permission,
|
||||
Setting,
|
||||
OfflineSetting,
|
||||
SharedSubscriptionInvitation,
|
||||
SubscriptionSetting,
|
||||
TypeORMSessionTrace,
|
||||
TypeORMAuthenticator,
|
||||
TypeORMAuthenticatorChallenge,
|
||||
TypeORMEmergencyAccessInvitation,
|
||||
TypeORMCacheEntry,
|
||||
],
|
||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
logging: <LoggerOptions>env.get('DB_DEBUG_LEVEL', true) ?? 'info',
|
||||
return this._dataSource.getRepository(target)
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.dataSource.initialize()
|
||||
}
|
||||
|
||||
get dataSource(): DataSource {
|
||||
this.env.load()
|
||||
|
||||
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
|
||||
|
||||
const maxQueryExecutionTime = this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
? +this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||
: 45_000
|
||||
|
||||
const commonDataSourceOptions = {
|
||||
maxQueryExecutionTime,
|
||||
entities: [
|
||||
User,
|
||||
UserSubscription,
|
||||
OfflineUserSubscription,
|
||||
Session,
|
||||
RevokedSession,
|
||||
Role,
|
||||
Permission,
|
||||
Setting,
|
||||
OfflineSetting,
|
||||
SharedSubscriptionInvitation,
|
||||
SubscriptionSetting,
|
||||
TypeORMSessionTrace,
|
||||
TypeORMAuthenticator,
|
||||
TypeORMAuthenticatorChallenge,
|
||||
TypeORMEmergencyAccessInvitation,
|
||||
TypeORMCacheEntry,
|
||||
],
|
||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
logging: <LoggerOptions>this.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
|
||||
}
|
||||
|
||||
if (isConfiguredForMySQL) {
|
||||
const inReplicaMode = this.env.get('DB_REPLICA_HOST', true) ? true : false
|
||||
|
||||
const replicationConfig = {
|
||||
master: {
|
||||
host: this.env.get('DB_HOST'),
|
||||
port: parseInt(this.env.get('DB_PORT')),
|
||||
username: this.env.get('DB_USERNAME'),
|
||||
password: this.env.get('DB_PASSWORD'),
|
||||
database: this.env.get('DB_DATABASE'),
|
||||
},
|
||||
slaves: [
|
||||
{
|
||||
host: this.env.get('DB_REPLICA_HOST', true),
|
||||
port: parseInt(this.env.get('DB_PORT')),
|
||||
username: this.env.get('DB_USERNAME'),
|
||||
password: this.env.get('DB_PASSWORD'),
|
||||
database: this.env.get('DB_DATABASE'),
|
||||
},
|
||||
],
|
||||
removeNodeErrorCount: 10,
|
||||
restoreNodeTimeout: 5,
|
||||
}
|
||||
|
||||
const mySQLDataSourceOptions: MysqlConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
type: 'mysql',
|
||||
charset: 'utf8mb4',
|
||||
supportBigNumbers: true,
|
||||
bigNumberStrings: false,
|
||||
replication: inReplicaMode ? replicationConfig : undefined,
|
||||
host: inReplicaMode ? undefined : this.env.get('DB_HOST'),
|
||||
port: inReplicaMode ? undefined : parseInt(this.env.get('DB_PORT')),
|
||||
username: inReplicaMode ? undefined : this.env.get('DB_USERNAME'),
|
||||
password: inReplicaMode ? undefined : this.env.get('DB_PASSWORD'),
|
||||
database: inReplicaMode ? undefined : this.env.get('DB_DATABASE'),
|
||||
}
|
||||
|
||||
this._dataSource = new DataSource(mySQLDataSourceOptions)
|
||||
} else {
|
||||
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
type: 'sqlite',
|
||||
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
|
||||
}
|
||||
|
||||
this._dataSource = new DataSource(sqliteDataSourceOptions)
|
||||
}
|
||||
|
||||
return this._dataSource
|
||||
}
|
||||
}
|
||||
|
||||
let dataSource: DataSource
|
||||
if (isConfiguredForMySQL) {
|
||||
const inReplicaMode = env.get('DB_REPLICA_HOST', true) ? true : false
|
||||
|
||||
const replicationConfig = {
|
||||
master: {
|
||||
host: env.get('DB_HOST'),
|
||||
port: parseInt(env.get('DB_PORT')),
|
||||
username: env.get('DB_USERNAME'),
|
||||
password: env.get('DB_PASSWORD'),
|
||||
database: env.get('DB_DATABASE'),
|
||||
},
|
||||
slaves: [
|
||||
{
|
||||
host: env.get('DB_REPLICA_HOST', true),
|
||||
port: parseInt(env.get('DB_PORT')),
|
||||
username: env.get('DB_USERNAME'),
|
||||
password: env.get('DB_PASSWORD'),
|
||||
database: env.get('DB_DATABASE'),
|
||||
},
|
||||
],
|
||||
removeNodeErrorCount: 10,
|
||||
restoreNodeTimeout: 5,
|
||||
}
|
||||
|
||||
const mySQLDataSourceOptions: MysqlConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
type: 'mysql',
|
||||
charset: 'utf8mb4',
|
||||
supportBigNumbers: true,
|
||||
bigNumberStrings: false,
|
||||
replication: inReplicaMode ? replicationConfig : undefined,
|
||||
host: inReplicaMode ? undefined : env.get('DB_HOST'),
|
||||
port: inReplicaMode ? undefined : parseInt(env.get('DB_PORT')),
|
||||
username: inReplicaMode ? undefined : env.get('DB_USERNAME'),
|
||||
password: inReplicaMode ? undefined : env.get('DB_PASSWORD'),
|
||||
database: inReplicaMode ? undefined : env.get('DB_DATABASE'),
|
||||
}
|
||||
|
||||
dataSource = new DataSource(mySQLDataSourceOptions)
|
||||
} else {
|
||||
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
type: 'sqlite',
|
||||
database: `data/${env.get('DB_DATABASE')}.sqlite`,
|
||||
}
|
||||
|
||||
dataSource = new DataSource(sqliteDataSourceOptions)
|
||||
}
|
||||
|
||||
export const AppDataSource = dataSource
|
||||
|
||||
@@ -1,24 +1,9 @@
|
||||
import { AbstractEnv } from '@standardnotes/domain-core'
|
||||
import { config, DotenvParseOutput } from 'dotenv'
|
||||
import { injectable } from 'inversify'
|
||||
|
||||
@injectable()
|
||||
export class Env {
|
||||
private env?: DotenvParseOutput
|
||||
|
||||
public load(): void {
|
||||
export class Env extends AbstractEnv {
|
||||
load(): void {
|
||||
const output = config()
|
||||
this.env = <DotenvParseOutput>output.parsed
|
||||
}
|
||||
|
||||
public get(key: string, optional = false): string {
|
||||
if (!this.env) {
|
||||
this.load()
|
||||
}
|
||||
|
||||
if (!process.env[key] && !optional) {
|
||||
throw new Error(`Environment variable ${key} not set`)
|
||||
}
|
||||
|
||||
return <string>process.env[key]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { Env } from './Env'
|
||||
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
export const MigrationsDataSource = new AppDataSource(env).dataSource
|
||||
@@ -1,16 +1,20 @@
|
||||
import {
|
||||
ControllerContainerInterface,
|
||||
Result,
|
||||
ServiceConfiguration,
|
||||
ServiceContainerInterface,
|
||||
ServiceIdentifier,
|
||||
ServiceInterface,
|
||||
} from '@standardnotes/domain-core'
|
||||
|
||||
import { ContainerConfigLoader } from './Container'
|
||||
import { DirectCallDomainEventPublisher } from '@standardnotes/domain-events-infra'
|
||||
import { Transform } from 'stream'
|
||||
import TYPES from './Types'
|
||||
import { Container } from 'inversify'
|
||||
import { ActivatePremiumFeatures } from '../Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeatures'
|
||||
import { AuthServiceInterface } from './AuthServiceInterface'
|
||||
|
||||
export class Service implements ServiceInterface {
|
||||
private logger: Transform | undefined
|
||||
export class Service implements AuthServiceInterface {
|
||||
private container: Container | undefined
|
||||
|
||||
constructor(
|
||||
private serviceContainer: ServiceContainerInterface,
|
||||
@@ -20,8 +24,14 @@ export class Service implements ServiceInterface {
|
||||
this.serviceContainer.register(this.getId(), this)
|
||||
}
|
||||
|
||||
setLogger(logger: Transform): void {
|
||||
this.logger = logger
|
||||
async activatePremiumFeatures(username: string): Promise<Result<string>> {
|
||||
if (!this.container) {
|
||||
return Result.fail('Container not initialized')
|
||||
}
|
||||
|
||||
const activatePremiumFeatures = this.container.get(TYPES.Auth_ActivatePremiumFeatures) as ActivatePremiumFeatures
|
||||
|
||||
return activatePremiumFeatures.execute({ username })
|
||||
}
|
||||
|
||||
async handleRequest(request: never, response: never, endpointOrMethodIdentifier: string): Promise<unknown> {
|
||||
@@ -34,14 +44,19 @@ export class Service implements ServiceInterface {
|
||||
return method(request, response)
|
||||
}
|
||||
|
||||
async getContainer(): Promise<unknown> {
|
||||
async getContainer(configuration?: ServiceConfiguration): Promise<unknown> {
|
||||
const config = new ContainerConfigLoader()
|
||||
|
||||
return config.load({
|
||||
const container = await config.load({
|
||||
controllerConatiner: this.controllerContainer,
|
||||
directCallDomainEventPublisher: this.directCallDomainEventPublisher,
|
||||
logger: this.logger,
|
||||
logger: configuration?.logger,
|
||||
environmentOverrides: configuration?.environmentOverrides,
|
||||
})
|
||||
|
||||
this.container = container
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
getId(): ServiceIdentifier {
|
||||
|
||||
@@ -149,6 +149,7 @@ const TYPES = {
|
||||
Auth_ListAuthenticators: Symbol.for('Auth_ListAuthenticators'),
|
||||
Auth_DeleteAuthenticator: Symbol.for('Auth_DeleteAuthenticator'),
|
||||
Auth_GenerateRecoveryCodes: Symbol.for('Auth_GenerateRecoveryCodes'),
|
||||
Auth_ActivatePremiumFeatures: Symbol.for('Auth_ActivatePremiumFeatures'),
|
||||
Auth_SignInWithRecoveryCodes: Symbol.for('Auth_SignInWithRecoveryCodes'),
|
||||
Auth_GetUserKeyParamsRecovery: Symbol.for('Auth_GetUserKeyParamsRecovery'),
|
||||
// Handlers
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './AuthServiceInterface'
|
||||
export * from './Service'
|
||||
|
||||
@@ -8,12 +8,12 @@ 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 { ApiVersion } from '@standardnotes/api'
|
||||
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
|
||||
@@ -73,7 +73,7 @@ describe('AuthController', () => {
|
||||
email: 'test@test.te',
|
||||
password: 'asdzxc',
|
||||
version: ProtocolVersion.V004,
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
origination: KeyParamsOrigination.Registration,
|
||||
userAgent: 'Google Chrome',
|
||||
identifier: 'test@test.te',
|
||||
@@ -103,7 +103,7 @@ describe('AuthController', () => {
|
||||
email: 'test@test.te',
|
||||
password: '',
|
||||
version: ProtocolVersion.V004,
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
origination: KeyParamsOrigination.Registration,
|
||||
userAgent: 'Google Chrome',
|
||||
identifier: 'test@test.te',
|
||||
@@ -123,7 +123,7 @@ describe('AuthController', () => {
|
||||
email: 'test@test.te',
|
||||
password: 'test',
|
||||
version: ProtocolVersion.V004,
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
origination: KeyParamsOrigination.Registration,
|
||||
userAgent: 'Google Chrome',
|
||||
identifier: 'test@test.te',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import {
|
||||
ApiVersion,
|
||||
UserRegistrationRequestParams,
|
||||
UserServerInterface,
|
||||
UserDeletionResponseBody,
|
||||
UserRegistrationResponseBody,
|
||||
UserUpdateRequestParams,
|
||||
} from '@standardnotes/api'
|
||||
import { ErrorTag, HttpResponse, HttpStatusCode } from '@standardnotes/responses'
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
@@ -23,6 +23,8 @@ import { GenerateRecoveryCodes } from '../Domain/UseCase/GenerateRecoveryCodes/G
|
||||
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 {
|
||||
constructor(
|
||||
@@ -37,6 +39,10 @@ export class AuthController implements UserServerInterface {
|
||||
private sessionService: SessionServiceInterface,
|
||||
) {}
|
||||
|
||||
async update(_params: UserUpdateRequestParams): Promise<HttpResponse<UserUpdateResponse>> {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
async deleteAccount(_params: never): Promise<HttpResponse<UserDeletionResponseBody>> {
|
||||
throw new Error('This method is implemented on the payments server.')
|
||||
}
|
||||
@@ -121,7 +127,7 @@ export class AuthController implements UserServerInterface {
|
||||
async signInWithRecoveryCodes(
|
||||
params: SignInWithRecoveryCodesRequestParams,
|
||||
): Promise<HttpResponse<SignInWithRecoveryCodesResponseBody>> {
|
||||
if (params.apiVersion !== ApiVersion.v0) {
|
||||
if (params.apiVersion !== ApiVersion.v20200115) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
data: {
|
||||
@@ -162,7 +168,7 @@ export class AuthController implements UserServerInterface {
|
||||
async recoveryKeyParams(
|
||||
params: RecoveryKeyParamsRequestParams,
|
||||
): Promise<HttpResponse<RecoveryKeyParamsResponseBody>> {
|
||||
if (params.apiVersion !== ApiVersion.v0) {
|
||||
if (params.apiVersion !== ApiVersion.v20200115) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
data: {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AcceptSharedSubscriptionInvitation } from '../Domain/UseCase/AcceptShar
|
||||
import { DeclineSharedSubscriptionInvitation } from '../Domain/UseCase/DeclineSharedSubscriptionInvitation/DeclineSharedSubscriptionInvitation'
|
||||
import { CancelSharedSubscriptionInvitation } from '../Domain/UseCase/CancelSharedSubscriptionInvitation/CancelSharedSubscriptionInvitation'
|
||||
import { ListSharedSubscriptionInvitations } from '../Domain/UseCase/ListSharedSubscriptionInvitations/ListSharedSubscriptionInvitations'
|
||||
import { ApiVersion } from '@standardnotes/api'
|
||||
import { ApiVersion } from '../Domain/Api/ApiVersion'
|
||||
|
||||
describe('SubscriptionInvitesController', () => {
|
||||
let inviteToSharedSubscription: InviteToSharedSubscription
|
||||
@@ -53,7 +53,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
invitations: [],
|
||||
})
|
||||
|
||||
const result = await createController().listInvites({ api: ApiVersion.v0, inviterEmail: 'test@test.te' })
|
||||
const result = await createController().listInvites({ api: ApiVersion.v20200115, inviterEmail: 'test@test.te' })
|
||||
|
||||
expect(listSharedSubscriptionInvitations.execute).toHaveBeenCalledWith({
|
||||
inviterEmail: 'test@test.te',
|
||||
@@ -68,7 +68,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().cancelInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
})
|
||||
@@ -87,7 +87,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().cancelInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -100,7 +100,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().declineInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -117,7 +117,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().declineInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -134,7 +134,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().acceptInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -151,7 +151,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().acceptInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -168,7 +168,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().invite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
identifier: 'invitee@test.te',
|
||||
inviterUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
@@ -187,7 +187,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.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
identifier: '',
|
||||
inviterUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
@@ -205,7 +205,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().invite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
identifier: 'invitee@test.te',
|
||||
inviterUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import { SimpleUserProjection } from '../../Projection/SimpleUserProjection'
|
||||
|
||||
export interface AuthResponse {
|
||||
user: {
|
||||
uuid: string
|
||||
email: string
|
||||
protocolVersion: ProtocolVersion
|
||||
}
|
||||
user: SimpleUserProjection
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@ import {
|
||||
TokenEncoderInterface,
|
||||
} from '@standardnotes/security'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import { SessionBody } from '@standardnotes/responses'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { ProjectorInterface } from '../../Projection/ProjectorInterface'
|
||||
import { SimpleUserProjection } from '../../Projection/SimpleUserProjection'
|
||||
import { SessionServiceInterface } from '../Session/SessionServiceInterface'
|
||||
import { KeyParamsFactoryInterface } from '../User/KeyParamsFactoryInterface'
|
||||
import { User } from '../User/User'
|
||||
@@ -54,11 +54,7 @@ export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
||||
return {
|
||||
session: sessionPayload,
|
||||
key_params: this.keyParamsFactory.create(dto.user, true),
|
||||
user: this.userProjector.projectSimple(dto.user) as {
|
||||
uuid: string
|
||||
email: string
|
||||
protocolVersion: ProtocolVersion
|
||||
},
|
||||
user: this.userProjector.projectSimple(dto.user) as SimpleUserProjection,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { User } from '../User/User'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
|
||||
import { AuthenticationMethodResolver } from './AuthenticationMethodResolver'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
describe('AuthenticationMethodResolver', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
@@ -18,11 +19,15 @@ describe('AuthenticationMethodResolver', () => {
|
||||
let user: User
|
||||
let session: Session
|
||||
let revokedSession: RevokedSession
|
||||
let logger: Logger
|
||||
|
||||
const createResolver = () =>
|
||||
new AuthenticationMethodResolver(userRepository, sessionService, sessionTokenDecoder, fallbackTokenDecoder)
|
||||
new AuthenticationMethodResolver(userRepository, sessionService, sessionTokenDecoder, fallbackTokenDecoder, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.debug = jest.fn()
|
||||
|
||||
user = {} as jest.Mocked<User>
|
||||
|
||||
session = {} as jest.Mocked<Session>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { SessionServiceInterface } from '../Session/SessionServiceInterface'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { AuthenticationMethod } from './AuthenticationMethod'
|
||||
import { AuthenticationMethodResolverInterface } from './AuthenticationMethodResolverInterface'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@injectable()
|
||||
export class AuthenticationMethodResolver implements AuthenticationMethodResolverInterface {
|
||||
@@ -14,15 +15,20 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
|
||||
@inject(TYPES.Auth_SessionTokenDecoder) private sessionTokenDecoder: TokenDecoderInterface<SessionTokenData>,
|
||||
@inject(TYPES.Auth_FallbackSessionTokenDecoder)
|
||||
private fallbackSessionTokenDecoder: TokenDecoderInterface<SessionTokenData>,
|
||||
@inject(TYPES.Auth_Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async resolve(token: string): Promise<AuthenticationMethod | undefined> {
|
||||
let decodedToken: SessionTokenData | undefined = this.sessionTokenDecoder.decodeToken(token)
|
||||
if (decodedToken === undefined) {
|
||||
this.logger.debug('Could not decode token with primary decoder, trying fallback decoder.')
|
||||
|
||||
decodedToken = this.fallbackSessionTokenDecoder.decodeToken(token)
|
||||
}
|
||||
|
||||
if (decodedToken) {
|
||||
this.logger.debug('Token decoded successfully. User found.')
|
||||
|
||||
return {
|
||||
type: 'jwt',
|
||||
user: await this.userRepository.findOneByUuid(<string>decodedToken.user_uuid),
|
||||
@@ -32,6 +38,8 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
|
||||
|
||||
const session = await this.sessionService.getSessionFromToken(token)
|
||||
if (session) {
|
||||
this.logger.debug('Token decoded successfully. Session found.')
|
||||
|
||||
return {
|
||||
type: 'session_token',
|
||||
user: await this.userRepository.findOneByUuid(session.userUuid),
|
||||
@@ -41,6 +49,8 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
|
||||
|
||||
const revokedSession = await this.sessionService.getRevokedSessionFromToken(token)
|
||||
if (revokedSession) {
|
||||
this.logger.debug('Token decoded successfully. Revoked session found.')
|
||||
|
||||
return {
|
||||
type: 'revoked',
|
||||
revokedSession: await this.sessionService.markRevokedSessionAsReceived(revokedSession),
|
||||
@@ -48,6 +58,8 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug('Could not decode token.')
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ describe('SettingService', () => {
|
||||
user = {
|
||||
uuid: '4-5-6',
|
||||
} as jest.Mocked<User>
|
||||
user.isPotentiallyAVaultAccount = jest.fn().mockReturnValue(false)
|
||||
user.isPotentiallyAPrivateUsernameAccount = jest.fn().mockReturnValue(false)
|
||||
|
||||
setting = {
|
||||
name: SettingName.NAMES.DropboxBackupToken,
|
||||
@@ -66,7 +66,7 @@ describe('SettingService', () => {
|
||||
]),
|
||||
)
|
||||
|
||||
settingsAssociationService.getDefaultSettingsAndValuesForNewVaultAccount = jest.fn().mockReturnValue(
|
||||
settingsAssociationService.getDefaultSettingsAndValuesForNewPrivateUsernameAccount = jest.fn().mockReturnValue(
|
||||
new Map([
|
||||
[
|
||||
SettingName.NAMES.LogSessionUserAgent,
|
||||
@@ -98,7 +98,7 @@ describe('SettingService', () => {
|
||||
})
|
||||
|
||||
it('should create default settings for a newly registered vault account', async () => {
|
||||
user.isPotentiallyAVaultAccount = jest.fn().mockReturnValue(true)
|
||||
user.isPotentiallyAPrivateUsernameAccount = jest.fn().mockReturnValue(true)
|
||||
|
||||
await createService().applyDefaultSettingsUponRegistration(user)
|
||||
|
||||
|
||||
@@ -28,8 +28,9 @@ export class SettingService implements SettingServiceInterface {
|
||||
|
||||
async applyDefaultSettingsUponRegistration(user: User): Promise<void> {
|
||||
let defaultSettingsWithValues = this.settingsAssociationService.getDefaultSettingsAndValuesForNewUser()
|
||||
if (user.isPotentiallyAVaultAccount()) {
|
||||
defaultSettingsWithValues = this.settingsAssociationService.getDefaultSettingsAndValuesForNewVaultAccount()
|
||||
if (user.isPotentiallyAPrivateUsernameAccount()) {
|
||||
defaultSettingsWithValues =
|
||||
this.settingsAssociationService.getDefaultSettingsAndValuesForNewPrivateUsernameAccount()
|
||||
}
|
||||
|
||||
for (const settingName of defaultSettingsWithValues.keys()) {
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('SettingsAssociationService', () => {
|
||||
})
|
||||
|
||||
it('should return the default set of settings for a newly registered vault account', () => {
|
||||
const settings = createService().getDefaultSettingsAndValuesForNewVaultAccount()
|
||||
const settings = createService().getDefaultSettingsAndValuesForNewPrivateUsernameAccount()
|
||||
const flatSettings = [...(settings as Map<string, SettingDescription>).keys()]
|
||||
expect(flatSettings).toEqual(['MUTE_MARKETING_EMAILS', 'LOG_SESSION_USER_AGENT'])
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
],
|
||||
])
|
||||
|
||||
private readonly vaultAccountDefaultSettingsOverwrites = new Map<string, SettingDescription>([
|
||||
private readonly privateUsernameAccountDefaultSettingsOverwrites = new Map<string, SettingDescription>([
|
||||
[
|
||||
SettingName.NAMES.LogSessionUserAgent,
|
||||
{
|
||||
@@ -114,16 +114,18 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
return this.defaultSettings
|
||||
}
|
||||
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription> {
|
||||
const defaultVaultSettings = new Map(this.defaultSettings)
|
||||
getDefaultSettingsAndValuesForNewPrivateUsernameAccount(): Map<string, SettingDescription> {
|
||||
const defaultPrivateUsernameSettings = new Map(this.defaultSettings)
|
||||
|
||||
for (const vaultAccountDefaultSettingOverwriteKey of this.vaultAccountDefaultSettingsOverwrites.keys()) {
|
||||
defaultVaultSettings.set(
|
||||
vaultAccountDefaultSettingOverwriteKey,
|
||||
this.vaultAccountDefaultSettingsOverwrites.get(vaultAccountDefaultSettingOverwriteKey) as SettingDescription,
|
||||
for (const privateUsernameAccountDefaultSettingOverwriteKey of this.privateUsernameAccountDefaultSettingsOverwrites.keys()) {
|
||||
defaultPrivateUsernameSettings.set(
|
||||
privateUsernameAccountDefaultSettingOverwriteKey,
|
||||
this.privateUsernameAccountDefaultSettingsOverwrites.get(
|
||||
privateUsernameAccountDefaultSettingOverwriteKey,
|
||||
) as SettingDescription,
|
||||
)
|
||||
}
|
||||
|
||||
return defaultVaultSettings
|
||||
return defaultPrivateUsernameSettings
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { SettingDescription } from './SettingDescription'
|
||||
|
||||
export interface SettingsAssociationServiceInterface {
|
||||
getDefaultSettingsAndValuesForNewUser(): Map<string, SettingDescription>
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription>
|
||||
getDefaultSettingsAndValuesForNewPrivateUsernameAccount(): Map<string, SettingDescription>
|
||||
getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined
|
||||
getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion
|
||||
getSensitivityForSetting(settingName: SettingName): boolean
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
||||
import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
|
||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
|
||||
import { ActivatePremiumFeatures } from './ActivatePremiumFeatures'
|
||||
import { User } from '../../User/User'
|
||||
|
||||
describe('ActivatePremiumFeatures', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
let userSubscriptionRepository: UserSubscriptionRepositoryInterface
|
||||
let roleService: RoleServiceInterface
|
||||
let timer: TimerInterface
|
||||
let user: User
|
||||
|
||||
const createUseCase = () =>
|
||||
new ActivatePremiumFeatures(userRepository, userSubscriptionRepository, roleService, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
user = {} as jest.Mocked<User>
|
||||
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
userRepository.findOneByUsernameOrEmail = jest.fn().mockResolvedValue(user)
|
||||
|
||||
userSubscriptionRepository = {} as jest.Mocked<UserSubscriptionRepositoryInterface>
|
||||
userSubscriptionRepository.save = jest.fn()
|
||||
|
||||
roleService = {} as jest.Mocked<RoleServiceInterface>
|
||||
roleService.addUserRole = jest.fn()
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123456789)
|
||||
timer.convertDateToMicroseconds = jest.fn().mockReturnValue(123456789)
|
||||
timer.getUTCDateNDaysAhead = jest.fn().mockReturnValue(new Date('2024-01-01T00:00:00.000Z'))
|
||||
})
|
||||
|
||||
it('should return error when username is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ username: '' })
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Username cannot be empty')
|
||||
})
|
||||
|
||||
it('should return error when user is not found', async () => {
|
||||
userRepository.findOneByUsernameOrEmail = jest.fn().mockResolvedValue(null)
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ username: 'test@test.te' })
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('User not found with username: test@test.te')
|
||||
})
|
||||
|
||||
it('should save a subscription and add role to user', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ username: 'test@test.te' })
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
|
||||
expect(userSubscriptionRepository.save).toHaveBeenCalled()
|
||||
expect(roleService.addUserRole).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Result, SubscriptionPlanName, UseCaseInterface, Username } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
||||
import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
|
||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
import { UserSubscription } from '../../Subscription/UserSubscription'
|
||||
import { UserSubscriptionType } from '../../Subscription/UserSubscriptionType'
|
||||
import { ActivatePremiumFeaturesDTO } from './ActivatePremiumFeaturesDTO'
|
||||
|
||||
export class ActivatePremiumFeatures implements UseCaseInterface<string> {
|
||||
constructor(
|
||||
private userRepository: UserRepositoryInterface,
|
||||
private userSubscriptionRepository: UserSubscriptionRepositoryInterface,
|
||||
private roleService: RoleServiceInterface,
|
||||
private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: ActivatePremiumFeaturesDTO): Promise<Result<string>> {
|
||||
const usernameOrError = Username.create(dto.username)
|
||||
if (usernameOrError.isFailed()) {
|
||||
return Result.fail(usernameOrError.getError())
|
||||
}
|
||||
const username = usernameOrError.getValue()
|
||||
|
||||
const user = await this.userRepository.findOneByUsernameOrEmail(username)
|
||||
if (user === null) {
|
||||
return Result.fail(`User not found with username: ${username.value}`)
|
||||
}
|
||||
|
||||
const timestamp = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
const subscription = new UserSubscription()
|
||||
subscription.planName = SubscriptionPlanName.NAMES.ProPlan
|
||||
subscription.user = Promise.resolve(user)
|
||||
subscription.createdAt = timestamp
|
||||
subscription.updatedAt = timestamp
|
||||
subscription.endsAt = this.timer.convertDateToMicroseconds(this.timer.getUTCDateNDaysAhead(365))
|
||||
subscription.cancelled = false
|
||||
subscription.subscriptionId = 1
|
||||
subscription.subscriptionType = UserSubscriptionType.Regular
|
||||
|
||||
await this.userSubscriptionRepository.save(subscription)
|
||||
|
||||
await this.roleService.addUserRole(user, SubscriptionPlanName.NAMES.ProPlan)
|
||||
|
||||
return Result.ok('Premium features activated.')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface ActivatePremiumFeaturesDTO {
|
||||
username: string
|
||||
}
|
||||
@@ -16,6 +16,8 @@ export class AuthenticateRequest implements UseCaseInterface {
|
||||
|
||||
async execute(dto: AuthenticateRequestDTO): Promise<AuthenticateRequestResponse> {
|
||||
if (!dto.authorizationHeader) {
|
||||
this.logger.debug('Authorization header not provided.')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
responseCode: 401,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AuthenticateUser } from './AuthenticateUser'
|
||||
import { RevokedSession } from '../Session/RevokedSession'
|
||||
import { AuthenticationMethodResolverInterface } from '../Auth/AuthenticationMethodResolverInterface'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
describe('AuthenticateUser', () => {
|
||||
let user: User
|
||||
@@ -14,11 +15,15 @@ describe('AuthenticateUser', () => {
|
||||
let revokedSession: RevokedSession
|
||||
let authenticationMethodResolver: AuthenticationMethodResolverInterface
|
||||
let timer: TimerInterface
|
||||
let logger: Logger
|
||||
const accessTokenAge = 3600
|
||||
|
||||
const createUseCase = () => new AuthenticateUser(authenticationMethodResolver, timer, accessTokenAge)
|
||||
const createUseCase = () => new AuthenticateUser(authenticationMethodResolver, timer, accessTokenAge, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.debug = jest.fn()
|
||||
|
||||
user = {} as jest.Mocked<User>
|
||||
user.supportsSessions = jest.fn().mockReturnValue(false)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Session } from '../Session/Session'
|
||||
import { AuthenticateUserDTO } from './AuthenticateUserDTO'
|
||||
import { AuthenticateUserResponse } from './AuthenticateUserResponse'
|
||||
import { UseCaseInterface } from './UseCaseInterface'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@injectable()
|
||||
export class AuthenticateUser implements UseCaseInterface {
|
||||
@@ -17,11 +18,14 @@ export class AuthenticateUser implements UseCaseInterface {
|
||||
private authenticationMethodResolver: AuthenticationMethodResolverInterface,
|
||||
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
||||
@inject(TYPES.Auth_ACCESS_TOKEN_AGE) private accessTokenAge: number,
|
||||
@inject(TYPES.Auth_Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(dto: AuthenticateUserDTO): Promise<AuthenticateUserResponse> {
|
||||
const authenticationMethod = await this.authenticationMethodResolver.resolve(dto.token)
|
||||
if (!authenticationMethod) {
|
||||
this.logger.debug('No authentication method found for token.')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
failureType: 'INVALID_AUTH',
|
||||
@@ -37,6 +41,8 @@ export class AuthenticateUser implements UseCaseInterface {
|
||||
|
||||
const user = authenticationMethod.user
|
||||
if (!user) {
|
||||
this.logger.debug('No user found for authentication method.')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
failureType: 'INVALID_AUTH',
|
||||
@@ -44,6 +50,8 @@ export class AuthenticateUser implements UseCaseInterface {
|
||||
}
|
||||
|
||||
if (authenticationMethod.type == 'jwt' && user.supportsSessions()) {
|
||||
this.logger.debug('User supports sessions but is trying to authenticate with a JWT.')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
failureType: 'INVALID_AUTH',
|
||||
@@ -56,6 +64,8 @@ export class AuthenticateUser implements UseCaseInterface {
|
||||
const encryptedPasswordDigest = crypto.createHash('sha256').update(user.encryptedPassword).digest('hex')
|
||||
|
||||
if (!pwHash || !crypto.timingSafeEqual(Buffer.from(pwHash), Buffer.from(encryptedPasswordDigest))) {
|
||||
this.logger.debug('Password hash does not match.')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
failureType: 'INVALID_AUTH',
|
||||
@@ -66,6 +76,8 @@ export class AuthenticateUser implements UseCaseInterface {
|
||||
case 'session_token': {
|
||||
const session = authenticationMethod.session
|
||||
if (!session) {
|
||||
this.logger.debug('No session found for authentication method.')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
failureType: 'INVALID_AUTH',
|
||||
@@ -73,6 +85,8 @@ export class AuthenticateUser implements UseCaseInterface {
|
||||
}
|
||||
|
||||
if (session.refreshExpiration < this.timer.getUTCDate()) {
|
||||
this.logger.debug('Session refresh token has expired.')
|
||||
|
||||
return {
|
||||
success: false,
|
||||
failureType: 'INVALID_AUTH',
|
||||
|
||||
@@ -2,7 +2,7 @@ import { inject, injectable } from 'inversify'
|
||||
import { SubscriptionName } from '@standardnotes/common'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { TokenEncoderInterface, ValetTokenData } from '@standardnotes/security'
|
||||
import { CreateValetTokenPayload, CreateValetTokenResponseData } from '@standardnotes/responses'
|
||||
import { CreateValetTokenResponseData } from '@standardnotes/responses'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
@@ -12,6 +12,7 @@ import { SubscriptionSettingServiceInterface } from '../../Setting/SubscriptionS
|
||||
import { CreateValetTokenDTO } from './CreateValetTokenDTO'
|
||||
import { SubscriptionSettingsAssociationServiceInterface } from '../../Setting/SubscriptionSettingsAssociationServiceInterface'
|
||||
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
||||
import { CreateValetTokenPayload } from '../../ValetToken/CreateValetTokenPayload'
|
||||
|
||||
@injectable()
|
||||
export class CreateValetToken implements UseCaseInterface {
|
||||
|
||||
+2
-2
@@ -69,7 +69,7 @@ export class InviteToSharedSubscription implements UseCaseInterface {
|
||||
sharedSubscriptionInvition.inviterIdentifier = dto.inviterEmail
|
||||
sharedSubscriptionInvition.inviterIdentifierType = InviterIdentifierType.Email
|
||||
sharedSubscriptionInvition.inviteeIdentifier = dto.inviteeIdentifier
|
||||
sharedSubscriptionInvition.inviteeIdentifierType = this.isInviteeIdentifierPotentiallyAVaultAccount(
|
||||
sharedSubscriptionInvition.inviteeIdentifierType = this.isInviteeIdentifierPotentiallyAPrivateUsernameAccount(
|
||||
dto.inviteeIdentifier,
|
||||
)
|
||||
? InviteeIdentifierType.Hash
|
||||
@@ -107,7 +107,7 @@ export class InviteToSharedSubscription implements UseCaseInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private isInviteeIdentifierPotentiallyAVaultAccount(identifier: string): boolean {
|
||||
private isInviteeIdentifierPotentiallyAPrivateUsernameAccount(identifier: string): boolean {
|
||||
return identifier.length === 64 && !identifier.includes('@')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as bcrypt from 'bcryptjs'
|
||||
import { RoleName, Username } from '@standardnotes/domain-core'
|
||||
import { ApiVersion } from '@standardnotes/api'
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { inject, injectable } from 'inversify'
|
||||
@@ -16,6 +15,7 @@ import { TimerInterface } from '@standardnotes/time'
|
||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||
import { AuthResponseFactory20200115 } from '../Auth/AuthResponseFactory20200115'
|
||||
import { AuthResponse20200115 } from '../Auth/AuthResponse20200115'
|
||||
import { ApiVersion } from '../Api/ApiVersion'
|
||||
|
||||
@injectable()
|
||||
export class Register implements UseCaseInterface {
|
||||
@@ -39,7 +39,7 @@ export class Register implements UseCaseInterface {
|
||||
|
||||
const { email, password, apiVersion, ephemeralSession, ...registrationFields } = dto
|
||||
|
||||
if (apiVersion !== ApiVersion.v0) {
|
||||
if (apiVersion !== ApiVersion.v20200115) {
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: `Unsupported api version: ${apiVersion}`,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as bcrypt from 'bcryptjs'
|
||||
import { Result, UseCaseInterface, Username, Uuid, Validator } from '@standardnotes/domain-core'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { ApiVersion } from '@standardnotes/api'
|
||||
|
||||
import { AuthResponse20200115 } from '../../Auth/AuthResponse20200115'
|
||||
import { SettingServiceInterface } from '../../Setting/SettingServiceInterface'
|
||||
@@ -16,6 +15,7 @@ import { IncreaseLoginAttempts } from '../IncreaseLoginAttempts'
|
||||
import { ClearLoginAttempts } from '../ClearLoginAttempts'
|
||||
import { DeleteSetting } from '../DeleteSetting/DeleteSetting'
|
||||
import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
|
||||
export class SignInWithRecoveryCodes implements UseCaseInterface<AuthResponse20200115> {
|
||||
constructor(
|
||||
@@ -100,7 +100,7 @@ export class SignInWithRecoveryCodes implements UseCaseInterface<AuthResponse202
|
||||
|
||||
const authResponse = await this.authResponseFactory.createResponse({
|
||||
user,
|
||||
apiVersion: ApiVersion.v0,
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
userAgent: dto.userAgent,
|
||||
ephemeralSession: false,
|
||||
readonlyAccess: false,
|
||||
|
||||
@@ -44,21 +44,13 @@ describe('UpdateUser', () => {
|
||||
user,
|
||||
updatedWithUserAgent: 'Mozilla',
|
||||
apiVersion: '20190520',
|
||||
version: '004',
|
||||
pwCost: 11,
|
||||
pwSalt: 'qweqwe',
|
||||
pwNonce: undefined,
|
||||
}),
|
||||
).toEqual({ success: true, authResponse: { foo: 'bar' } })
|
||||
|
||||
expect(userRepository.save).toHaveBeenCalledWith({
|
||||
createdAt: new Date(1),
|
||||
pwCost: 11,
|
||||
email: 'test@test.te',
|
||||
pwSalt: 'qweqwe',
|
||||
updatedWithUserAgent: 'Mozilla',
|
||||
uuid: '123',
|
||||
version: '004',
|
||||
updatedAt: new Date(1),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,25 +17,17 @@ export class UpdateUser implements UseCaseInterface {
|
||||
) {}
|
||||
|
||||
async execute(dto: UpdateUserDTO): Promise<UpdateUserResponse> {
|
||||
const { user, apiVersion, ...updateFields } = dto
|
||||
dto.user.updatedAt = this.timer.getUTCDate()
|
||||
|
||||
Object.keys(updateFields).forEach(
|
||||
(key) => (updateFields[key] === undefined || updateFields[key] === null) && delete updateFields[key],
|
||||
)
|
||||
const updatedUser = await this.userRepository.save(dto.user)
|
||||
|
||||
Object.assign(user, updateFields)
|
||||
|
||||
user.updatedAt = this.timer.getUTCDate()
|
||||
|
||||
await this.userRepository.save(user)
|
||||
|
||||
const authResponseFactory = this.authResponseFactoryResolver.resolveAuthResponseFactoryVersion(apiVersion)
|
||||
const authResponseFactory = this.authResponseFactoryResolver.resolveAuthResponseFactoryVersion(dto.apiVersion)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
authResponse: await authResponseFactory.createResponse({
|
||||
user,
|
||||
apiVersion,
|
||||
user: updatedUser,
|
||||
apiVersion: dto.apiVersion,
|
||||
userAgent: dto.updatedWithUserAgent,
|
||||
ephemeralSession: false,
|
||||
readonlyAccess: false,
|
||||
|
||||
@@ -1,18 +1,7 @@
|
||||
import { User } from '../User/User'
|
||||
|
||||
export type UpdateUserDTO = {
|
||||
[key: string]: string | User | Date | undefined | number
|
||||
user: User
|
||||
updatedWithUserAgent: string
|
||||
apiVersion: string
|
||||
email?: string
|
||||
pwFunc?: string
|
||||
pwAlg?: string
|
||||
pwCost?: number
|
||||
pwKeySize?: number
|
||||
pwNonce?: string
|
||||
pwSalt?: string
|
||||
kpOrigination?: string
|
||||
kpCreated?: Date
|
||||
version?: string
|
||||
updatedWithUserAgent: string
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ describe('User', () => {
|
||||
const user = createUser()
|
||||
user.email = 'a75a31ce95365904ef0e0a8e6cefc1f5e99adfef81bbdb6d4499eeb10ae0ff67'
|
||||
|
||||
expect(user.isPotentiallyAVaultAccount()).toBeTruthy()
|
||||
expect(user.isPotentiallyAPrivateUsernameAccount()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should indicate if the user is not a vault account', () => {
|
||||
const user = createUser()
|
||||
user.email = 'test@test.te'
|
||||
|
||||
expect(user.isPotentiallyAVaultAccount()).toBeFalsy()
|
||||
expect(user.isPotentiallyAPrivateUsernameAccount()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -202,7 +202,7 @@ export class User {
|
||||
return parseInt(this.version) >= parseInt(ProtocolVersion.V004)
|
||||
}
|
||||
|
||||
isPotentiallyAVaultAccount(): boolean {
|
||||
isPotentiallyAPrivateUsernameAccount(): boolean {
|
||||
return this.email.length === 64 && !this.email.includes('@')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export type CreateValetTokenPayload = {
|
||||
operation: 'read' | 'write' | 'delete' | 'move'
|
||||
resources: Array<{
|
||||
remoteIdentifier: string
|
||||
unencryptedFileSize?: number
|
||||
}>
|
||||
}
|
||||
@@ -60,15 +60,6 @@ export class HomeServerUsersController extends BaseHttpController {
|
||||
user: response.locals.user,
|
||||
updatedWithUserAgent: <string>request.headers['user-agent'],
|
||||
apiVersion: request.body.api,
|
||||
pwFunc: request.body.pw_func,
|
||||
pwAlg: request.body.pw_alg,
|
||||
pwCost: request.body.pw_cost,
|
||||
pwKeySize: request.body.pw_key_size,
|
||||
pwNonce: request.body.pw_nonce,
|
||||
pwSalt: request.body.pw_salt,
|
||||
kpOrigination: request.body.origination,
|
||||
kpCreated: request.body.created,
|
||||
version: request.body.version,
|
||||
})
|
||||
|
||||
if (updateResult.success) {
|
||||
|
||||
+3
-2
@@ -1,10 +1,11 @@
|
||||
import { ControllerContainerInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Request, Response } from 'express'
|
||||
import { BaseHttpController, results } from 'inversify-express-utils'
|
||||
import { ErrorTag } from '@standardnotes/responses'
|
||||
import { ValetTokenOperation } from '@standardnotes/security'
|
||||
|
||||
import { CreateValetToken } from '../../../Domain/UseCase/CreateValetToken/CreateValetToken'
|
||||
import { CreateValetTokenPayload, ErrorTag } from '@standardnotes/responses'
|
||||
import { ValetTokenOperation } from '@standardnotes/security'
|
||||
import { CreateValetTokenPayload } from '../../../Domain/ValetToken/CreateValetTokenPayload'
|
||||
|
||||
export class HomeServerValetTokenController extends BaseHttpController {
|
||||
constructor(protected createValetKey: CreateValetToken, private controllerContainer?: ControllerContainerInterface) {
|
||||
|
||||
@@ -99,9 +99,7 @@ describe('InversifyExpressUsersController', () => {
|
||||
|
||||
expect(updateUser.execute).toHaveBeenCalledWith({
|
||||
apiVersion: '20190520',
|
||||
kpOrigination: 'test',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
version: '002',
|
||||
user: {
|
||||
uuid: '123',
|
||||
email: 'test@test.te',
|
||||
@@ -143,9 +141,7 @@ describe('InversifyExpressUsersController', () => {
|
||||
|
||||
expect(updateUser.execute).toHaveBeenCalledWith({
|
||||
apiVersion: '20190520',
|
||||
kpOrigination: 'test',
|
||||
updatedWithUserAgent: 'Google Chrome',
|
||||
version: '002',
|
||||
user: {
|
||||
uuid: '123',
|
||||
email: 'test@test.te',
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export type SimpleUserProjection = {
|
||||
uuid: string
|
||||
email: string
|
||||
protocolVersion: string
|
||||
}
|
||||
@@ -2,10 +2,11 @@ import { injectable } from 'inversify'
|
||||
|
||||
import { User } from '../Domain/User/User'
|
||||
import { ProjectorInterface } from './ProjectorInterface'
|
||||
import { SimpleUserProjection } from './SimpleUserProjection'
|
||||
|
||||
@injectable()
|
||||
export class UserProjector implements ProjectorInterface<User> {
|
||||
projectSimple(user: User): Record<string, unknown> {
|
||||
projectSimple(user: User): SimpleUserProjection {
|
||||
return {
|
||||
uuid: user.uuid,
|
||||
email: user.email,
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.50.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.49.0...@standardnotes/common@1.50.0) (2023-07-12)
|
||||
|
||||
### Features
|
||||
|
||||
* domain items ([#655](https://github.com/standardnotes/server/issues/655)) ([a0af8f0](https://github.com/standardnotes/server/commit/a0af8f00252e1219e58cb7e066c11a8e71692e9d))
|
||||
|
||||
# [1.49.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.48.3...@standardnotes/common@1.49.0) (2023-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
* shared vaults functionality in api-gateway,auth,files,common,security,domain-events. ([#629](https://github.com/standardnotes/server/issues/629)) ([fa7fbe2](https://github.com/standardnotes/server/commit/fa7fbe26e7b0707fc21d71e04af76870f5248baf))
|
||||
|
||||
## [1.48.3](https://github.com/standardnotes/server/compare/@standardnotes/common@1.48.2...@standardnotes/common@1.48.3) (2023-06-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/common
|
||||
|
||||
## [1.48.2](https://github.com/standardnotes/server/compare/@standardnotes/common@1.48.0...@standardnotes/common@1.48.2) (2023-05-31)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/common",
|
||||
"version": "1.48.2",
|
||||
"version": "1.50.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
export enum ContentType {
|
||||
Any = '*',
|
||||
Item = 'SF|Item',
|
||||
RootKey = 'SN|RootKey|NoSync',
|
||||
ItemsKey = 'SN|ItemsKey',
|
||||
EncryptedStorage = 'SN|EncryptedStorage',
|
||||
Privileges = 'SN|Privileges',
|
||||
Note = 'Note',
|
||||
Tag = 'Tag',
|
||||
SmartView = 'SN|SmartTag',
|
||||
Component = 'SN|Component',
|
||||
Editor = 'SN|Editor',
|
||||
ActionsExtension = 'Extension',
|
||||
UserPrefs = 'SN|UserPreferences',
|
||||
HistorySession = 'SN|HistorySession',
|
||||
Theme = 'SN|Theme',
|
||||
File = 'SN|File',
|
||||
FilesafeCredentials = 'SN|FileSafe|Credentials',
|
||||
FilesafeFileMetadata = 'SN|FileSafe|FileMetadata',
|
||||
FilesafeIntegration = 'SN|FileSafe|Integration',
|
||||
ExtensionRepo = 'SN|ExtensionRepo',
|
||||
Unknown = 'Unknown',
|
||||
}
|
||||
|
||||
export function DisplayStringForContentType(contentType: ContentType): string | undefined {
|
||||
const map: Partial<Record<ContentType, string>> = {
|
||||
[ContentType.ActionsExtension]: 'action-based extension',
|
||||
[ContentType.Component]: 'component',
|
||||
[ContentType.Editor]: 'editor',
|
||||
[ContentType.File]: 'file',
|
||||
[ContentType.FilesafeCredentials]: 'FileSafe credential',
|
||||
[ContentType.FilesafeFileMetadata]: 'FileSafe file',
|
||||
[ContentType.FilesafeIntegration]: 'FileSafe integration',
|
||||
[ContentType.ItemsKey]: 'encryption key',
|
||||
[ContentType.Note]: 'note',
|
||||
[ContentType.SmartView]: 'smart view',
|
||||
[ContentType.Tag]: 'tag',
|
||||
[ContentType.Theme]: 'theme',
|
||||
[ContentType.UserPrefs]: 'user preferences',
|
||||
}
|
||||
|
||||
return map[contentType]
|
||||
}
|
||||
@@ -3,7 +3,6 @@ export enum ProtocolVersion {
|
||||
V002 = '002',
|
||||
V003 = '003',
|
||||
V004 = '004',
|
||||
V005 = '005',
|
||||
}
|
||||
|
||||
export const ProtocolVersionLatest = ProtocolVersion.V004
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './Content/ContentType'
|
||||
export * from './Content/ContentDecoder'
|
||||
export * from './Content/ContentDecoderInterface'
|
||||
export * from './DataType/AnyRecord'
|
||||
|
||||
@@ -3,6 +3,42 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.22.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.21.1...@standardnotes/domain-core@1.22.0) (2023-07-12)
|
||||
|
||||
### Features
|
||||
|
||||
* domain items ([#655](https://github.com/standardnotes/server/issues/655)) ([a0af8f0](https://github.com/standardnotes/server/commit/a0af8f00252e1219e58cb7e066c11a8e71692e9d))
|
||||
|
||||
## [1.21.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.21.0...@standardnotes/domain-core@1.21.1) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/server/issues/648)) ([c288e5d](https://github.com/standardnotes/server/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.20.0...@standardnotes/domain-core@1.21.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* getting shared vault users and removing shared vault user ([#642](https://github.com/standardnotes/server/issues/642)) ([e905128](https://github.com/standardnotes/server/commit/e905128d45eaadb34d3465d4480dfb3a2c5f3f79))
|
||||
|
||||
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.19.0...@standardnotes/domain-core@1.20.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.19.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.18.0...@standardnotes/domain-core@1.19.0) (2023-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
* add shared vaults model. ([#631](https://github.com/standardnotes/server/issues/631)) ([38e77f0](https://github.com/standardnotes/server/commit/38e77f04be441b7506c3390fb0d9894b34119c3e))
|
||||
|
||||
# [1.18.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.17.0...@standardnotes/domain-core@1.18.0) (2023-06-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **home-server:** add overriding environment variables in underlying services ([#621](https://github.com/standardnotes/server/issues/621)) ([f0cbec0](https://github.com/standardnotes/server/commit/f0cbec07b87d60dfad92072944553f76e0bea164))
|
||||
|
||||
# [1.17.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.16.2...@standardnotes/domain-core@1.17.0) (2023-05-31)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.17.0",
|
||||
"version": "1.22.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { ContentType } from './ContentType'
|
||||
|
||||
describe('ContentType', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = ContentType.create(ContentType.TYPES.Component)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('SN|Component')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
for (const value of ['', undefined, 0, 'FOOBAR']) {
|
||||
const valueOrError = ContentType.create(value as string)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should return a display name', () => {
|
||||
const valueOrError = ContentType.create(ContentType.TYPES.FilesafeFileMetadata)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().getDisplayName()).toEqual('FileSafe file')
|
||||
})
|
||||
|
||||
it('should return null for a display name if the value is null', () => {
|
||||
const valueOrError = ContentType.create(null)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().getDisplayName()).toBeNull()
|
||||
})
|
||||
|
||||
it('should fallback to the value if the display name is not found', () => {
|
||||
const valueOrError = ContentType.create(ContentType.TYPES.Unknown)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().getDisplayName()).toEqual('Unknown')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
|
||||
import { ContentTypeProps } from './ContentTypeProps'
|
||||
|
||||
export class ContentType extends ValueObject<ContentTypeProps> {
|
||||
static readonly TYPES = {
|
||||
Any: '*',
|
||||
Item: 'SF|Item',
|
||||
KeySystemItemsKey: 'SN|KeySystemItemsKey',
|
||||
KeySystemRootKey: 'SN|KeySystemRootKey',
|
||||
TrustedContact: 'SN|TrustedContact',
|
||||
VaultListing: 'SN|VaultListing',
|
||||
RootKey: 'SN|RootKey|NoSync',
|
||||
ItemsKey: 'SN|ItemsKey',
|
||||
EncryptedStorage: 'SN|EncryptedStorage',
|
||||
Privileges: 'SN|Privileges',
|
||||
Note: 'Note',
|
||||
Tag: 'Tag',
|
||||
SmartView: 'SN|SmartTag',
|
||||
Component: 'SN|Component',
|
||||
Editor: 'SN|Editor',
|
||||
ActionsExtension: 'Extension',
|
||||
UserPrefs: 'SN|UserPreferences',
|
||||
HistorySession: 'SN|HistorySession',
|
||||
Theme: 'SN|Theme',
|
||||
File: 'SN|File',
|
||||
FilesafeCredentials: 'SN|FileSafe|Credentials',
|
||||
FilesafeFileMetadata: 'SN|FileSafe|FileMetadata',
|
||||
FilesafeIntegration: 'SN|FileSafe|Integration',
|
||||
ExtensionRepo: 'SN|ExtensionRepo',
|
||||
Unknown: 'Unknown',
|
||||
}
|
||||
|
||||
private readonly displayNamesMap: Partial<Record<string, string>> = {
|
||||
[ContentType.TYPES.ActionsExtension]: 'action-based extension',
|
||||
[ContentType.TYPES.Component]: 'component',
|
||||
[ContentType.TYPES.Editor]: 'editor',
|
||||
[ContentType.TYPES.File]: 'file',
|
||||
[ContentType.TYPES.FilesafeCredentials]: 'FileSafe credential',
|
||||
[ContentType.TYPES.FilesafeFileMetadata]: 'FileSafe file',
|
||||
[ContentType.TYPES.FilesafeIntegration]: 'FileSafe integration',
|
||||
[ContentType.TYPES.ItemsKey]: 'encryption key',
|
||||
[ContentType.TYPES.Note]: 'note',
|
||||
[ContentType.TYPES.SmartView]: 'smart view',
|
||||
[ContentType.TYPES.Tag]: 'tag',
|
||||
[ContentType.TYPES.Theme]: 'theme',
|
||||
[ContentType.TYPES.UserPrefs]: 'user preferences',
|
||||
}
|
||||
|
||||
get value(): string | null {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: ContentTypeProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(type: string | null): Result<ContentType> {
|
||||
if (type === null) {
|
||||
return Result.ok<ContentType>(new ContentType({ value: null }))
|
||||
}
|
||||
|
||||
const isValidType = Object.values(this.TYPES).includes(type)
|
||||
if (!isValidType) {
|
||||
return Result.fail<ContentType>(`Invalid content type: ${type}`)
|
||||
} else {
|
||||
return Result.ok<ContentType>(new ContentType({ value: type }))
|
||||
}
|
||||
}
|
||||
|
||||
getDisplayName(): string | null {
|
||||
if (!this.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.displayNamesMap[this.value] || this.value
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export class Dates extends ValueObject<DatesProps> {
|
||||
return Result.fail<Dates>(`Could not create Dates. Creation date should be a date object, given: ${createdAt}`)
|
||||
}
|
||||
if (!(updatedAt instanceof Date)) {
|
||||
return Result.fail<Dates>(`Could not create Dates. Update date should be a date object, given: ${createdAt}`)
|
||||
return Result.fail<Dates>(`Could not create Dates. Update date should be a date object, given: ${updatedAt}`)
|
||||
}
|
||||
|
||||
return Result.ok<Dates>(new Dates({ createdAt, updatedAt }))
|
||||
|
||||
@@ -28,7 +28,7 @@ export class RoleNameCollection extends ValueObject<RoleNameCollectionProps> {
|
||||
return false
|
||||
}
|
||||
|
||||
equals(roleNameCollection: RoleNameCollection): boolean {
|
||||
override equals(roleNameCollection: RoleNameCollection): boolean {
|
||||
if (this.props.value.length !== roleNameCollection.value.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { TimestampsProps } from './TimestampsProps'
|
||||
|
||||
export class Timestamps extends ValueObject<TimestampsProps> {
|
||||
get createdAt(): number {
|
||||
return this.props.createdAt
|
||||
}
|
||||
|
||||
get updatedAt(): number {
|
||||
return this.props.updatedAt
|
||||
}
|
||||
|
||||
private constructor(props: TimestampsProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(createdAt: number, updatedAt: number): Result<Timestamps> {
|
||||
if (isNaN(createdAt)) {
|
||||
return Result.fail<Timestamps>(
|
||||
`Could not create Timestamps. Creation date should be a number, given: ${createdAt}`,
|
||||
)
|
||||
}
|
||||
if (isNaN(updatedAt)) {
|
||||
return Result.fail<Timestamps>(`Could not create Timestamps. Update date should be a number, given: ${updatedAt}`)
|
||||
}
|
||||
|
||||
return Result.ok<Timestamps>(new Timestamps({ createdAt, updatedAt }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface TimestampsProps {
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
@@ -13,4 +13,25 @@ describe('Uuid', () => {
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should check equality between two value objects', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
|
||||
expect(uuid1.equals(uuid2)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should check inequality between two value objects', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1e').getValue()
|
||||
|
||||
expect(uuid1.equals(uuid2)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should check inequality between two value objects of different types', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
|
||||
expect(uuid1.equals(null as unknown as Uuid)).toBeFalsy()
|
||||
expect(uuid1.equals(undefined as unknown as Uuid)).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,4 +7,12 @@ export abstract class ValueObject<T extends ValueObjectProps> {
|
||||
constructor(props: T) {
|
||||
this.props = Object.freeze(props)
|
||||
}
|
||||
|
||||
public equals(vo?: ValueObject<T>): boolean {
|
||||
if (vo === null || vo === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
return JSON.stringify(this.props) === JSON.stringify(vo.props)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
export abstract class AbstractEnv {
|
||||
protected env?: { [key: string]: string } = {}
|
||||
protected overrides: { [key: string]: string }
|
||||
|
||||
constructor(overrides: { [key: string]: string } = {}) {
|
||||
this.overrides = overrides
|
||||
}
|
||||
|
||||
abstract load(): void
|
||||
|
||||
get(key: string, optional = false): string {
|
||||
if (!this.env) {
|
||||
this.load()
|
||||
}
|
||||
|
||||
if (this.overrides[key]) {
|
||||
return this.overrides[key]
|
||||
}
|
||||
|
||||
if (!process.env[key] && !optional) {
|
||||
throw new Error(`Environment variable ${key} not set`)
|
||||
}
|
||||
|
||||
return <string>process.env[key]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Transform } from 'stream'
|
||||
|
||||
export interface ServiceConfiguration {
|
||||
logger?: Transform
|
||||
environmentOverrides?: { [name: string]: string }
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
import { ServiceConfiguration } from './ServiceConfiguration'
|
||||
import { ServiceIdentifier } from './ServiceIdentifier'
|
||||
import { Transform } from 'stream'
|
||||
|
||||
export interface ServiceInterface {
|
||||
getContainer(): Promise<unknown>
|
||||
setLogger(logger: Transform): void
|
||||
getContainer(configuration?: ServiceConfiguration): Promise<unknown>
|
||||
getId(): ServiceIdentifier
|
||||
handleRequest(request: never, response: never, endpointOrMethodIdentifier: string): Promise<unknown>
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ export * from './Cache/CacheEntry'
|
||||
export * from './Cache/CacheEntryProps'
|
||||
export * from './Cache/CacheEntryRepositoryInterface'
|
||||
|
||||
export * from './Common/ContentType'
|
||||
export * from './Common/ContentTypeProps'
|
||||
export * from './Common/Dates'
|
||||
export * from './Common/DatesProps'
|
||||
export * from './Common/Email'
|
||||
@@ -17,6 +19,8 @@ export * from './Common/RoleName'
|
||||
export * from './Common/RoleNameProps'
|
||||
export * from './Common/RoleNameCollection'
|
||||
export * from './Common/RoleNameCollectionProps'
|
||||
export * from './Common/Timestamps'
|
||||
export * from './Common/TimestampsProps'
|
||||
export * from './Common/Username'
|
||||
export * from './Common/UsernameProps'
|
||||
export * from './Common/Uuid'
|
||||
@@ -37,8 +41,11 @@ export * from './DI/ControllerContainerInterface'
|
||||
export * from './Email/EmailLevel'
|
||||
export * from './Email/EmailLevelProps'
|
||||
|
||||
export * from './Env/AbstractEnv'
|
||||
|
||||
export * from './Mapping/MapperInterface'
|
||||
|
||||
export * from './Service/ServiceConfiguration'
|
||||
export * from './Service/ServiceContainer'
|
||||
export * from './Service/ServiceContainerInterface'
|
||||
export * from './Service/ServiceIdentifier'
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.12.9](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.8...@standardnotes/domain-events-infra@1.12.9) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.8](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.7...@standardnotes/domain-events-infra@1.12.8) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.7](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.6...@standardnotes/domain-events-infra@1.12.7) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.6](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.5...@standardnotes/domain-events-infra@1.12.6) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.5](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.4...@standardnotes/domain-events-infra@1.12.5) (2023-06-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.12.5",
|
||||
"version": "1.12.9",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user