mirror of
https://github.com/standardnotes/server
synced 2026-01-17 05:04:27 -05:00
Compare commits
67 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0188f290f9 | ||
|
|
676cf36f8d | ||
|
|
f8aef6c8ef | ||
|
|
5bf8cf49c1 | ||
|
|
51cd0a4dad | ||
|
|
1d06ffe9d5 | ||
|
|
dbf532f55e | ||
|
|
ca6dbc0053 | ||
|
|
1bb5980b45 | ||
|
|
a02a28774b | ||
|
|
2d9b3578b6 | ||
|
|
3d5e747590 | ||
|
|
94467747ac | ||
|
|
cebab59a02 | ||
|
|
09e1a892ca | ||
|
|
7b1eec21e5 | ||
|
|
a58262d584 | ||
|
|
a8f03e157b | ||
|
|
a401962bcd | ||
|
|
9759814f63 | ||
|
|
c7cf53722c | ||
|
|
8cb33dc906 | ||
|
|
1d73e4f072 | ||
|
|
0a0f82ea3d | ||
|
|
f9b1f40ddf | ||
|
|
0562b0a621 | ||
|
|
15ed1fd789 | ||
|
|
5001496c7b | ||
|
|
0a1080ce2a | ||
|
|
4802d7e876 | ||
|
|
bcd95cdbe9 | ||
|
|
d50c4440c2 | ||
|
|
921c30f641 | ||
|
|
22540ee834 | ||
|
|
4f4443a882 | ||
|
|
80dbacf933 | ||
|
|
dc77ff3e45 | ||
|
|
6515dcf487 | ||
|
|
d0fd6b98df | ||
|
|
345efacb44 | ||
|
|
d0dba1b66d | ||
|
|
da119af8b2 | ||
|
|
a5da42bddd | ||
|
|
12767237d2 | ||
|
|
230c96dcf1 | ||
|
|
e2696fcd1a | ||
|
|
a621cf1e3b | ||
|
|
db35b9fcab | ||
|
|
880db1038a | ||
|
|
414b090efb | ||
|
|
41e2136bc0 | ||
|
|
378ecedfcc | ||
|
|
06d4200909 | ||
|
|
22a8cc90f1 | ||
|
|
5cf84e31b0 | ||
|
|
31e7aaf253 | ||
|
|
8ec3d37c18 | ||
|
|
857c6af946 | ||
|
|
de081fe786 | ||
|
|
0aeeb2d1cf | ||
|
|
e589029722 | ||
|
|
b265a39b63 | ||
|
|
ed5cfd86db | ||
|
|
a1a3e9f586 | ||
|
|
a40b17b141 | ||
|
|
18181ed9df | ||
|
|
0ae028db73 |
33
.github/workflows/common-e2e.yml
vendored
33
.github/workflows/common-e2e.yml
vendored
@@ -23,8 +23,8 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
suite: ['base', 'vaults']
|
||||
secondary_db_enabled: [true, false]
|
||||
transition_mode_enabled: [true, false]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
@@ -51,31 +51,32 @@ jobs:
|
||||
DB_TYPE: mysql
|
||||
CACHE_TYPE: redis
|
||||
SECONDARY_DB_ENABLED: ${{ matrix.secondary_db_enabled }}
|
||||
TRANSITION_MODE_ENABLED: ${{ matrix.transition_mode_enabled }}
|
||||
|
||||
- name: Wait for server to start
|
||||
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
|
||||
|
||||
- name: Run E2E Test Suite
|
||||
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html
|
||||
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html?suite=${{ matrix.suite }}
|
||||
|
||||
- name: Show logs on failure
|
||||
- name: Archive failed run logs
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
echo "# Errors:"
|
||||
tail -n 100 logs/*.err
|
||||
echo "# Logs:"
|
||||
tail -n 100 logs/*.log
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: self-hosted-failure-logs-${{ matrix.suite }}-${{ matrix.secondary_db_enabled }}
|
||||
retention-days: 5
|
||||
path: |
|
||||
logs/*.err
|
||||
logs/*.log
|
||||
|
||||
e2e-home-server:
|
||||
name: (Home Server) E2E Test Suite
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
suite: ['base', 'vaults']
|
||||
db_type: [mysql, sqlite]
|
||||
cache_type: [redis, memory]
|
||||
secondary_db_enabled: [true, false]
|
||||
transition_mode_enabled: [true, false]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -145,7 +146,6 @@ jobs:
|
||||
echo "REDIS_URL=redis://localhost:6379" >> packages/home-server/.env
|
||||
echo "CACHE_TYPE=${{ matrix.cache_type }}" >> packages/home-server/.env
|
||||
echo "SECONDARY_DB_ENABLED=${{ matrix.secondary_db_enabled }}" >> packages/home-server/.env
|
||||
echo "TRANSITION_MODE_ENABLED=${{ matrix.transition_mode_enabled }}" >> packages/home-server/.env
|
||||
echo "MONGO_HOST=localhost" >> packages/home-server/.env
|
||||
echo "MONGO_PORT=27017" >> packages/home-server/.env
|
||||
echo "MONGO_DATABASE=standardnotes" >> packages/home-server/.env
|
||||
@@ -163,8 +163,13 @@ jobs:
|
||||
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
|
||||
|
||||
- name: Run E2E Test Suite
|
||||
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html
|
||||
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html?suite=${{ matrix.suite }}
|
||||
|
||||
- name: Show logs on failure
|
||||
- name: Archive failed run logs
|
||||
if: ${{ failure() }}
|
||||
run: tail -n 500 logs/output.log
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: home-server-failure-logs-${{ matrix.suite }}-${{ matrix.db_type }}-${{ matrix.cache_type }}-${{ matrix.secondary_db_enabled }}
|
||||
retention-days: 5
|
||||
path: |
|
||||
logs/output.log
|
||||
|
||||
8
.pnp.cjs
generated
8
.pnp.cjs
generated
@@ -6156,6 +6156,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/dotenv", "npm:8.2.0"],\
|
||||
["@types/express", "npm:4.17.17"],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
["@types/jest", "npm:29.5.2"],\
|
||||
["@types/newrelic", "npm:9.14.0"],\
|
||||
["@types/node", "npm:20.5.7"],\
|
||||
@@ -6168,6 +6169,7 @@ const RAW_RUNTIME_STATE =
|
||||
["express", "npm:4.18.2"],\
|
||||
["inversify", "npm:6.0.1"],\
|
||||
["inversify-express-utils", "npm:6.4.3"],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
@@ -6340,6 +6342,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/dotenv", "npm:8.2.0"],\
|
||||
["@types/express", "npm:4.17.17"],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
["@types/jest", "npm:29.5.2"],\
|
||||
["@types/jsonwebtoken", "npm:9.0.2"],\
|
||||
["@types/newrelic", "npm:9.14.0"],\
|
||||
@@ -6359,6 +6362,7 @@ const RAW_RUNTIME_STATE =
|
||||
["helmet", "npm:7.0.0"],\
|
||||
["inversify", "npm:6.0.1"],\
|
||||
["inversify-express-utils", "npm:6.4.3"],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["jsonwebtoken", "npm:9.0.0"],\
|
||||
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
|
||||
@@ -16772,7 +16776,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@types/better-sqlite3", null],\
|
||||
["@types/google-cloud__spanner", null],\
|
||||
["@types/hdb-pool", null],\
|
||||
["@types/ioredis", null],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
["@types/mongodb", null],\
|
||||
["@types/mssql", null],\
|
||||
["@types/mysql2", null],\
|
||||
@@ -16796,7 +16800,7 @@ const RAW_RUNTIME_STATE =
|
||||
["dotenv", "npm:16.1.3"],\
|
||||
["glob", "npm:8.1.0"],\
|
||||
["hdb-pool", null],\
|
||||
["ioredis", null],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["mkdirp", "npm:2.1.6"],\
|
||||
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
|
||||
["mssql", null],\
|
||||
|
||||
@@ -24,7 +24,6 @@ services:
|
||||
DB_TYPE: "${DB_TYPE}"
|
||||
CACHE_TYPE: "${CACHE_TYPE}"
|
||||
SECONDARY_DB_ENABLED: "${SECONDARY_DB_ENABLED}"
|
||||
TRANSITION_MODE_ENABLED: "${TRANSITION_MODE_ENABLED}"
|
||||
container_name: server-ci
|
||||
ports:
|
||||
- 3123:3000
|
||||
|
||||
@@ -68,9 +68,6 @@ fi
|
||||
if [ -z "$SECONDARY_DB_ENABLED" ]; then
|
||||
export SECONDARY_DB_ENABLED=false
|
||||
fi
|
||||
if [ -z "$TRANSITION_MODE_ENABLED" ]; then
|
||||
export TRANSITION_MODE_ENABLED=false
|
||||
fi
|
||||
export DB_MIGRATIONS_PATH="dist/migrations/*.js"
|
||||
|
||||
#########
|
||||
|
||||
@@ -139,6 +139,11 @@ LINKING_RESULT=$(link_queue_and_topic $AUTH_TOPIC_ARN $FILES_QUEUE_ARN)
|
||||
echo "linking done:"
|
||||
echo "$LINKING_RESULT"
|
||||
|
||||
echo "linking topic $SYNCING_SERVER_TOPIC_ARN to queue $FILES_QUEUE_ARN"
|
||||
LINKING_RESULT=$(link_queue_and_topic $SYNCING_SERVER_TOPIC_ARN $FILES_QUEUE_ARN)
|
||||
echo "linking done:"
|
||||
echo "$LINKING_RESULT"
|
||||
|
||||
QUEUE_NAME="syncing-server-local-queue"
|
||||
|
||||
echo "creating queue $QUEUE_NAME"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"release": "lerna version --conventional-graduate --conventional-commits --yes -m \"chore(release): publish new version\"",
|
||||
"publish": "lerna publish from-git --yes --no-verify-access --loglevel verbose",
|
||||
"postversion": "./scripts/push-tags-one-by-one.sh",
|
||||
"e2e": "yarn workspace @standardnotes/home-server run build && PORT=3123 yarn workspace @standardnotes/home-server start",
|
||||
"e2e": "yarn build && PORT=3123 yarn workspace @standardnotes/home-server start",
|
||||
"start": "yarn workspace @standardnotes/home-server run build && yarn workspace @standardnotes/home-server start"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.27.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.1...@standardnotes/analytics@2.27.2) (2023-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.27.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.27.0...@standardnotes/analytics@2.27.1) (2023-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
# [2.27.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.24...@standardnotes/analytics@2.27.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
## [2.26.24](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.23...@standardnotes/analytics@2.26.24) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.23](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.22...@standardnotes/analytics@2.26.23) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.22](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.21...@standardnotes/analytics@2.26.22) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.21](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.20...@standardnotes/analytics@2.26.21) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.20](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.19...@standardnotes/analytics@2.26.20) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.19](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.18...@standardnotes/analytics@2.26.19) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.18](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.17...@standardnotes/analytics@2.26.18) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.17](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.16...@standardnotes/analytics@2.26.17) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.26.16](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.15...@standardnotes/analytics@2.26.16) (2023-09-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.26.16",
|
||||
"version": "2.27.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -18,7 +18,7 @@
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --ext .ts --fix",
|
||||
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
|
||||
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
|
||||
"worker": "yarn node dist/bin/worker.js",
|
||||
"report": "yarn node dist/bin/report.js",
|
||||
"setup:env": "cp .env.sample .env",
|
||||
|
||||
@@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.75.7](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.6...@standardnotes/api-gateway@1.75.7) (2023-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.75.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.5...@standardnotes/api-gateway@1.75.6) (2023-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.75.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.4...@standardnotes/api-gateway@1.75.5) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.75.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.3...@standardnotes/api-gateway@1.75.4) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.75.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.2...@standardnotes/api-gateway@1.75.3) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.75.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.1...@standardnotes/api-gateway@1.75.2) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.75.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.0...@standardnotes/api-gateway@1.75.1) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
# [1.75.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.17...@standardnotes/api-gateway@1.75.0) (2023-09-21)
|
||||
|
||||
### Features
|
||||
|
||||
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/api-gateway/issues/841)) ([230c96d](https://github.com/standardnotes/api-gateway/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
|
||||
|
||||
## [1.74.17](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.16...@standardnotes/api-gateway@1.74.17) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.74.16](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.15...@standardnotes/api-gateway@1.74.16) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.74.15](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.14...@standardnotes/api-gateway@1.74.15) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.74.14](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.13...@standardnotes/api-gateway@1.74.14) (2023-09-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.74.14",
|
||||
"version": "1.75.7",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import { BaseHttpController, controller, httpDelete, httpGet } from 'inversify-express-utils'
|
||||
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'
|
||||
@@ -42,4 +42,19 @@ export class SharedVaultUsersController extends BaseHttpController {
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
|
||||
@httpPost('/:userUuid/designate-survivor')
|
||||
async designateSurvivor(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callSyncingServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier(
|
||||
'POST',
|
||||
'shared-vaults/:sharedVaultUuid/users/:userUuid/designate-survivor',
|
||||
request.params.sharedVaultUuid,
|
||||
request.params.userUuid,
|
||||
),
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,10 @@ export class EndpointResolver implements EndpointResolverInterface {
|
||||
// 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'],
|
||||
[
|
||||
'[POST]:shared-vaults/:sharedVaultUuid/users/:userUuid/designate-survivor',
|
||||
'sync.shared-vault-users.designate-survivor',
|
||||
],
|
||||
])
|
||||
|
||||
resolveEndpointOrMethodIdentifier(method: string, endpoint: string, ...params: string[]): string {
|
||||
|
||||
@@ -3,6 +3,72 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.148.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.147.1...@standardnotes/auth-server@1.148.0) (2023-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
* block file operations during transition ([#856](https://github.com/standardnotes/server/issues/856)) ([676cf36](https://github.com/standardnotes/server/commit/676cf36f8d769aa433880b2800f0457d06fbbf14))
|
||||
|
||||
## [1.147.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.147.0...@standardnotes/auth-server@1.147.1) (2023-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.147.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.4...@standardnotes/auth-server@1.147.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
## [1.146.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.3...@standardnotes/auth-server@1.146.4) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.146.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.2...@standardnotes/auth-server@1.146.3) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.146.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.1...@standardnotes/auth-server@1.146.2) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.146.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.0...@standardnotes/auth-server@1.146.1) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.146.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.145.0...@standardnotes/auth-server@1.146.0) (2023-09-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **auth:** register specs ([f9b1f40](https://github.com/standardnotes/server/commit/f9b1f40ddf2d733d106ea64b9a7c4b38c5ec43ce))
|
||||
|
||||
### Features
|
||||
|
||||
* remove transition mode from code ([5001496](https://github.com/standardnotes/server/commit/5001496c7bc1df9e20c2d88ebf52ed77f868110c))
|
||||
|
||||
# [1.145.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.144.0...@standardnotes/auth-server@1.145.0) (2023-09-22)
|
||||
|
||||
### Features
|
||||
|
||||
* remove user from all shared vaults upon account deletion ([#843](https://github.com/standardnotes/server/issues/843)) ([dc77ff3](https://github.com/standardnotes/server/commit/dc77ff3e45983d231bc9c132802428e77b4be431))
|
||||
|
||||
# [1.144.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.9...@standardnotes/auth-server@1.144.0) (2023-09-21)
|
||||
|
||||
### Features
|
||||
|
||||
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/server/issues/841)) ([230c96d](https://github.com/standardnotes/server/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
|
||||
|
||||
## [1.143.9](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.8...@standardnotes/auth-server@1.143.9) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.143.8](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.7...@standardnotes/auth-server@1.143.8) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.143.7](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.6...@standardnotes/auth-server@1.143.7) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.143.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.4...@standardnotes/auth-server@1.143.6) (2023-09-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddDesignatedSurvivor1695283870612 implements MigrationInterface {
|
||||
name = 'AddDesignatedSurvivor1695283870612'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE `auth_shared_vault_users` ADD `is_designated_survivor` tinyint NOT NULL DEFAULT 0',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `auth_shared_vault_users` DROP COLUMN `is_designated_survivor`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddDesignatedSurvivor1695283961201 implements MigrationInterface {
|
||||
name = 'AddDesignatedSurvivor1695283961201'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "user_uuid_on_auth_shared_vault_users"')
|
||||
await queryRunner.query('DROP INDEX "shared_vault_uuid_on_auth_shared_vault_users"')
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "temporary_auth_shared_vault_users" ("uuid" varchar PRIMARY KEY NOT NULL, "shared_vault_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36) NOT NULL, "permission" varchar(24) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL, "is_designated_survivor" boolean NOT NULL DEFAULT (0))',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO "temporary_auth_shared_vault_users"("uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp" FROM "auth_shared_vault_users"',
|
||||
)
|
||||
await queryRunner.query('DROP TABLE "auth_shared_vault_users"')
|
||||
await queryRunner.query('ALTER TABLE "temporary_auth_shared_vault_users" RENAME TO "auth_shared_vault_users"')
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "user_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("user_uuid") ',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "shared_vault_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("shared_vault_uuid") ',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "shared_vault_uuid_on_auth_shared_vault_users"')
|
||||
await queryRunner.query('DROP INDEX "user_uuid_on_auth_shared_vault_users"')
|
||||
await queryRunner.query('ALTER TABLE "auth_shared_vault_users" RENAME TO "temporary_auth_shared_vault_users"')
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "auth_shared_vault_users" ("uuid" varchar PRIMARY KEY NOT NULL, "shared_vault_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36) NOT NULL, "permission" varchar(24) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'INSERT INTO "auth_shared_vault_users"("uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp" FROM "temporary_auth_shared_vault_users"',
|
||||
)
|
||||
await queryRunner.query('DROP TABLE "temporary_auth_shared_vault_users"')
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "shared_vault_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("shared_vault_uuid") ',
|
||||
)
|
||||
await queryRunner.query(
|
||||
'CREATE INDEX "user_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("user_uuid") ',
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.143.6",
|
||||
"version": "1.148.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -19,7 +19,7 @@
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --fix --ext .ts",
|
||||
"pretest": "yarn lint && yarn build",
|
||||
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
|
||||
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
|
||||
"start": "yarn node dist/bin/server.js",
|
||||
"worker": "yarn node dist/bin/worker.js",
|
||||
"cleanup": "yarn node dist/bin/cleanup.js",
|
||||
|
||||
@@ -271,6 +271,8 @@ import { AddSharedVaultUser } from '../Domain/UseCase/AddSharedVaultUser/AddShar
|
||||
import { RemoveSharedVaultUser } from '../Domain/UseCase/RemoveSharedVaultUser/RemoveSharedVaultUser'
|
||||
import { UserAddedToSharedVaultEventHandler } from '../Domain/Handler/UserAddedToSharedVaultEventHandler'
|
||||
import { UserRemovedFromSharedVaultEventHandler } from '../Domain/Handler/UserRemovedFromSharedVaultEventHandler'
|
||||
import { DesignateSurvivor } from '../Domain/UseCase/DesignateSurvivor/DesignateSurvivor'
|
||||
import { UserDesignatedAsSurvivorInSharedVaultEventHandler } from '../Domain/Handler/UserDesignatedAsSurvivorInSharedVaultEventHandler'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
constructor(private mode: 'server' | 'worker' = 'server') {}
|
||||
@@ -367,7 +369,7 @@ export class ContainerConfigLoader {
|
||||
// Mapping
|
||||
container
|
||||
.bind<MapperInterface<SessionTrace, TypeORMSessionTrace>>(TYPES.Auth_SessionTracePersistenceMapper)
|
||||
.toConstantValue(new SessionTracePersistenceMapper())
|
||||
.toConstantValue(new SessionTracePersistenceMapper(container.get<TimerInterface>(TYPES.Auth_Timer)))
|
||||
container
|
||||
.bind<MapperInterface<Authenticator, TypeORMAuthenticator>>(TYPES.Auth_AuthenticatorPersistenceMapper)
|
||||
.toConstantValue(new AuthenticatorPersistenceMapper())
|
||||
@@ -456,8 +458,9 @@ export class ContainerConfigLoader {
|
||||
.bind<SessionTraceRepositoryInterface>(TYPES.Auth_SessionTraceRepository)
|
||||
.toConstantValue(
|
||||
new TypeORMSessionTraceRepository(
|
||||
container.get(TYPES.Auth_ORMSessionTraceRepository),
|
||||
container.get(TYPES.Auth_SessionTracePersistenceMapper),
|
||||
container.get<Repository<TypeORMSessionTrace>>(TYPES.Auth_ORMSessionTraceRepository),
|
||||
container.get<MapperInterface<SessionTrace, TypeORMSessionTrace>>(TYPES.Auth_SessionTracePersistenceMapper),
|
||||
container.get<TimerInterface>(TYPES.Auth_Timer),
|
||||
),
|
||||
)
|
||||
container
|
||||
@@ -593,9 +596,6 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind(TYPES.Auth_READONLY_USERS)
|
||||
.toConstantValue(env.get('READONLY_USERS', true) ? env.get('READONLY_USERS', true).split(',') : [])
|
||||
container
|
||||
.bind(TYPES.Auth_TRANSITION_MODE_ENABLED)
|
||||
.toConstantValue(env.get('TRANSITION_MODE_ENABLED', true) === 'true')
|
||||
|
||||
if (isConfiguredForInMemoryCache) {
|
||||
container
|
||||
@@ -957,6 +957,14 @@ export class ContainerConfigLoader {
|
||||
container.get<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<DesignateSurvivor>(TYPES.Auth_DesignateSurvivor)
|
||||
.toConstantValue(
|
||||
new DesignateSurvivor(
|
||||
container.get<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository),
|
||||
container.get<TimerInterface>(TYPES.Auth_Timer),
|
||||
),
|
||||
)
|
||||
|
||||
// Controller
|
||||
container
|
||||
@@ -998,7 +1006,15 @@ export class ContainerConfigLoader {
|
||||
container.bind<UserRegisteredEventHandler>(TYPES.Auth_UserRegisteredEventHandler).to(UserRegisteredEventHandler)
|
||||
container
|
||||
.bind<AccountDeletionRequestedEventHandler>(TYPES.Auth_AccountDeletionRequestedEventHandler)
|
||||
.to(AccountDeletionRequestedEventHandler)
|
||||
.toConstantValue(
|
||||
new AccountDeletionRequestedEventHandler(
|
||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||
container.get<SessionRepositoryInterface>(TYPES.Auth_SessionRepository),
|
||||
container.get<EphemeralSessionRepositoryInterface>(TYPES.Auth_EphemeralSessionRepository),
|
||||
container.get<RevokedSessionRepositoryInterface>(TYPES.Auth_RevokedSessionRepository),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SubscriptionPurchasedEventHandler>(TYPES.Auth_SubscriptionPurchasedEventHandler)
|
||||
.to(SubscriptionPurchasedEventHandler)
|
||||
@@ -1122,6 +1138,16 @@ export class ContainerConfigLoader {
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<UserDesignatedAsSurvivorInSharedVaultEventHandler>(
|
||||
TYPES.Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler,
|
||||
)
|
||||
.toConstantValue(
|
||||
new UserDesignatedAsSurvivorInSharedVaultEventHandler(
|
||||
container.get<DesignateSurvivor>(TYPES.Auth_DesignateSurvivor),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['USER_REGISTERED', container.get(TYPES.Auth_UserRegisteredEventHandler)],
|
||||
@@ -1156,6 +1182,10 @@ export class ContainerConfigLoader {
|
||||
['TRANSITION_STATUS_UPDATED', container.get(TYPES.Auth_TransitionStatusUpdatedEventHandler)],
|
||||
['USER_ADDED_TO_SHARED_VAULT', container.get(TYPES.Auth_UserAddedToSharedVaultEventHandler)],
|
||||
['USER_REMOVED_FROM_SHARED_VAULT', container.get(TYPES.Auth_UserRemovedFromSharedVaultEventHandler)],
|
||||
[
|
||||
'USER_DESIGNATED_AS_SURVIVOR_IN_SHARED_VAULT',
|
||||
container.get(TYPES.Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler),
|
||||
],
|
||||
])
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
|
||||
@@ -105,7 +105,6 @@ const TYPES = {
|
||||
Auth_U2F_EXPECTED_ORIGIN: Symbol.for('Auth_U2F_EXPECTED_ORIGIN'),
|
||||
Auth_U2F_REQUIRE_USER_VERIFICATION: Symbol.for('Auth_U2F_REQUIRE_USER_VERIFICATION'),
|
||||
Auth_READONLY_USERS: Symbol.for('Auth_READONLY_USERS'),
|
||||
Auth_TRANSITION_MODE_ENABLED: Symbol.for('Auth_TRANSITION_MODE_ENABLED'),
|
||||
// use cases
|
||||
Auth_AuthenticateUser: Symbol.for('Auth_AuthenticateUser'),
|
||||
Auth_AuthenticateRequest: Symbol.for('Auth_AuthenticateRequest'),
|
||||
@@ -161,6 +160,7 @@ const TYPES = {
|
||||
Auth_UpdateTransitionStatus: Symbol.for('Auth_UpdateTransitionStatus'),
|
||||
Auth_AddSharedVaultUser: Symbol.for('Auth_AddSharedVaultUser'),
|
||||
Auth_RemoveSharedVaultUser: Symbol.for('Auth_RemoveSharedVaultUser'),
|
||||
Auth_DesignateSurvivor: Symbol.for('Auth_DesignateSurvivor'),
|
||||
// Handlers
|
||||
Auth_UserRegisteredEventHandler: Symbol.for('Auth_UserRegisteredEventHandler'),
|
||||
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
||||
@@ -192,6 +192,9 @@ const TYPES = {
|
||||
Auth_TransitionStatusUpdatedEventHandler: Symbol.for('Auth_TransitionStatusUpdatedEventHandler'),
|
||||
Auth_UserAddedToSharedVaultEventHandler: Symbol.for('Auth_UserAddedToSharedVaultEventHandler'),
|
||||
Auth_UserRemovedFromSharedVaultEventHandler: Symbol.for('Auth_UserRemovedFromSharedVaultEventHandler'),
|
||||
Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler: Symbol.for(
|
||||
'Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler',
|
||||
),
|
||||
// Services
|
||||
Auth_DeviceDetector: Symbol.for('Auth_DeviceDetector'),
|
||||
Auth_SessionService: Symbol.for('Auth_SessionService'),
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { AccountDeletionRequestedEvent } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
import { EphemeralSession } from '../Session/EphemeralSession'
|
||||
import { EphemeralSessionRepositoryInterface } from '../Session/EphemeralSessionRepositoryInterface'
|
||||
import { RevokedSession } from '../Session/RevokedSession'
|
||||
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
|
||||
import { Session } from '../Session/Session'
|
||||
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
|
||||
import { User } from '../User/User'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
|
||||
|
||||
describe('AccountDeletionRequestedEventHandler', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
let sessionRepository: SessionRepositoryInterface
|
||||
let ephemeralSessionRepository: EphemeralSessionRepositoryInterface
|
||||
let revokedSessionRepository: RevokedSessionRepositoryInterface
|
||||
let logger: Logger
|
||||
let session: Session
|
||||
let ephemeralSession: EphemeralSession
|
||||
let revokedSession: RevokedSession
|
||||
let user: User
|
||||
let event: AccountDeletionRequestedEvent
|
||||
|
||||
const createHandler = () =>
|
||||
new AccountDeletionRequestedEventHandler(
|
||||
userRepository,
|
||||
sessionRepository,
|
||||
ephemeralSessionRepository,
|
||||
revokedSessionRepository,
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
user = {} as jest.Mocked<User>
|
||||
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
|
||||
userRepository.remove = jest.fn()
|
||||
|
||||
session = {
|
||||
uuid: '1-2-3',
|
||||
} as jest.Mocked<Session>
|
||||
|
||||
sessionRepository = {} as jest.Mocked<SessionRepositoryInterface>
|
||||
sessionRepository.findAllByUserUuid = jest.fn().mockReturnValue([session])
|
||||
sessionRepository.remove = jest.fn()
|
||||
|
||||
ephemeralSession = {
|
||||
uuid: '2-3-4',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
} as jest.Mocked<EphemeralSession>
|
||||
|
||||
ephemeralSessionRepository = {} as jest.Mocked<EphemeralSessionRepositoryInterface>
|
||||
ephemeralSessionRepository.findAllByUserUuid = jest.fn().mockReturnValue([ephemeralSession])
|
||||
ephemeralSessionRepository.deleteOne = jest.fn()
|
||||
|
||||
revokedSession = {
|
||||
uuid: '3-4-5',
|
||||
} as jest.Mocked<RevokedSession>
|
||||
|
||||
revokedSessionRepository = {} as jest.Mocked<RevokedSessionRepositoryInterface>
|
||||
revokedSessionRepository.findAllByUserUuid = jest.fn().mockReturnValue([revokedSession])
|
||||
revokedSessionRepository.remove = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
|
||||
event.createdAt = new Date(1)
|
||||
event.payload = {
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscriptionUuid: '2-3-4',
|
||||
roleNames: ['CORE_USER'],
|
||||
}
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.info = jest.fn()
|
||||
logger.warn = jest.fn()
|
||||
})
|
||||
|
||||
it('should remove a user', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(userRepository.remove).toHaveBeenCalledWith(user)
|
||||
})
|
||||
|
||||
it('should not remove a user with invalid uuid', async () => {
|
||||
event.payload.userUuid = 'invalid'
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(userRepository.remove).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not remove a user if one does not exist', async () => {
|
||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(userRepository.remove).not.toHaveBeenCalled()
|
||||
expect(sessionRepository.remove).not.toHaveBeenCalled()
|
||||
expect(revokedSessionRepository.remove).not.toHaveBeenCalled()
|
||||
expect(ephemeralSessionRepository.deleteOne).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should remove all user sessions', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(sessionRepository.remove).toHaveBeenCalledWith(session)
|
||||
expect(revokedSessionRepository.remove).toHaveBeenCalledWith(revokedSession)
|
||||
expect(ephemeralSessionRepository.deleteOne).toHaveBeenCalledWith('2-3-4', '00000000-0000-0000-0000-000000000000')
|
||||
})
|
||||
})
|
||||
@@ -1,22 +1,19 @@
|
||||
import { AccountDeletionRequestedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
import { Logger } from 'winston'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
|
||||
import { EphemeralSessionRepositoryInterface } from '../Session/EphemeralSessionRepositoryInterface'
|
||||
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
|
||||
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
|
||||
@inject(TYPES.Auth_SessionRepository) private sessionRepository: SessionRepositoryInterface,
|
||||
@inject(TYPES.Auth_EphemeralSessionRepository)
|
||||
private userRepository: UserRepositoryInterface,
|
||||
private sessionRepository: SessionRepositoryInterface,
|
||||
private ephemeralSessionRepository: EphemeralSessionRepositoryInterface,
|
||||
@inject(TYPES.Auth_RevokedSessionRepository) private revokedSessionRepository: RevokedSessionRepositoryInterface,
|
||||
@inject(TYPES.Auth_Logger) private logger: Logger,
|
||||
private revokedSessionRepository: RevokedSessionRepositoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: AccountDeletionRequestedEvent): Promise<void> {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { DomainEventHandlerInterface, UserDesignatedAsSurvivorInSharedVaultEvent } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
import { DesignateSurvivor } from '../UseCase/DesignateSurvivor/DesignateSurvivor'
|
||||
|
||||
export class UserDesignatedAsSurvivorInSharedVaultEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private designateSurvivorUseCase: DesignateSurvivor,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: UserDesignatedAsSurvivorInSharedVaultEvent): Promise<void> {
|
||||
const result = await this.designateSurvivorUseCase.execute({
|
||||
sharedVaultUuid: event.payload.sharedVaultUuid,
|
||||
userUuid: event.payload.userUuid,
|
||||
timestamp: event.payload.timestamp,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(
|
||||
`Failed designate survivor for user ${event.payload.userUuid} and shared vault ${
|
||||
event.payload.sharedVaultUuid
|
||||
}: ${result.getError()}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,12 @@ export class UserRemovedFromSharedVaultEventHandler implements DomainEventHandle
|
||||
) {}
|
||||
|
||||
async handle(event: UserRemovedFromSharedVaultEvent): Promise<void> {
|
||||
if (!event.payload.sharedVaultUuid) {
|
||||
this.logger.error(`Shared vault uuid is missing from event: ${JSON.stringify(event)}`)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const result = await this.removeSharedVaultUser.execute({
|
||||
userUuid: event.payload.userUuid,
|
||||
sharedVaultUuid: event.payload.sharedVaultUuid,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { SubscriptionPlanName, Uuid } from '@standardnotes/domain-core'
|
||||
import { SessionTrace } from './SessionTrace'
|
||||
|
||||
export interface SessionTraceRepositoryInterface {
|
||||
save(sessionTrace: SessionTrace): Promise<void>
|
||||
insert(sessionTrace: SessionTrace): Promise<void>
|
||||
removeExpiredBefore(date: Date): Promise<void>
|
||||
findOneByUserUuidAndDate(userUuid: Uuid, date: Date): Promise<SessionTrace | null>
|
||||
countByDate(date: Date): Promise<number>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { SharedVaultUser, Uuid } from '@standardnotes/domain-core'
|
||||
export interface SharedVaultUserRepositoryInterface {
|
||||
findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultUser | null>
|
||||
findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]>
|
||||
findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultUser | null>
|
||||
save(sharedVaultUser: SharedVaultUser): Promise<void>
|
||||
remove(sharedVault: SharedVaultUser): Promise<void>
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ export class AddSharedVaultUser implements UseCaseInterface<void> {
|
||||
sharedVaultUuid,
|
||||
permission,
|
||||
timestamps,
|
||||
isDesignatedSurvivor: false,
|
||||
})
|
||||
if (sharedVaultUserOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUserOrError.getError())
|
||||
|
||||
@@ -90,6 +90,7 @@ describe('CreateCrossServiceToken', () => {
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123456789, 123456789).getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
isDesignatedSurvivor: false,
|
||||
}).getValue(),
|
||||
])
|
||||
})
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { TokenEncoderInterface, ValetTokenData, ValetTokenOperation } from '@standardnotes/security'
|
||||
import { CreateValetToken } from './CreateValetToken'
|
||||
import { TransitionStatus } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { TokenEncoderInterface, ValetTokenData, ValetTokenOperation } from '@standardnotes/security'
|
||||
|
||||
import { CreateValetToken } from './CreateValetToken'
|
||||
import { UserSubscription } from '../../Subscription/UserSubscription'
|
||||
import { SubscriptionSettingServiceInterface } from '../../Setting/SubscriptionSettingServiceInterface'
|
||||
import { User } from '../../User/User'
|
||||
import { UserSubscriptionType } from '../../Subscription/UserSubscriptionType'
|
||||
import { SubscriptionSettingsAssociationServiceInterface } from '../../Setting/SubscriptionSettingsAssociationServiceInterface'
|
||||
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
|
||||
describe('CreateValetToken', () => {
|
||||
let tokenEncoder: TokenEncoderInterface<ValetTokenData>
|
||||
@@ -20,6 +23,7 @@ describe('CreateValetToken', () => {
|
||||
let regularSubscription: UserSubscription
|
||||
let sharedSubscription: UserSubscription
|
||||
let user: User
|
||||
let transitionStatusRepository: TransitionStatusRepositoryInterface
|
||||
|
||||
const createUseCase = () =>
|
||||
new CreateValetToken(
|
||||
@@ -29,6 +33,7 @@ describe('CreateValetToken', () => {
|
||||
userSubscriptionService,
|
||||
timer,
|
||||
valetTokenTTL,
|
||||
transitionStatusRepository,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -66,6 +71,11 @@ describe('CreateValetToken', () => {
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(100)
|
||||
|
||||
transitionStatusRepository = {} as jest.Mocked<TransitionStatusRepositoryInterface>
|
||||
transitionStatusRepository.getStatus = jest
|
||||
.fn()
|
||||
.mockReturnValue(TransitionStatus.create(TransitionStatus.STATUSES.Verified).getValue())
|
||||
})
|
||||
|
||||
it('should create a read valet token', async () => {
|
||||
@@ -166,6 +176,7 @@ describe('CreateValetToken', () => {
|
||||
{
|
||||
sharedSubscriptionUuid: undefined,
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
ongoingTransition: false,
|
||||
permittedOperation: 'write',
|
||||
permittedResources: [
|
||||
{
|
||||
@@ -206,6 +217,7 @@ describe('CreateValetToken', () => {
|
||||
{
|
||||
sharedSubscriptionUuid: '2-3-4',
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
ongoingTransition: false,
|
||||
permittedOperation: 'write',
|
||||
permittedResources: [
|
||||
{
|
||||
@@ -266,6 +278,7 @@ describe('CreateValetToken', () => {
|
||||
{
|
||||
sharedSubscriptionUuid: undefined,
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
ongoingTransition: false,
|
||||
permittedOperation: 'write',
|
||||
permittedResources: [
|
||||
{
|
||||
|
||||
@@ -13,6 +13,8 @@ import { CreateValetTokenDTO } from './CreateValetTokenDTO'
|
||||
import { SubscriptionSettingsAssociationServiceInterface } from '../../Setting/SubscriptionSettingsAssociationServiceInterface'
|
||||
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
||||
import { CreateValetTokenPayload } from '../../ValetToken/CreateValetTokenPayload'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
import { TransitionStatus } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class CreateValetToken implements UseCaseInterface {
|
||||
@@ -25,6 +27,8 @@ export class CreateValetToken implements UseCaseInterface {
|
||||
@inject(TYPES.Auth_UserSubscriptionService) private userSubscriptionService: UserSubscriptionServiceInterface,
|
||||
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
||||
@inject(TYPES.Auth_VALET_TOKEN_TTL) private valetTokenTTL: number,
|
||||
@inject(TYPES.Auth_TransitionStatusRepository)
|
||||
private transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: CreateValetTokenDTO): Promise<CreateValetTokenResponseData> {
|
||||
@@ -83,6 +87,8 @@ export class CreateValetToken implements UseCaseInterface {
|
||||
sharedSubscriptionUuid = sharedSubscription.uuid
|
||||
}
|
||||
|
||||
const transitionStatus = await this.transitionStatusRepository.getStatus(userUuid, 'items')
|
||||
|
||||
const tokenData: ValetTokenData = {
|
||||
userUuid: dto.userUuid,
|
||||
permittedOperation: dto.operation,
|
||||
@@ -91,6 +97,7 @@ export class CreateValetToken implements UseCaseInterface {
|
||||
uploadBytesLimit,
|
||||
sharedSubscriptionUuid,
|
||||
regularSubscriptionUuid: regularSubscription.uuid,
|
||||
ongoingTransition: transitionStatus?.value === TransitionStatus.STATUSES.InProgress,
|
||||
}
|
||||
|
||||
const valetToken = this.tokenEncoder.encodeExpirableToken(tokenData, this.valetTokenTTL)
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
import { SharedVaultUser, SharedVaultUserPermission, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { DesignateSurvivor } from './DesignateSurvivor'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||
|
||||
describe('DesignateSurvivor', () => {
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
let sharedVaultUser: SharedVaultUser
|
||||
let timer: TimerInterface
|
||||
|
||||
const createUseCase = () => new DesignateSurvivor(sharedVaultUserRepository, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123)
|
||||
|
||||
sharedVaultUser = SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
isDesignatedSurvivor: false,
|
||||
}).getValue()
|
||||
|
||||
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(null)
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
|
||||
sharedVaultUserRepository.save = jest.fn()
|
||||
})
|
||||
|
||||
it('should fail if shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: 'invalid',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
timestamp: 123,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
})
|
||||
|
||||
it('should fail if user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userUuid: 'invalid',
|
||||
timestamp: 123,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
})
|
||||
|
||||
it('should fail if shared vault user is not found', async () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
timestamp: 123,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
})
|
||||
|
||||
it('should designate a survivor if the user is a member', async () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
timestamp: 123,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(sharedVaultUser.props.isDesignatedSurvivor).toBe(true)
|
||||
expect(sharedVaultUserRepository.save).toBeCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should designate a survivor if the user is a member and there is already a survivor', async () => {
|
||||
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
isDesignatedSurvivor: true,
|
||||
}).getValue(),
|
||||
)
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
timestamp: 123,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(sharedVaultUser.props.isDesignatedSurvivor).toBe(true)
|
||||
expect(sharedVaultUserRepository.save).toBeCalledTimes(2)
|
||||
})
|
||||
|
||||
it('should fail if the timestamp is older than the existing survivor', async () => {
|
||||
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
isDesignatedSurvivor: true,
|
||||
}).getValue(),
|
||||
)
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
timestamp: 122,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
})
|
||||
|
||||
it('should do nothing if the user is already a survivor', async () => {
|
||||
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
isDesignatedSurvivor: true,
|
||||
}).getValue(),
|
||||
)
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
timestamp: 200,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,66 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { Result, Timestamps, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { DesignateSurvivorDTO } from './DesignateSurvivorDTO'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||
|
||||
export class DesignateSurvivor implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
|
||||
private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: DesignateSurvivorDTO): Promise<Result<void>> {
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const existingSurvivor =
|
||||
await this.sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid)
|
||||
|
||||
if (existingSurvivor) {
|
||||
if (existingSurvivor.props.timestamps.updatedAt > dto.timestamp) {
|
||||
return Result.fail(
|
||||
'Cannot designate survivor to a previous version of the shared vault. Most probably a race condition.',
|
||||
)
|
||||
}
|
||||
if (existingSurvivor.props.userUuid.value === userUuid.value) {
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
existingSurvivor.props.isDesignatedSurvivor = false
|
||||
existingSurvivor.props.timestamps = Timestamps.create(
|
||||
existingSurvivor.props.timestamps.createdAt,
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
).getValue()
|
||||
|
||||
await this.sharedVaultUserRepository.save(existingSurvivor)
|
||||
}
|
||||
|
||||
const toBeDesignatedAsASurvivor = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
|
||||
userUuid,
|
||||
sharedVaultUuid,
|
||||
})
|
||||
if (!toBeDesignatedAsASurvivor) {
|
||||
return Result.fail('User is not a member of the shared vault')
|
||||
}
|
||||
|
||||
toBeDesignatedAsASurvivor.props.isDesignatedSurvivor = true
|
||||
toBeDesignatedAsASurvivor.props.timestamps = Timestamps.create(
|
||||
toBeDesignatedAsASurvivor.props.timestamps.createdAt,
|
||||
this.timer.getTimestampInMicroseconds(),
|
||||
).getValue()
|
||||
|
||||
await this.sharedVaultUserRepository.save(toBeDesignatedAsASurvivor)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface DesignateSurvivorDTO {
|
||||
sharedVaultUuid: string
|
||||
userUuid: string
|
||||
timestamp: number
|
||||
}
|
||||
@@ -21,19 +21,9 @@ describe('Register', () => {
|
||||
let user: User
|
||||
let crypter: CrypterInterface
|
||||
let timer: TimerInterface
|
||||
let transitionModeEnabled = false
|
||||
|
||||
const createUseCase = () =>
|
||||
new Register(
|
||||
userRepository,
|
||||
roleRepository,
|
||||
authResponseFactory,
|
||||
crypter,
|
||||
false,
|
||||
settingService,
|
||||
timer,
|
||||
transitionModeEnabled,
|
||||
)
|
||||
new Register(userRepository, roleRepository, authResponseFactory, crypter, false, settingService, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
@@ -94,45 +84,7 @@ describe('Register', () => {
|
||||
expect(settingService.applyDefaultSettingsUponRegistration).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should register a new user with default role', async () => {
|
||||
const role = new Role()
|
||||
role.name = 'role1'
|
||||
roleRepository.findOneByName = jest.fn().mockReturnValue(role)
|
||||
|
||||
expect(
|
||||
await createUseCase().execute({
|
||||
email: 'test@test.te',
|
||||
password: 'asdzxc',
|
||||
updatedWithUserAgent: 'Mozilla',
|
||||
apiVersion: '20200115',
|
||||
ephemeralSession: false,
|
||||
version: '004',
|
||||
pwCost: 11,
|
||||
pwSalt: 'qweqwe',
|
||||
pwNonce: undefined,
|
||||
}),
|
||||
).toEqual({ success: true, authResponse: { foo: 'bar' } })
|
||||
|
||||
expect(userRepository.save).toHaveBeenCalledWith({
|
||||
email: 'test@test.te',
|
||||
encryptedPassword: expect.any(String),
|
||||
encryptedServerKey: 'test',
|
||||
serverEncryptionVersion: 1,
|
||||
pwCost: 11,
|
||||
pwNonce: undefined,
|
||||
pwSalt: 'qweqwe',
|
||||
updatedWithUserAgent: 'Mozilla',
|
||||
uuid: expect.any(String),
|
||||
version: '004',
|
||||
createdAt: new Date(1),
|
||||
updatedAt: new Date(1),
|
||||
roles: Promise.resolve([role]),
|
||||
})
|
||||
})
|
||||
|
||||
it('should register a new user with default role and transition role', async () => {
|
||||
transitionModeEnabled = true
|
||||
|
||||
const role = new Role()
|
||||
role.name = RoleName.NAMES.CoreUser
|
||||
|
||||
@@ -249,7 +201,6 @@ describe('Register', () => {
|
||||
true,
|
||||
settingService,
|
||||
timer,
|
||||
transitionModeEnabled,
|
||||
).execute({
|
||||
email: 'test@test.te',
|
||||
password: 'asdzxc',
|
||||
|
||||
@@ -27,7 +27,6 @@ export class Register implements UseCaseInterface {
|
||||
@inject(TYPES.Auth_DISABLE_USER_REGISTRATION) private disableUserRegistration: boolean,
|
||||
@inject(TYPES.Auth_SettingService) private settingService: SettingServiceInterface,
|
||||
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
||||
@inject(TYPES.Auth_TRANSITION_MODE_ENABLED) private transitionModeEnabled: boolean,
|
||||
) {}
|
||||
|
||||
async execute(dto: RegisterDTO): Promise<RegisterResponse> {
|
||||
@@ -78,11 +77,9 @@ export class Register implements UseCaseInterface {
|
||||
if (defaultRole) {
|
||||
roles.push(defaultRole)
|
||||
}
|
||||
if (this.transitionModeEnabled) {
|
||||
const transitionRole = await this.roleRepository.findOneByName(RoleName.NAMES.TransitionUser)
|
||||
if (transitionRole) {
|
||||
roles.push(transitionRole)
|
||||
}
|
||||
const transitionRole = await this.roleRepository.findOneByName(RoleName.NAMES.TransitionUser)
|
||||
if (transitionRole) {
|
||||
roles.push(transitionRole)
|
||||
}
|
||||
user.roles = Promise.resolve(roles)
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ describe('RemoveSharedVaultUser', () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<SharedVaultUser>)
|
||||
sharedVaultUserRepository.findByUserUuid = jest.fn().mockReturnValue([{} as jest.Mocked<SharedVaultUser>])
|
||||
sharedVaultUserRepository.remove = jest.fn()
|
||||
})
|
||||
|
||||
@@ -28,6 +29,17 @@ describe('RemoveSharedVaultUser', () => {
|
||||
expect(sharedVaultUserRepository.remove).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should remove all shared vault users', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(sharedVaultUserRepository.remove).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail when user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
|
||||
@@ -13,21 +13,31 @@ export class RemoveSharedVaultUser implements UseCaseInterface<void> {
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const sharedVaultUser = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
|
||||
userUuid,
|
||||
sharedVaultUuid,
|
||||
})
|
||||
if (!sharedVaultUser) {
|
||||
return Result.fail('Shared vault user not found')
|
||||
let sharedVaultUuid: Uuid | undefined
|
||||
if (dto.sharedVaultUuid !== undefined) {
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
}
|
||||
|
||||
await this.sharedVaultUserRepository.remove(sharedVaultUser)
|
||||
if (sharedVaultUuid) {
|
||||
const sharedVaultUser = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
|
||||
userUuid,
|
||||
sharedVaultUuid,
|
||||
})
|
||||
if (!sharedVaultUser) {
|
||||
return Result.fail('Shared vault user not found')
|
||||
}
|
||||
|
||||
await this.sharedVaultUserRepository.remove(sharedVaultUser)
|
||||
} else {
|
||||
const sharedVaultUsers = await this.sharedVaultUserRepository.findByUserUuid(userUuid)
|
||||
for (const sharedVaultUser of sharedVaultUsers) {
|
||||
await this.sharedVaultUserRepository.remove(sharedVaultUser)
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface RemoveSharedVaultUserDTO {
|
||||
sharedVaultUuid: string
|
||||
sharedVaultUuid?: string
|
||||
userUuid: string
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('TraceSession', () => {
|
||||
beforeEach(() => {
|
||||
sessionTraceRepository = {} as jest.Mocked<SessionTraceRepositoryInterface>
|
||||
sessionTraceRepository.findOneByUserUuidAndDate = jest.fn().mockReturnValue(null)
|
||||
sessionTraceRepository.save = jest.fn()
|
||||
sessionTraceRepository.insert = jest.fn()
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getUTCDateNDaysAhead = jest.fn().mockReturnValue(new Date())
|
||||
@@ -30,7 +30,7 @@ describe('TraceSession', () => {
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(result.getValue().props.userUuid.value).toEqual('0702b137-4f5c-438a-915e-8f8b46572ce5')
|
||||
expect(sessionTraceRepository.save).toHaveBeenCalledTimes(1)
|
||||
expect(sessionTraceRepository.insert).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should not save a session trace if one already exists for the same user and date', async () => {
|
||||
@@ -43,7 +43,7 @@ describe('TraceSession', () => {
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
|
||||
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return an error if userUuid is invalid', async () => {
|
||||
@@ -54,7 +54,7 @@ describe('TraceSession', () => {
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
|
||||
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return an error if username is invalid', async () => {
|
||||
@@ -65,7 +65,7 @@ describe('TraceSession', () => {
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
|
||||
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return an error if subscriptionPlanName is invalid', async () => {
|
||||
@@ -76,7 +76,7 @@ describe('TraceSession', () => {
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
|
||||
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not save a session trace if creating of the session trace fails', async () => {
|
||||
@@ -90,7 +90,7 @@ describe('TraceSession', () => {
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(sessionTraceRepository.save).not.toHaveBeenCalled()
|
||||
expect(sessionTraceRepository.insert).not.toHaveBeenCalled()
|
||||
|
||||
mock.mockRestore()
|
||||
})
|
||||
|
||||
@@ -53,7 +53,7 @@ export class TraceSession implements UseCaseInterface<SessionTrace> {
|
||||
}
|
||||
const sessionTrace = sessionTraceOrError.getValue()
|
||||
|
||||
await this.sessionTraceRepository.save(sessionTrace)
|
||||
await this.sessionTraceRepository.insert(sessionTrace)
|
||||
|
||||
return Result.ok<SessionTrace>(sessionTrace)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { MapperInterface, SubscriptionPlanName, Uuid } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { Repository } from 'typeorm'
|
||||
|
||||
import { SessionTrace } from '../../Domain/Session/SessionTrace'
|
||||
import { SessionTraceRepositoryInterface } from '../../Domain/Session/SessionTraceRepositoryInterface'
|
||||
import { TypeORMSessionTrace } from './TypeORMSessionTrace'
|
||||
@@ -8,13 +10,14 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
|
||||
constructor(
|
||||
private ormRepository: Repository<TypeORMSessionTrace>,
|
||||
private mapper: MapperInterface<SessionTrace, TypeORMSessionTrace>,
|
||||
private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async countByDateAndSubscriptionPlanName(date: Date, subscriptionPlanName: SubscriptionPlanName): Promise<number> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('trace')
|
||||
.where('trace.creation_date = :creationDate', {
|
||||
creationDate: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`,
|
||||
creationDate: this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD'),
|
||||
})
|
||||
.andWhere('trace.subscription_plan_name = :subscriptionPlanName', {
|
||||
subscriptionPlanName: subscriptionPlanName.value,
|
||||
@@ -26,7 +29,7 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('trace')
|
||||
.where('trace.creation_date = :creationDate', {
|
||||
creationDate: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`,
|
||||
creationDate: this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD'),
|
||||
})
|
||||
.getCount()
|
||||
}
|
||||
@@ -44,7 +47,7 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
|
||||
.createQueryBuilder('trace')
|
||||
.where('trace.user_uuid = :userUuid AND trace.creation_date = :creationDate', {
|
||||
userUuid: userUuid.value,
|
||||
creationDate: `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`,
|
||||
creationDate: this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD'),
|
||||
})
|
||||
.getOne()
|
||||
|
||||
@@ -55,9 +58,9 @@ export class TypeORMSessionTraceRepository implements SessionTraceRepositoryInte
|
||||
return this.mapper.toDomain(typeOrm)
|
||||
}
|
||||
|
||||
async save(sessionTrace: SessionTrace): Promise<void> {
|
||||
async insert(sessionTrace: SessionTrace): Promise<void> {
|
||||
const persistence = this.mapper.toProjection(sessionTrace)
|
||||
|
||||
await this.ormRepository.save(persistence)
|
||||
await this.ormRepository.insert(persistence)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,13 @@ export class TypeORMSharedVaultUser {
|
||||
})
|
||||
declare permission: string
|
||||
|
||||
@Column({
|
||||
name: 'is_designated_survivor',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
})
|
||||
declare isDesignatedSurvivor: boolean
|
||||
|
||||
@Column({
|
||||
name: 'created_at_timestamp',
|
||||
type: 'bigint',
|
||||
|
||||
@@ -10,6 +10,24 @@ export class TypeORMSharedVaultUserRepository implements SharedVaultUserReposito
|
||||
private mapper: MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>,
|
||||
) {}
|
||||
|
||||
async findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultUser | null> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_user')
|
||||
.where('shared_vault_user.shared_vault_uuid = :sharedVaultUuid', {
|
||||
sharedVaultUuid: sharedVaultUuid.value,
|
||||
})
|
||||
.andWhere('shared_vault_user.is_designated_survivor = :isDesignatedSurvivor', {
|
||||
isDesignatedSurvivor: true,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
if (persistence === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.mapper.toDomain(persistence)
|
||||
}
|
||||
|
||||
async findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_user')
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { MapperInterface, SubscriptionPlanName, UniqueEntityId, Username, Uuid } from '@standardnotes/domain-core'
|
||||
import { SessionTrace } from '../Domain/Session/SessionTrace'
|
||||
import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
export class SessionTracePersistenceMapper implements MapperInterface<SessionTrace, TypeORMSessionTrace> {
|
||||
constructor(private timer: TimerInterface) {}
|
||||
|
||||
toDomain(projection: TypeORMSessionTrace): SessionTrace {
|
||||
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
@@ -50,11 +53,7 @@ export class SessionTracePersistenceMapper implements MapperInterface<SessionTra
|
||||
typeOrm.username = domain.props.username.value
|
||||
typeOrm.subscriptionPlanName = domain.props.subscriptionPlanName ? domain.props.subscriptionPlanName.value : null
|
||||
typeOrm.createdAt = domain.props.createdAt
|
||||
typeOrm.creationDate = new Date(
|
||||
domain.props.createdAt.getFullYear(),
|
||||
domain.props.createdAt.getMonth(),
|
||||
domain.props.createdAt.getDate(),
|
||||
)
|
||||
typeOrm.creationDate = new Date(this.timer.convertDateToFormattedString(domain.props.createdAt, 'YYYY-MM-DD'))
|
||||
typeOrm.expiresAt = domain.props.expiresAt
|
||||
|
||||
return typeOrm
|
||||
|
||||
@@ -41,6 +41,7 @@ export class SharedVaultUserPersistenceMapper implements MapperInterface<SharedV
|
||||
sharedVaultUuid,
|
||||
permission,
|
||||
timestamps,
|
||||
isDesignatedSurvivor: !!projection.isDesignatedSurvivor,
|
||||
},
|
||||
new UniqueEntityId(projection.uuid),
|
||||
)
|
||||
@@ -61,6 +62,7 @@ export class SharedVaultUserPersistenceMapper implements MapperInterface<SharedV
|
||||
typeorm.permission = domain.props.permission.value
|
||||
typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
|
||||
typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
|
||||
typeorm.isDesignatedSurvivor = !!domain.props.isDesignatedSurvivor
|
||||
|
||||
return typeorm
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.51.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.50.4...@standardnotes/common@1.51.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
## [1.50.4](https://github.com/standardnotes/server/compare/@standardnotes/common@1.50.3...@standardnotes/common@1.50.4) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/common
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/common",
|
||||
"version": "1.50.4",
|
||||
"version": "1.51.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -20,7 +20,7 @@
|
||||
"clean": "rm -fr dist",
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"test": "jest spec --coverage"
|
||||
"test": "jest --coverage --no-cache"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.1",
|
||||
|
||||
@@ -3,6 +3,54 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.34.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.34.0...@standardnotes/domain-core@1.34.1) (2023-09-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* removing items in a vault and notifying about designated survivor ([#855](https://github.com/standardnotes/server/issues/855)) ([1d06ffe](https://github.com/standardnotes/server/commit/1d06ffe9d51722ada7baa040e1d5ed351fc28f39))
|
||||
|
||||
# [1.34.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.2...@standardnotes/domain-core@1.34.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
## [1.33.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.1...@standardnotes/domain-core@1.33.2) (2023-09-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-core:** notification paylod to string casting ([a02a287](https://github.com/standardnotes/server/commit/a02a28774b6d500200043faefb9ebac3719e7661))
|
||||
|
||||
## [1.33.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.0...@standardnotes/domain-core@1.33.1) (2023-09-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* refactor the structure of notifications ([#853](https://github.com/standardnotes/server/issues/853)) ([cebab59](https://github.com/standardnotes/server/commit/cebab59a026c6868886e0945787a8ddb0442fbc3))
|
||||
|
||||
# [1.33.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.32.0...@standardnotes/domain-core@1.33.0) (2023-09-21)
|
||||
|
||||
### Features
|
||||
|
||||
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/server/issues/841)) ([230c96d](https://github.com/standardnotes/server/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
|
||||
|
||||
# [1.32.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.31.0...@standardnotes/domain-core@1.32.0) (2023-09-20)
|
||||
|
||||
### Features
|
||||
|
||||
* **syncing-server:** distinct notifications upon user removal from shared vault ([#840](https://github.com/standardnotes/server/issues/840)) ([41e2136](https://github.com/standardnotes/server/commit/41e2136bc07312974701a70652528d304105e0f9))
|
||||
|
||||
# [1.31.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.30.1...@standardnotes/domain-core@1.31.0) (2023-09-20)
|
||||
|
||||
### Features
|
||||
|
||||
* **syncing-server:** add notification for user upon declined shared vault invitation ([#837](https://github.com/standardnotes/server/issues/837)) ([31e7aaf](https://github.com/standardnotes/server/commit/31e7aaf253029a951d8b943d6cffd655cd5ca765))
|
||||
|
||||
## [1.30.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.30.0...@standardnotes/domain-core@1.30.1) (2023-09-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-core:** allow any version and variant of the UUID format ([de081fe](https://github.com/standardnotes/server/commit/de081fe78658bdd36c8c5d86b70a16a2880aa3d1))
|
||||
|
||||
# [1.30.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.29.0...@standardnotes/domain-core@1.30.0) (2023-09-18)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.30.0",
|
||||
"version": "1.34.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -21,7 +21,7 @@
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --ext .ts --fix",
|
||||
"test": "jest spec --coverage --passWithNoTests"
|
||||
"test": "jest --coverage --no-cache --passWithNoTests"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "^9.0.0"
|
||||
|
||||
@@ -5,6 +5,7 @@ describe('Validator', () => {
|
||||
'2221101c-1da9-4d2b-9b32-b8be2a8d1c82',
|
||||
'c08f2f29-a74b-42b4-aefd-98af9832391c',
|
||||
'b453fa64-1493-443b-b5bb-bca7b9c696c7',
|
||||
'fa7350b3-77cf-8c0c-40b2-6046b13254fe',
|
||||
]
|
||||
|
||||
const invalidUuids = [
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Result } from './Result'
|
||||
|
||||
export class Validator {
|
||||
private static readonly UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
||||
private static readonly UUID_ANY_VERSION_AND_VARIANT_REGEX =
|
||||
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
||||
private static readonly EMAIL_REGEX =
|
||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
|
||||
static isValidUuid(value: string): Result<string> {
|
||||
const matchesUuidRegex = String(value).toLowerCase().match(Validator.UUID_REGEX) !== null
|
||||
const matchesUuidRegex = String(value).toLowerCase().match(Validator.UUID_ANY_VERSION_AND_VARIANT_REGEX) !== null
|
||||
if (matchesUuidRegex) {
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Result } from '../Core/Result'
|
||||
import { NotificationPayloadProps } from './NotificationPayloadProps'
|
||||
import { NotificationType } from './NotificationType'
|
||||
import { Uuid } from '../Common/Uuid'
|
||||
import { NotificationPayloadIdentifierType } from './NotificationPayloadIdentifierType'
|
||||
|
||||
export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
|
||||
private constructor(props: NotificationPayloadProps) {
|
||||
@@ -14,8 +15,10 @@ export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
|
||||
return JSON.stringify({
|
||||
version: this.props.version,
|
||||
type: this.props.type.value,
|
||||
sharedVaultUuid: this.props.sharedVaultUuid.value,
|
||||
itemUuid: this.props.itemUuid ? this.props.itemUuid.value : undefined,
|
||||
primaryIdentifier: this.props.primaryIdentifier.value,
|
||||
primaryIndentifierType: this.props.primaryIndentifierType.value,
|
||||
secondaryIdentifier: this.props.secondaryIdentifier?.value,
|
||||
secondaryIdentifierType: this.props.secondaryIdentifierType?.value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -29,26 +32,43 @@ export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
|
||||
}
|
||||
const type = typeOrError.getValue()
|
||||
|
||||
const sharedVaultUuidOrError = Uuid.create(props.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(sharedVaultUuidOrError.getError())
|
||||
const primaryIdentifierOrError = Uuid.create(props.primaryIdentifier)
|
||||
if (primaryIdentifierOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(primaryIdentifierOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
const primaryIdentifier = primaryIdentifierOrError.getValue()
|
||||
|
||||
let itemUuid: Uuid | undefined = undefined
|
||||
if (props.itemUuid) {
|
||||
const itemUuidOrError = Uuid.create(props.itemUuid)
|
||||
if (itemUuidOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(itemUuidOrError.getError())
|
||||
const primaryIndentifierTypeOrError = NotificationPayloadIdentifierType.create(props.primaryIndentifierType)
|
||||
if (primaryIndentifierTypeOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(primaryIndentifierTypeOrError.getError())
|
||||
}
|
||||
const primaryIndentifierType = primaryIndentifierTypeOrError.getValue()
|
||||
|
||||
let secondaryIdentifier: Uuid | undefined
|
||||
if (props.secondaryIdentifier) {
|
||||
const secondaryIdentifierOrError = Uuid.create(props.secondaryIdentifier)
|
||||
if (secondaryIdentifierOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(secondaryIdentifierOrError.getError())
|
||||
}
|
||||
itemUuid = itemUuidOrError.getValue()
|
||||
secondaryIdentifier = secondaryIdentifierOrError.getValue()
|
||||
}
|
||||
|
||||
let secondaryIdentifierType: NotificationPayloadIdentifierType | undefined
|
||||
if (props.secondaryIdentifierType) {
|
||||
const secondaryIdentifierTypeOrError = NotificationPayloadIdentifierType.create(props.secondaryIdentifierType)
|
||||
if (secondaryIdentifierTypeOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(secondaryIdentifierTypeOrError.getError())
|
||||
}
|
||||
secondaryIdentifierType = secondaryIdentifierTypeOrError.getValue()
|
||||
}
|
||||
|
||||
return NotificationPayload.create({
|
||||
version: props.version,
|
||||
type,
|
||||
sharedVaultUuid,
|
||||
itemUuid,
|
||||
primaryIdentifier,
|
||||
primaryIndentifierType,
|
||||
secondaryIdentifier,
|
||||
secondaryIdentifierType,
|
||||
})
|
||||
} catch (error) {
|
||||
return Result.fail<NotificationPayload>((error as Error).message)
|
||||
@@ -57,7 +77,7 @@ export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
|
||||
|
||||
static create(props: NotificationPayloadProps): Result<NotificationPayload> {
|
||||
if (
|
||||
props.itemUuid === undefined &&
|
||||
props.secondaryIdentifier === undefined &&
|
||||
props.type.equals(NotificationType.create(NotificationType.TYPES.SharedVaultItemRemoved).getValue())
|
||||
) {
|
||||
return Result.fail<NotificationPayload>(
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { NotificationPayloadIdentifierTypeProps } from './NotificationPayloadIdentifierTypeProps'
|
||||
|
||||
export class NotificationPayloadIdentifierType extends ValueObject<NotificationPayloadIdentifierTypeProps> {
|
||||
static readonly TYPES = {
|
||||
SharedVaultUuid: 'shared_vault_uuid',
|
||||
UserUuid: 'user_uuid',
|
||||
SharedVaultInviteUuid: 'shared_vault_invite_uuid',
|
||||
ItemUuid: 'item_uuid',
|
||||
}
|
||||
|
||||
private constructor(props: NotificationPayloadIdentifierTypeProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
static create(type: string): Result<NotificationPayloadIdentifierType> {
|
||||
if (!Object.values(this.TYPES).includes(type)) {
|
||||
return Result.fail<NotificationPayloadIdentifierType>(`Invalid notification payload identifier type: ${type}`)
|
||||
}
|
||||
|
||||
return Result.ok<NotificationPayloadIdentifierType>(new NotificationPayloadIdentifierType({ value: type }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface NotificationPayloadIdentifierTypeProps {
|
||||
value: string
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Uuid } from '../Common/Uuid'
|
||||
import { NotificationPayloadIdentifierType } from './NotificationPayloadIdentifierType'
|
||||
import { NotificationType } from './NotificationType'
|
||||
|
||||
export interface NotificationPayloadProps {
|
||||
type: NotificationType
|
||||
sharedVaultUuid: Uuid
|
||||
primaryIdentifier: Uuid
|
||||
primaryIndentifierType: NotificationPayloadIdentifierType
|
||||
secondaryIdentifier?: Uuid
|
||||
secondaryIdentifierType?: NotificationPayloadIdentifierType
|
||||
version: string
|
||||
itemUuid?: Uuid
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@ import { NotificationTypeProps } from './NotificationTypeProps'
|
||||
export class NotificationType extends ValueObject<NotificationTypeProps> {
|
||||
static readonly TYPES = {
|
||||
SharedVaultItemRemoved: 'shared_vault_item_removed',
|
||||
RemovedFromSharedVault: 'removed_from_shared_vault',
|
||||
SelfRemovedFromSharedVault: 'self_removed_from_shared_vault',
|
||||
UserRemovedFromSharedVault: 'user_removed_from_shared_vault',
|
||||
UserDesignatedAsSurvivor: 'user_designated_as_survivor',
|
||||
UserAddedToSharedVault: 'user_added_to_shared_vault',
|
||||
SharedVaultInviteCanceled: 'shared_vault_invite_canceled',
|
||||
SharedVaultFileUploaded: 'shared_vault_file_uploaded',
|
||||
SharedVaultFileRemoved: 'shared_vault_file_removed',
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ describe('SharedVaultUser', () => {
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123456789, 123456789).getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
isDesignatedSurvivor: false,
|
||||
})
|
||||
|
||||
expect(entityOrError.isFailed()).toBeFalsy()
|
||||
|
||||
@@ -6,5 +6,6 @@ export interface SharedVaultUserProps {
|
||||
sharedVaultUuid: Uuid
|
||||
userUuid: Uuid
|
||||
permission: SharedVaultUserPermission
|
||||
isDesignatedSurvivor: boolean
|
||||
timestamps: Timestamps
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ export * from './Env/AbstractEnv'
|
||||
export * from './Mapping/MapperInterface'
|
||||
|
||||
export * from './Notification/NotificationPayload'
|
||||
export * from './Notification/NotificationPayloadIdentifierType'
|
||||
export * from './Notification/NotificationPayloadIdentifierTypeProps'
|
||||
export * from './Notification/NotificationPayloadProps'
|
||||
export * from './Notification/NotificationType'
|
||||
export * from './Notification/NotificationTypeProps'
|
||||
|
||||
@@ -3,6 +3,32 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.13.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.13.0...@standardnotes/domain-events-infra@1.13.1) (2023-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
# [1.13.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.34...@standardnotes/domain-events-infra@1.13.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
## [1.12.34](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.33...@standardnotes/domain-events-infra@1.12.34) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.33](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.32...@standardnotes/domain-events-infra@1.12.33) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.32](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.31...@standardnotes/domain-events-infra@1.12.32) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.31](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.30...@standardnotes/domain-events-infra@1.12.31) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.30](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.29...@standardnotes/domain-events-infra@1.12.30) (2023-09-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.12.30",
|
||||
"version": "1.13.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -21,7 +21,7 @@
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --ext .ts --fix",
|
||||
"test": "jest spec --coverage"
|
||||
"test": "jest --coverage --no-cache"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sns": "^3.332.0",
|
||||
|
||||
@@ -3,6 +3,40 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.131.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.131.0...@standardnotes/domain-events@2.131.1) (2023-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events
|
||||
|
||||
# [2.131.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.130.0...@standardnotes/domain-events@2.131.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
# [2.130.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.129.1...@standardnotes/domain-events@2.130.0) (2023-09-25)
|
||||
|
||||
### Features
|
||||
|
||||
* remove shared vault files upon shared vault removal ([#852](https://github.com/standardnotes/server/issues/852)) ([7b1eec2](https://github.com/standardnotes/server/commit/7b1eec21e54330bebbeebb80cec3ba4284112aab))
|
||||
|
||||
## [2.129.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.129.0...@standardnotes/domain-events@2.129.1) (2023-09-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remember paging progress on transitioning ([1d73e4f](https://github.com/standardnotes/server/commit/1d73e4f0720d41029af4d4b2b7a10d101add6c82))
|
||||
|
||||
# [2.129.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.128.0...@standardnotes/domain-events@2.129.0) (2023-09-21)
|
||||
|
||||
### Features
|
||||
|
||||
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/server/issues/841)) ([230c96d](https://github.com/standardnotes/server/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
|
||||
|
||||
# [2.128.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.127.0...@standardnotes/domain-events@2.128.0) (2023-09-20)
|
||||
|
||||
### Features
|
||||
|
||||
* add unassigning items and revisions upon shared vault removal ([#839](https://github.com/standardnotes/server/issues/839)) ([378eced](https://github.com/standardnotes/server/commit/378ecedfcc4fb23475c2329fb37479edb3b48a39))
|
||||
|
||||
# [2.127.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.126.0...@standardnotes/domain-events@2.127.0) (2023-09-15)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.127.0",
|
||||
"version": "2.131.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -20,7 +20,7 @@
|
||||
"clean": "rm -fr dist",
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"test": "jest spec --coverage --passWithNoTests"
|
||||
"test": "jest --coverage --no-cache --passWithNoTests"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/predicates": "workspace:*",
|
||||
|
||||
@@ -2,5 +2,4 @@ export interface ItemRemovedFromSharedVaultEventPayload {
|
||||
userUuid: string
|
||||
itemUuid: string
|
||||
sharedVaultUuid: string
|
||||
roleNames: string[]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { SharedVaultRemovedEventPayload } from './SharedVaultRemovedEventPayload'
|
||||
|
||||
export interface SharedVaultRemovedEvent extends DomainEventInterface {
|
||||
type: 'SHARED_VAULT_REMOVED'
|
||||
payload: SharedVaultRemovedEventPayload
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface SharedVaultRemovedEventPayload {
|
||||
sharedVaultUuid: string
|
||||
vaultOwnerUuid: string
|
||||
}
|
||||
@@ -3,4 +3,5 @@ export interface TransitionStatusUpdatedEventPayload {
|
||||
transitionType: 'items' | 'revisions'
|
||||
transitionTimestamp: number
|
||||
status: string
|
||||
page?: number
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { UserDesignatedAsSurvivorInSharedVaultEventPayload } from './UserDesignatedAsSurvivorInSharedVaultEventPayload'
|
||||
|
||||
export interface UserDesignatedAsSurvivorInSharedVaultEvent extends DomainEventInterface {
|
||||
type: 'USER_DESIGNATED_AS_SURVIVOR_IN_SHARED_VAULT'
|
||||
payload: UserDesignatedAsSurvivorInSharedVaultEventPayload
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface UserDesignatedAsSurvivorInSharedVaultEventPayload {
|
||||
userUuid: string
|
||||
sharedVaultUuid: string
|
||||
timestamp: number
|
||||
}
|
||||
@@ -76,6 +76,8 @@ export * from './Event/SharedVaultFileRemovedEvent'
|
||||
export * from './Event/SharedVaultFileRemovedEventPayload'
|
||||
export * from './Event/SharedVaultFileUploadedEvent'
|
||||
export * from './Event/SharedVaultFileUploadedEventPayload'
|
||||
export * from './Event/SharedVaultRemovedEvent'
|
||||
export * from './Event/SharedVaultRemovedEventPayload'
|
||||
export * from './Event/StatisticPersistenceRequestedEvent'
|
||||
export * from './Event/StatisticPersistenceRequestedEventPayload'
|
||||
export * from './Event/SubscriptionCancelledEvent'
|
||||
@@ -102,6 +104,8 @@ export * from './Event/TransitionStatusUpdatedEvent'
|
||||
export * from './Event/TransitionStatusUpdatedEventPayload'
|
||||
export * from './Event/UserAddedToSharedVaultEvent'
|
||||
export * from './Event/UserAddedToSharedVaultEventPayload'
|
||||
export * from './Event/UserDesignatedAsSurvivorInSharedVaultEvent'
|
||||
export * from './Event/UserDesignatedAsSurvivorInSharedVaultEventPayload'
|
||||
export * from './Event/UserDisabledSessionUserAgentLoggingEvent'
|
||||
export * from './Event/UserDisabledSessionUserAgentLoggingEventPayload'
|
||||
export * from './Event/UserEmailChangedEvent'
|
||||
|
||||
@@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.12.2](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.1...@standardnotes/event-store@1.12.2) (2023-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.12.1](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.12.0...@standardnotes/event-store@1.12.1) (2023-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
# [1.12.0](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.52...@standardnotes/event-store@1.12.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
## [1.11.52](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.51...@standardnotes/event-store@1.11.52) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.51](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.50...@standardnotes/event-store@1.11.51) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.50](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.49...@standardnotes/event-store@1.11.50) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.49](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.48...@standardnotes/event-store@1.11.49) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.48](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.47...@standardnotes/event-store@1.11.48) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.47](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.46...@standardnotes/event-store@1.11.47) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.46](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.45...@standardnotes/event-store@1.11.46) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.45](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.44...@standardnotes/event-store@1.11.45) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.44](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.43...@standardnotes/event-store@1.11.44) (2023-09-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.11.44",
|
||||
"version": "1.12.2",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
@@ -13,7 +13,7 @@
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"pretest": "yarn lint && yarn build",
|
||||
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
|
||||
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
|
||||
"worker": "yarn node dist/bin/worker.js"
|
||||
},
|
||||
"author": "Karol Sójko <karol@standardnotes.com>",
|
||||
|
||||
@@ -3,6 +3,56 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.25.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.24.1...@standardnotes/files-server@1.25.0) (2023-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
* block file operations during transition ([#856](https://github.com/standardnotes/files/issues/856)) ([676cf36](https://github.com/standardnotes/files/commit/676cf36f8d769aa433880b2800f0457d06fbbf14))
|
||||
|
||||
## [1.24.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.24.0...@standardnotes/files-server@1.24.1) (2023-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
# [1.24.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.23.2...@standardnotes/files-server@1.24.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/files/issues/854)) ([ca6dbc0](https://github.com/standardnotes/files/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
## [1.23.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.23.1...@standardnotes/files-server@1.23.2) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.23.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.23.0...@standardnotes/files-server@1.23.1) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
# [1.23.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.28...@standardnotes/files-server@1.23.0) (2023-09-25)
|
||||
|
||||
### Features
|
||||
|
||||
* remove shared vault files upon shared vault removal ([#852](https://github.com/standardnotes/files/issues/852)) ([7b1eec2](https://github.com/standardnotes/files/commit/7b1eec21e54330bebbeebb80cec3ba4284112aab))
|
||||
|
||||
## [1.22.28](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.27...@standardnotes/files-server@1.22.28) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.22.27](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.26...@standardnotes/files-server@1.22.27) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.22.26](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.25...@standardnotes/files-server@1.22.26) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.22.25](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.24...@standardnotes/files-server@1.22.25) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.22.24](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.23...@standardnotes/files-server@1.22.24) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.22.23](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.22...@standardnotes/files-server@1.22.23) (2023-09-18)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.22.23",
|
||||
"version": "1.25.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --fix --ext .ts",
|
||||
"pretest": "yarn lint && yarn build",
|
||||
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
|
||||
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
|
||||
"start": "yarn node dist/bin/server.js",
|
||||
"worker": "yarn node dist/bin/worker.js"
|
||||
},
|
||||
|
||||
@@ -198,7 +198,9 @@ export class ContainerConfigLoader {
|
||||
.toConstantValue(
|
||||
new FSFileUploader(container.get(TYPES.Files_FILE_UPLOAD_PATH), container.get(TYPES.Files_Logger)),
|
||||
)
|
||||
container.bind<FileRemoverInterface>(TYPES.Files_FileRemover).to(FSFileRemover)
|
||||
container
|
||||
.bind<FileRemoverInterface>(TYPES.Files_FileRemover)
|
||||
.toConstantValue(new FSFileRemover(container.get<string>(TYPES.Files_FILE_UPLOAD_PATH)))
|
||||
container.bind<FileMoverInterface>(TYPES.Files_FileMover).to(FSFileMover)
|
||||
}
|
||||
|
||||
@@ -247,12 +249,26 @@ export class ContainerConfigLoader {
|
||||
// Handlers
|
||||
container
|
||||
.bind<AccountDeletionRequestedEventHandler>(TYPES.Files_AccountDeletionRequestedEventHandler)
|
||||
.to(AccountDeletionRequestedEventHandler)
|
||||
.toConstantValue(
|
||||
new AccountDeletionRequestedEventHandler(
|
||||
container.get<MarkFilesToBeRemoved>(TYPES.Files_MarkFilesToBeRemoved),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Files_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Files_DomainEventFactory),
|
||||
container.get<winston.Logger>(TYPES.Files_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SharedSubscriptionInvitationCanceledEventHandler>(
|
||||
TYPES.Files_SharedSubscriptionInvitationCanceledEventHandler,
|
||||
)
|
||||
.to(SharedSubscriptionInvitationCanceledEventHandler)
|
||||
.toConstantValue(
|
||||
new SharedSubscriptionInvitationCanceledEventHandler(
|
||||
container.get<MarkFilesToBeRemoved>(TYPES.Files_MarkFilesToBeRemoved),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Files_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Files_DomainEventFactory),
|
||||
container.get<winston.Logger>(TYPES.Files_Logger),
|
||||
),
|
||||
)
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.Files_AccountDeletionRequestedEventHandler)],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type RemovedFileDescription = {
|
||||
userUuid: string
|
||||
userOrSharedVaultUuid: string
|
||||
filePath: string
|
||||
fileName: string
|
||||
fileByteSize: number
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import {
|
||||
AccountDeletionRequestedEvent,
|
||||
AccountDeletionRequestedEventPayload,
|
||||
DomainEventPublisherInterface,
|
||||
FileRemovedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
|
||||
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { RemovedFileDescription } from '../File/RemovedFileDescription'
|
||||
|
||||
describe('AccountDeletionRequestedEventHandler', () => {
|
||||
let markFilesToBeRemoved: MarkFilesToBeRemoved
|
||||
let event: AccountDeletionRequestedEvent
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
|
||||
const createHandler = () =>
|
||||
new AccountDeletionRequestedEventHandler(markFilesToBeRemoved, domainEventPublisher, domainEventFactory)
|
||||
|
||||
beforeEach(() => {
|
||||
markFilesToBeRemoved = {} as jest.Mocked<MarkFilesToBeRemoved>
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: true,
|
||||
filesRemoved: [{} as jest.Mocked<RemovedFileDescription>],
|
||||
})
|
||||
|
||||
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
|
||||
event.payload = {
|
||||
userUuid: '1-2-3',
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
} as jest.Mocked<AccountDeletionRequestedEventPayload>
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createFileRemovedEvent = jest.fn().mockReturnValue({} as jest.Mocked<FileRemovedEvent>)
|
||||
})
|
||||
|
||||
it('should mark files to be remove for user', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not mark files to be remove for user if user has no regular subscription', async () => {
|
||||
event.payload.regularSubscriptionUuid = undefined
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).not.toHaveBeenCalled()
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not publish events if failed to mark files to be removed', async () => {
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: false,
|
||||
})
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -3,18 +3,17 @@ import {
|
||||
DomainEventHandlerInterface,
|
||||
DomainEventPublisherInterface,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
|
||||
@injectable()
|
||||
export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.Files_MarkFilesToBeRemoved) private markFilesToBeRemoved: MarkFilesToBeRemoved,
|
||||
@inject(TYPES.Files_DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
|
||||
@inject(TYPES.Files_DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
|
||||
private markFilesToBeRemoved: MarkFilesToBeRemoved,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: AccountDeletionRequestedEvent): Promise<void> {
|
||||
@@ -22,19 +21,28 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
|
||||
return
|
||||
}
|
||||
|
||||
const response = await this.markFilesToBeRemoved.execute({
|
||||
const result = await this.markFilesToBeRemoved.execute({
|
||||
ownerUuid: event.payload.userUuid,
|
||||
})
|
||||
|
||||
if (!response.success) {
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`Could not mark files for removal for user ${event.payload.userUuid}: ${result.getError()}`)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for (const fileRemoved of response.filesRemoved) {
|
||||
const filesRemoved = result.getValue()
|
||||
|
||||
this.logger.debug(`Marked ${filesRemoved.length} files for removal for user ${event.payload.userUuid}`)
|
||||
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createFileRemovedEvent({
|
||||
regularSubscriptionUuid: event.payload.regularSubscriptionUuid,
|
||||
...fileRemoved,
|
||||
userUuid: fileRemoved.userOrSharedVaultUuid,
|
||||
filePath: fileRemoved.filePath,
|
||||
fileName: fileRemoved.fileName,
|
||||
fileByteSize: fileRemoved.fileByteSize,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import {
|
||||
SharedSubscriptionInvitationCanceledEvent,
|
||||
SharedSubscriptionInvitationCanceledEventPayload,
|
||||
DomainEventPublisherInterface,
|
||||
FileRemovedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
|
||||
import { SharedSubscriptionInvitationCanceledEventHandler } from './SharedSubscriptionInvitationCanceledEventHandler'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { RemovedFileDescription } from '../File/RemovedFileDescription'
|
||||
|
||||
describe('SharedSubscriptionInvitationCanceledEventHandler', () => {
|
||||
let markFilesToBeRemoved: MarkFilesToBeRemoved
|
||||
let event: SharedSubscriptionInvitationCanceledEvent
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
|
||||
const createHandler = () =>
|
||||
new SharedSubscriptionInvitationCanceledEventHandler(markFilesToBeRemoved, domainEventPublisher, domainEventFactory)
|
||||
|
||||
beforeEach(() => {
|
||||
markFilesToBeRemoved = {} as jest.Mocked<MarkFilesToBeRemoved>
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: true,
|
||||
filesRemoved: [{} as jest.Mocked<RemovedFileDescription>],
|
||||
})
|
||||
|
||||
event = {} as jest.Mocked<SharedSubscriptionInvitationCanceledEvent>
|
||||
event.payload = {
|
||||
inviteeIdentifier: '1-2-3',
|
||||
inviteeIdentifierType: 'uuid',
|
||||
} as jest.Mocked<SharedSubscriptionInvitationCanceledEventPayload>
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createFileRemovedEvent = jest.fn().mockReturnValue({} as jest.Mocked<FileRemovedEvent>)
|
||||
})
|
||||
|
||||
it('should mark files to be remove for user', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not mark files to be remove for user if identifier is not of uuid type', async () => {
|
||||
event.payload.inviteeIdentifierType = 'email'
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).not.toHaveBeenCalled()
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not publish events if failed to mark files to be removed', async () => {
|
||||
markFilesToBeRemoved.execute = jest.fn().mockReturnValue({
|
||||
success: false,
|
||||
})
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(markFilesToBeRemoved.execute).toHaveBeenCalledWith({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -3,18 +3,17 @@ import {
|
||||
DomainEventHandlerInterface,
|
||||
DomainEventPublisherInterface,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
|
||||
@injectable()
|
||||
export class SharedSubscriptionInvitationCanceledEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.Files_MarkFilesToBeRemoved) private markFilesToBeRemoved: MarkFilesToBeRemoved,
|
||||
@inject(TYPES.Files_DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
|
||||
@inject(TYPES.Files_DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
|
||||
private markFilesToBeRemoved: MarkFilesToBeRemoved,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: SharedSubscriptionInvitationCanceledEvent): Promise<void> {
|
||||
@@ -22,19 +21,30 @@ export class SharedSubscriptionInvitationCanceledEventHandler implements DomainE
|
||||
return
|
||||
}
|
||||
|
||||
const response = await this.markFilesToBeRemoved.execute({
|
||||
const result = await this.markFilesToBeRemoved.execute({
|
||||
ownerUuid: event.payload.inviteeIdentifier,
|
||||
})
|
||||
|
||||
if (!response.success) {
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(
|
||||
`Could not mark files to be removed for invitee: ${event.payload.inviteeIdentifier}: ${result.getError()}`,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for (const fileRemoved of response.filesRemoved) {
|
||||
const filesRemoved = result.getValue()
|
||||
|
||||
this.logger.debug(`Marked ${filesRemoved.length} files for removal for invitee ${event.payload.inviteeIdentifier}`)
|
||||
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createFileRemovedEvent({
|
||||
regularSubscriptionUuid: event.payload.inviterSubscriptionUuid,
|
||||
...fileRemoved,
|
||||
userUuid: fileRemoved.userOrSharedVaultUuid,
|
||||
filePath: fileRemoved.filePath,
|
||||
fileName: fileRemoved.fileName,
|
||||
fileByteSize: fileRemoved.fileByteSize,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
DomainEventHandlerInterface,
|
||||
DomainEventPublisherInterface,
|
||||
SharedVaultRemovedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { MarkFilesToBeRemoved } from '../UseCase/MarkFilesToBeRemoved/MarkFilesToBeRemoved'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
|
||||
export class SharedVaultRemovedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private markFilesToBeRemoved: MarkFilesToBeRemoved,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: SharedVaultRemovedEvent): Promise<void> {
|
||||
const result = await this.markFilesToBeRemoved.execute({
|
||||
ownerUuid: event.payload.sharedVaultUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(
|
||||
`Could not mark files to be removed for shared vault: ${event.payload.sharedVaultUuid}: ${result.getError()}`,
|
||||
)
|
||||
}
|
||||
|
||||
const filesRemoved = result.getValue()
|
||||
|
||||
this.logger.debug(
|
||||
`Marked ${filesRemoved.length} files for removal for shared vault ${event.payload.sharedVaultUuid}`,
|
||||
)
|
||||
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createSharedVaultFileRemovedEvent({
|
||||
fileByteSize: fileRemoved.fileByteSize,
|
||||
fileName: fileRemoved.fileName,
|
||||
filePath: fileRemoved.filePath,
|
||||
sharedVaultUuid: event.payload.sharedVaultUuid,
|
||||
vaultOwnerUuid: event.payload.vaultOwnerUuid,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,5 @@ import { RemovedFileDescription } from '../File/RemovedFileDescription'
|
||||
|
||||
export interface FileRemoverInterface {
|
||||
remove(filePath: string): Promise<number>
|
||||
markFilesToBeRemoved(userUuid: string): Promise<Array<RemovedFileDescription>>
|
||||
markFilesToBeRemoved(userOrSharedVaultUuid: string): Promise<Array<RemovedFileDescription>>
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ describe('MarkFilesToBeRemoved', () => {
|
||||
})
|
||||
|
||||
it('should mark files for being removed', async () => {
|
||||
expect(await createUseCase().execute({ ownerUuid: '1-2-3' })).toEqual({ success: true })
|
||||
const result = await createUseCase().execute({ ownerUuid: '1-2-3' })
|
||||
|
||||
expect(result.isFailed()).toEqual(false)
|
||||
|
||||
expect(fileRemover.markFilesToBeRemoved).toHaveBeenCalledWith('1-2-3')
|
||||
})
|
||||
@@ -31,9 +33,7 @@ describe('MarkFilesToBeRemoved', () => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
expect(await createUseCase().execute({ ownerUuid: '1-2-3' })).toEqual({
|
||||
success: false,
|
||||
message: 'Could not mark resources for removal',
|
||||
})
|
||||
const result = await createUseCase().execute({ ownerUuid: '1-2-3' })
|
||||
expect(result.isFailed()).toEqual(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { FileRemoverInterface } from '../../Services/FileRemoverInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
import { MarkFilesToBeRemovedDTO } from './MarkFilesToBeRemovedDTO'
|
||||
import { MarkFilesToBeRemovedResponse } from './MarkFilesToBeRemovedResponse'
|
||||
import { RemovedFileDescription } from '../../File/RemovedFileDescription'
|
||||
|
||||
@injectable()
|
||||
export class MarkFilesToBeRemoved implements UseCaseInterface {
|
||||
export class MarkFilesToBeRemoved implements UseCaseInterface<RemovedFileDescription[]> {
|
||||
constructor(
|
||||
@inject(TYPES.Files_FileRemover) private fileRemover: FileRemoverInterface,
|
||||
@inject(TYPES.Files_Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(dto: MarkFilesToBeRemovedDTO): Promise<MarkFilesToBeRemovedResponse> {
|
||||
async execute(dto: MarkFilesToBeRemovedDTO): Promise<Result<RemovedFileDescription[]>> {
|
||||
try {
|
||||
this.logger.debug(`Marking files for later removal for user: ${dto.ownerUuid}`)
|
||||
|
||||
const filesRemoved = await this.fileRemover.markFilesToBeRemoved(dto.ownerUuid)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
filesRemoved,
|
||||
}
|
||||
return Result.ok(filesRemoved)
|
||||
} catch (error) {
|
||||
this.logger.error(`Could not mark resources for removal: ${dto.ownerUuid} - ${(error as Error).message}`)
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: 'Could not mark resources for removal',
|
||||
}
|
||||
return Result.fail('Could not mark resources for removal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,42 @@
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { promises } from 'fs'
|
||||
|
||||
import { FileRemoverInterface } from '../../Domain/Services/FileRemoverInterface'
|
||||
import { RemovedFileDescription } from '../../Domain/File/RemovedFileDescription'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
|
||||
@injectable()
|
||||
export class FSFileRemover implements FileRemoverInterface {
|
||||
constructor(@inject(TYPES.Files_FILE_UPLOAD_PATH) private fileUploadPath: string) {}
|
||||
constructor(private fileUploadPath: string) {}
|
||||
|
||||
async markFilesToBeRemoved(userUuid: string): Promise<Array<RemovedFileDescription>> {
|
||||
await promises.rmdir(`${this.fileUploadPath}/${userUuid}`)
|
||||
async markFilesToBeRemoved(userOrSharedVaultUuid: string): Promise<Array<RemovedFileDescription>> {
|
||||
const removedFileDescriptions: RemovedFileDescription[] = []
|
||||
|
||||
return []
|
||||
let directoryExists: boolean
|
||||
try {
|
||||
await promises.access(`${this.fileUploadPath}/${userOrSharedVaultUuid}`)
|
||||
directoryExists = true
|
||||
} catch (error) {
|
||||
directoryExists = false
|
||||
}
|
||||
|
||||
if (!directoryExists) {
|
||||
return []
|
||||
}
|
||||
|
||||
const files = await promises.readdir(`${this.fileUploadPath}/${userOrSharedVaultUuid}`, { withFileTypes: true })
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = `${this.fileUploadPath}/${userOrSharedVaultUuid}/${file.name}`
|
||||
|
||||
const fileByteSize = await this.remove(`${userOrSharedVaultUuid}/${file.name}`)
|
||||
|
||||
removedFileDescriptions.push({
|
||||
filePath,
|
||||
fileByteSize,
|
||||
userOrSharedVaultUuid,
|
||||
fileName: file.name,
|
||||
})
|
||||
}
|
||||
|
||||
return removedFileDescriptions
|
||||
}
|
||||
|
||||
async remove(filePath: string): Promise<number> {
|
||||
|
||||
@@ -13,6 +13,7 @@ describe('ValetTokenAuthMiddleware', () => {
|
||||
|
||||
const logger = {
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
} as unknown as jest.Mocked<Logger>
|
||||
|
||||
const createMiddleware = () => new ValetTokenAuthMiddleware(tokenDecoder, logger)
|
||||
@@ -222,4 +223,27 @@ describe('ValetTokenAuthMiddleware', () => {
|
||||
|
||||
expect(next).toHaveBeenCalledWith(error)
|
||||
})
|
||||
|
||||
it('should throw an error if the valet token indicates an ongoing transition', async () => {
|
||||
request.headers['x-valet-token'] = 'valet-token'
|
||||
|
||||
tokenDecoder.decodeToken = jest.fn().mockReturnValue({
|
||||
userUuid: '1-2-3',
|
||||
permittedResources: [
|
||||
{
|
||||
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
|
||||
unencryptedFileSize: 30,
|
||||
},
|
||||
],
|
||||
permittedOperation: 'write',
|
||||
uploadBytesLimit: -1,
|
||||
uploadBytesUsed: 80,
|
||||
ongoingTransition: true,
|
||||
})
|
||||
|
||||
await createMiddleware().handler(request, response, next)
|
||||
|
||||
expect(response.status).toHaveBeenCalledWith(500)
|
||||
expect(next).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -46,6 +46,19 @@ export class ValetTokenAuthMiddleware extends BaseMiddleware {
|
||||
return
|
||||
}
|
||||
|
||||
if (valetTokenData.ongoingTransition === true) {
|
||||
this.logger.error(`Cannot perform file operations for user ${valetTokenData.userUuid} during transition`)
|
||||
|
||||
response.status(500).send({
|
||||
error: {
|
||||
tag: 'ongoing-transition',
|
||||
message: 'Cannot perform file operations during transition',
|
||||
},
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for (const resource of valetTokenData.permittedResources) {
|
||||
const resourceUuidOrError = Uuid.create(resource.remoteIdentifier)
|
||||
if (resourceUuidOrError.isFailed()) {
|
||||
|
||||
@@ -59,7 +59,7 @@ export class S3FileRemover implements FileRemoverInterface {
|
||||
fileByteSize: file.Size as number,
|
||||
fileName: file.Key.replace(`${userUuid}/`, ''),
|
||||
filePath: file.Key,
|
||||
userUuid,
|
||||
userOrSharedVaultUuid: userUuid,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -16,5 +16,3 @@ MONGO_PORT=27017
|
||||
MONGO_USERNAME=standardnotes
|
||||
MONGO_PASSWORD=standardnotes
|
||||
MONGO_DATABASE=standardnotes
|
||||
|
||||
TRANSITION_MODE_ENABLED=false
|
||||
|
||||
@@ -3,6 +3,112 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.16.9](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.8...@standardnotes/home-server@1.16.9) (2023-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.8](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.7...@standardnotes/home-server@1.16.8) (2023-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.7](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.6...@standardnotes/home-server@1.16.7) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.6](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.5...@standardnotes/home-server@1.16.6) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.5](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.4...@standardnotes/home-server@1.16.5) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.4](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.3...@standardnotes/home-server@1.16.4) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.3](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.2...@standardnotes/home-server@1.16.3) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.2](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.1...@standardnotes/home-server@1.16.2) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.16.1](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.0...@standardnotes/home-server@1.16.1) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
# [1.16.0](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.80...@standardnotes/home-server@1.16.0) (2023-09-22)
|
||||
|
||||
### Features
|
||||
|
||||
* remove transition mode from code ([5001496](https://github.com/standardnotes/server/commit/5001496c7bc1df9e20c2d88ebf52ed77f868110c))
|
||||
|
||||
## [1.15.80](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.79...@standardnotes/home-server@1.15.80) (2023-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.79](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.78...@standardnotes/home-server@1.15.79) (2023-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.78](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.77...@standardnotes/home-server@1.15.78) (2023-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.77](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.76...@standardnotes/home-server@1.15.77) (2023-09-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.76](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.75...@standardnotes/home-server@1.15.76) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.75](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.74...@standardnotes/home-server@1.15.75) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.74](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.73...@standardnotes/home-server@1.15.74) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.73](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.72...@standardnotes/home-server@1.15.73) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.72](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.71...@standardnotes/home-server@1.15.72) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.71](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.70...@standardnotes/home-server@1.15.71) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.70](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.69...@standardnotes/home-server@1.15.70) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.69](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.68...@standardnotes/home-server@1.15.69) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.68](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.67...@standardnotes/home-server@1.15.68) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.67](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.66...@standardnotes/home-server@1.15.67) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.66](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.65...@standardnotes/home-server@1.15.66) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.65](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.64...@standardnotes/home-server@1.15.65) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.15.64](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.63...@standardnotes/home-server@1.15.64) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/home-server",
|
||||
"version": "1.15.64",
|
||||
"version": "1.16.9",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.7.0](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.6.11...@standardnotes/predicates@1.7.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
## [1.6.11](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.6.10...@standardnotes/predicates@1.6.11) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/predicates
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/predicates",
|
||||
"version": "1.6.11",
|
||||
"version": "1.7.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"start": "tsc -p tsconfig.json --watch",
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"test": "jest spec --coverage --passWithNoTests"
|
||||
"test": "jest --coverage --no-cache --passWithNoTests"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.1",
|
||||
|
||||
@@ -3,6 +3,117 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.38.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.38.1...@standardnotes/revisions-server@1.38.2) (2023-09-28)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.38.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.38.0...@standardnotes/revisions-server@1.38.1) (2023-09-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
# [1.38.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.37.3...@standardnotes/revisions-server@1.38.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
## [1.37.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.37.2...@standardnotes/revisions-server@1.37.3) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.37.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.37.1...@standardnotes/revisions-server@1.37.2) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.37.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.37.0...@standardnotes/revisions-server@1.37.1) (2023-09-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
# [1.37.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.7...@standardnotes/revisions-server@1.37.0) (2023-09-25)
|
||||
|
||||
### Features
|
||||
|
||||
* add storing paging progress in redis ([9759814](https://github.com/standardnotes/server/commit/9759814f637b8ae25b325e35bc7f5159747980b6))
|
||||
|
||||
## [1.36.7](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.6...@standardnotes/revisions-server@1.36.7) (2023-09-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remember paging progress on transitioning ([1d73e4f](https://github.com/standardnotes/server/commit/1d73e4f0720d41029af4d4b2b7a10d101add6c82))
|
||||
|
||||
## [1.36.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.5...@standardnotes/revisions-server@1.36.6) (2023-09-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add more logs to transition process ([0562b0a](https://github.com/standardnotes/server/commit/0562b0a621eb878026fbdc0346b6170e815b64bf))
|
||||
* remove excessive logs ([15ed1fd](https://github.com/standardnotes/server/commit/15ed1fd789aba306cbec6a23e88d5c1f837dabc0))
|
||||
|
||||
## [1.36.5](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.4...@standardnotes/revisions-server@1.36.5) (2023-09-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable cleaning secondary database on transition ([4f4443a](https://github.com/standardnotes/server/commit/4f4443a882f69c2e76ef831ef36347c9c54f31cd))
|
||||
* integrity check during transition ([921c30f](https://github.com/standardnotes/server/commit/921c30f6415ef122a7d1af83ffa3f6840a42edba))
|
||||
* processing migration optimization ([22540ee](https://github.com/standardnotes/server/commit/22540ee83436b986949127a6923285a702162706))
|
||||
|
||||
## [1.36.4](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.3...@standardnotes/revisions-server@1.36.4) (2023-09-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **revisions:** add log info about skipping already existing revision ([d0dba1b](https://github.com/standardnotes/server/commit/d0dba1b66df0fb4ab64ede8f0d4e1c4e2a23ad3c))
|
||||
|
||||
## [1.36.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.2...@standardnotes/revisions-server@1.36.3) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.36.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.1...@standardnotes/revisions-server@1.36.2) (2023-09-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **revisions:** rename revisions table to all users stuck mid-migration process - fixes [#836](https://github.com/standardnotes/server/issues/836) ([#842](https://github.com/standardnotes/server/issues/842)) ([a621cf1](https://github.com/standardnotes/server/commit/a621cf1e3b891c450272e9762e4a71a199ea2932))
|
||||
|
||||
## [1.36.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.0...@standardnotes/revisions-server@1.36.1) (2023-09-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* secondary database catch up time ([880db10](https://github.com/standardnotes/server/commit/880db1038a39d4610a2593489a18e207487347a2))
|
||||
|
||||
# [1.36.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.8...@standardnotes/revisions-server@1.36.0) (2023-09-20)
|
||||
|
||||
### Features
|
||||
|
||||
* add unassigning items and revisions upon shared vault removal ([#839](https://github.com/standardnotes/server/issues/839)) ([378eced](https://github.com/standardnotes/server/commit/378ecedfcc4fb23475c2329fb37479edb3b48a39))
|
||||
|
||||
## [1.35.8](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.7...@standardnotes/revisions-server@1.35.8) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.35.7](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.6...@standardnotes/revisions-server@1.35.7) (2023-09-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* skip removing already existing content in secondary to pick up where the transition left of ([857c6af](https://github.com/standardnotes/server/commit/857c6af9468ec829ff4dce9a96ba7bf9c14d55a5))
|
||||
|
||||
## [1.35.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.5...@standardnotes/revisions-server@1.35.6) (2023-09-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* increase timeout for secondary database to catch up for indexes to be rebuilt ([b265a39](https://github.com/standardnotes/server/commit/b265a39b635373c36ee8c3d8e09f0631159b3574))
|
||||
* logs verbosity during transitions ([e589029](https://github.com/standardnotes/server/commit/e589029722ab9f4debc8aa6cc78913f877eda2e3))
|
||||
|
||||
## [1.35.5](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.4...@standardnotes/revisions-server@1.35.5) (2023-09-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add checking for secondary items logs ([a1a3e9f](https://github.com/standardnotes/server/commit/a1a3e9f586358b943b1b490a1382e42f081f7d06))
|
||||
* logs for removing already existing content and paging through diff of the content ([a40b17b](https://github.com/standardnotes/server/commit/a40b17b141f1d5954e1a45b969d5a941386c68d0))
|
||||
|
||||
## [1.35.4](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.3...@standardnotes/revisions-server@1.35.4) (2023-09-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* logs formatting during transition for better readability ([0ae028d](https://github.com/standardnotes/server/commit/0ae028db739decec8c50321b18b0af515e00bd23))
|
||||
|
||||
## [1.35.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.2...@standardnotes/revisions-server@1.35.3) (2023-09-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -4,6 +4,8 @@ export class removeDateIndexes1669636497932 implements MigrationInterface {
|
||||
name = 'removeDateIndexes1669636497932'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await this.renameRevisionsTable(queryRunner)
|
||||
|
||||
const indexRevisionsOnCreatedAt = await queryRunner.manager.query(
|
||||
'SHOW INDEX FROM `revisions_revisions` where `key_name` = "created_at"',
|
||||
)
|
||||
@@ -25,4 +27,14 @@ export class removeDateIndexes1669636497932 implements MigrationInterface {
|
||||
await queryRunner.query('CREATE INDEX `creation_date` ON `revisions_revisions` (`creation_date`)')
|
||||
await queryRunner.query('CREATE INDEX `created_at` ON `revisions_revisions` (`created_at`)')
|
||||
}
|
||||
|
||||
private async renameRevisionsTable(queryRunner: QueryRunner) {
|
||||
const revisionsTableExistsQueryResult = await queryRunner.manager.query(
|
||||
'SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = "revisions"',
|
||||
)
|
||||
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
|
||||
if (revisionsTableExists) {
|
||||
await queryRunner.query('RENAME TABLE `revisions` TO `revisions_revisions`')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,22 @@ export class makeUserUuidNullable1669735585016 implements MigrationInterface {
|
||||
name = 'makeUserUuidNullable1669735585016'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await this.renameRevisionsTable(queryRunner)
|
||||
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` CHANGE `user_uuid` `user_uuid` varchar(36) NULL')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` CHANGE `user_uuid` `user_uuid` varchar(36) NOT NULL')
|
||||
}
|
||||
|
||||
private async renameRevisionsTable(queryRunner: QueryRunner) {
|
||||
const revisionsTableExistsQueryResult = await queryRunner.manager.query(
|
||||
'SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = "revisions"',
|
||||
)
|
||||
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
|
||||
if (revisionsTableExists) {
|
||||
await queryRunner.query('RENAME TABLE `revisions` TO `revisions_revisions`')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddSharedVaultInformation1693915383950 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await this.renameRevisionsTable(queryRunner)
|
||||
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` ADD `edited_by` varchar(36) NULL')
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` ADD `shared_vault_uuid` varchar(36) NULL')
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` ADD `key_system_identifier` varchar(36) NULL')
|
||||
@@ -16,4 +18,14 @@ export class AddSharedVaultInformation1693915383950 implements MigrationInterfac
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` DROP COLUMN `shared_vault_uuid`')
|
||||
await queryRunner.query('ALTER TABLE `revisions_revisions` DROP COLUMN `last_edited_by`')
|
||||
}
|
||||
|
||||
private async renameRevisionsTable(queryRunner: QueryRunner) {
|
||||
const revisionsTableExistsQueryResult = await queryRunner.manager.query(
|
||||
'SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = "revisions"',
|
||||
)
|
||||
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
|
||||
if (revisionsTableExists) {
|
||||
await queryRunner.query('RENAME TABLE `revisions` TO `revisions_revisions`')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.35.3",
|
||||
"version": "1.38.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -20,7 +20,7 @@
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --ext .ts --fix",
|
||||
"pretest": "yarn lint && yarn build",
|
||||
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
|
||||
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
|
||||
"start": "yarn node dist/bin/server.js",
|
||||
"worker": "yarn node dist/bin/worker.js"
|
||||
},
|
||||
@@ -41,6 +41,7 @@
|
||||
"express": "^4.18.2",
|
||||
"inversify": "^6.0.1",
|
||||
"inversify-express-utils": "^6.4.3",
|
||||
"ioredis": "^5.3.2",
|
||||
"mongodb": "^6.0.0",
|
||||
"mysql2": "^3.0.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
@@ -52,6 +53,7 @@
|
||||
"@types/cors": "^2.8.9",
|
||||
"@types/dotenv": "^8.2.0",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/ioredis": "^5.0.0",
|
||||
"@types/jest": "^29.5.1",
|
||||
"@types/node": "^20.5.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ControllerContainer, ControllerContainerInterface, MapperInterface } from '@standardnotes/domain-core'
|
||||
import Redis from 'ioredis'
|
||||
import { Container, interfaces } from 'inversify'
|
||||
import { MongoRepository, Repository } from 'typeorm'
|
||||
import * as winston from 'winston'
|
||||
@@ -67,6 +68,10 @@ import { SQLRevisionPersistenceMapper } from '../Mapping/Persistence/SQL/SQLRevi
|
||||
import { RemoveRevisionsFromSharedVault } from '../Domain/UseCase/RemoveRevisionsFromSharedVault/RemoveRevisionsFromSharedVault'
|
||||
import { ItemRemovedFromSharedVaultEventHandler } from '../Domain/Handler/ItemRemovedFromSharedVaultEventHandler'
|
||||
import { TransitionRequestedEventHandler } from '../Domain/Handler/TransitionRequestedEventHandler'
|
||||
import { SharedVaultRemovedEventHandler } from '../Domain/Handler/SharedVaultRemovedEventHandler'
|
||||
import { TransitionRepositoryInterface } from '../Domain/Transition/TransitionRepositoryInterface'
|
||||
import { RedisTransitionRepository } from '../Infra/Redis/RedisTransitionRepository'
|
||||
import { CreateRevisionFromDump } from '../Domain/UseCase/CreateRevisionFromDump/CreateRevisionFromDump'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
constructor(private mode: 'server' | 'worker' = 'server') {}
|
||||
@@ -87,11 +92,28 @@ export class ContainerConfigLoader {
|
||||
const isConfiguredForSelfHosting = env.get('MODE', true) === 'self-hosted'
|
||||
const isConfiguredForHomeServerOrSelfHosting = isConfiguredForHomeServer || isConfiguredForSelfHosting
|
||||
const isSecondaryDatabaseEnabled = env.get('SECONDARY_DB_ENABLED', true) === 'true'
|
||||
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
|
||||
|
||||
const container = new Container({
|
||||
defaultScope: 'Singleton',
|
||||
})
|
||||
|
||||
if (!isConfiguredForInMemoryCache) {
|
||||
const redisUrl = env.get('REDIS_URL')
|
||||
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||
let redis
|
||||
if (isRedisInClusterMode) {
|
||||
redis = new Redis.Cluster(redisUrl.split(','))
|
||||
} else {
|
||||
redis = new Redis(redisUrl)
|
||||
}
|
||||
|
||||
container.bind(TYPES.Revisions_Redis).toConstantValue(redis)
|
||||
container
|
||||
.bind<TransitionRepositoryInterface>(TYPES.Revisions_TransitionStatusRepository)
|
||||
.toConstantValue(new RedisTransitionRepository(container.get<Redis>(TYPES.Revisions_Redis)))
|
||||
}
|
||||
|
||||
let logger: winston.Logger
|
||||
if (configuration?.logger) {
|
||||
logger = configuration.logger as winston.Logger
|
||||
@@ -309,6 +331,21 @@ export class ContainerConfigLoader {
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new RevisionMetadataHttpMapper(context.container.get(TYPES.Revisions_GetRequiredRoleToViewRevision))
|
||||
})
|
||||
container
|
||||
.bind<MapperInterface<Revision, string>>(TYPES.Revisions_RevisionItemStringMapper)
|
||||
.toDynamicValue(() => new RevisionItemStringMapper())
|
||||
|
||||
container
|
||||
.bind<DumpRepositoryInterface>(TYPES.Revisions_DumpRepository)
|
||||
.toConstantValue(
|
||||
env.get('S3_AWS_REGION', true)
|
||||
? new S3DumpRepository(
|
||||
container.get(TYPES.Revisions_S3_BACKUP_BUCKET_NAME),
|
||||
container.get(TYPES.Revisions_S3),
|
||||
container.get(TYPES.Revisions_RevisionItemStringMapper),
|
||||
)
|
||||
: new FSDumpRepository(container.get(TYPES.Revisions_RevisionItemStringMapper)),
|
||||
)
|
||||
|
||||
// use cases
|
||||
container
|
||||
@@ -347,6 +384,9 @@ export class ContainerConfigLoader {
|
||||
isSecondaryDatabaseEnabled
|
||||
? container.get<RevisionRepositoryInterface>(TYPES.Revisions_MongoDBRevisionRepository)
|
||||
: null,
|
||||
isConfiguredForInMemoryCache
|
||||
? null
|
||||
: container.get<TransitionRepositoryInterface>(TYPES.Revisions_TransitionStatusRepository),
|
||||
container.get<TimerInterface>(TYPES.Revisions_Timer),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
env.get('MIGRATION_BATCH_SIZE', true) ? +env.get('MIGRATION_BATCH_SIZE', true) : 100,
|
||||
@@ -356,6 +396,16 @@ export class ContainerConfigLoader {
|
||||
.bind<RemoveRevisionsFromSharedVault>(TYPES.Revisions_RemoveRevisionsFromSharedVault)
|
||||
.toConstantValue(
|
||||
new RemoveRevisionsFromSharedVault(
|
||||
isSecondaryDatabaseEnabled
|
||||
? container.get<RevisionRepositoryInterface>(TYPES.Revisions_MongoDBRevisionRepository)
|
||||
: container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<CreateRevisionFromDump>(TYPES.Revisions_CreateRevisionFromDump)
|
||||
.toConstantValue(
|
||||
new CreateRevisionFromDump(
|
||||
container.get<DumpRepositoryInterface>(TYPES.Revisions_DumpRepository),
|
||||
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
|
||||
),
|
||||
)
|
||||
@@ -383,31 +433,12 @@ export class ContainerConfigLoader {
|
||||
)
|
||||
})
|
||||
|
||||
// Map
|
||||
container
|
||||
.bind<MapperInterface<Revision, string>>(TYPES.Revisions_RevisionItemStringMapper)
|
||||
.toDynamicValue(() => new RevisionItemStringMapper())
|
||||
|
||||
container
|
||||
.bind<DumpRepositoryInterface>(TYPES.Revisions_DumpRepository)
|
||||
.toConstantValue(
|
||||
env.get('S3_AWS_REGION', true)
|
||||
? new S3DumpRepository(
|
||||
container.get(TYPES.Revisions_S3_BACKUP_BUCKET_NAME),
|
||||
container.get(TYPES.Revisions_S3),
|
||||
container.get(TYPES.Revisions_RevisionItemStringMapper),
|
||||
container.get(TYPES.Revisions_Logger),
|
||||
)
|
||||
: new FSDumpRepository(container.get(TYPES.Revisions_RevisionItemStringMapper)),
|
||||
)
|
||||
|
||||
// Handlers
|
||||
container
|
||||
.bind<ItemDumpedEventHandler>(TYPES.Revisions_ItemDumpedEventHandler)
|
||||
.toConstantValue(
|
||||
new ItemDumpedEventHandler(
|
||||
container.get<DumpRepositoryInterface>(TYPES.Revisions_DumpRepository),
|
||||
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
|
||||
container.get<CreateRevisionFromDump>(TYPES.Revisions_CreateRevisionFromDump),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
)
|
||||
@@ -448,6 +479,14 @@ export class ContainerConfigLoader {
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SharedVaultRemovedEventHandler>(TYPES.Revisions_SharedVaultRemovedEventHandler)
|
||||
.toConstantValue(
|
||||
new SharedVaultRemovedEventHandler(
|
||||
container.get<RemoveRevisionsFromSharedVault>(TYPES.Revisions_RemoveRevisionsFromSharedVault),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
)
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['ITEM_DUMPED', container.get(TYPES.Revisions_ItemDumpedEventHandler)],
|
||||
@@ -455,6 +494,7 @@ export class ContainerConfigLoader {
|
||||
['REVISIONS_COPY_REQUESTED', container.get(TYPES.Revisions_RevisionsCopyRequestedEventHandler)],
|
||||
['ITEM_REMOVED_FROM_SHARED_VAULT', container.get(TYPES.Revisions_ItemRemovedFromSharedVaultEventHandler)],
|
||||
['TRANSITION_REQUESTED', container.get(TYPES.Revisions_TransitionRequestedEventHandler)],
|
||||
['SHARED_VAULT_REMOVED', container.get(TYPES.Revisions_SharedVaultRemovedEventHandler)],
|
||||
])
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const TYPES = {
|
||||
Revisions_DBConnection: Symbol.for('Revisions_DBConnection'),
|
||||
Revisions_Logger: Symbol.for('Revisions_Logger'),
|
||||
Revisions_Redis: Symbol.for('Revisions_Redis'),
|
||||
Revisions_SQS: Symbol.for('Revisions_SQS'),
|
||||
Revisions_SNS: Symbol.for('Revisions_SNS'),
|
||||
Revisions_S3: Symbol.for('Revisions_S3'),
|
||||
@@ -27,6 +28,7 @@ const TYPES = {
|
||||
Revisions_MongoDBRevisionRepository: Symbol.for('Revisions_MongoDBRevisionRepository'),
|
||||
Revisions_DumpRepository: Symbol.for('Revisions_DumpRepository'),
|
||||
Revisions_RevisionRepositoryResolver: Symbol.for('Revisions_RevisionRepositoryResolver'),
|
||||
Revisions_TransitionStatusRepository: Symbol.for('Revisions_TransitionStatusRepository'),
|
||||
// env vars
|
||||
Revisions_AUTH_JWT_SECRET: Symbol.for('Revisions_AUTH_JWT_SECRET'),
|
||||
Revisions_SQS_QUEUE_URL: Symbol.for('Revisions_SQS_QUEUE_URL'),
|
||||
@@ -47,6 +49,7 @@ const TYPES = {
|
||||
'Revisions_TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser',
|
||||
),
|
||||
Revisions_RemoveRevisionsFromSharedVault: Symbol.for('Revisions_RemoveRevisionsFromSharedVault'),
|
||||
Revisions_CreateRevisionFromDump: Symbol.for('Revisions_CreateRevisionFromDump'),
|
||||
// Controller
|
||||
Revisions_ControllerContainer: Symbol.for('Revisions_ControllerContainer'),
|
||||
Revisions_RevisionsController: Symbol.for('Revisions_RevisionsController'),
|
||||
@@ -57,6 +60,7 @@ const TYPES = {
|
||||
Revisions_RevisionsCopyRequestedEventHandler: Symbol.for('Revisions_RevisionsCopyRequestedEventHandler'),
|
||||
Revisions_ItemRemovedFromSharedVaultEventHandler: Symbol.for('Revisions_ItemRemovedFromSharedVaultEventHandler'),
|
||||
Revisions_TransitionRequestedEventHandler: Symbol.for('Revisions_TransitionRequestedEventHandler'),
|
||||
Revisions_SharedVaultRemovedEventHandler: Symbol.for('Revisions_SharedVaultRemovedEventHandler'),
|
||||
// Services
|
||||
Revisions_CrossServiceTokenDecoder: Symbol.for('Revisions_CrossServiceTokenDecoder'),
|
||||
Revisions_DomainEventSubscriberFactory: Symbol.for('Revisions_DomainEventSubscriberFactory'),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { Revision } from '../Revision/Revision'
|
||||
|
||||
export interface DumpRepositoryInterface {
|
||||
getRevisionFromDumpPath(path: string): Promise<Revision | null>
|
||||
getRevisionFromDumpPath(path: string): Promise<Result<Revision>>
|
||||
removeDump(path: string): Promise<void>
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user