mirror of
https://github.com/standardnotes/server
synced 2026-05-10 00:57:19 -04:00
Compare commits
124 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b1fc718a2 | |||
| 1708c3f8a0 | |||
| 352e02028d | |||
| 1bbb639c83 | |||
| c14265f103 | |||
| c030a6b3d8 | |||
| af997ea658 | |||
| efa4d7fc60 | |||
| f714aaa0e9 | |||
| aee6e60583 | |||
| 4e602687d5 | |||
| d026152ac8 | |||
| 3f21a358d2 | |||
| dc55e47c98 | |||
| 3b804e2321 | |||
| b32f851a90 | |||
| 479d20e76f | |||
| fae4553fc8 | |||
| cb74b23e45 | |||
| af8f12c33a | |||
| a148c4d1f6 | |||
| f7190c0c9c | |||
| c00d7765a9 | |||
| 2b651d86e2 | |||
| 9be3517093 | |||
| fcfedaf7e7 | |||
| 0b82794e9c | |||
| 2a52e398cb | |||
| c31e882ad2 | |||
| 2f0903e0eb | |||
| 2396053bc1 | |||
| 17fd12305e | |||
| 425ea4374d | |||
| c076c3c74a | |||
| 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 |
@@ -4,6 +4,7 @@ DB_USERNAME=std_notes_user
|
|||||||
DB_PASSWORD=changeme123
|
DB_PASSWORD=changeme123
|
||||||
DB_DATABASE=standard_notes_db
|
DB_DATABASE=standard_notes_db
|
||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
|
DB_SQLITE_DATABASE_PATH=standard_notes_db
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
REDIS_HOST=cache
|
REDIS_HOST=cache
|
||||||
AUTH_SERVER_ACCESS_TOKEN_AGE=4
|
AUTH_SERVER_ACCESS_TOKEN_AGE=4
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ jobs:
|
|||||||
|
|
||||||
e2e-home-server:
|
e2e-home-server:
|
||||||
name: (Home Server) E2E Test Suite
|
name: (Home Server) E2E Test Suite
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
db_type: [mysql, sqlite]
|
||||||
|
cache_type: [redis, memory]
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
services:
|
services:
|
||||||
@@ -61,6 +66,19 @@ jobs:
|
|||||||
image: standardnotes/snjs:${{ inputs.snjs_image_tag }}
|
image: standardnotes/snjs:${{ inputs.snjs_image_tag }}
|
||||||
ports:
|
ports:
|
||||||
- 9001:9001
|
- 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:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -90,6 +108,15 @@ jobs:
|
|||||||
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
|
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
|
||||||
echo "REFRESH_TOKEN_AGE=7" >> packages/home-server/.env
|
echo "REFRESH_TOKEN_AGE=7" >> packages/home-server/.env
|
||||||
echo "REVISIONS_FREQUENCY=5" >> 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
|
||||||
|
echo "FILES_SERVER_URL=http://localhost:3123" >> packages/home-server/.env
|
||||||
|
echo "E2E_TESTING=true" >> packages/home-server/.env
|
||||||
|
|
||||||
- name: Run Server
|
- name: Run Server
|
||||||
run: nohup yarn workspace @standardnotes/home-server start &
|
run: nohup yarn workspace @standardnotes/home-server start &
|
||||||
@@ -100,4 +127,4 @@ jobs:
|
|||||||
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
|
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
|
||||||
|
|
||||||
- name: Run E2E Test Suite
|
- name: Run E2E Test Suite
|
||||||
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html?skip_paid_features=true
|
run: yarn dlx mocha-headless-chrome --timeout 1200000 -f http://localhost:9001/mocha/test.html
|
||||||
|
|||||||
@@ -4560,17 +4560,16 @@ const RAW_RUNTIME_STATE =
|
|||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["@standardnotes/api", [\
|
["@standardnotes/api", [\
|
||||||
["npm:1.26.10", {\
|
["npm:1.26.26", {\
|
||||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.26.10-f6165cafd3-3c3561aec8.zip/node_modules/@standardnotes/api/",\
|
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.26.26-4338a5fe92-db41aedfa3.zip/node_modules/@standardnotes/api/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@standardnotes/api", "npm:1.26.10"],\
|
["@standardnotes/api", "npm:1.26.26"],\
|
||||||
["@standardnotes/common", "workspace:packages/common"],\
|
["@standardnotes/common", "workspace:packages/common"],\
|
||||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||||
["@standardnotes/encryption", "npm:1.21.38"],\
|
["@standardnotes/models", "npm:1.46.8"],\
|
||||||
["@standardnotes/models", "npm:1.45.5"],\
|
["@standardnotes/responses", "npm:1.13.27"],\
|
||||||
["@standardnotes/responses", "npm:1.13.24"],\
|
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
["@standardnotes/utils", "npm:1.16.5"],\
|
["@standardnotes/utils", "npm:1.17.5"],\
|
||||||
["reflect-metadata", "npm:0.1.13"]\
|
["reflect-metadata", "npm:0.1.13"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
@@ -4635,17 +4634,17 @@ const RAW_RUNTIME_STATE =
|
|||||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||||
["@simplewebauthn/server", "npm:7.2.0"],\
|
["@simplewebauthn/server", "npm:7.2.0"],\
|
||||||
["@simplewebauthn/typescript-types", "npm:7.0.0"],\
|
["@simplewebauthn/typescript-types", "npm:7.0.0"],\
|
||||||
["@standardnotes/api", "npm:1.26.10"],\
|
["@standardnotes/api", "npm:1.26.26"],\
|
||||||
["@standardnotes/common", "workspace:packages/common"],\
|
["@standardnotes/common", "workspace:packages/common"],\
|
||||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||||
["@standardnotes/features", "npm:1.59.5"],\
|
["@standardnotes/features", "npm:1.59.7"],\
|
||||||
["@standardnotes/predicates", "workspace:packages/predicates"],\
|
["@standardnotes/predicates", "workspace:packages/predicates"],\
|
||||||
["@standardnotes/responses", "npm:1.13.24"],\
|
["@standardnotes/responses", "npm:1.13.27"],\
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||||
["@standardnotes/time", "workspace:packages/time"],\
|
["@standardnotes/time", "workspace:packages/time"],\
|
||||||
["@types/bcryptjs", "npm:2.4.2"],\
|
["@types/bcryptjs", "npm:2.4.2"],\
|
||||||
@@ -4781,21 +4780,6 @@ const RAW_RUNTIME_STATE =
|
|||||||
"linkType": "SOFT"\
|
"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", [\
|
["@standardnotes/event-store", [\
|
||||||
["workspace:packages/event-store", {\
|
["workspace:packages/event-store", {\
|
||||||
"packageLocation": "./packages/event-store/",\
|
"packageLocation": "./packages/event-store/",\
|
||||||
@@ -4831,10 +4815,10 @@ const RAW_RUNTIME_STATE =
|
|||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["@standardnotes/features", [\
|
["@standardnotes/features", [\
|
||||||
["npm:1.59.5", {\
|
["npm:1.59.7", {\
|
||||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.59.5-83c83acde9-173b1f5d52.zip/node_modules/@standardnotes/features/",\
|
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.59.7-27c3e5296e-1632d64cc1.zip/node_modules/@standardnotes/features/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@standardnotes/features", "npm:1.59.5"],\
|
["@standardnotes/features", "npm:1.59.7"],\
|
||||||
["@standardnotes/common", "workspace:packages/common"],\
|
["@standardnotes/common", "workspace:packages/common"],\
|
||||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
@@ -4855,7 +4839,7 @@ const RAW_RUNTIME_STATE =
|
|||||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||||
["@standardnotes/time", "workspace:packages/time"],\
|
["@standardnotes/time", "workspace:packages/time"],\
|
||||||
["@types/connect-busboy", "npm:1.0.0"],\
|
["@types/connect-busboy", "npm:1.0.0"],\
|
||||||
@@ -4935,14 +4919,16 @@ const RAW_RUNTIME_STATE =
|
|||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["@standardnotes/models", [\
|
["@standardnotes/models", [\
|
||||||
["npm:1.45.5", {\
|
["npm:1.46.8", {\
|
||||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.45.5-29326e959c-15f26c11b2.zip/node_modules/@standardnotes/models/",\
|
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.46.8-bc0390832e-8404340f27.zip/node_modules/@standardnotes/models/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@standardnotes/models", "npm:1.45.5"],\
|
["@standardnotes/models", "npm:1.46.8"],\
|
||||||
["@standardnotes/common", "workspace:packages/common"],\
|
["@standardnotes/common", "workspace:packages/common"],\
|
||||||
["@standardnotes/features", "npm:1.59.5"],\
|
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||||
["@standardnotes/responses", "npm:1.13.24"],\
|
["@standardnotes/features", "npm:1.59.7"],\
|
||||||
["@standardnotes/utils", "npm:1.16.5"],\
|
["@standardnotes/responses", "npm:1.13.27"],\
|
||||||
|
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||||
|
["@standardnotes/utils", "npm:1.17.5"],\
|
||||||
["lodash", "npm:4.17.21"]\
|
["lodash", "npm:4.17.21"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
@@ -4967,12 +4953,12 @@ const RAW_RUNTIME_STATE =
|
|||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["@standardnotes/responses", [\
|
["@standardnotes/responses", [\
|
||||||
["npm:1.13.24", {\
|
["npm:1.13.27", {\
|
||||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.13.24-3b4167c7ea-3bcfee90f0.zip/node_modules/@standardnotes/responses/",\
|
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.13.27-829dec3e6e-9bf55e5f02.zip/node_modules/@standardnotes/responses/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@standardnotes/responses", "npm:1.13.24"],\
|
["@standardnotes/responses", "npm:1.13.27"],\
|
||||||
["@standardnotes/common", "workspace:packages/common"],\
|
["@standardnotes/common", "workspace:packages/common"],\
|
||||||
["@standardnotes/features", "npm:1.59.5"],\
|
["@standardnotes/features", "npm:1.59.7"],\
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
["reflect-metadata", "npm:0.1.13"]\
|
["reflect-metadata", "npm:0.1.13"]\
|
||||||
],\
|
],\
|
||||||
@@ -4987,12 +4973,12 @@ const RAW_RUNTIME_STATE =
|
|||||||
["@aws-sdk/client-s3", "npm:3.342.0"],\
|
["@aws-sdk/client-s3", "npm:3.342.0"],\
|
||||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||||
["@standardnotes/api", "npm:1.26.10"],\
|
["@standardnotes/api", "npm:1.26.26"],\
|
||||||
["@standardnotes/common", "workspace:packages/common"],\
|
["@standardnotes/common", "workspace:packages/common"],\
|
||||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||||
["@standardnotes/responses", "npm:1.13.24"],\
|
["@standardnotes/responses", "npm:1.13.27"],\
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
["@standardnotes/time", "workspace:packages/time"],\
|
["@standardnotes/time", "workspace:packages/time"],\
|
||||||
["@types/cors", "npm:2.8.13"],\
|
["@types/cors", "npm:2.8.13"],\
|
||||||
@@ -5129,10 +5115,10 @@ const RAW_RUNTIME_STATE =
|
|||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["@standardnotes/sncrypto-common", [\
|
["@standardnotes/sncrypto-common", [\
|
||||||
["npm:1.13.3", {\
|
["npm:1.13.4", {\
|
||||||
"packageLocation": "./.yarn/cache/@standardnotes-sncrypto-common-npm-1.13.3-97ef3850ce-a73af90962.zip/node_modules/@standardnotes/sncrypto-common/",\
|
"packageLocation": "./.yarn/cache/@standardnotes-sncrypto-common-npm-1.13.4-3186513fa6-48e0e207f2.zip/node_modules/@standardnotes/sncrypto-common/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||||
["reflect-metadata", "npm:0.1.13"]\
|
["reflect-metadata", "npm:0.1.13"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
@@ -5143,7 +5129,7 @@ const RAW_RUNTIME_STATE =
|
|||||||
"packageLocation": "./packages/sncrypto-node/",\
|
"packageLocation": "./packages/sncrypto-node/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||||
["@types/jest", "npm:29.5.2"],\
|
["@types/jest", "npm:29.5.2"],\
|
||||||
["@types/node", "npm:20.2.5"],\
|
["@types/node", "npm:20.2.5"],\
|
||||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.59.8"],\
|
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.59.8"],\
|
||||||
@@ -5171,14 +5157,15 @@ const RAW_RUNTIME_STATE =
|
|||||||
["@aws-sdk/client-sns", "npm:3.342.0"],\
|
["@aws-sdk/client-sns", "npm:3.342.0"],\
|
||||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||||
["@standardnotes/api", "npm:1.26.10"],\
|
["@standardnotes/api", "npm:1.26.26"],\
|
||||||
["@standardnotes/common", "workspace:packages/common"],\
|
["@standardnotes/common", "workspace:packages/common"],\
|
||||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||||
["@standardnotes/responses", "npm:1.13.24"],\
|
["@standardnotes/responses", "npm:1.13.27"],\
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||||
|
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||||
["@standardnotes/time", "workspace:packages/time"],\
|
["@standardnotes/time", "workspace:packages/time"],\
|
||||||
["@types/cors", "npm:2.8.13"],\
|
["@types/cors", "npm:2.8.13"],\
|
||||||
["@types/dotenv", "npm:8.2.0"],\
|
["@types/dotenv", "npm:8.2.0"],\
|
||||||
@@ -5188,6 +5175,7 @@ const RAW_RUNTIME_STATE =
|
|||||||
["@types/newrelic", "npm:9.14.0"],\
|
["@types/newrelic", "npm:9.14.0"],\
|
||||||
["@types/node", "npm:20.2.5"],\
|
["@types/node", "npm:20.2.5"],\
|
||||||
["@types/prettyjson", "npm:0.0.30"],\
|
["@types/prettyjson", "npm:0.0.30"],\
|
||||||
|
["@types/semver", "npm:7.5.0"],\
|
||||||
["@types/ua-parser-js", "npm:0.7.36"],\
|
["@types/ua-parser-js", "npm:0.7.36"],\
|
||||||
["@types/uuid", "npm:8.3.4"],\
|
["@types/uuid", "npm:8.3.4"],\
|
||||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.59.8"],\
|
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.59.8"],\
|
||||||
@@ -5210,6 +5198,7 @@ const RAW_RUNTIME_STATE =
|
|||||||
["prettier", "npm:2.8.8"],\
|
["prettier", "npm:2.8.8"],\
|
||||||
["prettyjson", "npm:1.2.5"],\
|
["prettyjson", "npm:1.2.5"],\
|
||||||
["reflect-metadata", "npm:0.1.13"],\
|
["reflect-metadata", "npm:0.1.13"],\
|
||||||
|
["semver", "npm:7.5.1"],\
|
||||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||||
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.16"],\
|
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.16"],\
|
||||||
@@ -5244,10 +5233,10 @@ const RAW_RUNTIME_STATE =
|
|||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["@standardnotes/utils", [\
|
["@standardnotes/utils", [\
|
||||||
["npm:1.16.5", {\
|
["npm:1.17.5", {\
|
||||||
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.16.5-47f537f49f-d5caa7181f.zip/node_modules/@standardnotes/utils/",\
|
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.17.5-210b60222d-47e8520174.zip/node_modules/@standardnotes/utils/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["@standardnotes/utils", "npm:1.16.5"],\
|
["@standardnotes/utils", "npm:1.17.5"],\
|
||||||
["@standardnotes/common", "workspace:packages/common"],\
|
["@standardnotes/common", "workspace:packages/common"],\
|
||||||
["dompurify", "npm:2.4.5"],\
|
["dompurify", "npm:2.4.5"],\
|
||||||
["lodash", "npm:4.17.21"],\
|
["lodash", "npm:4.17.21"],\
|
||||||
@@ -5263,14 +5252,14 @@ const RAW_RUNTIME_STATE =
|
|||||||
["@standardnotes/websockets-server", "workspace:packages/websockets"],\
|
["@standardnotes/websockets-server", "workspace:packages/websockets"],\
|
||||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||||
["@standardnotes/api", "npm:1.26.10"],\
|
["@standardnotes/api", "npm:1.26.26"],\
|
||||||
["@standardnotes/common", "workspace:packages/common"],\
|
["@standardnotes/common", "workspace:packages/common"],\
|
||||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||||
["@standardnotes/responses", "npm:1.13.24"],\
|
["@standardnotes/responses", "npm:1.13.27"],\
|
||||||
["@standardnotes/security", "workspace:packages/security"],\
|
["@standardnotes/security", "workspace:packages/security"],\
|
||||||
["@standardnotes/utils", "npm:1.16.5"],\
|
["@standardnotes/utils", "npm:1.17.5"],\
|
||||||
["@types/cors", "npm:2.8.13"],\
|
["@types/cors", "npm:2.8.13"],\
|
||||||
["@types/express", "npm:4.17.17"],\
|
["@types/express", "npm:4.17.17"],\
|
||||||
["@types/ioredis", "npm:5.0.0"],\
|
["@types/ioredis", "npm:5.0.0"],\
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -3,6 +3,56 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [2.25.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.2...@standardnotes/analytics@2.25.3) (2023-07-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.1...@standardnotes/analytics@2.25.2) (2023-07-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.25.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.0...@standardnotes/analytics@2.25.1) (2023-07-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
# [2.25.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.9...@standardnotes/analytics@2.25.0) (2023-07-17)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **syncing-server:** refactor syncing to decouple getting and saving items ([#659](https://github.com/standardnotes/server/issues/659)) ([cb74b23](https://github.com/standardnotes/server/commit/cb74b23e45b207136e299ce8a3db2c04dc87e21e))
|
||||||
|
|
||||||
|
## [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)
|
## [2.24.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.0...@standardnotes/analytics@2.24.1) (2023-06-02)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@standardnotes/analytics",
|
"name": "@standardnotes/analytics",
|
||||||
"version": "2.24.1",
|
"version": "2.25.3",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0 <21.0.0"
|
"node": ">=18.0.0 <21.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ import { Result, Entity, UniqueEntityId } from '@standardnotes/domain-core'
|
|||||||
import { StatisticMeasureProps } from './StatisticMeasureProps'
|
import { StatisticMeasureProps } from './StatisticMeasureProps'
|
||||||
|
|
||||||
export class StatisticMeasure extends Entity<StatisticMeasureProps> {
|
export class StatisticMeasure extends Entity<StatisticMeasureProps> {
|
||||||
get id(): UniqueEntityId {
|
|
||||||
return this._id
|
|
||||||
}
|
|
||||||
|
|
||||||
get name(): string {
|
get name(): string {
|
||||||
return this.props.name.value
|
return this.props.name.value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
|||||||
import { SubscriptionProps } from './SubscriptionProps'
|
import { SubscriptionProps } from './SubscriptionProps'
|
||||||
|
|
||||||
export class Subscription extends Entity<SubscriptionProps> {
|
export class Subscription extends Entity<SubscriptionProps> {
|
||||||
get id(): UniqueEntityId {
|
|
||||||
return this._id
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructor(props: SubscriptionProps, id?: UniqueEntityId) {
|
private constructor(props: SubscriptionProps, id?: UniqueEntityId) {
|
||||||
super(props, id)
|
super(props, id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
|||||||
import { UserProps } from './UserProps'
|
import { UserProps } from './UserProps'
|
||||||
|
|
||||||
export class User extends Entity<UserProps> {
|
export class User extends Entity<UserProps> {
|
||||||
get id(): UniqueEntityId {
|
|
||||||
return this._id
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructor(props: UserProps, id?: UniqueEntityId) {
|
private constructor(props: UserProps, id?: UniqueEntityId) {
|
||||||
super(props, id)
|
super(props, id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
MODE=microservice # microservice | home-server
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=debug
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
VERSION=development
|
VERSION=development
|
||||||
|
|||||||
@@ -3,6 +3,90 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.67.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.67.1...@standardnotes/api-gateway@1.67.2) (2023-07-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.67.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.67.0...@standardnotes/api-gateway@1.67.1) (2023-07-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
# [1.67.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.66.1...@standardnotes/api-gateway@1.67.0) (2023-07-20)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **syncing-server:** add shared vaults, invites, messages and notifications to sync response ([#665](https://github.com/standardnotes/api-gateway/issues/665)) ([efa4d7f](https://github.com/standardnotes/api-gateway/commit/efa4d7fc6007ef668e3de3b04853ac11b2d13c30))
|
||||||
|
|
||||||
|
## [1.66.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.66.0...@standardnotes/api-gateway@1.66.1) (2023-07-19)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add missing imports and exports for controllers ([#664](https://github.com/standardnotes/api-gateway/issues/664)) ([aee6e60](https://github.com/standardnotes/api-gateway/commit/aee6e6058359e2b5231cc13387656f837699300f))
|
||||||
|
|
||||||
|
# [1.66.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.7...@standardnotes/api-gateway@1.66.0) (2023-07-19)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **syncing-server:** add persistence of shared vaults with users and invites + controllers ([#662](https://github.com/standardnotes/api-gateway/issues/662)) ([3f21a35](https://github.com/standardnotes/api-gateway/commit/3f21a358d24d70daf541aa62dc86cd9e29500e62))
|
||||||
|
|
||||||
|
## [1.65.7](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.6...@standardnotes/api-gateway@1.65.7) (2023-07-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
### Bug Fixes
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ import '../src/Controller/v1/OfflineController'
|
|||||||
import '../src/Controller/v1/FilesController'
|
import '../src/Controller/v1/FilesController'
|
||||||
import '../src/Controller/v1/SubscriptionInvitesController'
|
import '../src/Controller/v1/SubscriptionInvitesController'
|
||||||
import '../src/Controller/v1/AuthenticatorsController'
|
import '../src/Controller/v1/AuthenticatorsController'
|
||||||
|
import '../src/Controller/v1/MessagesController'
|
||||||
|
import '../src/Controller/v1/SharedVaultsController'
|
||||||
|
import '../src/Controller/v1/SharedVaultInvitesController'
|
||||||
|
import '../src/Controller/v1/SharedVaultUsersController'
|
||||||
|
|
||||||
import '../src/Controller/v2/PaymentsControllerV2'
|
import '../src/Controller/v2/PaymentsControllerV2'
|
||||||
import '../src/Controller/v2/ActionsControllerV2'
|
import '../src/Controller/v2/ActionsControllerV2'
|
||||||
@@ -45,28 +49,29 @@ void container.load().then((container) => {
|
|||||||
response.setHeader('X-API-Gateway-Version', container.get(TYPES.VERSION))
|
response.setHeader('X-API-Gateway-Version', container.get(TYPES.VERSION))
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
/* eslint-disable */
|
app.use(
|
||||||
app.use(helmet({
|
helmet({
|
||||||
contentSecurityPolicy: {
|
contentSecurityPolicy: {
|
||||||
directives: {
|
directives: {
|
||||||
defaultSrc: ["https: 'self'"],
|
defaultSrc: ["https: 'self'"],
|
||||||
baseUri: ["'self'"],
|
baseUri: ["'self'"],
|
||||||
childSrc: ["*", "blob:"],
|
childSrc: ['*', 'blob:'],
|
||||||
connectSrc: ["*"],
|
connectSrc: ['*'],
|
||||||
fontSrc: ["*", "'self'"],
|
fontSrc: ['*', "'self'"],
|
||||||
formAction: ["'self'"],
|
formAction: ["'self'"],
|
||||||
frameAncestors: ["*", "*.standardnotes.org", "*.standardnotes.com"],
|
frameAncestors: ['*', '*.standardnotes.org', '*.standardnotes.com'],
|
||||||
frameSrc: ["*", "blob:"],
|
frameSrc: ['*', 'blob:'],
|
||||||
imgSrc: ["'self'", "*", "data:"],
|
imgSrc: ["'self'", '*', 'data:'],
|
||||||
manifestSrc: ["'self'"],
|
manifestSrc: ["'self'"],
|
||||||
mediaSrc: ["'self'"],
|
mediaSrc: ["'self'"],
|
||||||
objectSrc: ["'self'"],
|
objectSrc: ["'self'"],
|
||||||
scriptSrc: ["'self'"],
|
scriptSrc: ["'self'"],
|
||||||
styleSrc: ["'self'"]
|
styleSrc: ["'self'"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}))
|
}),
|
||||||
/* eslint-enable */
|
)
|
||||||
|
|
||||||
app.use(json({ limit: '50mb' }))
|
app.use(json({ limit: '50mb' }))
|
||||||
app.use(
|
app.use(
|
||||||
text({
|
text({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@standardnotes/api-gateway",
|
"name": "@standardnotes/api-gateway",
|
||||||
"version": "1.63.2",
|
"version": "1.67.2",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0 <21.0.0"
|
"node": ">=18.0.0 <21.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ export class ContainerConfigLoader {
|
|||||||
|
|
||||||
const container = new Container()
|
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()]
|
const winstonFormatters = [winston.format.splat(), winston.format.json()]
|
||||||
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
|
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
|
||||||
@@ -45,19 +46,20 @@ export class ContainerConfigLoader {
|
|||||||
winstonFormatters.push(newrelicWinstonFormatter())
|
winstonFormatters.push(newrelicWinstonFormatter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let logger: winston.Logger
|
||||||
if (configuration?.logger) {
|
if (configuration?.logger) {
|
||||||
container.bind<winston.Logger>(TYPES.Logger).toConstantValue(configuration.logger as winston.Logger)
|
logger = configuration.logger as winston.Logger
|
||||||
} else {
|
} else {
|
||||||
const logger = winston.createLogger({
|
logger = winston.createLogger({
|
||||||
level: env.get('LOG_LEVEL', true) || 'info',
|
level: env.get('LOG_LEVEL', true) || 'info',
|
||||||
format: winston.format.combine(...winstonFormatters),
|
format: winston.format.combine(...winstonFormatters),
|
||||||
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
|
transports: [new winston.transports.Console({ level: env.get('LOG_LEVEL', true) || 'info' })],
|
||||||
defaultMeta: { service: 'api-gateway' },
|
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 redisUrl = env.get('REDIS_URL')
|
||||||
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||||
let redis
|
let redis
|
||||||
@@ -124,6 +126,8 @@ export class ContainerConfigLoader {
|
|||||||
.bind<EndpointResolverInterface>(TYPES.EndpointResolver)
|
.bind<EndpointResolverInterface>(TYPES.EndpointResolver)
|
||||||
.toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
|
.toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
|
||||||
|
|
||||||
|
logger.debug('Configuration complete')
|
||||||
|
|
||||||
return container
|
return container
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export abstract class AuthMiddleware extends BaseMiddleware {
|
|||||||
private crossServiceTokenCacheTTL: number,
|
private crossServiceTokenCacheTTL: number,
|
||||||
private crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
private crossServiceTokenCache: CrossServiceTokenCacheInterface,
|
||||||
private timer: TimerInterface,
|
private timer: TimerInterface,
|
||||||
private logger: Logger,
|
protected logger: Logger,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ export class RequiredCrossServiceTokenMiddleware extends AuthMiddleware {
|
|||||||
_next: NextFunction,
|
_next: NextFunction,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (!authHeaderValue) {
|
if (!authHeaderValue) {
|
||||||
|
this.logger.debug('Missing auth header')
|
||||||
|
|
||||||
response.status(401).send({
|
response.status(401).send({
|
||||||
error: {
|
error: {
|
||||||
tag: 'invalid-auth',
|
tag: 'invalid-auth',
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export * from './SubscriptionTokenAuthMiddleware'
|
|||||||
export * from './TokenAuthenticationMethod'
|
export * from './TokenAuthenticationMethod'
|
||||||
export * from './WebSocketAuthMiddleware'
|
export * from './WebSocketAuthMiddleware'
|
||||||
export * from './v1/ActionsController'
|
export * from './v1/ActionsController'
|
||||||
|
export * from './v1/MessagesController'
|
||||||
export * from './v1/AuthenticatorsController'
|
export * from './v1/AuthenticatorsController'
|
||||||
export * from './v1/FilesController'
|
export * from './v1/FilesController'
|
||||||
export * from './v1/InvoicesController'
|
export * from './v1/InvoicesController'
|
||||||
@@ -12,6 +13,9 @@ export * from './v1/OfflineController'
|
|||||||
export * from './v1/PaymentsController'
|
export * from './v1/PaymentsController'
|
||||||
export * from './v1/RevisionsController'
|
export * from './v1/RevisionsController'
|
||||||
export * from './v1/SessionsController'
|
export * from './v1/SessionsController'
|
||||||
|
export * from './v1/SharedVaultInvitesController'
|
||||||
|
export * from './v1/SharedVaultUsersController'
|
||||||
|
export * from './v1/SharedVaultsController'
|
||||||
export * from './v1/SubscriptionInvitesController'
|
export * from './v1/SubscriptionInvitesController'
|
||||||
export * from './v1/TokensController'
|
export * from './v1/TokensController'
|
||||||
export * from './v1/UsersController'
|
export * from './v1/UsersController'
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { Request, Response } from 'express'
|
||||||
|
import { inject } from 'inversify'
|
||||||
|
import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
|
||||||
|
import { TYPES } from '../../Bootstrap/Types'
|
||||||
|
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||||
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
|
||||||
|
@controller('/v1/messages', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||||
|
export class MessagesController extends BaseHttpController {
|
||||||
|
constructor(
|
||||||
|
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
|
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpGet('/')
|
||||||
|
async getMessages(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'messages/'),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpGet('/outbound')
|
||||||
|
async getMessagesSent(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'messages/outbound'),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpPost('/')
|
||||||
|
async sendMessage(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'messages/'),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpDelete('/inbound')
|
||||||
|
async deleteMessagesSentToUser(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('DELETE', 'messages/inbound'),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpDelete('/:messageUuid')
|
||||||
|
async deleteMessage(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'DELETE',
|
||||||
|
'messages/:messageUuid',
|
||||||
|
request.params.messageUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import { Request, Response } from 'express'
|
||||||
|
import { inject } from 'inversify'
|
||||||
|
import { BaseHttpController, controller, httpDelete, httpGet, httpPatch, httpPost } from 'inversify-express-utils'
|
||||||
|
import { TYPES } from '../../Bootstrap/Types'
|
||||||
|
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||||
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
|
||||||
|
@controller('/v1/shared-vaults', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||||
|
export class SharedVaultInvitesController extends BaseHttpController {
|
||||||
|
constructor(
|
||||||
|
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
|
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpPost('/:sharedVaultUuid/invites')
|
||||||
|
async createSharedVaultInvite(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'POST',
|
||||||
|
'shared-vaults/:sharedVaultUuid/invites',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpPatch('/:sharedVaultUuid/invites/:inviteUuid')
|
||||||
|
async updateSharedVaultInvite(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'PATCH',
|
||||||
|
'shared-vaults/:sharedVaultUuid/invites/:inviteUuid',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
request.params.inviteUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpPost('/:sharedVaultUuid/invites/:inviteUuid/accept')
|
||||||
|
async acceptSharedVaultInvite(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'POST',
|
||||||
|
'shared-vaults/:sharedVaultUuid/invites/:inviteUuid/accept',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
request.params.inviteUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpPost('/:sharedVaultUuid/invites/:inviteUuid/decline')
|
||||||
|
async declineSharedVaultInvite(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'POST',
|
||||||
|
'shared-vaults/:sharedVaultUuid/invites/:inviteUuid/decline',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
request.params.inviteUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpDelete('/invites/inbound')
|
||||||
|
async deleteInboundUserInvites(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('DELETE', 'shared-vaults/invites/inbound'),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpGet('/invites/outbound')
|
||||||
|
async getOutboundUserInvites(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'shared-vaults/invites/outbound'),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpGet('/invites')
|
||||||
|
async getUserInvites(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'shared-vaults/invites'),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpGet('/:sharedVaultUuid/invites')
|
||||||
|
async getSharedVaultInvites(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'GET',
|
||||||
|
'shared-vaults/:sharedVaultUuid/invites',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpDelete('/:sharedVaultUuid/invites/:inviteUuid')
|
||||||
|
async deleteSharedVaultInvite(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'DELETE',
|
||||||
|
'shared-vaults/:sharedVaultUuid/invites/:inviteUuid',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
request.params.inviteUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpDelete('/:sharedVaultUuid/invites')
|
||||||
|
async deleteAllSharedVaultInvites(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'DELETE',
|
||||||
|
'shared-vaults/:sharedVaultUuid/invites',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { Request, Response } from 'express'
|
||||||
|
import { inject } from 'inversify'
|
||||||
|
import { BaseHttpController, controller, httpDelete, httpGet } from 'inversify-express-utils'
|
||||||
|
import { TYPES } from '../../Bootstrap/Types'
|
||||||
|
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||||
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
|
||||||
|
@controller('/v1/shared-vaults/:sharedVaultUuid/users', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||||
|
export class SharedVaultUsersController extends BaseHttpController {
|
||||||
|
constructor(
|
||||||
|
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
|
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpGet('/')
|
||||||
|
async getSharedVaultUsers(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'GET',
|
||||||
|
'shared-vaults/:sharedVaultUuid/users',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpDelete('/:userUuid')
|
||||||
|
async removeUserFromSharedVault(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'DELETE',
|
||||||
|
'shared-vaults/:sharedVaultUuid/users/:userUuid',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
request.params.userUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { Request, Response } from 'express'
|
||||||
|
import { inject } from 'inversify'
|
||||||
|
import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
|
||||||
|
import { TYPES } from '../../Bootstrap/Types'
|
||||||
|
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||||
|
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
|
||||||
|
|
||||||
|
@controller('/v1/shared-vaults', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||||
|
export class SharedVaultsController extends BaseHttpController {
|
||||||
|
constructor(
|
||||||
|
@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
|
||||||
|
@inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpGet('/')
|
||||||
|
async getSharedVaults(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('GET', 'shared-vaults/'),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpPost('/')
|
||||||
|
async createSharedVault(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'shared-vaults/'),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpDelete('/:sharedVaultUuid')
|
||||||
|
async deleteSharedVault(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'DELETE',
|
||||||
|
'shared-vaults/:sharedVaultUuid',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@httpPost('/:sharedVaultUuid/valet-tokens')
|
||||||
|
async createValetTokenForSharedVaultFile(request: Request, response: Response): Promise<void> {
|
||||||
|
await this.httpService.callSyncingServer(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||||
|
'POST',
|
||||||
|
'shared-vaults/:sharedVaultUuid/valet-tokens',
|
||||||
|
request.params.sharedVaultUuid,
|
||||||
|
),
|
||||||
|
request.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response } from 'express'
|
import { Request, Response } from 'express'
|
||||||
|
import { ServiceContainerInterface, ServiceIdentifier } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
import { ServiceProxyInterface } from '../Http/ServiceProxyInterface'
|
import { ServiceProxyInterface } from '../Http/ServiceProxyInterface'
|
||||||
import { ServiceContainerInterface, ServiceIdentifier } from '@standardnotes/domain-core'
|
|
||||||
|
|
||||||
export class DirectCallServiceProxy implements ServiceProxyInterface {
|
export class DirectCallServiceProxy implements ServiceProxyInterface {
|
||||||
constructor(private serviceContainer: ServiceContainerInterface, private filesServerUrl: string) {}
|
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> {
|
async callEmailServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
|
||||||
throw new Error('Email server is not available.')
|
response.status(400).send({
|
||||||
|
error: {
|
||||||
|
message: 'Email server is not available.',
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async callAuthServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
|
async callAuthServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
|
||||||
@@ -54,10 +58,14 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
|
|||||||
|
|
||||||
async callAuthServerWithLegacyFormat(
|
async callAuthServerWithLegacyFormat(
|
||||||
_request: Request,
|
_request: Request,
|
||||||
_response: Response,
|
response: Response,
|
||||||
_endpointOrMethodIdentifier: string,
|
_endpointOrMethodIdentifier: string,
|
||||||
): Promise<void> {
|
): 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> {
|
async callRevisionsServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
|
||||||
@@ -92,22 +100,30 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
|
|||||||
|
|
||||||
async callLegacySyncingServer(
|
async callLegacySyncingServer(
|
||||||
_request: Request,
|
_request: Request,
|
||||||
_response: Response,
|
response: Response,
|
||||||
_endpointOrMethodIdentifier: string,
|
_endpointOrMethodIdentifier: string,
|
||||||
): Promise<void> {
|
): 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> {
|
async callPaymentsServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
|
||||||
throw new Error('Payments server is not available.')
|
response.status(400).send({
|
||||||
|
error: {
|
||||||
|
message: 'Payments server is not available.',
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async callWebSocketServer(
|
async callWebSocketServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
|
||||||
_request: Request,
|
response.status(400).send({
|
||||||
_response: Response,
|
error: {
|
||||||
_endpointOrMethodIdentifier: string,
|
message: 'Websockets server is not available.',
|
||||||
): Promise<void> {
|
},
|
||||||
throw new Error('Websockets server is not available.')
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private sendDecoratedResponse(
|
private sendDecoratedResponse(
|
||||||
|
|||||||
@@ -62,6 +62,31 @@ export class EndpointResolver implements EndpointResolverInterface {
|
|||||||
['[GET]:items/:itemUuid/revisions', 'revisions.revisions.getRevisions'],
|
['[GET]:items/:itemUuid/revisions', 'revisions.revisions.getRevisions'],
|
||||||
['[GET]:items/:itemUuid/revisions/:id', 'revisions.revisions.getRevision'],
|
['[GET]:items/:itemUuid/revisions/:id', 'revisions.revisions.getRevision'],
|
||||||
['[DELETE]:items/:itemUuid/revisions/:id', 'revisions.revisions.deleteRevision'],
|
['[DELETE]:items/:itemUuid/revisions/:id', 'revisions.revisions.deleteRevision'],
|
||||||
|
// Messages Controller
|
||||||
|
['[GET]:messages/', 'sync.messages.get-received'],
|
||||||
|
['[GET]:messages/outbound', 'sync.messages.get-sent'],
|
||||||
|
['[POST]:messages/', 'sync.messages.send'],
|
||||||
|
['[DELETE]:messages/inbound', 'sync.messages.delete-all'],
|
||||||
|
['[DELETE]:messages/:messageUuid', 'sync.messages.delete'],
|
||||||
|
// Shared Vaults Controller
|
||||||
|
['[GET]:shared-vaults/', 'sync.shared-vaults.get-vaults'],
|
||||||
|
['[POST]:shared-vaults/', 'sync.shared-vaults.create-vault'],
|
||||||
|
['[DELETE]:shared-vaults/:sharedVaultUuid', 'sync.shared-vaults.delete-vault'],
|
||||||
|
['[POST]:shared-vaults/:sharedVaultUuid/valet-tokens', 'sync.shared-vaults.create-file-valet-token'],
|
||||||
|
// Shared Vault Invites Controller
|
||||||
|
['[POST]:shared-vaults/:sharedVaultUuid/invites', 'sync.shared-vault-invites.create'],
|
||||||
|
['[PATCH]:shared-vaults/:sharedVaultUuid/invites/:inviteUuid', 'sync.shared-vault-invites.update'],
|
||||||
|
['[POST]:shared-vaults/:sharedVaultUuid/invites/:inviteUuid/accept', 'sync.shared-vault-invites.accept'],
|
||||||
|
['[POST]:shared-vaults/:sharedVaultUuid/invites/:inviteUuid/decline', 'sync.shared-vault-invites.decline'],
|
||||||
|
['[DELETE]:shared-vaults/invites/inbound', 'sync.shared-vault-invites.delete-inbound'],
|
||||||
|
['[GET]:shared-vaults/invites/outbound', 'sync.shared-vault-invites.get-outbound'],
|
||||||
|
['[GET]:shared-vaults/invites', 'sync.shared-vault-invites.get-user-invites'],
|
||||||
|
['[GET]:shared-vaults/:sharedVaultUuid/invites', 'sync.shared-vault-invites.get-vault-invites'],
|
||||||
|
['[DELETE]:shared-vaults/:sharedVaultUuid/invites/:inviteUuid', 'sync.shared-vault-invites.delete-invite'],
|
||||||
|
['[DELETE]:shared-vaults/:sharedVaultUuid/invites', 'sync.shared-vault-invites.delete-all'],
|
||||||
|
// Shared Vault Users Controller
|
||||||
|
['[GET]:shared-vaults/:sharedVaultUuid/users', 'sync.shared-vault-users.get-users'],
|
||||||
|
['[DELETE]:shared-vaults/:sharedVaultUuid/users/:userUuid', 'sync.shared-vault-users.remove-user'],
|
||||||
])
|
])
|
||||||
|
|
||||||
resolveEndpointOrMethodIdentifier(method: string, endpoint: string, ...params: string[]): string {
|
resolveEndpointOrMethodIdentifier(method: string, endpoint: string, ...params: string[]): string {
|
||||||
@@ -75,7 +100,7 @@ export class EndpointResolver implements EndpointResolverInterface {
|
|||||||
const identifier = this.endpointToIdentifierMap.get(`[${method}]:${endpoint}`)
|
const identifier = this.endpointToIdentifierMap.get(`[${method}]:${endpoint}`)
|
||||||
|
|
||||||
if (!identifier) {
|
if (!identifier) {
|
||||||
throw new Error(`Endpoint ${endpoint} not found`)
|
throw new Error(`Endpoint [${method}]:${endpoint} not found`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return identifier
|
return identifier
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
MODE=microservice # microservice | home-server
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=debug
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
VERSION=development
|
VERSION=development
|
||||||
@@ -20,8 +21,10 @@ DB_USERNAME=auth
|
|||||||
DB_PASSWORD=changeme123
|
DB_PASSWORD=changeme123
|
||||||
DB_DATABASE=auth
|
DB_DATABASE=auth
|
||||||
DB_DEBUG_LEVEL=all # "all" | "query" | "schema" | "error" | "warn" | "info" | "log" | "migration"
|
DB_DEBUG_LEVEL=all # "all" | "query" | "schema" | "error" | "warn" | "info" | "log" | "migration"
|
||||||
|
DB_TYPE=mysql
|
||||||
|
|
||||||
REDIS_URL=redis://cache
|
REDIS_URL=redis://cache
|
||||||
|
CACHE_TYPE=redis
|
||||||
|
|
||||||
DISABLE_USER_REGISTRATION=false
|
DISABLE_USER_REGISTRATION=false
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,150 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.126.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.126.1...@standardnotes/auth-server@1.126.2) (2023-07-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
## [1.126.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.126.0...@standardnotes/auth-server@1.126.1) (2023-07-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
# [1.126.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.125.1...@standardnotes/auth-server@1.126.0) (2023-07-20)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **syncing-server:** add shared vaults, invites, messages and notifications to sync response ([#665](https://github.com/standardnotes/server/issues/665)) ([efa4d7f](https://github.com/standardnotes/server/commit/efa4d7fc6007ef668e3de3b04853ac11b2d13c30))
|
||||||
|
|
||||||
|
## [1.125.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.125.0...@standardnotes/auth-server@1.125.1) (2023-07-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
# [1.125.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.124.2...@standardnotes/auth-server@1.125.0) (2023-07-17)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **syncing-server:** refactor syncing to decouple getting and saving items ([#659](https://github.com/standardnotes/server/issues/659)) ([cb74b23](https://github.com/standardnotes/server/commit/cb74b23e45b207136e299ce8a3db2c04dc87e21e))
|
||||||
|
|
||||||
|
## [1.124.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.124.1...@standardnotes/auth-server@1.124.2) (2023-07-14)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **home-server:** allow custom atributtes for activating premium features ([f7190c0](https://github.com/standardnotes/server/commit/f7190c0c9c2d105f97d1cf980ce6a4f0dae34805))
|
||||||
|
|
||||||
|
## [1.124.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.124.0...@standardnotes/auth-server@1.124.1) (2023-07-13)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **files:** handling unlimited storage quota on home server ([9be3517](https://github.com/standardnotes/server/commit/9be3517093f8dd7bbdd7507c1e2ff059e6c9a889))
|
||||||
|
|
||||||
|
# [1.124.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.123.2...@standardnotes/auth-server@1.124.0) (2023-07-13)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** add overriding subscription settings on home server ([#656](https://github.com/standardnotes/server/issues/656)) ([0b82794](https://github.com/standardnotes/server/commit/0b82794e9c7ed82cfc08a92eafc016fbde5c4fcc))
|
||||||
|
|
||||||
|
## [1.123.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.123.1...@standardnotes/auth-server@1.123.2) (2023-07-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
## [1.123.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.123.0...@standardnotes/auth-server@1.123.1) (2023-07-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
# [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)
|
## [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
|
### Bug Fixes
|
||||||
|
|||||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@standardnotes/auth-server",
|
"name": "@standardnotes/auth-server",
|
||||||
"version": "1.116.2",
|
"version": "1.126.2",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0 <21.0.0"
|
"node": ">=18.0.0 <21.0.0"
|
||||||
},
|
},
|
||||||
@@ -32,7 +32,8 @@
|
|||||||
"weekly-backup:email": "yarn node dist/bin/backup.js email weekly",
|
"weekly-backup:email": "yarn node dist/bin/backup.js email weekly",
|
||||||
"content-recalculation": "yarn node dist/bin/content.js",
|
"content-recalculation": "yarn node dist/bin/content.js",
|
||||||
"typeorm": "typeorm-ts-node-commonjs",
|
"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": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-sns": "^3.332.0",
|
"@aws-sdk/client-sns": "^3.332.0",
|
||||||
@@ -41,17 +42,17 @@
|
|||||||
"@cbor-extract/cbor-extract-linux-x64": "^2.1.1",
|
"@cbor-extract/cbor-extract-linux-x64": "^2.1.1",
|
||||||
"@simplewebauthn/server": "^7.2.0",
|
"@simplewebauthn/server": "^7.2.0",
|
||||||
"@simplewebauthn/typescript-types": "^7.0.0",
|
"@simplewebauthn/typescript-types": "^7.0.0",
|
||||||
"@standardnotes/api": "^1.25.3",
|
"@standardnotes/api": "^1.26.26",
|
||||||
"@standardnotes/common": "workspace:*",
|
"@standardnotes/common": "workspace:*",
|
||||||
"@standardnotes/domain-core": "workspace:^",
|
"@standardnotes/domain-core": "workspace:^",
|
||||||
"@standardnotes/domain-events": "workspace:*",
|
"@standardnotes/domain-events": "workspace:*",
|
||||||
"@standardnotes/domain-events-infra": "workspace:*",
|
"@standardnotes/domain-events-infra": "workspace:*",
|
||||||
"@standardnotes/features": "^1.58.12",
|
"@standardnotes/features": "^1.59.7",
|
||||||
"@standardnotes/predicates": "workspace:*",
|
"@standardnotes/predicates": "workspace:*",
|
||||||
"@standardnotes/responses": "^1.13.9",
|
"@standardnotes/responses": "^1.13.27",
|
||||||
"@standardnotes/security": "workspace:*",
|
"@standardnotes/security": "workspace:*",
|
||||||
"@standardnotes/settings": "workspace:*",
|
"@standardnotes/settings": "workspace:*",
|
||||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
"@standardnotes/sncrypto-common": "^1.13.4",
|
||||||
"@standardnotes/sncrypto-node": "workspace:*",
|
"@standardnotes/sncrypto-node": "workspace:*",
|
||||||
"@standardnotes/time": "workspace:*",
|
"@standardnotes/time": "workspace:*",
|
||||||
"axios": "^1.1.3",
|
"axios": "^1.1.3",
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Result, ServiceInterface } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
export interface AuthServiceInterface extends ServiceInterface {
|
||||||
|
activatePremiumFeatures(dto: {
|
||||||
|
username: string
|
||||||
|
subscriptionPlanName?: string
|
||||||
|
endsAt?: Date
|
||||||
|
}): Promise<Result<string>>
|
||||||
|
}
|
||||||
@@ -251,6 +251,7 @@ import { HomeServerValetTokenController } from '../Infra/InversifyExpressUtils/H
|
|||||||
import { HomeServerWebSocketsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerWebSocketsController'
|
import { HomeServerWebSocketsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerWebSocketsController'
|
||||||
import { HomeServerSessionsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSessionsController'
|
import { HomeServerSessionsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerSessionsController'
|
||||||
import { Transform } from 'stream'
|
import { Transform } from 'stream'
|
||||||
|
import { ActivatePremiumFeatures } from '../Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeatures'
|
||||||
|
|
||||||
export class ContainerConfigLoader {
|
export class ContainerConfigLoader {
|
||||||
async load(configuration?: {
|
async load(configuration?: {
|
||||||
@@ -267,12 +268,37 @@ export class ContainerConfigLoader {
|
|||||||
|
|
||||||
const container = new Container()
|
const container = new Container()
|
||||||
|
|
||||||
|
const winstonFormatters = [winston.format.splat(), winston.format.json()]
|
||||||
|
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
|
||||||
|
await import('newrelic')
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||||
|
const newrelicWinstonFormatter = newrelicFormatter(winston)
|
||||||
|
winstonFormatters.push(newrelicWinstonFormatter())
|
||||||
|
}
|
||||||
|
|
||||||
|
let logger: winston.Logger
|
||||||
|
if (configuration?.logger) {
|
||||||
|
logger = configuration.logger as winston.Logger
|
||||||
|
} else {
|
||||||
|
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', true) || 'info' })],
|
||||||
|
defaultMeta: { service: 'auth' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(logger)
|
||||||
|
|
||||||
const appDataSource = new AppDataSource(env)
|
const appDataSource = new AppDataSource(env)
|
||||||
await appDataSource.initialize()
|
await appDataSource.initialize()
|
||||||
|
|
||||||
const isConfiguredForHomeServer = env.get('DB_TYPE') === 'sqlite'
|
logger.debug('Database initialized')
|
||||||
|
|
||||||
if (!isConfiguredForHomeServer) {
|
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 redisUrl = env.get('REDIS_URL')
|
||||||
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||||
let redis
|
let redis
|
||||||
@@ -285,27 +311,6 @@ export class ContainerConfigLoader {
|
|||||||
container.bind(TYPES.Auth_Redis).toConstantValue(redis)
|
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')
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
||||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
|
||||||
const newrelicWinstonFormatter = newrelicFormatter(winston)
|
|
||||||
winstonFormatters.push(newrelicWinstonFormatter())
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configuration?.logger) {
|
|
||||||
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(configuration.logger as winston.Logger)
|
|
||||||
} else {
|
|
||||||
const 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', true) || 'info' })],
|
|
||||||
defaultMeta: { service: 'auth' },
|
|
||||||
})
|
|
||||||
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
container.bind<TimerInterface>(TYPES.Auth_Timer).toConstantValue(new Timer())
|
container.bind<TimerInterface>(TYPES.Auth_Timer).toConstantValue(new Timer())
|
||||||
|
|
||||||
if (!isConfiguredForHomeServer) {
|
if (!isConfiguredForHomeServer) {
|
||||||
@@ -488,7 +493,9 @@ export class ContainerConfigLoader {
|
|||||||
.bind(TYPES.Auth_AUTH_JWT_TTL)
|
.bind(TYPES.Auth_AUTH_JWT_TTL)
|
||||||
.toConstantValue(env.get('AUTH_JWT_TTL', true) ? +env.get('AUTH_JWT_TTL') : 60_000)
|
.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_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
|
container
|
||||||
.bind(TYPES.Auth_WEB_SOCKET_CONNECTION_TOKEN_SECRET)
|
.bind(TYPES.Auth_WEB_SOCKET_CONNECTION_TOKEN_SECRET)
|
||||||
.toConstantValue(env.get('WEB_SOCKET_CONNECTION_TOKEN_SECRET', true))
|
.toConstantValue(env.get('WEB_SOCKET_CONNECTION_TOKEN_SECRET', true))
|
||||||
@@ -550,7 +557,16 @@ export class ContainerConfigLoader {
|
|||||||
.bind(TYPES.Auth_READONLY_USERS)
|
.bind(TYPES.Auth_READONLY_USERS)
|
||||||
.toConstantValue(env.get('READONLY_USERS', true) ? env.get('READONLY_USERS', true).split(',') : [])
|
.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
|
container
|
||||||
.bind<LockRepositoryInterface>(TYPES.Auth_LockRepository)
|
.bind<LockRepositoryInterface>(TYPES.Auth_LockRepository)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
@@ -578,15 +594,6 @@ export class ContainerConfigLoader {
|
|||||||
container.get(TYPES.Auth_Timer),
|
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
|
container
|
||||||
.bind<SubscriptionTokenRepositoryInterface>(TYPES.Auth_SubscriptionTokenRepository)
|
.bind<SubscriptionTokenRepositoryInterface>(TYPES.Auth_SubscriptionTokenRepository)
|
||||||
.toConstantValue(
|
.toConstantValue(
|
||||||
@@ -780,6 +787,17 @@ export class ContainerConfigLoader {
|
|||||||
container.get(TYPES.Auth_CryptoNode),
|
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_SubscriptionSettingService),
|
||||||
|
container.get(TYPES.Auth_RoleService),
|
||||||
|
container.get(TYPES.Auth_Timer),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
container
|
container
|
||||||
.bind<CleanupSessionTraces>(TYPES.Auth_CleanupSessionTraces)
|
.bind<CleanupSessionTraces>(TYPES.Auth_CleanupSessionTraces)
|
||||||
@@ -1187,6 +1205,8 @@ export class ContainerConfigLoader {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug('Configuration complete')
|
||||||
|
|
||||||
return container
|
return container
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,19 +20,23 @@ import { Env } from './Env'
|
|||||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||||
|
|
||||||
export class AppDataSource {
|
export class AppDataSource {
|
||||||
private dataSource: DataSource | undefined
|
private _dataSource: DataSource | undefined
|
||||||
|
|
||||||
constructor(private env: Env) {}
|
constructor(private env: Env) {}
|
||||||
|
|
||||||
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
|
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
|
||||||
if (!this.dataSource) {
|
if (!this._dataSource) {
|
||||||
throw new Error('DataSource not initialized')
|
throw new Error('DataSource not initialized')
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.dataSource.getRepository(target)
|
return this._dataSource.getRepository(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize(): Promise<void> {
|
async initialize(): Promise<void> {
|
||||||
|
await this.dataSource.initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
get dataSource(): DataSource {
|
||||||
this.env.load()
|
this.env.load()
|
||||||
|
|
||||||
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
|
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
|
||||||
@@ -104,17 +108,17 @@ export class AppDataSource {
|
|||||||
database: inReplicaMode ? undefined : this.env.get('DB_DATABASE'),
|
database: inReplicaMode ? undefined : this.env.get('DB_DATABASE'),
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dataSource = new DataSource(mySQLDataSourceOptions)
|
this._dataSource = new DataSource(mySQLDataSourceOptions)
|
||||||
} else {
|
} else {
|
||||||
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
||||||
...commonDataSourceOptions,
|
...commonDataSourceOptions,
|
||||||
type: 'sqlite',
|
type: 'sqlite',
|
||||||
database: `data/${this.env.get('DB_DATABASE')}.sqlite`,
|
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dataSource = new DataSource(sqliteDataSourceOptions)
|
this._dataSource = new DataSource(sqliteDataSourceOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.dataSource.initialize()
|
return this._dataSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,15 +1,21 @@
|
|||||||
import {
|
import {
|
||||||
ControllerContainerInterface,
|
ControllerContainerInterface,
|
||||||
|
Result,
|
||||||
ServiceConfiguration,
|
ServiceConfiguration,
|
||||||
ServiceContainerInterface,
|
ServiceContainerInterface,
|
||||||
ServiceIdentifier,
|
ServiceIdentifier,
|
||||||
ServiceInterface,
|
|
||||||
} from '@standardnotes/domain-core'
|
} from '@standardnotes/domain-core'
|
||||||
|
|
||||||
import { ContainerConfigLoader } from './Container'
|
import { ContainerConfigLoader } from './Container'
|
||||||
import { DirectCallDomainEventPublisher } from '@standardnotes/domain-events-infra'
|
import { DirectCallDomainEventPublisher } from '@standardnotes/domain-events-infra'
|
||||||
|
import TYPES from './Types'
|
||||||
|
import { Container } from 'inversify'
|
||||||
|
import { ActivatePremiumFeatures } from '../Domain/UseCase/ActivatePremiumFeatures/ActivatePremiumFeatures'
|
||||||
|
import { AuthServiceInterface } from './AuthServiceInterface'
|
||||||
|
|
||||||
|
export class Service implements AuthServiceInterface {
|
||||||
|
private container: Container | undefined
|
||||||
|
|
||||||
export class Service implements ServiceInterface {
|
|
||||||
constructor(
|
constructor(
|
||||||
private serviceContainer: ServiceContainerInterface,
|
private serviceContainer: ServiceContainerInterface,
|
||||||
private controllerContainer: ControllerContainerInterface,
|
private controllerContainer: ControllerContainerInterface,
|
||||||
@@ -18,6 +24,20 @@ export class Service implements ServiceInterface {
|
|||||||
this.serviceContainer.register(this.getId(), this)
|
this.serviceContainer.register(this.getId(), this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async activatePremiumFeatures(dto: {
|
||||||
|
username: string
|
||||||
|
subscriptionPlanName?: string
|
||||||
|
endsAt?: Date
|
||||||
|
}): 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(dto)
|
||||||
|
}
|
||||||
|
|
||||||
async handleRequest(request: never, response: never, endpointOrMethodIdentifier: string): Promise<unknown> {
|
async handleRequest(request: never, response: never, endpointOrMethodIdentifier: string): Promise<unknown> {
|
||||||
const method = this.controllerContainer.get(endpointOrMethodIdentifier)
|
const method = this.controllerContainer.get(endpointOrMethodIdentifier)
|
||||||
|
|
||||||
@@ -31,12 +51,16 @@ export class Service implements ServiceInterface {
|
|||||||
async getContainer(configuration?: ServiceConfiguration): Promise<unknown> {
|
async getContainer(configuration?: ServiceConfiguration): Promise<unknown> {
|
||||||
const config = new ContainerConfigLoader()
|
const config = new ContainerConfigLoader()
|
||||||
|
|
||||||
return config.load({
|
const container = await config.load({
|
||||||
controllerConatiner: this.controllerContainer,
|
controllerConatiner: this.controllerContainer,
|
||||||
directCallDomainEventPublisher: this.directCallDomainEventPublisher,
|
directCallDomainEventPublisher: this.directCallDomainEventPublisher,
|
||||||
logger: configuration?.logger,
|
logger: configuration?.logger,
|
||||||
environmentOverrides: configuration?.environmentOverrides,
|
environmentOverrides: configuration?.environmentOverrides,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.container = container
|
||||||
|
|
||||||
|
return container
|
||||||
}
|
}
|
||||||
|
|
||||||
getId(): ServiceIdentifier {
|
getId(): ServiceIdentifier {
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ const TYPES = {
|
|||||||
Auth_ListAuthenticators: Symbol.for('Auth_ListAuthenticators'),
|
Auth_ListAuthenticators: Symbol.for('Auth_ListAuthenticators'),
|
||||||
Auth_DeleteAuthenticator: Symbol.for('Auth_DeleteAuthenticator'),
|
Auth_DeleteAuthenticator: Symbol.for('Auth_DeleteAuthenticator'),
|
||||||
Auth_GenerateRecoveryCodes: Symbol.for('Auth_GenerateRecoveryCodes'),
|
Auth_GenerateRecoveryCodes: Symbol.for('Auth_GenerateRecoveryCodes'),
|
||||||
|
Auth_ActivatePremiumFeatures: Symbol.for('Auth_ActivatePremiumFeatures'),
|
||||||
Auth_SignInWithRecoveryCodes: Symbol.for('Auth_SignInWithRecoveryCodes'),
|
Auth_SignInWithRecoveryCodes: Symbol.for('Auth_SignInWithRecoveryCodes'),
|
||||||
Auth_GetUserKeyParamsRecovery: Symbol.for('Auth_GetUserKeyParamsRecovery'),
|
Auth_GetUserKeyParamsRecovery: Symbol.for('Auth_GetUserKeyParamsRecovery'),
|
||||||
// Handlers
|
// Handlers
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
export * from './AuthServiceInterface'
|
||||||
export * from './Service'
|
export * from './Service'
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import { User } from '../Domain/User/User'
|
|||||||
import { Register } from '../Domain/UseCase/Register'
|
import { Register } from '../Domain/UseCase/Register'
|
||||||
import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface'
|
import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface'
|
||||||
import { KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
|
import { KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
|
||||||
import { ApiVersion } from '@standardnotes/api'
|
|
||||||
import { SignInWithRecoveryCodes } from '../Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes'
|
import { SignInWithRecoveryCodes } from '../Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes'
|
||||||
import { GetUserKeyParamsRecovery } from '../Domain/UseCase/GetUserKeyParamsRecovery/GetUserKeyParamsRecovery'
|
import { GetUserKeyParamsRecovery } from '../Domain/UseCase/GetUserKeyParamsRecovery/GetUserKeyParamsRecovery'
|
||||||
import { GenerateRecoveryCodes } from '../Domain/UseCase/GenerateRecoveryCodes/GenerateRecoveryCodes'
|
import { GenerateRecoveryCodes } from '../Domain/UseCase/GenerateRecoveryCodes/GenerateRecoveryCodes'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import { SessionServiceInterface } from '../Domain/Session/SessionServiceInterface'
|
import { SessionServiceInterface } from '../Domain/Session/SessionServiceInterface'
|
||||||
|
import { ApiVersion } from '../Domain/Api/ApiVersion'
|
||||||
|
|
||||||
describe('AuthController', () => {
|
describe('AuthController', () => {
|
||||||
let clearLoginAttempts: ClearLoginAttempts
|
let clearLoginAttempts: ClearLoginAttempts
|
||||||
@@ -73,7 +73,7 @@ describe('AuthController', () => {
|
|||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
password: 'asdzxc',
|
password: 'asdzxc',
|
||||||
version: ProtocolVersion.V004,
|
version: ProtocolVersion.V004,
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
origination: KeyParamsOrigination.Registration,
|
origination: KeyParamsOrigination.Registration,
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
identifier: 'test@test.te',
|
identifier: 'test@test.te',
|
||||||
@@ -103,7 +103,7 @@ describe('AuthController', () => {
|
|||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
password: '',
|
password: '',
|
||||||
version: ProtocolVersion.V004,
|
version: ProtocolVersion.V004,
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
origination: KeyParamsOrigination.Registration,
|
origination: KeyParamsOrigination.Registration,
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
identifier: 'test@test.te',
|
identifier: 'test@test.te',
|
||||||
@@ -123,7 +123,7 @@ describe('AuthController', () => {
|
|||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
password: 'test',
|
password: 'test',
|
||||||
version: ProtocolVersion.V004,
|
version: ProtocolVersion.V004,
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
origination: KeyParamsOrigination.Registration,
|
origination: KeyParamsOrigination.Registration,
|
||||||
userAgent: 'Google Chrome',
|
userAgent: 'Google Chrome',
|
||||||
identifier: 'test@test.te',
|
identifier: 'test@test.te',
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||||
import {
|
import {
|
||||||
ApiVersion,
|
|
||||||
UserRegistrationRequestParams,
|
UserRegistrationRequestParams,
|
||||||
UserServerInterface,
|
UserServerInterface,
|
||||||
UserDeletionResponseBody,
|
UserDeletionResponseBody,
|
||||||
UserRegistrationResponseBody,
|
UserRegistrationResponseBody,
|
||||||
|
UserUpdateRequestParams,
|
||||||
} from '@standardnotes/api'
|
} from '@standardnotes/api'
|
||||||
import { ErrorTag, HttpResponse, HttpStatusCode } from '@standardnotes/responses'
|
import { ErrorTag, HttpResponse, HttpStatusCode } from '@standardnotes/responses'
|
||||||
import { ProtocolVersion } from '@standardnotes/common'
|
import { ProtocolVersion } from '@standardnotes/common'
|
||||||
@@ -23,6 +23,8 @@ import { GenerateRecoveryCodes } from '../Domain/UseCase/GenerateRecoveryCodes/G
|
|||||||
import { GenerateRecoveryCodesRequestParams } from '../Infra/Http/Request/GenerateRecoveryCodesRequestParams'
|
import { GenerateRecoveryCodesRequestParams } from '../Infra/Http/Request/GenerateRecoveryCodesRequestParams'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
import { SessionServiceInterface } from '../Domain/Session/SessionServiceInterface'
|
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 {
|
export class AuthController implements UserServerInterface {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -37,6 +39,10 @@ export class AuthController implements UserServerInterface {
|
|||||||
private sessionService: SessionServiceInterface,
|
private sessionService: SessionServiceInterface,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
async update(_params: UserUpdateRequestParams): Promise<HttpResponse<UserUpdateResponse>> {
|
||||||
|
throw new Error('Method not implemented.')
|
||||||
|
}
|
||||||
|
|
||||||
async deleteAccount(_params: never): Promise<HttpResponse<UserDeletionResponseBody>> {
|
async deleteAccount(_params: never): Promise<HttpResponse<UserDeletionResponseBody>> {
|
||||||
throw new Error('This method is implemented on the payments server.')
|
throw new Error('This method is implemented on the payments server.')
|
||||||
}
|
}
|
||||||
@@ -121,7 +127,7 @@ export class AuthController implements UserServerInterface {
|
|||||||
async signInWithRecoveryCodes(
|
async signInWithRecoveryCodes(
|
||||||
params: SignInWithRecoveryCodesRequestParams,
|
params: SignInWithRecoveryCodesRequestParams,
|
||||||
): Promise<HttpResponse<SignInWithRecoveryCodesResponseBody>> {
|
): Promise<HttpResponse<SignInWithRecoveryCodesResponseBody>> {
|
||||||
if (params.apiVersion !== ApiVersion.v0) {
|
if (params.apiVersion !== ApiVersion.v20200115) {
|
||||||
return {
|
return {
|
||||||
status: HttpStatusCode.BadRequest,
|
status: HttpStatusCode.BadRequest,
|
||||||
data: {
|
data: {
|
||||||
@@ -162,7 +168,7 @@ export class AuthController implements UserServerInterface {
|
|||||||
async recoveryKeyParams(
|
async recoveryKeyParams(
|
||||||
params: RecoveryKeyParamsRequestParams,
|
params: RecoveryKeyParamsRequestParams,
|
||||||
): Promise<HttpResponse<RecoveryKeyParamsResponseBody>> {
|
): Promise<HttpResponse<RecoveryKeyParamsResponseBody>> {
|
||||||
if (params.apiVersion !== ApiVersion.v0) {
|
if (params.apiVersion !== ApiVersion.v20200115) {
|
||||||
return {
|
return {
|
||||||
status: HttpStatusCode.BadRequest,
|
status: HttpStatusCode.BadRequest,
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { AcceptSharedSubscriptionInvitation } from '../Domain/UseCase/AcceptShar
|
|||||||
import { DeclineSharedSubscriptionInvitation } from '../Domain/UseCase/DeclineSharedSubscriptionInvitation/DeclineSharedSubscriptionInvitation'
|
import { DeclineSharedSubscriptionInvitation } from '../Domain/UseCase/DeclineSharedSubscriptionInvitation/DeclineSharedSubscriptionInvitation'
|
||||||
import { CancelSharedSubscriptionInvitation } from '../Domain/UseCase/CancelSharedSubscriptionInvitation/CancelSharedSubscriptionInvitation'
|
import { CancelSharedSubscriptionInvitation } from '../Domain/UseCase/CancelSharedSubscriptionInvitation/CancelSharedSubscriptionInvitation'
|
||||||
import { ListSharedSubscriptionInvitations } from '../Domain/UseCase/ListSharedSubscriptionInvitations/ListSharedSubscriptionInvitations'
|
import { ListSharedSubscriptionInvitations } from '../Domain/UseCase/ListSharedSubscriptionInvitations/ListSharedSubscriptionInvitations'
|
||||||
import { ApiVersion } from '@standardnotes/api'
|
import { ApiVersion } from '../Domain/Api/ApiVersion'
|
||||||
|
|
||||||
describe('SubscriptionInvitesController', () => {
|
describe('SubscriptionInvitesController', () => {
|
||||||
let inviteToSharedSubscription: InviteToSharedSubscription
|
let inviteToSharedSubscription: InviteToSharedSubscription
|
||||||
@@ -53,7 +53,7 @@ describe('SubscriptionInvitesController', () => {
|
|||||||
invitations: [],
|
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({
|
expect(listSharedSubscriptionInvitations.execute).toHaveBeenCalledWith({
|
||||||
inviterEmail: 'test@test.te',
|
inviterEmail: 'test@test.te',
|
||||||
@@ -68,7 +68,7 @@ describe('SubscriptionInvitesController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = await createController().cancelInvite({
|
const result = await createController().cancelInvite({
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
inviteUuid: '1-2-3',
|
inviteUuid: '1-2-3',
|
||||||
inviterEmail: 'test@test.te',
|
inviterEmail: 'test@test.te',
|
||||||
})
|
})
|
||||||
@@ -87,7 +87,7 @@ describe('SubscriptionInvitesController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = await createController().cancelInvite({
|
const result = await createController().cancelInvite({
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
inviteUuid: '1-2-3',
|
inviteUuid: '1-2-3',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ describe('SubscriptionInvitesController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = await createController().declineInvite({
|
const result = await createController().declineInvite({
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
inviteUuid: '1-2-3',
|
inviteUuid: '1-2-3',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ describe('SubscriptionInvitesController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = await createController().declineInvite({
|
const result = await createController().declineInvite({
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
inviteUuid: '1-2-3',
|
inviteUuid: '1-2-3',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ describe('SubscriptionInvitesController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = await createController().acceptInvite({
|
const result = await createController().acceptInvite({
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
inviteUuid: '1-2-3',
|
inviteUuid: '1-2-3',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ describe('SubscriptionInvitesController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = await createController().acceptInvite({
|
const result = await createController().acceptInvite({
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
inviteUuid: '1-2-3',
|
inviteUuid: '1-2-3',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ describe('SubscriptionInvitesController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = await createController().invite({
|
const result = await createController().invite({
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
identifier: 'invitee@test.te',
|
identifier: 'invitee@test.te',
|
||||||
inviterUuid: '1-2-3',
|
inviterUuid: '1-2-3',
|
||||||
inviterEmail: 'test@test.te',
|
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 () => {
|
it('should not invite to user subscription if the identifier is missing in request', async () => {
|
||||||
const result = await createController().invite({
|
const result = await createController().invite({
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
identifier: '',
|
identifier: '',
|
||||||
inviterUuid: '1-2-3',
|
inviterUuid: '1-2-3',
|
||||||
inviterEmail: 'test@test.te',
|
inviterEmail: 'test@test.te',
|
||||||
@@ -205,7 +205,7 @@ describe('SubscriptionInvitesController', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = await createController().invite({
|
const result = await createController().invite({
|
||||||
api: ApiVersion.v0,
|
api: ApiVersion.v20200115,
|
||||||
identifier: 'invitee@test.te',
|
identifier: 'invitee@test.te',
|
||||||
inviterUuid: '1-2-3',
|
inviterUuid: '1-2-3',
|
||||||
inviterEmail: 'test@test.te',
|
inviterEmail: 'test@test.te',
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import { ProtocolVersion } from '@standardnotes/common'
|
import { SimpleUserProjection } from '../../Projection/SimpleUserProjection'
|
||||||
|
|
||||||
export interface AuthResponse {
|
export interface AuthResponse {
|
||||||
user: {
|
user: SimpleUserProjection
|
||||||
uuid: string
|
|
||||||
email: string
|
|
||||||
protocolVersion: ProtocolVersion
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import {
|
|||||||
TokenEncoderInterface,
|
TokenEncoderInterface,
|
||||||
} from '@standardnotes/security'
|
} from '@standardnotes/security'
|
||||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||||
import { ProtocolVersion } from '@standardnotes/common'
|
|
||||||
import { SessionBody } from '@standardnotes/responses'
|
import { SessionBody } from '@standardnotes/responses'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
import TYPES from '../../Bootstrap/Types'
|
import TYPES from '../../Bootstrap/Types'
|
||||||
import { ProjectorInterface } from '../../Projection/ProjectorInterface'
|
import { ProjectorInterface } from '../../Projection/ProjectorInterface'
|
||||||
|
import { SimpleUserProjection } from '../../Projection/SimpleUserProjection'
|
||||||
import { SessionServiceInterface } from '../Session/SessionServiceInterface'
|
import { SessionServiceInterface } from '../Session/SessionServiceInterface'
|
||||||
import { KeyParamsFactoryInterface } from '../User/KeyParamsFactoryInterface'
|
import { KeyParamsFactoryInterface } from '../User/KeyParamsFactoryInterface'
|
||||||
import { User } from '../User/User'
|
import { User } from '../User/User'
|
||||||
@@ -54,11 +54,7 @@ export class AuthResponseFactory20200115 extends AuthResponseFactory20190520 {
|
|||||||
return {
|
return {
|
||||||
session: sessionPayload,
|
session: sessionPayload,
|
||||||
key_params: this.keyParamsFactory.create(dto.user, true),
|
key_params: this.keyParamsFactory.create(dto.user, true),
|
||||||
user: this.userProjector.projectSimple(dto.user) as {
|
user: this.userProjector.projectSimple(dto.user) as SimpleUserProjection,
|
||||||
uuid: string
|
|
||||||
email: string
|
|
||||||
protocolVersion: ProtocolVersion
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { User } from '../User/User'
|
|||||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||||
|
|
||||||
import { AuthenticationMethodResolver } from './AuthenticationMethodResolver'
|
import { AuthenticationMethodResolver } from './AuthenticationMethodResolver'
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
describe('AuthenticationMethodResolver', () => {
|
describe('AuthenticationMethodResolver', () => {
|
||||||
let userRepository: UserRepositoryInterface
|
let userRepository: UserRepositoryInterface
|
||||||
@@ -18,11 +19,15 @@ describe('AuthenticationMethodResolver', () => {
|
|||||||
let user: User
|
let user: User
|
||||||
let session: Session
|
let session: Session
|
||||||
let revokedSession: RevokedSession
|
let revokedSession: RevokedSession
|
||||||
|
let logger: Logger
|
||||||
|
|
||||||
const createResolver = () =>
|
const createResolver = () =>
|
||||||
new AuthenticationMethodResolver(userRepository, sessionService, sessionTokenDecoder, fallbackTokenDecoder)
|
new AuthenticationMethodResolver(userRepository, sessionService, sessionTokenDecoder, fallbackTokenDecoder, logger)
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
logger = {} as jest.Mocked<Logger>
|
||||||
|
logger.debug = jest.fn()
|
||||||
|
|
||||||
user = {} as jest.Mocked<User>
|
user = {} as jest.Mocked<User>
|
||||||
|
|
||||||
session = {} as jest.Mocked<Session>
|
session = {} as jest.Mocked<Session>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { SessionServiceInterface } from '../Session/SessionServiceInterface'
|
|||||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||||
import { AuthenticationMethod } from './AuthenticationMethod'
|
import { AuthenticationMethod } from './AuthenticationMethod'
|
||||||
import { AuthenticationMethodResolverInterface } from './AuthenticationMethodResolverInterface'
|
import { AuthenticationMethodResolverInterface } from './AuthenticationMethodResolverInterface'
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class AuthenticationMethodResolver implements AuthenticationMethodResolverInterface {
|
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_SessionTokenDecoder) private sessionTokenDecoder: TokenDecoderInterface<SessionTokenData>,
|
||||||
@inject(TYPES.Auth_FallbackSessionTokenDecoder)
|
@inject(TYPES.Auth_FallbackSessionTokenDecoder)
|
||||||
private fallbackSessionTokenDecoder: TokenDecoderInterface<SessionTokenData>,
|
private fallbackSessionTokenDecoder: TokenDecoderInterface<SessionTokenData>,
|
||||||
|
@inject(TYPES.Auth_Logger) private logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async resolve(token: string): Promise<AuthenticationMethod | undefined> {
|
async resolve(token: string): Promise<AuthenticationMethod | undefined> {
|
||||||
let decodedToken: SessionTokenData | undefined = this.sessionTokenDecoder.decodeToken(token)
|
let decodedToken: SessionTokenData | undefined = this.sessionTokenDecoder.decodeToken(token)
|
||||||
if (decodedToken === undefined) {
|
if (decodedToken === undefined) {
|
||||||
|
this.logger.debug('Could not decode token with primary decoder, trying fallback decoder.')
|
||||||
|
|
||||||
decodedToken = this.fallbackSessionTokenDecoder.decodeToken(token)
|
decodedToken = this.fallbackSessionTokenDecoder.decodeToken(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decodedToken) {
|
if (decodedToken) {
|
||||||
|
this.logger.debug('Token decoded successfully. User found.')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'jwt',
|
type: 'jwt',
|
||||||
user: await this.userRepository.findOneByUuid(<string>decodedToken.user_uuid),
|
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)
|
const session = await this.sessionService.getSessionFromToken(token)
|
||||||
if (session) {
|
if (session) {
|
||||||
|
this.logger.debug('Token decoded successfully. Session found.')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'session_token',
|
type: 'session_token',
|
||||||
user: await this.userRepository.findOneByUuid(session.userUuid),
|
user: await this.userRepository.findOneByUuid(session.userUuid),
|
||||||
@@ -41,6 +49,8 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
|
|||||||
|
|
||||||
const revokedSession = await this.sessionService.getRevokedSessionFromToken(token)
|
const revokedSession = await this.sessionService.getRevokedSessionFromToken(token)
|
||||||
if (revokedSession) {
|
if (revokedSession) {
|
||||||
|
this.logger.debug('Token decoded successfully. Revoked session found.')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'revoked',
|
type: 'revoked',
|
||||||
revokedSession: await this.sessionService.markRevokedSessionAsReceived(revokedSession),
|
revokedSession: await this.sessionService.markRevokedSessionAsReceived(revokedSession),
|
||||||
@@ -48,6 +58,8 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.debug('Could not decode token.')
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
|||||||
import { AuthenticatorProps } from './AuthenticatorProps'
|
import { AuthenticatorProps } from './AuthenticatorProps'
|
||||||
|
|
||||||
export class Authenticator extends Entity<AuthenticatorProps> {
|
export class Authenticator extends Entity<AuthenticatorProps> {
|
||||||
get id(): UniqueEntityId {
|
|
||||||
return this._id
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructor(props: AuthenticatorProps, id?: UniqueEntityId) {
|
private constructor(props: AuthenticatorProps, id?: UniqueEntityId) {
|
||||||
super(props, id)
|
super(props, id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
|||||||
import { AuthenticatorChallengeProps } from './AuthenticatorChallengeProps'
|
import { AuthenticatorChallengeProps } from './AuthenticatorChallengeProps'
|
||||||
|
|
||||||
export class AuthenticatorChallenge extends Entity<AuthenticatorChallengeProps> {
|
export class AuthenticatorChallenge extends Entity<AuthenticatorChallengeProps> {
|
||||||
get id(): UniqueEntityId {
|
|
||||||
return this._id
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructor(props: AuthenticatorChallengeProps, id?: UniqueEntityId) {
|
private constructor(props: AuthenticatorChallengeProps, id?: UniqueEntityId) {
|
||||||
super(props, id)
|
super(props, id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
|||||||
import { EmergencyAccessInvitationProps } from './EmergencyAccessInvitationProps'
|
import { EmergencyAccessInvitationProps } from './EmergencyAccessInvitationProps'
|
||||||
|
|
||||||
export class EmergencyAccessInvitation extends Entity<EmergencyAccessInvitationProps> {
|
export class EmergencyAccessInvitation extends Entity<EmergencyAccessInvitationProps> {
|
||||||
get id(): UniqueEntityId {
|
|
||||||
return this._id
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructor(props: EmergencyAccessInvitationProps, id?: UniqueEntityId) {
|
private constructor(props: EmergencyAccessInvitationProps, id?: UniqueEntityId) {
|
||||||
super(props, id)
|
super(props, id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import { UserSubscriptionServiceInterface } from '../Subscription/UserSubscripti
|
|||||||
describe('FileRemovedEventHandler', () => {
|
describe('FileRemovedEventHandler', () => {
|
||||||
let userSubscriptionService: UserSubscriptionServiceInterface
|
let userSubscriptionService: UserSubscriptionServiceInterface
|
||||||
let logger: Logger
|
let logger: Logger
|
||||||
let user: User
|
let regularUser: User
|
||||||
|
let sharedUser: User
|
||||||
let event: FileRemovedEvent
|
let event: FileRemovedEvent
|
||||||
let subscriptionSettingService: SubscriptionSettingServiceInterface
|
let subscriptionSettingService: SubscriptionSettingServiceInterface
|
||||||
let regularSubscription: UserSubscription
|
let regularSubscription: UserSubscription
|
||||||
@@ -22,20 +23,24 @@ describe('FileRemovedEventHandler', () => {
|
|||||||
const createHandler = () => new FileRemovedEventHandler(userSubscriptionService, subscriptionSettingService, logger)
|
const createHandler = () => new FileRemovedEventHandler(userSubscriptionService, subscriptionSettingService, logger)
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
user = {
|
regularUser = {
|
||||||
uuid: '123',
|
uuid: '123',
|
||||||
} as jest.Mocked<User>
|
} as jest.Mocked<User>
|
||||||
|
|
||||||
|
sharedUser = {
|
||||||
|
uuid: '234',
|
||||||
|
} as jest.Mocked<User>
|
||||||
|
|
||||||
regularSubscription = {
|
regularSubscription = {
|
||||||
uuid: '1-2-3',
|
uuid: '1-2-3',
|
||||||
subscriptionType: UserSubscriptionType.Regular,
|
subscriptionType: UserSubscriptionType.Regular,
|
||||||
user: Promise.resolve(user),
|
user: Promise.resolve(regularUser),
|
||||||
} as jest.Mocked<UserSubscription>
|
} as jest.Mocked<UserSubscription>
|
||||||
|
|
||||||
sharedSubscription = {
|
sharedSubscription = {
|
||||||
uuid: '2-3-4',
|
uuid: '2-3-4',
|
||||||
subscriptionType: UserSubscriptionType.Shared,
|
subscriptionType: UserSubscriptionType.Shared,
|
||||||
user: Promise.resolve(user),
|
user: Promise.resolve(sharedUser),
|
||||||
} as jest.Mocked<UserSubscription>
|
} as jest.Mocked<UserSubscription>
|
||||||
|
|
||||||
userSubscriptionService = {} as jest.Mocked<UserSubscriptionServiceInterface>
|
userSubscriptionService = {} as jest.Mocked<UserSubscriptionServiceInterface>
|
||||||
@@ -93,10 +98,11 @@ describe('FileRemovedEventHandler', () => {
|
|||||||
unencryptedValue: '222',
|
unencryptedValue: '222',
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
},
|
},
|
||||||
|
user: regularUser,
|
||||||
userSubscription: {
|
userSubscription: {
|
||||||
uuid: '1-2-3',
|
uuid: '1-2-3',
|
||||||
subscriptionType: 'regular',
|
subscriptionType: 'regular',
|
||||||
user: Promise.resolve(user),
|
user: Promise.resolve(regularUser),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -118,10 +124,11 @@ describe('FileRemovedEventHandler', () => {
|
|||||||
unencryptedValue: '222',
|
unencryptedValue: '222',
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
},
|
},
|
||||||
|
user: regularUser,
|
||||||
userSubscription: {
|
userSubscription: {
|
||||||
uuid: '1-2-3',
|
uuid: '1-2-3',
|
||||||
subscriptionType: 'regular',
|
subscriptionType: 'regular',
|
||||||
user: Promise.resolve(user),
|
user: Promise.resolve(regularUser),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -132,10 +139,11 @@ describe('FileRemovedEventHandler', () => {
|
|||||||
unencryptedValue: '222',
|
unencryptedValue: '222',
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
},
|
},
|
||||||
|
user: sharedUser,
|
||||||
userSubscription: {
|
userSubscription: {
|
||||||
uuid: '2-3-4',
|
uuid: '2-3-4',
|
||||||
subscriptionType: 'shared',
|
subscriptionType: 'shared',
|
||||||
user: Promise.resolve(user),
|
user: Promise.resolve(sharedUser),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export class FileRemovedEventHandler implements DomainEventHandlerInterface {
|
|||||||
|
|
||||||
await this.subscriptionSettingService.createOrReplace({
|
await this.subscriptionSettingService.createOrReplace({
|
||||||
userSubscription: subscription,
|
userSubscription: subscription,
|
||||||
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: SettingName.NAMES.FileUploadBytesUsed,
|
name: SettingName.NAMES.FileUploadBytesUsed,
|
||||||
unencryptedValue: (+bytesUsed - byteSize).toString(),
|
unencryptedValue: (+bytesUsed - byteSize).toString(),
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ describe('FileUploadedEventHandler', () => {
|
|||||||
unencryptedValue: '123',
|
unencryptedValue: '123',
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
},
|
},
|
||||||
|
user,
|
||||||
userSubscription: {
|
userSubscription: {
|
||||||
uuid: '1-2-3',
|
uuid: '1-2-3',
|
||||||
subscriptionType: 'regular',
|
subscriptionType: 'regular',
|
||||||
@@ -118,6 +119,7 @@ describe('FileUploadedEventHandler', () => {
|
|||||||
unencryptedValue: '468',
|
unencryptedValue: '468',
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
},
|
},
|
||||||
|
user,
|
||||||
userSubscription: {
|
userSubscription: {
|
||||||
uuid: '1-2-3',
|
uuid: '1-2-3',
|
||||||
subscriptionType: 'regular',
|
subscriptionType: 'regular',
|
||||||
@@ -143,6 +145,7 @@ describe('FileUploadedEventHandler', () => {
|
|||||||
unencryptedValue: '468',
|
unencryptedValue: '468',
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
},
|
},
|
||||||
|
user,
|
||||||
userSubscription: {
|
userSubscription: {
|
||||||
uuid: '1-2-3',
|
uuid: '1-2-3',
|
||||||
subscriptionType: 'regular',
|
subscriptionType: 'regular',
|
||||||
@@ -157,6 +160,7 @@ describe('FileUploadedEventHandler', () => {
|
|||||||
unencryptedValue: '468',
|
unencryptedValue: '468',
|
||||||
serverEncryptionVersion: 0,
|
serverEncryptionVersion: 0,
|
||||||
},
|
},
|
||||||
|
user,
|
||||||
userSubscription: {
|
userSubscription: {
|
||||||
uuid: '2-3-4',
|
uuid: '2-3-4',
|
||||||
subscriptionType: 'shared',
|
subscriptionType: 'shared',
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSett
|
|||||||
import { UserSubscription } from '../Subscription/UserSubscription'
|
import { UserSubscription } from '../Subscription/UserSubscription'
|
||||||
import { UserSubscriptionServiceInterface } from '../Subscription/UserSubscriptionServiceInterface'
|
import { UserSubscriptionServiceInterface } from '../Subscription/UserSubscriptionServiceInterface'
|
||||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||||
|
import { User } from '../User/User'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
||||||
@@ -36,14 +37,18 @@ export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.updateUploadBytesUsedSetting(regularSubscription, event.payload.fileByteSize)
|
await this.updateUploadBytesUsedSetting(regularSubscription, user, event.payload.fileByteSize)
|
||||||
|
|
||||||
if (sharedSubscription !== null) {
|
if (sharedSubscription !== null) {
|
||||||
await this.updateUploadBytesUsedSetting(sharedSubscription, event.payload.fileByteSize)
|
await this.updateUploadBytesUsedSetting(sharedSubscription, user, event.payload.fileByteSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateUploadBytesUsedSetting(subscription: UserSubscription, byteSize: number): Promise<void> {
|
private async updateUploadBytesUsedSetting(
|
||||||
|
subscription: UserSubscription,
|
||||||
|
user: User,
|
||||||
|
byteSize: number,
|
||||||
|
): Promise<void> {
|
||||||
let bytesUsed = '0'
|
let bytesUsed = '0'
|
||||||
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({
|
||||||
userUuid: (await subscription.user).uuid,
|
userUuid: (await subscription.user).uuid,
|
||||||
@@ -56,6 +61,7 @@ export class FileUploadedEventHandler implements DomainEventHandlerInterface {
|
|||||||
|
|
||||||
await this.subscriptionSettingService.createOrReplace({
|
await this.subscriptionSettingService.createOrReplace({
|
||||||
userSubscription: subscription,
|
userSubscription: subscription,
|
||||||
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: SettingName.NAMES.FileUploadBytesUsed,
|
name: SettingName.NAMES.FileUploadBytesUsed,
|
||||||
unencryptedValue: (+bytesUsed + byteSize).toString(),
|
unencryptedValue: (+bytesUsed + byteSize).toString(),
|
||||||
|
|||||||
@@ -114,8 +114,6 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
|||||||
|
|
||||||
expect(subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription).toHaveBeenCalledWith(
|
expect(subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription).toHaveBeenCalledWith(
|
||||||
subscription,
|
subscription,
|
||||||
SubscriptionName.ProPlan,
|
|
||||||
'123',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -66,11 +66,7 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
|||||||
|
|
||||||
await this.addUserRole(user, event.payload.subscriptionName)
|
await this.addUserRole(user, event.payload.subscriptionName)
|
||||||
|
|
||||||
await this.subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription(
|
await this.subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription(userSubscription)
|
||||||
userSubscription,
|
|
||||||
event.payload.subscriptionName,
|
|
||||||
user.uuid,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async addUserRole(user: User, subscriptionName: string): Promise<void> {
|
private async addUserRole(user: User, subscriptionName: string): Promise<void> {
|
||||||
|
|||||||
@@ -94,8 +94,6 @@ describe('SubscriptionReassignedEventHandler', () => {
|
|||||||
|
|
||||||
expect(subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription).toHaveBeenCalledWith(
|
expect(subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription).toHaveBeenCalledWith(
|
||||||
subscription,
|
subscription,
|
||||||
SubscriptionName.ProPlan,
|
|
||||||
'123',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -63,11 +63,7 @@ export class SubscriptionReassignedEventHandler implements DomainEventHandlerInt
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await this.subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription(
|
await this.subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription(userSubscription)
|
||||||
userSubscription,
|
|
||||||
event.payload.subscriptionName,
|
|
||||||
user.uuid,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async addUserRole(user: User, subscriptionName: string): Promise<void> {
|
private async addUserRole(user: User, subscriptionName: string): Promise<void> {
|
||||||
|
|||||||
@@ -129,8 +129,6 @@ describe('SubscriptionSyncRequestedEventHandler', () => {
|
|||||||
|
|
||||||
expect(subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription).toHaveBeenCalledWith(
|
expect(subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription).toHaveBeenCalledWith(
|
||||||
subscription,
|
subscription,
|
||||||
SubscriptionName.ProPlan,
|
|
||||||
'123',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(settingService.createOrReplace).toHaveBeenCalledWith({
|
expect(settingService.createOrReplace).toHaveBeenCalledWith({
|
||||||
|
|||||||
@@ -95,11 +95,7 @@ export class SubscriptionSyncRequestedEventHandler implements DomainEventHandler
|
|||||||
|
|
||||||
await this.roleService.addUserRole(user, event.payload.subscriptionName)
|
await this.roleService.addUserRole(user, event.payload.subscriptionName)
|
||||||
|
|
||||||
await this.subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription(
|
await this.subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription(userSubscription)
|
||||||
userSubscription,
|
|
||||||
event.payload.subscriptionName,
|
|
||||||
user.uuid,
|
|
||||||
)
|
|
||||||
|
|
||||||
await this.settingService.createOrReplace({
|
await this.settingService.createOrReplace({
|
||||||
user,
|
user,
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
|||||||
import { SessionTraceProps } from './SessionTraceProps'
|
import { SessionTraceProps } from './SessionTraceProps'
|
||||||
|
|
||||||
export class SessionTrace extends Entity<SessionTraceProps> {
|
export class SessionTrace extends Entity<SessionTraceProps> {
|
||||||
get id(): UniqueEntityId {
|
|
||||||
return this._id
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructor(props: SessionTraceProps, id?: UniqueEntityId) {
|
private constructor(props: SessionTraceProps, id?: UniqueEntityId) {
|
||||||
super(props, id)
|
super(props, id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { UserSubscription } from '../Subscription/UserSubscription'
|
import { UserSubscription } from '../Subscription/UserSubscription'
|
||||||
|
import { User } from '../User/User'
|
||||||
import { SubscriptionSettingProps } from './SubscriptionSettingProps'
|
import { SubscriptionSettingProps } from './SubscriptionSettingProps'
|
||||||
|
|
||||||
export type CreateOrReplaceSubscriptionSettingDTO = {
|
export type CreateOrReplaceSubscriptionSettingDTO = {
|
||||||
userSubscription: UserSubscription
|
userSubscription: UserSubscription
|
||||||
|
user: User
|
||||||
props: SubscriptionSettingProps
|
props: SubscriptionSettingProps
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ describe('SettingService', () => {
|
|||||||
user = {
|
user = {
|
||||||
uuid: '4-5-6',
|
uuid: '4-5-6',
|
||||||
} as jest.Mocked<User>
|
} as jest.Mocked<User>
|
||||||
user.isPotentiallyAVaultAccount = jest.fn().mockReturnValue(false)
|
user.isPotentiallyAPrivateUsernameAccount = jest.fn().mockReturnValue(false)
|
||||||
|
|
||||||
setting = {
|
setting = {
|
||||||
name: SettingName.NAMES.DropboxBackupToken,
|
name: SettingName.NAMES.DropboxBackupToken,
|
||||||
@@ -66,7 +66,7 @@ describe('SettingService', () => {
|
|||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
|
|
||||||
settingsAssociationService.getDefaultSettingsAndValuesForNewVaultAccount = jest.fn().mockReturnValue(
|
settingsAssociationService.getDefaultSettingsAndValuesForNewPrivateUsernameAccount = jest.fn().mockReturnValue(
|
||||||
new Map([
|
new Map([
|
||||||
[
|
[
|
||||||
SettingName.NAMES.LogSessionUserAgent,
|
SettingName.NAMES.LogSessionUserAgent,
|
||||||
@@ -98,7 +98,7 @@ describe('SettingService', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should create default settings for a newly registered vault account', async () => {
|
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)
|
await createService().applyDefaultSettingsUponRegistration(user)
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ export class SettingService implements SettingServiceInterface {
|
|||||||
|
|
||||||
async applyDefaultSettingsUponRegistration(user: User): Promise<void> {
|
async applyDefaultSettingsUponRegistration(user: User): Promise<void> {
|
||||||
let defaultSettingsWithValues = this.settingsAssociationService.getDefaultSettingsAndValuesForNewUser()
|
let defaultSettingsWithValues = this.settingsAssociationService.getDefaultSettingsAndValuesForNewUser()
|
||||||
if (user.isPotentiallyAVaultAccount()) {
|
if (user.isPotentiallyAPrivateUsernameAccount()) {
|
||||||
defaultSettingsWithValues = this.settingsAssociationService.getDefaultSettingsAndValuesForNewVaultAccount()
|
defaultSettingsWithValues =
|
||||||
|
this.settingsAssociationService.getDefaultSettingsAndValuesForNewPrivateUsernameAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const settingName of defaultSettingsWithValues.keys()) {
|
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', () => {
|
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()]
|
const flatSettings = [...(settings as Map<string, SettingDescription>).keys()]
|
||||||
expect(flatSettings).toEqual(['MUTE_MARKETING_EMAILS', 'LOG_SESSION_USER_AGENT'])
|
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,
|
SettingName.NAMES.LogSessionUserAgent,
|
||||||
{
|
{
|
||||||
@@ -114,16 +114,18 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
|||||||
return this.defaultSettings
|
return this.defaultSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription> {
|
getDefaultSettingsAndValuesForNewPrivateUsernameAccount(): Map<string, SettingDescription> {
|
||||||
const defaultVaultSettings = new Map(this.defaultSettings)
|
const defaultPrivateUsernameSettings = new Map(this.defaultSettings)
|
||||||
|
|
||||||
for (const vaultAccountDefaultSettingOverwriteKey of this.vaultAccountDefaultSettingsOverwrites.keys()) {
|
for (const privateUsernameAccountDefaultSettingOverwriteKey of this.privateUsernameAccountDefaultSettingsOverwrites.keys()) {
|
||||||
defaultVaultSettings.set(
|
defaultPrivateUsernameSettings.set(
|
||||||
vaultAccountDefaultSettingOverwriteKey,
|
privateUsernameAccountDefaultSettingOverwriteKey,
|
||||||
this.vaultAccountDefaultSettingsOverwrites.get(vaultAccountDefaultSettingOverwriteKey) as SettingDescription,
|
this.privateUsernameAccountDefaultSettingsOverwrites.get(
|
||||||
|
privateUsernameAccountDefaultSettingOverwriteKey,
|
||||||
|
) as SettingDescription,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultVaultSettings
|
return defaultPrivateUsernameSettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { SettingDescription } from './SettingDescription'
|
|||||||
|
|
||||||
export interface SettingsAssociationServiceInterface {
|
export interface SettingsAssociationServiceInterface {
|
||||||
getDefaultSettingsAndValuesForNewUser(): Map<string, SettingDescription>
|
getDefaultSettingsAndValuesForNewUser(): Map<string, SettingDescription>
|
||||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription>
|
getDefaultSettingsAndValuesForNewPrivateUsernameAccount(): Map<string, SettingDescription>
|
||||||
getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined
|
getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined
|
||||||
getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion
|
getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion
|
||||||
getSensitivityForSetting(settingName: SettingName): boolean
|
getSensitivityForSetting(settingName: SettingName): boolean
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
userSubscription = {
|
userSubscription = {
|
||||||
uuid: '1-2-3',
|
uuid: '1-2-3',
|
||||||
user: Promise.resolve(user),
|
user: Promise.resolve(user),
|
||||||
|
planName: SubscriptionName.PlusPlan,
|
||||||
} as jest.Mocked<UserSubscription>
|
} as jest.Mocked<UserSubscription>
|
||||||
|
|
||||||
setting = {
|
setting = {
|
||||||
@@ -97,15 +98,70 @@ describe('SubscriptionSettingService', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should create default settings for a subscription', async () => {
|
it('should create default settings for a subscription', async () => {
|
||||||
await createService().applyDefaultSubscriptionSettingsForSubscription(
|
await createService().applyDefaultSubscriptionSettingsForSubscription(userSubscription)
|
||||||
userSubscription,
|
|
||||||
SubscriptionName.PlusPlan,
|
|
||||||
'1-2-3',
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(subscriptionSettingRepository.save).toHaveBeenCalledWith(setting)
|
expect(subscriptionSettingRepository.save).toHaveBeenCalledWith(setting)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should create default settings for a subscription with overrides', async () => {
|
||||||
|
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
SettingName.NAMES.FileUploadBytesUsed,
|
||||||
|
{
|
||||||
|
value: '0',
|
||||||
|
sensitive: 0,
|
||||||
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
|
replaceable: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
SettingName.NAMES.FileUploadBytesLimit,
|
||||||
|
{
|
||||||
|
value: '345',
|
||||||
|
sensitive: 0,
|
||||||
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
|
replaceable: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
|
||||||
|
await createService().applyDefaultSubscriptionSettingsForSubscription(
|
||||||
|
userSubscription,
|
||||||
|
new Map([[SettingName.NAMES.FileUploadBytesLimit, '123']]),
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(factory.createSubscriptionSetting).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
name: SettingName.NAMES.FileUploadBytesUsed,
|
||||||
|
sensitive: 0,
|
||||||
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
|
unencryptedValue: '0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
planName: SubscriptionName.PlusPlan,
|
||||||
|
user: Promise.resolve(user),
|
||||||
|
uuid: '1-2-3',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(factory.createSubscriptionSetting).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
name: SettingName.NAMES.FileUploadBytesLimit,
|
||||||
|
sensitive: 0,
|
||||||
|
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||||
|
unencryptedValue: '123',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
planName: SubscriptionName.PlusPlan,
|
||||||
|
user: Promise.resolve(user),
|
||||||
|
uuid: '1-2-3',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('should throw error if subscription setting is invalid', async () => {
|
it('should throw error if subscription setting is invalid', async () => {
|
||||||
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
subscriptionSettingsAssociationService.getDefaultSettingsAndValuesForSubscriptionName = jest.fn().mockReturnValue(
|
||||||
new Map([
|
new Map([
|
||||||
@@ -121,13 +177,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
|
|
||||||
await expect(
|
await expect(createService().applyDefaultSubscriptionSettingsForSubscription(userSubscription)).rejects.toThrow()
|
||||||
createService().applyDefaultSubscriptionSettingsForSubscription(
|
|
||||||
userSubscription,
|
|
||||||
SubscriptionName.PlusPlan,
|
|
||||||
'1-2-3',
|
|
||||||
),
|
|
||||||
).rejects.toThrow()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should throw error if setting name is not a subscription setting when applying defaults', async () => {
|
it('should throw error if setting name is not a subscription setting when applying defaults', async () => {
|
||||||
@@ -145,13 +195,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
|
|
||||||
await expect(
|
await expect(createService().applyDefaultSubscriptionSettingsForSubscription(userSubscription)).rejects.toThrow()
|
||||||
createService().applyDefaultSubscriptionSettingsForSubscription(
|
|
||||||
userSubscription,
|
|
||||||
SubscriptionName.PlusPlan,
|
|
||||||
'1-2-3',
|
|
||||||
),
|
|
||||||
).rejects.toThrow()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should reassign existing default settings for a subscription if it is not replaceable', async () => {
|
it('should reassign existing default settings for a subscription if it is not replaceable', async () => {
|
||||||
@@ -170,11 +214,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
)
|
)
|
||||||
subscriptionSettingRepository.findLastByNameAndUserSubscriptionUuid = jest.fn().mockReturnValue(setting)
|
subscriptionSettingRepository.findLastByNameAndUserSubscriptionUuid = jest.fn().mockReturnValue(setting)
|
||||||
|
|
||||||
await createService().applyDefaultSubscriptionSettingsForSubscription(
|
await createService().applyDefaultSubscriptionSettingsForSubscription(userSubscription)
|
||||||
userSubscription,
|
|
||||||
SubscriptionName.PlusPlan,
|
|
||||||
'1-2-3',
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(subscriptionSettingRepository.save).toHaveBeenCalled()
|
expect(subscriptionSettingRepository.save).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
@@ -195,11 +235,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
)
|
)
|
||||||
subscriptionSettingRepository.findLastByNameAndUserSubscriptionUuid = jest.fn().mockReturnValue(null)
|
subscriptionSettingRepository.findLastByNameAndUserSubscriptionUuid = jest.fn().mockReturnValue(null)
|
||||||
|
|
||||||
await createService().applyDefaultSubscriptionSettingsForSubscription(
|
await createService().applyDefaultSubscriptionSettingsForSubscription(userSubscription)
|
||||||
userSubscription,
|
|
||||||
SubscriptionName.PlusPlan,
|
|
||||||
'1-2-3',
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(subscriptionSettingRepository.save).toHaveBeenCalledWith(setting)
|
expect(subscriptionSettingRepository.save).toHaveBeenCalledWith(setting)
|
||||||
})
|
})
|
||||||
@@ -225,11 +261,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
} as jest.Mocked<UserSubscription>,
|
} as jest.Mocked<UserSubscription>,
|
||||||
])
|
])
|
||||||
|
|
||||||
await createService().applyDefaultSubscriptionSettingsForSubscription(
|
await createService().applyDefaultSubscriptionSettingsForSubscription(userSubscription)
|
||||||
userSubscription,
|
|
||||||
SubscriptionName.PlusPlan,
|
|
||||||
'1-2-3',
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(subscriptionSettingRepository.save).toHaveBeenCalledWith(setting)
|
expect(subscriptionSettingRepository.save).toHaveBeenCalledWith(setting)
|
||||||
})
|
})
|
||||||
@@ -239,11 +271,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
.fn()
|
.fn()
|
||||||
.mockReturnValue(undefined)
|
.mockReturnValue(undefined)
|
||||||
|
|
||||||
await createService().applyDefaultSubscriptionSettingsForSubscription(
|
await createService().applyDefaultSubscriptionSettingsForSubscription(userSubscription)
|
||||||
userSubscription,
|
|
||||||
SubscriptionName.PlusPlan,
|
|
||||||
'1-2-3',
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(subscriptionSettingRepository.save).not.toHaveBeenCalled()
|
expect(subscriptionSettingRepository.save).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
@@ -251,6 +279,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
it("should create setting if it doesn't exist", async () => {
|
it("should create setting if it doesn't exist", async () => {
|
||||||
const result = await createService().createOrReplace({
|
const result = await createService().createOrReplace({
|
||||||
userSubscription,
|
userSubscription,
|
||||||
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: SettingName.NAMES.FileUploadBytesLimit,
|
name: SettingName.NAMES.FileUploadBytesLimit,
|
||||||
unencryptedValue: 'value',
|
unencryptedValue: 'value',
|
||||||
@@ -266,6 +295,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
await expect(
|
await expect(
|
||||||
createService().createOrReplace({
|
createService().createOrReplace({
|
||||||
userSubscription,
|
userSubscription,
|
||||||
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: 'invalid',
|
name: 'invalid',
|
||||||
unencryptedValue: 'value',
|
unencryptedValue: 'value',
|
||||||
@@ -280,6 +310,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
await expect(
|
await expect(
|
||||||
createService().createOrReplace({
|
createService().createOrReplace({
|
||||||
userSubscription,
|
userSubscription,
|
||||||
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: SettingName.NAMES.DropboxBackupFrequency,
|
name: SettingName.NAMES.DropboxBackupFrequency,
|
||||||
unencryptedValue: 'value',
|
unencryptedValue: 'value',
|
||||||
@@ -295,6 +326,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
|
|
||||||
const result = await createService().createOrReplace({
|
const result = await createService().createOrReplace({
|
||||||
userSubscription,
|
userSubscription,
|
||||||
|
user,
|
||||||
props: {
|
props: {
|
||||||
uuid: '1-2-3',
|
uuid: '1-2-3',
|
||||||
name: SettingName.NAMES.FileUploadBytesLimit,
|
name: SettingName.NAMES.FileUploadBytesLimit,
|
||||||
@@ -312,6 +344,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
|
|
||||||
const result = await createService().createOrReplace({
|
const result = await createService().createOrReplace({
|
||||||
userSubscription,
|
userSubscription,
|
||||||
|
user,
|
||||||
props: {
|
props: {
|
||||||
...setting,
|
...setting,
|
||||||
unencryptedValue: 'value',
|
unencryptedValue: 'value',
|
||||||
@@ -327,6 +360,7 @@ describe('SubscriptionSettingService', () => {
|
|||||||
|
|
||||||
const result = await createService().createOrReplace({
|
const result = await createService().createOrReplace({
|
||||||
userSubscription,
|
userSubscription,
|
||||||
|
user,
|
||||||
props: {
|
props: {
|
||||||
...setting,
|
...setting,
|
||||||
uuid: '1-2-3',
|
uuid: '1-2-3',
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { SubscriptionName } from '@standardnotes/common'
|
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
import { Logger } from 'winston'
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
@@ -36,17 +35,20 @@ export class SubscriptionSettingService implements SubscriptionSettingServiceInt
|
|||||||
|
|
||||||
async applyDefaultSubscriptionSettingsForSubscription(
|
async applyDefaultSubscriptionSettingsForSubscription(
|
||||||
userSubscription: UserSubscription,
|
userSubscription: UserSubscription,
|
||||||
subscriptionName: SubscriptionName,
|
overrides?: Map<string, string>,
|
||||||
userUuid: string,
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const defaultSettingsWithValues =
|
const defaultSettingsWithValues =
|
||||||
await this.subscriptionSettingAssociationService.getDefaultSettingsAndValuesForSubscriptionName(subscriptionName)
|
await this.subscriptionSettingAssociationService.getDefaultSettingsAndValuesForSubscriptionName(
|
||||||
|
userSubscription.planName,
|
||||||
|
)
|
||||||
if (defaultSettingsWithValues === undefined) {
|
if (defaultSettingsWithValues === undefined) {
|
||||||
this.logger.warn(`Could not find settings for subscription: ${subscriptionName}`)
|
this.logger.warn(`Could not find settings for subscription: ${userSubscription.planName}`)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const user = await userSubscription.user
|
||||||
|
|
||||||
for (const settingNameString of defaultSettingsWithValues.keys()) {
|
for (const settingNameString of defaultSettingsWithValues.keys()) {
|
||||||
const settingNameOrError = SettingName.create(settingNameString)
|
const settingNameOrError = SettingName.create(settingNameString)
|
||||||
if (settingNameOrError.isFailed()) {
|
if (settingNameOrError.isFailed()) {
|
||||||
@@ -59,7 +61,11 @@ export class SubscriptionSettingService implements SubscriptionSettingServiceInt
|
|||||||
|
|
||||||
const setting = defaultSettingsWithValues.get(settingName.value) as SettingDescription
|
const setting = defaultSettingsWithValues.get(settingName.value) as SettingDescription
|
||||||
if (!setting.replaceable) {
|
if (!setting.replaceable) {
|
||||||
const existingSetting = await this.findPreviousSubscriptionSetting(settingName, userSubscription.uuid, userUuid)
|
const existingSetting = await this.findPreviousSubscriptionSetting(
|
||||||
|
settingName,
|
||||||
|
userSubscription.uuid,
|
||||||
|
user.uuid,
|
||||||
|
)
|
||||||
if (existingSetting !== null) {
|
if (existingSetting !== null) {
|
||||||
existingSetting.userSubscription = Promise.resolve(userSubscription)
|
existingSetting.userSubscription = Promise.resolve(userSubscription)
|
||||||
await this.subscriptionSettingRepository.save(existingSetting)
|
await this.subscriptionSettingRepository.save(existingSetting)
|
||||||
@@ -68,11 +74,17 @@ export class SubscriptionSettingService implements SubscriptionSettingServiceInt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let unencryptedValue = setting.value
|
||||||
|
if (overrides && overrides.has(settingName.value)) {
|
||||||
|
unencryptedValue = overrides.get(settingName.value) as string
|
||||||
|
}
|
||||||
|
|
||||||
await this.createOrReplace({
|
await this.createOrReplace({
|
||||||
userSubscription,
|
userSubscription,
|
||||||
|
user,
|
||||||
props: {
|
props: {
|
||||||
name: settingName.value,
|
name: settingName.value,
|
||||||
unencryptedValue: setting.value,
|
unencryptedValue,
|
||||||
serverEncryptionVersion: setting.serverEncryptionVersion,
|
serverEncryptionVersion: setting.serverEncryptionVersion,
|
||||||
sensitive: setting.sensitive,
|
sensitive: setting.sensitive,
|
||||||
},
|
},
|
||||||
@@ -109,7 +121,7 @@ export class SubscriptionSettingService implements SubscriptionSettingServiceInt
|
|||||||
async createOrReplace(
|
async createOrReplace(
|
||||||
dto: CreateOrReplaceSubscriptionSettingDTO,
|
dto: CreateOrReplaceSubscriptionSettingDTO,
|
||||||
): Promise<CreateOrReplaceSubscriptionSettingResponse> {
|
): Promise<CreateOrReplaceSubscriptionSettingResponse> {
|
||||||
const { userSubscription, props } = dto
|
const { userSubscription, user, props } = dto
|
||||||
|
|
||||||
const settingNameOrError = SettingName.create(props.name)
|
const settingNameOrError = SettingName.create(props.name)
|
||||||
if (settingNameOrError.isFailed()) {
|
if (settingNameOrError.isFailed()) {
|
||||||
@@ -121,7 +133,6 @@ export class SubscriptionSettingService implements SubscriptionSettingServiceInt
|
|||||||
throw new Error(`Setting ${settingName.value} is not a subscription setting`)
|
throw new Error(`Setting ${settingName.value} is not a subscription setting`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await userSubscription.user
|
|
||||||
const existing = await this.findSubscriptionSettingWithDecryptedValue({
|
const existing = await this.findSubscriptionSettingWithDecryptedValue({
|
||||||
userUuid: user.uuid,
|
userUuid: user.uuid,
|
||||||
userSubscriptionUuid: userSubscription.uuid,
|
userSubscriptionUuid: userSubscription.uuid,
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import { SubscriptionSetting } from './SubscriptionSetting'
|
|||||||
export interface SubscriptionSettingServiceInterface {
|
export interface SubscriptionSettingServiceInterface {
|
||||||
applyDefaultSubscriptionSettingsForSubscription(
|
applyDefaultSubscriptionSettingsForSubscription(
|
||||||
userSubscription: UserSubscription,
|
userSubscription: UserSubscription,
|
||||||
subscriptionName: string,
|
overrides?: Map<string, string>,
|
||||||
userUuid: string,
|
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
createOrReplace(dto: CreateOrReplaceSubscriptionSettingDTO): Promise<CreateOrReplaceSubscriptionSettingResponse>
|
createOrReplace(dto: CreateOrReplaceSubscriptionSettingDTO): Promise<CreateOrReplaceSubscriptionSettingResponse>
|
||||||
findSubscriptionSettingWithDecryptedValue(dto: FindSubscriptionSettingDTO): Promise<SubscriptionSetting | null>
|
findSubscriptionSettingWithDecryptedValue(dto: FindSubscriptionSettingDTO): Promise<SubscriptionSetting | null>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { SettingDescription } from './SettingDescription'
|
|||||||
|
|
||||||
export interface SubscriptionSettingsAssociationServiceInterface {
|
export interface SubscriptionSettingsAssociationServiceInterface {
|
||||||
getDefaultSettingsAndValuesForSubscriptionName(
|
getDefaultSettingsAndValuesForSubscriptionName(
|
||||||
subscriptionName: SubscriptionName,
|
subscriptionName: string,
|
||||||
): Promise<Map<string, SettingDescription> | undefined>
|
): Promise<Map<string, SettingDescription> | undefined>
|
||||||
getFileUploadLimit(subscriptionName: SubscriptionName): Promise<number>
|
getFileUploadLimit(subscriptionName: SubscriptionName): Promise<number>
|
||||||
}
|
}
|
||||||
|
|||||||
-4
@@ -106,8 +106,6 @@ describe('AcceptSharedSubscriptionInvitation', () => {
|
|||||||
expect(roleService.addUserRole).toHaveBeenCalledWith(invitee, 'PLUS_PLAN')
|
expect(roleService.addUserRole).toHaveBeenCalledWith(invitee, 'PLUS_PLAN')
|
||||||
expect(subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription).toHaveBeenCalledWith(
|
expect(subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription).toHaveBeenCalledWith(
|
||||||
inviteeSubscription,
|
inviteeSubscription,
|
||||||
'PLUS_PLAN',
|
|
||||||
'123',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -148,8 +146,6 @@ describe('AcceptSharedSubscriptionInvitation', () => {
|
|||||||
expect(roleService.addUserRole).toHaveBeenCalledWith(invitee, 'PLUS_PLAN')
|
expect(roleService.addUserRole).toHaveBeenCalledWith(invitee, 'PLUS_PLAN')
|
||||||
expect(subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription).toHaveBeenCalledWith(
|
expect(subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription).toHaveBeenCalledWith(
|
||||||
inviteeSubscription,
|
inviteeSubscription,
|
||||||
'PLUS_PLAN',
|
|
||||||
'123',
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+1
-5
@@ -92,11 +92,7 @@ export class AcceptSharedSubscriptionInvitation implements UseCaseInterface {
|
|||||||
|
|
||||||
await this.addUserRole(invitee, inviterUserSubscription.planName as SubscriptionName)
|
await this.addUserRole(invitee, inviterUserSubscription.planName as SubscriptionName)
|
||||||
|
|
||||||
await this.subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription(
|
await this.subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription(inviteeSubscription)
|
||||||
inviteeSubscription,
|
|
||||||
inviteeSubscription.planName as SubscriptionName,
|
|
||||||
invitee.uuid,
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
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'
|
||||||
|
import { SubscriptionSettingServiceInterface } from '../../Setting/SubscriptionSettingServiceInterface'
|
||||||
|
|
||||||
|
describe('ActivatePremiumFeatures', () => {
|
||||||
|
let userRepository: UserRepositoryInterface
|
||||||
|
let userSubscriptionRepository: UserSubscriptionRepositoryInterface
|
||||||
|
let subscriptionSettingsService: SubscriptionSettingServiceInterface
|
||||||
|
let roleService: RoleServiceInterface
|
||||||
|
let timer: TimerInterface
|
||||||
|
let user: User
|
||||||
|
|
||||||
|
const createUseCase = () =>
|
||||||
|
new ActivatePremiumFeatures(
|
||||||
|
userRepository,
|
||||||
|
userSubscriptionRepository,
|
||||||
|
subscriptionSettingsService,
|
||||||
|
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'))
|
||||||
|
|
||||||
|
subscriptionSettingsService = {} as jest.Mocked<SubscriptionSettingServiceInterface>
|
||||||
|
subscriptionSettingsService.applyDefaultSubscriptionSettingsForSubscription = jest.fn()
|
||||||
|
})
|
||||||
|
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should save a subscription with custom plan name and endsAt', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
username: 'test@test.te',
|
||||||
|
subscriptionPlanName: 'PRO_PLAN',
|
||||||
|
endsAt: new Date('2024-01-01T00:00:00.000Z'),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fail when subscription plan name is invalid', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
username: 'test@test.te',
|
||||||
|
subscriptionPlanName: 'some invalid plan name',
|
||||||
|
endsAt: new Date('2024-01-01T00:00:00.000Z'),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
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'
|
||||||
|
import { SubscriptionSettingServiceInterface } from '../../Setting/SubscriptionSettingServiceInterface'
|
||||||
|
import { SettingName } from '@standardnotes/settings'
|
||||||
|
|
||||||
|
export class ActivatePremiumFeatures implements UseCaseInterface<string> {
|
||||||
|
constructor(
|
||||||
|
private userRepository: UserRepositoryInterface,
|
||||||
|
private userSubscriptionRepository: UserSubscriptionRepositoryInterface,
|
||||||
|
private subscriptionSettingService: SubscriptionSettingServiceInterface,
|
||||||
|
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 subscriptionPlanNameString = dto.subscriptionPlanName ?? SubscriptionPlanName.NAMES.ProPlan
|
||||||
|
const subscriptionPlanNameOrError = SubscriptionPlanName.create(subscriptionPlanNameString)
|
||||||
|
if (subscriptionPlanNameOrError.isFailed()) {
|
||||||
|
return Result.fail(subscriptionPlanNameOrError.getError())
|
||||||
|
}
|
||||||
|
const subscriptionPlanName = subscriptionPlanNameOrError.getValue()
|
||||||
|
|
||||||
|
const timestamp = this.timer.getTimestampInMicroseconds()
|
||||||
|
|
||||||
|
const endsAt = dto.endsAt ?? this.timer.getUTCDateNDaysAhead(365)
|
||||||
|
|
||||||
|
const subscription = new UserSubscription()
|
||||||
|
subscription.planName = subscriptionPlanName.value
|
||||||
|
subscription.user = Promise.resolve(user)
|
||||||
|
subscription.createdAt = timestamp
|
||||||
|
subscription.updatedAt = timestamp
|
||||||
|
subscription.endsAt = this.timer.convertDateToMicroseconds(endsAt)
|
||||||
|
subscription.cancelled = false
|
||||||
|
subscription.subscriptionId = 1
|
||||||
|
subscription.subscriptionType = UserSubscriptionType.Regular
|
||||||
|
|
||||||
|
await this.userSubscriptionRepository.save(subscription)
|
||||||
|
|
||||||
|
await this.roleService.addUserRole(user, subscriptionPlanName.value)
|
||||||
|
|
||||||
|
await this.subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription(
|
||||||
|
subscription,
|
||||||
|
new Map([[SettingName.NAMES.FileUploadBytesLimit, '-1']]),
|
||||||
|
)
|
||||||
|
|
||||||
|
return Result.ok('Premium features activated.')
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export interface ActivatePremiumFeaturesDTO {
|
||||||
|
username: string
|
||||||
|
subscriptionPlanName?: string
|
||||||
|
endsAt?: Date
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@ export class AuthenticateRequest implements UseCaseInterface {
|
|||||||
|
|
||||||
async execute(dto: AuthenticateRequestDTO): Promise<AuthenticateRequestResponse> {
|
async execute(dto: AuthenticateRequestDTO): Promise<AuthenticateRequestResponse> {
|
||||||
if (!dto.authorizationHeader) {
|
if (!dto.authorizationHeader) {
|
||||||
|
this.logger.debug('Authorization header not provided.')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
responseCode: 401,
|
responseCode: 401,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { AuthenticateUser } from './AuthenticateUser'
|
|||||||
import { RevokedSession } from '../Session/RevokedSession'
|
import { RevokedSession } from '../Session/RevokedSession'
|
||||||
import { AuthenticationMethodResolverInterface } from '../Auth/AuthenticationMethodResolverInterface'
|
import { AuthenticationMethodResolverInterface } from '../Auth/AuthenticationMethodResolverInterface'
|
||||||
import { TimerInterface } from '@standardnotes/time'
|
import { TimerInterface } from '@standardnotes/time'
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
describe('AuthenticateUser', () => {
|
describe('AuthenticateUser', () => {
|
||||||
let user: User
|
let user: User
|
||||||
@@ -14,11 +15,15 @@ describe('AuthenticateUser', () => {
|
|||||||
let revokedSession: RevokedSession
|
let revokedSession: RevokedSession
|
||||||
let authenticationMethodResolver: AuthenticationMethodResolverInterface
|
let authenticationMethodResolver: AuthenticationMethodResolverInterface
|
||||||
let timer: TimerInterface
|
let timer: TimerInterface
|
||||||
|
let logger: Logger
|
||||||
const accessTokenAge = 3600
|
const accessTokenAge = 3600
|
||||||
|
|
||||||
const createUseCase = () => new AuthenticateUser(authenticationMethodResolver, timer, accessTokenAge)
|
const createUseCase = () => new AuthenticateUser(authenticationMethodResolver, timer, accessTokenAge, logger)
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
logger = {} as jest.Mocked<Logger>
|
||||||
|
logger.debug = jest.fn()
|
||||||
|
|
||||||
user = {} as jest.Mocked<User>
|
user = {} as jest.Mocked<User>
|
||||||
user.supportsSessions = jest.fn().mockReturnValue(false)
|
user.supportsSessions = jest.fn().mockReturnValue(false)
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Session } from '../Session/Session'
|
|||||||
import { AuthenticateUserDTO } from './AuthenticateUserDTO'
|
import { AuthenticateUserDTO } from './AuthenticateUserDTO'
|
||||||
import { AuthenticateUserResponse } from './AuthenticateUserResponse'
|
import { AuthenticateUserResponse } from './AuthenticateUserResponse'
|
||||||
import { UseCaseInterface } from './UseCaseInterface'
|
import { UseCaseInterface } from './UseCaseInterface'
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class AuthenticateUser implements UseCaseInterface {
|
export class AuthenticateUser implements UseCaseInterface {
|
||||||
@@ -17,11 +18,14 @@ export class AuthenticateUser implements UseCaseInterface {
|
|||||||
private authenticationMethodResolver: AuthenticationMethodResolverInterface,
|
private authenticationMethodResolver: AuthenticationMethodResolverInterface,
|
||||||
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
||||||
@inject(TYPES.Auth_ACCESS_TOKEN_AGE) private accessTokenAge: number,
|
@inject(TYPES.Auth_ACCESS_TOKEN_AGE) private accessTokenAge: number,
|
||||||
|
@inject(TYPES.Auth_Logger) private logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: AuthenticateUserDTO): Promise<AuthenticateUserResponse> {
|
async execute(dto: AuthenticateUserDTO): Promise<AuthenticateUserResponse> {
|
||||||
const authenticationMethod = await this.authenticationMethodResolver.resolve(dto.token)
|
const authenticationMethod = await this.authenticationMethodResolver.resolve(dto.token)
|
||||||
if (!authenticationMethod) {
|
if (!authenticationMethod) {
|
||||||
|
this.logger.debug('No authentication method found for token.')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
failureType: 'INVALID_AUTH',
|
failureType: 'INVALID_AUTH',
|
||||||
@@ -37,6 +41,8 @@ export class AuthenticateUser implements UseCaseInterface {
|
|||||||
|
|
||||||
const user = authenticationMethod.user
|
const user = authenticationMethod.user
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
this.logger.debug('No user found for authentication method.')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
failureType: 'INVALID_AUTH',
|
failureType: 'INVALID_AUTH',
|
||||||
@@ -44,6 +50,8 @@ export class AuthenticateUser implements UseCaseInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (authenticationMethod.type == 'jwt' && user.supportsSessions()) {
|
if (authenticationMethod.type == 'jwt' && user.supportsSessions()) {
|
||||||
|
this.logger.debug('User supports sessions but is trying to authenticate with a JWT.')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
failureType: 'INVALID_AUTH',
|
failureType: 'INVALID_AUTH',
|
||||||
@@ -56,6 +64,8 @@ export class AuthenticateUser implements UseCaseInterface {
|
|||||||
const encryptedPasswordDigest = crypto.createHash('sha256').update(user.encryptedPassword).digest('hex')
|
const encryptedPasswordDigest = crypto.createHash('sha256').update(user.encryptedPassword).digest('hex')
|
||||||
|
|
||||||
if (!pwHash || !crypto.timingSafeEqual(Buffer.from(pwHash), Buffer.from(encryptedPasswordDigest))) {
|
if (!pwHash || !crypto.timingSafeEqual(Buffer.from(pwHash), Buffer.from(encryptedPasswordDigest))) {
|
||||||
|
this.logger.debug('Password hash does not match.')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
failureType: 'INVALID_AUTH',
|
failureType: 'INVALID_AUTH',
|
||||||
@@ -66,6 +76,8 @@ export class AuthenticateUser implements UseCaseInterface {
|
|||||||
case 'session_token': {
|
case 'session_token': {
|
||||||
const session = authenticationMethod.session
|
const session = authenticationMethod.session
|
||||||
if (!session) {
|
if (!session) {
|
||||||
|
this.logger.debug('No session found for authentication method.')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
failureType: 'INVALID_AUTH',
|
failureType: 'INVALID_AUTH',
|
||||||
@@ -73,6 +85,8 @@ export class AuthenticateUser implements UseCaseInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (session.refreshExpiration < this.timer.getUTCDate()) {
|
if (session.refreshExpiration < this.timer.getUTCDate()) {
|
||||||
|
this.logger.debug('Session refresh token has expired.')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
failureType: 'INVALID_AUTH',
|
failureType: 'INVALID_AUTH',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { inject, injectable } from 'inversify'
|
|||||||
import { SubscriptionName } from '@standardnotes/common'
|
import { SubscriptionName } from '@standardnotes/common'
|
||||||
import { TimerInterface } from '@standardnotes/time'
|
import { TimerInterface } from '@standardnotes/time'
|
||||||
import { TokenEncoderInterface, ValetTokenData } from '@standardnotes/security'
|
import { TokenEncoderInterface, ValetTokenData } from '@standardnotes/security'
|
||||||
import { CreateValetTokenPayload, CreateValetTokenResponseData } from '@standardnotes/responses'
|
import { CreateValetTokenResponseData } from '@standardnotes/responses'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/settings'
|
||||||
|
|
||||||
import TYPES from '../../../Bootstrap/Types'
|
import TYPES from '../../../Bootstrap/Types'
|
||||||
@@ -12,6 +12,7 @@ import { SubscriptionSettingServiceInterface } from '../../Setting/SubscriptionS
|
|||||||
import { CreateValetTokenDTO } from './CreateValetTokenDTO'
|
import { CreateValetTokenDTO } from './CreateValetTokenDTO'
|
||||||
import { SubscriptionSettingsAssociationServiceInterface } from '../../Setting/SubscriptionSettingsAssociationServiceInterface'
|
import { SubscriptionSettingsAssociationServiceInterface } from '../../Setting/SubscriptionSettingsAssociationServiceInterface'
|
||||||
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
||||||
|
import { CreateValetTokenPayload } from '../../ValetToken/CreateValetTokenPayload'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class CreateValetToken implements UseCaseInterface {
|
export class CreateValetToken implements UseCaseInterface {
|
||||||
|
|||||||
+2
-2
@@ -69,7 +69,7 @@ export class InviteToSharedSubscription implements UseCaseInterface {
|
|||||||
sharedSubscriptionInvition.inviterIdentifier = dto.inviterEmail
|
sharedSubscriptionInvition.inviterIdentifier = dto.inviterEmail
|
||||||
sharedSubscriptionInvition.inviterIdentifierType = InviterIdentifierType.Email
|
sharedSubscriptionInvition.inviterIdentifierType = InviterIdentifierType.Email
|
||||||
sharedSubscriptionInvition.inviteeIdentifier = dto.inviteeIdentifier
|
sharedSubscriptionInvition.inviteeIdentifier = dto.inviteeIdentifier
|
||||||
sharedSubscriptionInvition.inviteeIdentifierType = this.isInviteeIdentifierPotentiallyAVaultAccount(
|
sharedSubscriptionInvition.inviteeIdentifierType = this.isInviteeIdentifierPotentiallyAPrivateUsernameAccount(
|
||||||
dto.inviteeIdentifier,
|
dto.inviteeIdentifier,
|
||||||
)
|
)
|
||||||
? InviteeIdentifierType.Hash
|
? 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('@')
|
return identifier.length === 64 && !identifier.includes('@')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as bcrypt from 'bcryptjs'
|
import * as bcrypt from 'bcryptjs'
|
||||||
import { RoleName, Username } from '@standardnotes/domain-core'
|
import { RoleName, Username } from '@standardnotes/domain-core'
|
||||||
import { ApiVersion } from '@standardnotes/api'
|
|
||||||
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { inject, injectable } from 'inversify'
|
import { inject, injectable } from 'inversify'
|
||||||
@@ -16,6 +15,7 @@ import { TimerInterface } from '@standardnotes/time'
|
|||||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||||
import { AuthResponseFactory20200115 } from '../Auth/AuthResponseFactory20200115'
|
import { AuthResponseFactory20200115 } from '../Auth/AuthResponseFactory20200115'
|
||||||
import { AuthResponse20200115 } from '../Auth/AuthResponse20200115'
|
import { AuthResponse20200115 } from '../Auth/AuthResponse20200115'
|
||||||
|
import { ApiVersion } from '../Api/ApiVersion'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class Register implements UseCaseInterface {
|
export class Register implements UseCaseInterface {
|
||||||
@@ -39,7 +39,7 @@ export class Register implements UseCaseInterface {
|
|||||||
|
|
||||||
const { email, password, apiVersion, ephemeralSession, ...registrationFields } = dto
|
const { email, password, apiVersion, ephemeralSession, ...registrationFields } = dto
|
||||||
|
|
||||||
if (apiVersion !== ApiVersion.v0) {
|
if (apiVersion !== ApiVersion.v20200115) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
errorMessage: `Unsupported api version: ${apiVersion}`,
|
errorMessage: `Unsupported api version: ${apiVersion}`,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as bcrypt from 'bcryptjs'
|
import * as bcrypt from 'bcryptjs'
|
||||||
import { Result, UseCaseInterface, Username, Uuid, Validator } from '@standardnotes/domain-core'
|
import { Result, UseCaseInterface, Username, Uuid, Validator } from '@standardnotes/domain-core'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/settings'
|
||||||
import { ApiVersion } from '@standardnotes/api'
|
|
||||||
|
|
||||||
import { AuthResponse20200115 } from '../../Auth/AuthResponse20200115'
|
import { AuthResponse20200115 } from '../../Auth/AuthResponse20200115'
|
||||||
import { SettingServiceInterface } from '../../Setting/SettingServiceInterface'
|
import { SettingServiceInterface } from '../../Setting/SettingServiceInterface'
|
||||||
@@ -16,6 +15,7 @@ import { IncreaseLoginAttempts } from '../IncreaseLoginAttempts'
|
|||||||
import { ClearLoginAttempts } from '../ClearLoginAttempts'
|
import { ClearLoginAttempts } from '../ClearLoginAttempts'
|
||||||
import { DeleteSetting } from '../DeleteSetting/DeleteSetting'
|
import { DeleteSetting } from '../DeleteSetting/DeleteSetting'
|
||||||
import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface'
|
import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface'
|
||||||
|
import { ApiVersion } from '../../Api/ApiVersion'
|
||||||
|
|
||||||
export class SignInWithRecoveryCodes implements UseCaseInterface<AuthResponse20200115> {
|
export class SignInWithRecoveryCodes implements UseCaseInterface<AuthResponse20200115> {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -100,7 +100,7 @@ export class SignInWithRecoveryCodes implements UseCaseInterface<AuthResponse202
|
|||||||
|
|
||||||
const authResponse = await this.authResponseFactory.createResponse({
|
const authResponse = await this.authResponseFactory.createResponse({
|
||||||
user,
|
user,
|
||||||
apiVersion: ApiVersion.v0,
|
apiVersion: ApiVersion.v20200115,
|
||||||
userAgent: dto.userAgent,
|
userAgent: dto.userAgent,
|
||||||
ephemeralSession: false,
|
ephemeralSession: false,
|
||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ describe('UpdateSetting', () => {
|
|||||||
serverEncryptionVersion: 1,
|
serverEncryptionVersion: 1,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
},
|
},
|
||||||
|
user,
|
||||||
userSubscription: regularSubscription,
|
userSubscription: regularSubscription,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -303,6 +304,7 @@ describe('UpdateSetting', () => {
|
|||||||
serverEncryptionVersion: 1,
|
serverEncryptionVersion: 1,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
},
|
},
|
||||||
|
user,
|
||||||
userSubscription: sharedSubscription,
|
userSubscription: sharedSubscription,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ export class UpdateSetting implements UseCaseInterface {
|
|||||||
|
|
||||||
const response = await this.subscriptionSettingService.createOrReplace({
|
const response = await this.subscriptionSettingService.createOrReplace({
|
||||||
userSubscription: subscription,
|
userSubscription: subscription,
|
||||||
|
user,
|
||||||
props,
|
props,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -44,21 +44,13 @@ describe('UpdateUser', () => {
|
|||||||
user,
|
user,
|
||||||
updatedWithUserAgent: 'Mozilla',
|
updatedWithUserAgent: 'Mozilla',
|
||||||
apiVersion: '20190520',
|
apiVersion: '20190520',
|
||||||
version: '004',
|
|
||||||
pwCost: 11,
|
|
||||||
pwSalt: 'qweqwe',
|
|
||||||
pwNonce: undefined,
|
|
||||||
}),
|
}),
|
||||||
).toEqual({ success: true, authResponse: { foo: 'bar' } })
|
).toEqual({ success: true, authResponse: { foo: 'bar' } })
|
||||||
|
|
||||||
expect(userRepository.save).toHaveBeenCalledWith({
|
expect(userRepository.save).toHaveBeenCalledWith({
|
||||||
createdAt: new Date(1),
|
createdAt: new Date(1),
|
||||||
pwCost: 11,
|
|
||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
pwSalt: 'qweqwe',
|
|
||||||
updatedWithUserAgent: 'Mozilla',
|
|
||||||
uuid: '123',
|
uuid: '123',
|
||||||
version: '004',
|
|
||||||
updatedAt: new Date(1),
|
updatedAt: new Date(1),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -17,25 +17,17 @@ export class UpdateUser implements UseCaseInterface {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: UpdateUserDTO): Promise<UpdateUserResponse> {
|
async execute(dto: UpdateUserDTO): Promise<UpdateUserResponse> {
|
||||||
const { user, apiVersion, ...updateFields } = dto
|
dto.user.updatedAt = this.timer.getUTCDate()
|
||||||
|
|
||||||
Object.keys(updateFields).forEach(
|
const updatedUser = await this.userRepository.save(dto.user)
|
||||||
(key) => (updateFields[key] === undefined || updateFields[key] === null) && delete updateFields[key],
|
|
||||||
)
|
|
||||||
|
|
||||||
Object.assign(user, updateFields)
|
const authResponseFactory = this.authResponseFactoryResolver.resolveAuthResponseFactoryVersion(dto.apiVersion)
|
||||||
|
|
||||||
user.updatedAt = this.timer.getUTCDate()
|
|
||||||
|
|
||||||
await this.userRepository.save(user)
|
|
||||||
|
|
||||||
const authResponseFactory = this.authResponseFactoryResolver.resolveAuthResponseFactoryVersion(apiVersion)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
authResponse: await authResponseFactory.createResponse({
|
authResponse: await authResponseFactory.createResponse({
|
||||||
user,
|
user: updatedUser,
|
||||||
apiVersion,
|
apiVersion: dto.apiVersion,
|
||||||
userAgent: dto.updatedWithUserAgent,
|
userAgent: dto.updatedWithUserAgent,
|
||||||
ephemeralSession: false,
|
ephemeralSession: false,
|
||||||
readonlyAccess: false,
|
readonlyAccess: false,
|
||||||
|
|||||||
@@ -1,18 +1,7 @@
|
|||||||
import { User } from '../User/User'
|
import { User } from '../User/User'
|
||||||
|
|
||||||
export type UpdateUserDTO = {
|
export type UpdateUserDTO = {
|
||||||
[key: string]: string | User | Date | undefined | number
|
|
||||||
user: User
|
user: User
|
||||||
updatedWithUserAgent: string
|
|
||||||
apiVersion: string
|
apiVersion: string
|
||||||
email?: string
|
updatedWithUserAgent: string
|
||||||
pwFunc?: string
|
|
||||||
pwAlg?: string
|
|
||||||
pwCost?: number
|
|
||||||
pwKeySize?: number
|
|
||||||
pwNonce?: string
|
|
||||||
pwSalt?: string
|
|
||||||
kpOrigination?: string
|
|
||||||
kpCreated?: Date
|
|
||||||
version?: string
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ describe('User', () => {
|
|||||||
const user = createUser()
|
const user = createUser()
|
||||||
user.email = 'a75a31ce95365904ef0e0a8e6cefc1f5e99adfef81bbdb6d4499eeb10ae0ff67'
|
user.email = 'a75a31ce95365904ef0e0a8e6cefc1f5e99adfef81bbdb6d4499eeb10ae0ff67'
|
||||||
|
|
||||||
expect(user.isPotentiallyAVaultAccount()).toBeTruthy()
|
expect(user.isPotentiallyAPrivateUsernameAccount()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should indicate if the user is not a vault account', () => {
|
it('should indicate if the user is not a vault account', () => {
|
||||||
const user = createUser()
|
const user = createUser()
|
||||||
user.email = 'test@test.te'
|
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)
|
return parseInt(this.version) >= parseInt(ProtocolVersion.V004)
|
||||||
}
|
}
|
||||||
|
|
||||||
isPotentiallyAVaultAccount(): boolean {
|
isPotentiallyAPrivateUsernameAccount(): boolean {
|
||||||
return this.email.length === 64 && !this.email.includes('@')
|
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,
|
user: response.locals.user,
|
||||||
updatedWithUserAgent: <string>request.headers['user-agent'],
|
updatedWithUserAgent: <string>request.headers['user-agent'],
|
||||||
apiVersion: request.body.api,
|
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) {
|
if (updateResult.success) {
|
||||||
|
|||||||
+3
-2
@@ -1,10 +1,11 @@
|
|||||||
import { ControllerContainerInterface, Uuid } from '@standardnotes/domain-core'
|
import { ControllerContainerInterface, Uuid } from '@standardnotes/domain-core'
|
||||||
import { Request, Response } from 'express'
|
import { Request, Response } from 'express'
|
||||||
import { BaseHttpController, results } from 'inversify-express-utils'
|
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 { CreateValetToken } from '../../../Domain/UseCase/CreateValetToken/CreateValetToken'
|
||||||
import { CreateValetTokenPayload, ErrorTag } from '@standardnotes/responses'
|
import { CreateValetTokenPayload } from '../../../Domain/ValetToken/CreateValetTokenPayload'
|
||||||
import { ValetTokenOperation } from '@standardnotes/security'
|
|
||||||
|
|
||||||
export class HomeServerValetTokenController extends BaseHttpController {
|
export class HomeServerValetTokenController extends BaseHttpController {
|
||||||
constructor(protected createValetKey: CreateValetToken, private controllerContainer?: ControllerContainerInterface) {
|
constructor(protected createValetKey: CreateValetToken, private controllerContainer?: ControllerContainerInterface) {
|
||||||
|
|||||||
@@ -99,9 +99,7 @@ describe('InversifyExpressUsersController', () => {
|
|||||||
|
|
||||||
expect(updateUser.execute).toHaveBeenCalledWith({
|
expect(updateUser.execute).toHaveBeenCalledWith({
|
||||||
apiVersion: '20190520',
|
apiVersion: '20190520',
|
||||||
kpOrigination: 'test',
|
|
||||||
updatedWithUserAgent: 'Google Chrome',
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
version: '002',
|
|
||||||
user: {
|
user: {
|
||||||
uuid: '123',
|
uuid: '123',
|
||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
@@ -143,9 +141,7 @@ describe('InversifyExpressUsersController', () => {
|
|||||||
|
|
||||||
expect(updateUser.execute).toHaveBeenCalledWith({
|
expect(updateUser.execute).toHaveBeenCalledWith({
|
||||||
apiVersion: '20190520',
|
apiVersion: '20190520',
|
||||||
kpOrigination: 'test',
|
|
||||||
updatedWithUserAgent: 'Google Chrome',
|
updatedWithUserAgent: 'Google Chrome',
|
||||||
version: '002',
|
|
||||||
user: {
|
user: {
|
||||||
uuid: '123',
|
uuid: '123',
|
||||||
email: 'test@test.te',
|
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 { User } from '../Domain/User/User'
|
||||||
import { ProjectorInterface } from './ProjectorInterface'
|
import { ProjectorInterface } from './ProjectorInterface'
|
||||||
|
import { SimpleUserProjection } from './SimpleUserProjection'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class UserProjector implements ProjectorInterface<User> {
|
export class UserProjector implements ProjectorInterface<User> {
|
||||||
projectSimple(user: User): Record<string, unknown> {
|
projectSimple(user: User): SimpleUserProjection {
|
||||||
return {
|
return {
|
||||||
uuid: user.uuid,
|
uuid: user.uuid,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [1.48.2](https://github.com/standardnotes/server/compare/@standardnotes/common@1.48.0...@standardnotes/common@1.48.2) (2023-05-31)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user