mirror of
https://github.com/standardnotes/server
synced 2026-04-20 11:02:33 -04:00
Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a26221385 | |||
| 54113abe2a | |||
| afe385aed4 | |||
| f055e52e06 | |||
| fab5d18064 | |||
| a1e654a0d0 | |||
| aa835268ea | |||
| 74b4312928 | |||
| e91a832152 | |||
| 4f95bbee3f | |||
| b9c9f74d0c | |||
| e535cd504c | |||
| db0360860a | |||
| aa2b5f3b74 | |||
| 6241661e27 | |||
| 25047bf46d | |||
| a1820ed212 | |||
| 0a1d1624e8 | |||
| 7367de6832 | |||
| f0abfe89fc | |||
| d1244d165a | |||
| 106d8f9192 | |||
| 1d86ba8fcb | |||
| f20a947f8a | |||
| 19b9de05ae | |||
| 1d751c0fbe | |||
| fa2564e164 | |||
| 330bff0124 | |||
| ca57c8e7b5 | |||
| a82b9a0c8a | |||
| ea7e9d73c4 | |||
| 117b7b4b99 | |||
| b4bf11d9da | |||
| 0306e10469 | |||
| 0ab47013f2 | |||
| 836883b82d | |||
| ed671be9c5 | |||
| 9676a2586c | |||
| e95ba61c7f | |||
| a0718aea26 | |||
| 156fa7a618 | |||
| 8d006ece30 | |||
| 037c994040 | |||
| a164ba291d | |||
| 610fba2601 | |||
| 398338c8f8 | |||
| 34be157d8e | |||
| 76372fe357 | |||
| 46879c336b | |||
| aef9e936bd | |||
| 8cb92d9678 | |||
| 52db89de81 | |||
| c5af8dfc05 | |||
| 27ad8e6959 | |||
| c4a1502f70 | |||
| d1d6c753c4 | |||
| 3bd63f7674 | |||
| 376466d9b2 | |||
| e100c52bbc | |||
| d4830dec01 | |||
| 7e11821021 | |||
| 4715e019a2 | |||
| 794cd8734a | |||
| 14d42b26bb | |||
| 6bb44afd91 | |||
| c82345aeeb | |||
| 72ab08a0d0 | |||
| f2d1b47e40 | |||
| d9ee2c5be2 | |||
| eb59902cf7 | |||
| 002074e4d1 | |||
| 45b55068f9 | |||
| 157eee5d93 | |||
| d5f2b4f6eb | |||
| a7a93497e8 | |||
| 8f96f0ed7a | |||
| 3f064176f2 | |||
| c7b0c7dfa8 | |||
| df20dd46db | |||
| 6dfd09989e | |||
| fc821709e2 | |||
| e986abaab5 | |||
| a006fb3119 |
@@ -4,6 +4,7 @@ DB_USERNAME=std_notes_user
|
|||||||
DB_PASSWORD=changeme123
|
DB_PASSWORD=changeme123
|
||||||
DB_DATABASE=standard_notes_db
|
DB_DATABASE=standard_notes_db
|
||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
|
DB_DEBUG_LEVEL=all
|
||||||
DB_SQLITE_DATABASE_PATH=standard_notes_db
|
DB_SQLITE_DATABASE_PATH=standard_notes_db
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
REDIS_HOST=cache
|
REDIS_HOST=cache
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@master
|
uses: docker/setup-buildx-action@master
|
||||||
|
|
||||||
- name: Publish Docker image
|
- name: Publish Docker image
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: ${{ steps.bundle-dir.outputs.temp_dir }}
|
context: ${{ steps.bundle-dir.outputs.temp_dir }}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
e2e:
|
e2e:
|
||||||
name: (Self Hosting) E2E Test Suite
|
name: (Self Hosting) E2E Test Suite
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
secondary_db_enabled: [true, false]
|
secondary_db_enabled: [true, false]
|
||||||
transition_mode_enabled: [true, false]
|
transition_mode_enabled: [true, false]
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
|
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
|
||||||
|
|
||||||
- name: Run E2E Test Suite
|
- name: Run E2E Test Suite
|
||||||
run: yarn dlx mocha-headless-chrome --timeout 1800000 -f http://localhost:9001/mocha/test.html
|
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html
|
||||||
|
|
||||||
- name: Show logs on failure
|
- name: Show logs on failure
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
e2e-home-server:
|
e2e-home-server:
|
||||||
name: (Home Server) E2E Test Suite
|
name: (Home Server) E2E Test Suite
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
db_type: [mysql, sqlite]
|
db_type: [mysql, sqlite]
|
||||||
cache_type: [redis, memory]
|
cache_type: [redis, memory]
|
||||||
@@ -141,6 +141,7 @@ jobs:
|
|||||||
echo "DB_USERNAME=standardnotes" >> packages/home-server/.env
|
echo "DB_USERNAME=standardnotes" >> packages/home-server/.env
|
||||||
echo "DB_PASSWORD=standardnotes" >> packages/home-server/.env
|
echo "DB_PASSWORD=standardnotes" >> packages/home-server/.env
|
||||||
echo "DB_TYPE=${{ matrix.db_type }}" >> packages/home-server/.env
|
echo "DB_TYPE=${{ matrix.db_type }}" >> packages/home-server/.env
|
||||||
|
echo "DB_DEBUG_LEVEL=all" >> packages/home-server/.env
|
||||||
echo "REDIS_URL=redis://localhost:6379" >> packages/home-server/.env
|
echo "REDIS_URL=redis://localhost:6379" >> packages/home-server/.env
|
||||||
echo "CACHE_TYPE=${{ matrix.cache_type }}" >> 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 "SECONDARY_DB_ENABLED=${{ matrix.secondary_db_enabled }}" >> packages/home-server/.env
|
||||||
@@ -162,7 +163,7 @@ jobs:
|
|||||||
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
|
run: for i in {1..30}; do curl -s http://localhost:3123/healthcheck && break || sleep 1; done
|
||||||
|
|
||||||
- name: Run E2E Test Suite
|
- name: Run E2E Test Suite
|
||||||
run: yarn dlx mocha-headless-chrome --timeout 1800000 -f http://localhost:9001/mocha/test.html
|
run: yarn dlx mocha-headless-chrome --timeout 3600000 -f http://localhost:9001/mocha/test.html
|
||||||
|
|
||||||
- name: Show logs on failure
|
- name: Show logs on failure
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@master
|
uses: docker/setup-buildx-action@master
|
||||||
|
|
||||||
- name: Publish Docker image
|
- name: Publish Docker image
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
name: E2E Test Suite On Self Hosted Server
|
name: E2E Test Suite On Self Hosted Server
|
||||||
|
|
||||||
|
run-name: E2E Test Suite against ${{ inputs.ref_name }} by ${{ inputs.author }}
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 */12 * * *'
|
- cron: '0 */12 * * *'
|
||||||
@@ -9,6 +11,14 @@ on:
|
|||||||
type: string
|
type: string
|
||||||
default: latest
|
default: latest
|
||||||
description: The Docker image tag used for SNJS container
|
description: The Docker image tag used for SNJS container
|
||||||
|
author:
|
||||||
|
type: string
|
||||||
|
default: unknown
|
||||||
|
description: The author that triggered the workflow
|
||||||
|
ref_name:
|
||||||
|
type: string
|
||||||
|
default: unknown
|
||||||
|
description: The ref name from which the workflow was triggered
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
e2e:
|
e2e:
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -53,7 +53,7 @@ services:
|
|||||||
image: mysql:8
|
image: mysql:8
|
||||||
container_name: db-ci
|
container_name: db-ci
|
||||||
env_file: .github/ci.env
|
env_file: .github/ci.env
|
||||||
expose:
|
ports:
|
||||||
- 3306
|
- 3306
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
|
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
|
||||||
@@ -66,7 +66,7 @@ services:
|
|||||||
secondary_db:
|
secondary_db:
|
||||||
image: mongo:5.0
|
image: mongo:5.0
|
||||||
container_name: secondary_db-ci
|
container_name: secondary_db-ci
|
||||||
expose:
|
ports:
|
||||||
- 27017
|
- 27017
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
@@ -83,7 +83,7 @@ services:
|
|||||||
container_name: cache-ci
|
container_name: cache-ci
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/redis/:/data
|
- ./data/redis/:/data
|
||||||
expose:
|
ports:
|
||||||
- 6379
|
- 6379
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -3,6 +3,42 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [2.26.11](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.10...@standardnotes/analytics@2.26.11) (2023-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.26.10](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.9...@standardnotes/analytics@2.26.10) (2023-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.26.9](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.8...@standardnotes/analytics@2.26.9) (2023-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.26.8](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.7...@standardnotes/analytics@2.26.8) (2023-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.26.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.6...@standardnotes/analytics@2.26.7) (2023-09-08)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.26.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.5...@standardnotes/analytics@2.26.6) (2023-09-07)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.26.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.4...@standardnotes/analytics@2.26.5) (2023-09-07)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.26.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.3...@standardnotes/analytics@2.26.4) (2023-09-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
|
## [2.26.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.2...@standardnotes/analytics@2.26.3) (2023-09-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|
||||||
## [2.26.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.1...@standardnotes/analytics@2.26.2) (2023-09-01)
|
## [2.26.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.1...@standardnotes/analytics@2.26.2) (2023-09-01)
|
||||||
|
|
||||||
**Note:** Version bump only for package @standardnotes/analytics
|
**Note:** Version bump only for package @standardnotes/analytics
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@standardnotes/analytics",
|
"name": "@standardnotes/analytics",
|
||||||
"version": "2.26.2",
|
"version": "2.26.11",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0 <21.0.0"
|
"node": ">=18.0.0 <21.0.0"
|
||||||
},
|
},
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"@types/jest": "^29.5.1",
|
"@types/jest": "^29.5.1",
|
||||||
"@types/mixpanel": "^2.14.4",
|
"@types/mixpanel": "^2.14.4",
|
||||||
"@types/node": "^20.5.7",
|
"@types/node": "^20.5.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
||||||
"@typescript-eslint/parser": "^6.5.0",
|
"@typescript-eslint/parser": "^6.5.0",
|
||||||
"eslint": "^8.39.0",
|
"eslint": "^8.39.0",
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
|
|||||||
@@ -3,6 +3,50 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.74.8](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.7...@standardnotes/api-gateway@1.74.8) (2023-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.74.7](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.6...@standardnotes/api-gateway@1.74.7) (2023-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.74.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.5...@standardnotes/api-gateway@1.74.6) (2023-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.74.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.4...@standardnotes/api-gateway@1.74.5) (2023-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.74.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.3...@standardnotes/api-gateway@1.74.4) (2023-09-11)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **api-gateway:** awaiting for other services to start ([0ab4701](https://github.com/standardnotes/api-gateway/commit/0ab47013f210dca7aa404966798011947fb5c362))
|
||||||
|
|
||||||
|
## [1.74.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.2...@standardnotes/api-gateway@1.74.3) (2023-09-08)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.74.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.1...@standardnotes/api-gateway@1.74.2) (2023-09-07)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
## [1.74.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.0...@standardnotes/api-gateway@1.74.1) (2023-09-07)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
|
# [1.74.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.73.7...@standardnotes/api-gateway@1.74.0) (2023-09-06)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* should be able to access shared item revisions as third party user ([#807](https://github.com/standardnotes/api-gateway/issues/807)) ([794cd87](https://github.com/standardnotes/api-gateway/commit/794cd8734acf89fb29f09dfb169a3f08f252bb6a))
|
||||||
|
|
||||||
|
## [1.73.7](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.73.6...@standardnotes/api-gateway@1.73.7) (2023-09-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|
||||||
## [1.73.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.73.5...@standardnotes/api-gateway@1.73.6) (2023-09-01)
|
## [1.73.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.73.5...@standardnotes/api-gateway@1.73.6) (2023-09-01)
|
||||||
|
|
||||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@standardnotes/api-gateway",
|
"name": "@standardnotes/api-gateway",
|
||||||
"version": "1.73.6",
|
"version": "1.74.8",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0 <21.0.0"
|
"node": ">=18.0.0 <21.0.0"
|
||||||
},
|
},
|
||||||
@@ -54,12 +54,11 @@
|
|||||||
"@types/jsonwebtoken": "^9.0.1",
|
"@types/jsonwebtoken": "^9.0.1",
|
||||||
"@types/node": "^20.5.7",
|
"@types/node": "^20.5.7",
|
||||||
"@types/prettyjson": "^0.0.30",
|
"@types/prettyjson": "^0.0.30",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
||||||
"@typescript-eslint/parser": "^6.5.0",
|
"@typescript-eslint/parser": "^6.5.0",
|
||||||
"eslint": "^8.39.0",
|
"eslint": "^8.39.0",
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"nodemon": "^2.0.19",
|
|
||||||
"npm-check-updates": "^16.13.2",
|
"npm-check-updates": "^16.13.2",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export abstract class AuthMiddleware extends BaseMiddleware {
|
|||||||
response.locals.session = decodedToken.session
|
response.locals.session = decodedToken.session
|
||||||
response.locals.roles = decodedToken.roles
|
response.locals.roles = decodedToken.roles
|
||||||
response.locals.sharedVaultOwnerContext = decodedToken.shared_vault_owner_context
|
response.locals.sharedVaultOwnerContext = decodedToken.shared_vault_owner_context
|
||||||
|
response.locals.belongsToSharedVaults = decodedToken.belongs_to_shared_vaults ?? []
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = (error as AxiosError).isAxiosError
|
const errorMessage = (error as AxiosError).isAxiosError
|
||||||
? JSON.stringify((error as AxiosError).response?.data)
|
? JSON.stringify((error as AxiosError).response?.data)
|
||||||
|
|||||||
@@ -2,5 +2,8 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
sh supervisor/wait-for.sh localhost $AUTH_SERVER_PORT
|
||||||
|
sh supervisor/wait-for.sh localhost $FILES_SERVER_PORT
|
||||||
|
sh supervisor/wait-for.sh localhost $REVISIONS_SERVER_PORT
|
||||||
sh supervisor/wait-for.sh localhost $SYNCING_SERVER_PORT
|
sh supervisor/wait-for.sh localhost $SYNCING_SERVER_PORT
|
||||||
node docker/entrypoint-server.js
|
node docker/entrypoint-server.js
|
||||||
|
|||||||
@@ -3,6 +3,95 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.141.8](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.7...@standardnotes/auth-server@1.141.8) (2023-09-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** add transition role only if the items transition has completed ([f055e52](https://github.com/standardnotes/server/commit/f055e52e06b6e93501abd340dfce214d5363bc30))
|
||||||
|
* **auth:** remove the transition role constraint ([afe385a](https://github.com/standardnotes/server/commit/afe385aed4ba5ca53d8ef429ae4154f4ccf81419))
|
||||||
|
* imports ([54113ab](https://github.com/standardnotes/server/commit/54113abe2a961720a3561e5ff3a0069046ea8d25))
|
||||||
|
|
||||||
|
## [1.141.7](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.6...@standardnotes/auth-server@1.141.7) (2023-09-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* retry failed revision transitions ([e535cd5](https://github.com/standardnotes/server/commit/e535cd504cf1929539ff7faf13e9c1fdd2b7bfd1))
|
||||||
|
|
||||||
|
## [1.141.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.5...@standardnotes/auth-server@1.141.6) (2023-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
## [1.141.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.4...@standardnotes/auth-server@1.141.5) (2023-09-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* domain event values ([f0abfe8](https://github.com/standardnotes/server/commit/f0abfe89fca0049c47131389683efe2f5aff23f8))
|
||||||
|
|
||||||
|
## [1.141.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.3...@standardnotes/auth-server@1.141.4) (2023-09-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* adjust transitions to not create revisions during ongoing revisions transition ([106d8f9](https://github.com/standardnotes/server/commit/106d8f9192f630794ca4ddc2c4503f2c6cd196e7))
|
||||||
|
|
||||||
|
## [1.141.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.2...@standardnotes/auth-server@1.141.3) (2023-09-12)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* transition adjustments ([f20a947](https://github.com/standardnotes/server/commit/f20a947f8a555c074d8dc1543c7a8bf61d1d887e))
|
||||||
|
|
||||||
|
## [1.141.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.1...@standardnotes/auth-server@1.141.2) (2023-09-11)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** remove transitioning upon sign out ([#819](https://github.com/standardnotes/server/issues/819)) ([330bff0](https://github.com/standardnotes/server/commit/330bff0124f5f49c3441304d166ea43c21fea7bc))
|
||||||
|
|
||||||
|
## [1.141.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.141.0...@standardnotes/auth-server@1.141.1) (2023-09-11)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* disable running migrations in worker mode of a given service ([a82b9a0](https://github.com/standardnotes/server/commit/a82b9a0c8a023ba8a450ff9e34bcd62f928fcab3))
|
||||||
|
|
||||||
|
# [1.141.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.140.0...@standardnotes/auth-server@1.141.0) (2023-09-11)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** add procedure to transition users created between dates ([#816](https://github.com/standardnotes/server/issues/816)) ([e95ba61](https://github.com/standardnotes/server/commit/e95ba61c7f769736698ebbc38179d6dc05a8cc5e))
|
||||||
|
|
||||||
|
# [1.140.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.139.0...@standardnotes/auth-server@1.140.0) (2023-09-08)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** specs ([a164ba2](https://github.com/standardnotes/server/commit/a164ba291d4893b5a0bb4b39a8795d654212a1a6))
|
||||||
|
* **auth:** transition users only when transition mode enabled ([610fba2](https://github.com/standardnotes/server/commit/610fba260150d0757020e01ac0c8967feeadd4d6))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* transition users after sign out ([398338c](https://github.com/standardnotes/server/commit/398338c8f81b12406c7ab3bf1654e60b94d7cfd0))
|
||||||
|
|
||||||
|
# [1.139.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.138.2...@standardnotes/auth-server@1.139.0) (2023-09-08)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** add vaults user role into database ([aef9e93](https://github.com/standardnotes/server/commit/aef9e936bdbd1f4ccc32658d3d892e7675ec0e0e))
|
||||||
|
|
||||||
|
## [1.138.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.138.1...@standardnotes/auth-server@1.138.2) (2023-09-07)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
## [1.138.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.138.0...@standardnotes/auth-server@1.138.1) (2023-09-07)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
|
# [1.138.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.137.6...@standardnotes/auth-server@1.138.0) (2023-09-06)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* should be able to access shared item revisions as third party user ([#807](https://github.com/standardnotes/server/issues/807)) ([794cd87](https://github.com/standardnotes/server/commit/794cd8734acf89fb29f09dfb169a3f08f252bb6a))
|
||||||
|
|
||||||
|
## [1.137.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.137.5...@standardnotes/auth-server@1.137.6) (2023-09-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|
||||||
## [1.137.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.137.4...@standardnotes/auth-server@1.137.5) (2023-09-01)
|
## [1.137.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.137.4...@standardnotes/auth-server@1.137.5) (2023-09-01)
|
||||||
|
|
||||||
**Note:** Version bump only for package @standardnotes/auth-server
|
**Note:** Version bump only for package @standardnotes/auth-server
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const requestBackups = async (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = new ContainerConfigLoader()
|
const container = new ContainerConfigLoader('worker')
|
||||||
void container.load().then((container) => {
|
void container.load().then((container) => {
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const cleanup = async (
|
|||||||
await cleanupExpiredSessions.execute({ date })
|
await cleanupExpiredSessions.execute({ date })
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = new ContainerConfigLoader()
|
const container = new ContainerConfigLoader('worker')
|
||||||
void container.load().then((container) => {
|
void container.load().then((container) => {
|
||||||
const env: Env = new Env()
|
const env: Env = new Env()
|
||||||
env.load()
|
env.load()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import TYPES from '../src/Bootstrap/Types'
|
|||||||
import { Env } from '../src/Bootstrap/Env'
|
import { Env } from '../src/Bootstrap/Env'
|
||||||
import { PersistStatistics } from '../src/Domain/UseCase/PersistStatistics/PersistStatistics'
|
import { PersistStatistics } from '../src/Domain/UseCase/PersistStatistics/PersistStatistics'
|
||||||
|
|
||||||
const container = new ContainerConfigLoader()
|
const container = new ContainerConfigLoader('worker')
|
||||||
void container.load().then((container) => {
|
void container.load().then((container) => {
|
||||||
const env: Env = new Env()
|
const env: Env = new Env()
|
||||||
env.load()
|
env.load()
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import 'reflect-metadata'
|
||||||
|
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
import * as dayjs from 'dayjs'
|
||||||
|
import * as utc from 'dayjs/plugin/utc'
|
||||||
|
|
||||||
|
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||||
|
import TYPES from '../src/Bootstrap/Types'
|
||||||
|
import { Env } from '../src/Bootstrap/Env'
|
||||||
|
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||||
|
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||||
|
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
|
||||||
|
import { TransitionStatusRepositoryInterface } from '../src/Domain/Transition/TransitionStatusRepositoryInterface'
|
||||||
|
|
||||||
|
const inputArgs = process.argv.slice(2)
|
||||||
|
const startDateString = inputArgs[0]
|
||||||
|
const endDateString = inputArgs[1]
|
||||||
|
|
||||||
|
const requestTransition = async (
|
||||||
|
userRepository: UserRepositoryInterface,
|
||||||
|
transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||||
|
logger: Logger,
|
||||||
|
domainEventFactory: DomainEventFactoryInterface,
|
||||||
|
domainEventPublisher: DomainEventPublisherInterface,
|
||||||
|
): Promise<void> => {
|
||||||
|
const startDate = new Date(startDateString)
|
||||||
|
const endDate = new Date(endDateString)
|
||||||
|
|
||||||
|
const users = await userRepository.findAllCreatedBetween(startDate, endDate)
|
||||||
|
|
||||||
|
logger.info(`Found ${users.length} users created between ${startDateString} and ${endDateString}`)
|
||||||
|
|
||||||
|
let usersTriggered = 0
|
||||||
|
for (const user of users) {
|
||||||
|
const transitionRequestedEvent = domainEventFactory.createTransitionRequestedEvent({
|
||||||
|
userUuid: user.uuid,
|
||||||
|
type: 'items',
|
||||||
|
})
|
||||||
|
|
||||||
|
usersTriggered += 1
|
||||||
|
|
||||||
|
await domainEventPublisher.publish(transitionRequestedEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`Triggered transition for ${usersTriggered} users created between ${startDateString} and ${endDateString}`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const revisionStatuses = await transitionStatusRepository.getStatuses('revisions')
|
||||||
|
const failedStatuses = revisionStatuses.filter((status) => status.status === 'FAILED')
|
||||||
|
|
||||||
|
logger.info(`Found ${failedStatuses.length} failed revision transitions`)
|
||||||
|
|
||||||
|
for (const status of failedStatuses) {
|
||||||
|
const transitionRequestedEvent = domainEventFactory.createTransitionRequestedEvent({
|
||||||
|
userUuid: status.userUuid,
|
||||||
|
type: 'revisions',
|
||||||
|
})
|
||||||
|
|
||||||
|
await domainEventPublisher.publish(transitionRequestedEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = new ContainerConfigLoader('worker')
|
||||||
|
void container.load().then((container) => {
|
||||||
|
dayjs.extend(utc)
|
||||||
|
|
||||||
|
const env: Env = new Env()
|
||||||
|
env.load()
|
||||||
|
|
||||||
|
const logger: Logger = container.get(TYPES.Auth_Logger)
|
||||||
|
|
||||||
|
logger.info(`Starting transition request for users created between ${startDateString} and ${endDateString}`)
|
||||||
|
|
||||||
|
const userRepository: UserRepositoryInterface = container.get(TYPES.Auth_UserRepository)
|
||||||
|
const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.Auth_DomainEventFactory)
|
||||||
|
const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.Auth_DomainEventPublisher)
|
||||||
|
const transitionStatusRepository: TransitionStatusRepositoryInterface = container.get(
|
||||||
|
TYPES.Auth_TransitionStatusRepository,
|
||||||
|
)
|
||||||
|
|
||||||
|
Promise.resolve(
|
||||||
|
requestTransition(userRepository, transitionStatusRepository, logger, domainEventFactory, domainEventPublisher),
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
logger.info(`Finished transition request for users created between ${startDateString} and ${endDateString}`)
|
||||||
|
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.error(
|
||||||
|
`Error while requesting transition for users created between ${startDateString} and ${endDateString}: ${error}`,
|
||||||
|
)
|
||||||
|
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -63,7 +63,7 @@ const requestBackups = async (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = new ContainerConfigLoader()
|
const container = new ContainerConfigLoader('worker')
|
||||||
void container.load().then((container) => {
|
void container.load().then((container) => {
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { DomainEventSubscriberFactoryInterface } from '@standardnotes/domain-eve
|
|||||||
import * as dayjs from 'dayjs'
|
import * as dayjs from 'dayjs'
|
||||||
import * as utc from 'dayjs/plugin/utc'
|
import * as utc from 'dayjs/plugin/utc'
|
||||||
|
|
||||||
const container = new ContainerConfigLoader()
|
const container = new ContainerConfigLoader('worker')
|
||||||
void container.load().then((container) => {
|
void container.load().then((container) => {
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const pnp = require(path.normalize(path.resolve(__dirname, '../../..', '.pnp.cjs'))).setup()
|
||||||
|
|
||||||
|
const index = require(path.normalize(path.resolve(__dirname, '../dist/bin/transition.js')))
|
||||||
|
|
||||||
|
Object.defineProperty(exports, '__esModule', { value: true })
|
||||||
|
|
||||||
|
exports.default = index
|
||||||
@@ -55,6 +55,13 @@ case "$COMMAND" in
|
|||||||
node docker/entrypoint-backup.js one_drive daily
|
node docker/entrypoint-backup.js one_drive daily
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
'transition' )
|
||||||
|
START_DATE=$1 && shift 1
|
||||||
|
END_DATE=$1 && shift 1
|
||||||
|
echo "[Docker] Starting Transition..."
|
||||||
|
node docker/entrypoint-transition.js $START_DATE $END_DATE
|
||||||
|
;;
|
||||||
|
|
||||||
* )
|
* )
|
||||||
echo "[Docker] Unknown command"
|
echo "[Docker] Unknown command"
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||||
|
|
||||||
|
export class AddSharedVaultUsers1694000575425 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
'CREATE TABLE `auth_shared_vault_users` (`uuid` varchar(36) 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, INDEX `shared_vault_uuid_on_auth_shared_vault_users` (`shared_vault_uuid`), INDEX `user_uuid_on_auth_shared_vault_users` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query('DROP INDEX `user_uuid_on_auth_shared_vault_users` ON `auth_shared_vault_users`')
|
||||||
|
await queryRunner.query('DROP INDEX `shared_vault_uuid_on_auth_shared_vault_users` ON `auth_shared_vault_users`')
|
||||||
|
await queryRunner.query('DROP TABLE `auth_shared_vault_users`')
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||||
|
|
||||||
|
export class AddVaultsUser1694157482134 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
'INSERT INTO `roles` (uuid, name, version) VALUES ("35669f45-a2d8-4172-bdab-b7b3d42044ce", "VAULTS_USER", 1)',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(): Promise<void> {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||||
|
|
||||||
|
export class AddSharedVaultUsers1694000640645 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
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(
|
||||||
|
'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") ',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(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('DROP TABLE "auth_shared_vault_users"')
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@standardnotes/auth-server",
|
"name": "@standardnotes/auth-server",
|
||||||
"version": "1.137.5",
|
"version": "1.141.8",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0 <21.0.0"
|
"node": ">=18.0.0 <21.0.0"
|
||||||
},
|
},
|
||||||
@@ -84,13 +84,12 @@
|
|||||||
"@types/otplib": "^10.0.0",
|
"@types/otplib": "^10.0.0",
|
||||||
"@types/prettyjson": "^0.0.30",
|
"@types/prettyjson": "^0.0.30",
|
||||||
"@types/ua-parser-js": "^0.7.36",
|
"@types/ua-parser-js": "^0.7.36",
|
||||||
"@types/uuid": "^9.0.2",
|
"@types/uuid": "^9.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
||||||
"@typescript-eslint/parser": "^6.5.0",
|
"@typescript-eslint/parser": "^6.5.0",
|
||||||
"eslint": "^8.39.0",
|
"eslint": "^8.39.0",
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"nodemon": "^2.0.19",
|
|
||||||
"npm-check-updates": "^16.13.2",
|
"npm-check-updates": "^16.13.2",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
|
|||||||
@@ -189,6 +189,7 @@ import {
|
|||||||
ControllerContainer,
|
ControllerContainer,
|
||||||
ControllerContainerInterface,
|
ControllerContainerInterface,
|
||||||
MapperInterface,
|
MapperInterface,
|
||||||
|
SharedVaultUser,
|
||||||
} from '@standardnotes/domain-core'
|
} from '@standardnotes/domain-core'
|
||||||
import { SessionTracePersistenceMapper } from '../Mapping/SessionTracePersistenceMapper'
|
import { SessionTracePersistenceMapper } from '../Mapping/SessionTracePersistenceMapper'
|
||||||
import { SessionTrace } from '../Domain/Session/SessionTrace'
|
import { SessionTrace } from '../Domain/Session/SessionTrace'
|
||||||
@@ -263,8 +264,18 @@ import { InMemoryTransitionStatusRepository } from '../Infra/InMemory/InMemoryTr
|
|||||||
import { TransitionStatusUpdatedEventHandler } from '../Domain/Handler/TransitionStatusUpdatedEventHandler'
|
import { TransitionStatusUpdatedEventHandler } from '../Domain/Handler/TransitionStatusUpdatedEventHandler'
|
||||||
import { UpdateTransitionStatus } from '../Domain/UseCase/UpdateTransitionStatus/UpdateTransitionStatus'
|
import { UpdateTransitionStatus } from '../Domain/UseCase/UpdateTransitionStatus/UpdateTransitionStatus'
|
||||||
import { GetTransitionStatus } from '../Domain/UseCase/GetTransitionStatus/GetTransitionStatus'
|
import { GetTransitionStatus } from '../Domain/UseCase/GetTransitionStatus/GetTransitionStatus'
|
||||||
|
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||||
|
import { SharedVaultUserPersistenceMapper } from '../Mapping/SharedVaultUserPersistenceMapper'
|
||||||
|
import { SharedVaultUserRepositoryInterface } from '../Domain/SharedVault/SharedVaultUserRepositoryInterface'
|
||||||
|
import { TypeORMSharedVaultUserRepository } from '../Infra/TypeORM/TypeORMSharedVaultUserRepository'
|
||||||
|
import { AddSharedVaultUser } from '../Domain/UseCase/AddSharedVaultUser/AddSharedVaultUser'
|
||||||
|
import { RemoveSharedVaultUser } from '../Domain/UseCase/RemoveSharedVaultUser/RemoveSharedVaultUser'
|
||||||
|
import { UserAddedToSharedVaultEventHandler } from '../Domain/Handler/UserAddedToSharedVaultEventHandler'
|
||||||
|
import { UserRemovedFromSharedVaultEventHandler } from '../Domain/Handler/UserRemovedFromSharedVaultEventHandler'
|
||||||
|
|
||||||
export class ContainerConfigLoader {
|
export class ContainerConfigLoader {
|
||||||
|
constructor(private mode: 'server' | 'worker' = 'server') {}
|
||||||
|
|
||||||
async load(configuration?: {
|
async load(configuration?: {
|
||||||
controllerConatiner?: ControllerContainerInterface
|
controllerConatiner?: ControllerContainerInterface
|
||||||
directCallDomainEventPublisher?: DirectCallDomainEventPublisher
|
directCallDomainEventPublisher?: DirectCallDomainEventPublisher
|
||||||
@@ -301,7 +312,7 @@ export class ContainerConfigLoader {
|
|||||||
}
|
}
|
||||||
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(logger)
|
container.bind<winston.Logger>(TYPES.Auth_Logger).toConstantValue(logger)
|
||||||
|
|
||||||
const appDataSource = new AppDataSource(env)
|
const appDataSource = new AppDataSource({ env, runMigrations: this.mode === 'server' })
|
||||||
await appDataSource.initialize()
|
await appDataSource.initialize()
|
||||||
|
|
||||||
logger.debug('Database initialized')
|
logger.debug('Database initialized')
|
||||||
@@ -372,6 +383,9 @@ export class ContainerConfigLoader {
|
|||||||
container
|
container
|
||||||
.bind<MapperInterface<CacheEntry, TypeORMCacheEntry>>(TYPES.Auth_CacheEntryPersistenceMapper)
|
.bind<MapperInterface<CacheEntry, TypeORMCacheEntry>>(TYPES.Auth_CacheEntryPersistenceMapper)
|
||||||
.toConstantValue(new CacheEntryPersistenceMapper())
|
.toConstantValue(new CacheEntryPersistenceMapper())
|
||||||
|
container
|
||||||
|
.bind<MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>>(TYPES.Auth_SharedVaultUserPersistenceMapper)
|
||||||
|
.toConstantValue(new SharedVaultUserPersistenceMapper())
|
||||||
|
|
||||||
// ORM
|
// ORM
|
||||||
container
|
container
|
||||||
@@ -412,6 +426,9 @@ export class ContainerConfigLoader {
|
|||||||
container
|
container
|
||||||
.bind<Repository<TypeORMCacheEntry>>(TYPES.Auth_ORMCacheEntryRepository)
|
.bind<Repository<TypeORMCacheEntry>>(TYPES.Auth_ORMCacheEntryRepository)
|
||||||
.toConstantValue(appDataSource.getRepository(TypeORMCacheEntry))
|
.toConstantValue(appDataSource.getRepository(TypeORMCacheEntry))
|
||||||
|
container
|
||||||
|
.bind<Repository<TypeORMSharedVaultUser>>(TYPES.Auth_ORMSharedVaultUserRepository)
|
||||||
|
.toConstantValue(appDataSource.getRepository(TypeORMSharedVaultUser))
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
container.bind<SessionRepositoryInterface>(TYPES.Auth_SessionRepository).to(TypeORMSessionRepository)
|
container.bind<SessionRepositoryInterface>(TYPES.Auth_SessionRepository).to(TypeORMSessionRepository)
|
||||||
@@ -468,6 +485,16 @@ export class ContainerConfigLoader {
|
|||||||
container.get(TYPES.Auth_CacheEntryPersistenceMapper),
|
container.get(TYPES.Auth_CacheEntryPersistenceMapper),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
container
|
||||||
|
.bind<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository)
|
||||||
|
.toConstantValue(
|
||||||
|
new TypeORMSharedVaultUserRepository(
|
||||||
|
container.get<Repository<TypeORMSharedVaultUser>>(TYPES.Auth_ORMSharedVaultUserRepository),
|
||||||
|
container.get<MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>>(
|
||||||
|
TYPES.Auth_SharedVaultUserPersistenceMapper,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
container.bind<SessionMiddleware>(TYPES.Auth_SessionMiddleware).to(SessionMiddleware)
|
container.bind<SessionMiddleware>(TYPES.Auth_SessionMiddleware).to(SessionMiddleware)
|
||||||
@@ -926,6 +953,18 @@ export class ContainerConfigLoader {
|
|||||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
container
|
||||||
|
.bind<AddSharedVaultUser>(TYPES.Auth_AddSharedVaultUser)
|
||||||
|
.toConstantValue(
|
||||||
|
new AddSharedVaultUser(container.get<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository)),
|
||||||
|
)
|
||||||
|
container
|
||||||
|
.bind<RemoveSharedVaultUser>(TYPES.Auth_RemoveSharedVaultUser)
|
||||||
|
.toConstantValue(
|
||||||
|
new RemoveSharedVaultUser(
|
||||||
|
container.get<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// Controller
|
// Controller
|
||||||
container
|
container
|
||||||
@@ -1075,6 +1114,22 @@ export class ContainerConfigLoader {
|
|||||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
container
|
||||||
|
.bind<UserAddedToSharedVaultEventHandler>(TYPES.Auth_UserAddedToSharedVaultEventHandler)
|
||||||
|
.toConstantValue(
|
||||||
|
new UserAddedToSharedVaultEventHandler(
|
||||||
|
container.get<AddSharedVaultUser>(TYPES.Auth_AddSharedVaultUser),
|
||||||
|
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
container
|
||||||
|
.bind<UserRemovedFromSharedVaultEventHandler>(TYPES.Auth_UserRemovedFromSharedVaultEventHandler)
|
||||||
|
.toConstantValue(
|
||||||
|
new UserRemovedFromSharedVaultEventHandler(
|
||||||
|
container.get<RemoveSharedVaultUser>(TYPES.Auth_RemoveSharedVaultUser),
|
||||||
|
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||||
['USER_REGISTERED', container.get(TYPES.Auth_UserRegisteredEventHandler)],
|
['USER_REGISTERED', container.get(TYPES.Auth_UserRegisteredEventHandler)],
|
||||||
@@ -1107,6 +1162,8 @@ export class ContainerConfigLoader {
|
|||||||
['EMAIL_SUBSCRIPTION_UNSUBSCRIBED', container.get(TYPES.Auth_EmailSubscriptionUnsubscribedEventHandler)],
|
['EMAIL_SUBSCRIPTION_UNSUBSCRIBED', container.get(TYPES.Auth_EmailSubscriptionUnsubscribedEventHandler)],
|
||||||
['PAYMENTS_ACCOUNT_DELETED', container.get(TYPES.Auth_PaymentsAccountDeletedEventHandler)],
|
['PAYMENTS_ACCOUNT_DELETED', container.get(TYPES.Auth_PaymentsAccountDeletedEventHandler)],
|
||||||
['TRANSITION_STATUS_UPDATED', container.get(TYPES.Auth_TransitionStatusUpdatedEventHandler)],
|
['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)],
|
||||||
])
|
])
|
||||||
|
|
||||||
if (isConfiguredForHomeServer) {
|
if (isConfiguredForHomeServer) {
|
||||||
|
|||||||
@@ -18,11 +18,17 @@ import { TypeORMEmergencyAccessInvitation } from '../Infra/TypeORM/TypeORMEmerge
|
|||||||
import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
|
import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace'
|
||||||
import { Env } from './Env'
|
import { Env } from './Env'
|
||||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||||
|
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||||
|
|
||||||
export class AppDataSource {
|
export class AppDataSource {
|
||||||
private _dataSource: DataSource | undefined
|
private _dataSource: DataSource | undefined
|
||||||
|
|
||||||
constructor(private env: Env) {}
|
constructor(
|
||||||
|
private configuration: {
|
||||||
|
env: Env
|
||||||
|
runMigrations: boolean
|
||||||
|
},
|
||||||
|
) {}
|
||||||
|
|
||||||
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
|
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
|
||||||
if (!this._dataSource) {
|
if (!this._dataSource) {
|
||||||
@@ -37,12 +43,12 @@ export class AppDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get dataSource(): DataSource {
|
get dataSource(): DataSource {
|
||||||
this.env.load()
|
this.configuration.env.load()
|
||||||
|
|
||||||
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
|
const isConfiguredForMySQL = this.configuration.env.get('DB_TYPE') === 'mysql'
|
||||||
|
|
||||||
const maxQueryExecutionTime = this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
const maxQueryExecutionTime = this.configuration.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||||
? +this.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
? +this.configuration.env.get('DB_MAX_QUERY_EXECUTION_TIME', true)
|
||||||
: 45_000
|
: 45_000
|
||||||
|
|
||||||
const commonDataSourceOptions = {
|
const commonDataSourceOptions = {
|
||||||
@@ -64,30 +70,31 @@ export class AppDataSource {
|
|||||||
TypeORMAuthenticatorChallenge,
|
TypeORMAuthenticatorChallenge,
|
||||||
TypeORMEmergencyAccessInvitation,
|
TypeORMEmergencyAccessInvitation,
|
||||||
TypeORMCacheEntry,
|
TypeORMCacheEntry,
|
||||||
|
TypeORMSharedVaultUser,
|
||||||
],
|
],
|
||||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||||
migrationsRun: true,
|
migrationsRun: this.configuration.runMigrations,
|
||||||
logging: <LoggerOptions>this.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
|
logging: <LoggerOptions>this.configuration.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isConfiguredForMySQL) {
|
if (isConfiguredForMySQL) {
|
||||||
const inReplicaMode = this.env.get('DB_REPLICA_HOST', true) ? true : false
|
const inReplicaMode = this.configuration.env.get('DB_REPLICA_HOST', true) ? true : false
|
||||||
|
|
||||||
const replicationConfig = {
|
const replicationConfig = {
|
||||||
master: {
|
master: {
|
||||||
host: this.env.get('DB_HOST'),
|
host: this.configuration.env.get('DB_HOST'),
|
||||||
port: parseInt(this.env.get('DB_PORT')),
|
port: parseInt(this.configuration.env.get('DB_PORT')),
|
||||||
username: this.env.get('DB_USERNAME'),
|
username: this.configuration.env.get('DB_USERNAME'),
|
||||||
password: this.env.get('DB_PASSWORD'),
|
password: this.configuration.env.get('DB_PASSWORD'),
|
||||||
database: this.env.get('DB_DATABASE'),
|
database: this.configuration.env.get('DB_DATABASE'),
|
||||||
},
|
},
|
||||||
slaves: [
|
slaves: [
|
||||||
{
|
{
|
||||||
host: this.env.get('DB_REPLICA_HOST', true),
|
host: this.configuration.env.get('DB_REPLICA_HOST', true),
|
||||||
port: parseInt(this.env.get('DB_PORT')),
|
port: parseInt(this.configuration.env.get('DB_PORT')),
|
||||||
username: this.env.get('DB_USERNAME'),
|
username: this.configuration.env.get('DB_USERNAME'),
|
||||||
password: this.env.get('DB_PASSWORD'),
|
password: this.configuration.env.get('DB_PASSWORD'),
|
||||||
database: this.env.get('DB_DATABASE'),
|
database: this.configuration.env.get('DB_DATABASE'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
removeNodeErrorCount: 10,
|
removeNodeErrorCount: 10,
|
||||||
@@ -101,11 +108,11 @@ export class AppDataSource {
|
|||||||
supportBigNumbers: true,
|
supportBigNumbers: true,
|
||||||
bigNumberStrings: false,
|
bigNumberStrings: false,
|
||||||
replication: inReplicaMode ? replicationConfig : undefined,
|
replication: inReplicaMode ? replicationConfig : undefined,
|
||||||
host: inReplicaMode ? undefined : this.env.get('DB_HOST'),
|
host: inReplicaMode ? undefined : this.configuration.env.get('DB_HOST'),
|
||||||
port: inReplicaMode ? undefined : parseInt(this.env.get('DB_PORT')),
|
port: inReplicaMode ? undefined : parseInt(this.configuration.env.get('DB_PORT')),
|
||||||
username: inReplicaMode ? undefined : this.env.get('DB_USERNAME'),
|
username: inReplicaMode ? undefined : this.configuration.env.get('DB_USERNAME'),
|
||||||
password: inReplicaMode ? undefined : this.env.get('DB_PASSWORD'),
|
password: inReplicaMode ? undefined : this.configuration.env.get('DB_PASSWORD'),
|
||||||
database: inReplicaMode ? undefined : this.env.get('DB_DATABASE'),
|
database: inReplicaMode ? undefined : this.configuration.env.get('DB_DATABASE'),
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dataSource = new DataSource(mySQLDataSourceOptions)
|
this._dataSource = new DataSource(mySQLDataSourceOptions)
|
||||||
@@ -113,7 +120,7 @@ export class AppDataSource {
|
|||||||
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
||||||
...commonDataSourceOptions,
|
...commonDataSourceOptions,
|
||||||
type: 'sqlite',
|
type: 'sqlite',
|
||||||
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
|
database: this.configuration.env.get('DB_SQLITE_DATABASE_PATH'),
|
||||||
enableWAL: true,
|
enableWAL: true,
|
||||||
busyErrorRetry: 2000,
|
busyErrorRetry: 2000,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ import { Env } from './Env'
|
|||||||
const env: Env = new Env()
|
const env: Env = new Env()
|
||||||
env.load()
|
env.load()
|
||||||
|
|
||||||
export const MigrationsDataSource = new AppDataSource(env).dataSource
|
export const MigrationsDataSource = new AppDataSource({ env, runMigrations: true }).dataSource
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const TYPES = {
|
|||||||
Auth_AuthenticatorPersistenceMapper: Symbol.for('Auth_AuthenticatorPersistenceMapper'),
|
Auth_AuthenticatorPersistenceMapper: Symbol.for('Auth_AuthenticatorPersistenceMapper'),
|
||||||
Auth_AuthenticatorHttpMapper: Symbol.for('Auth_AuthenticatorHttpMapper'),
|
Auth_AuthenticatorHttpMapper: Symbol.for('Auth_AuthenticatorHttpMapper'),
|
||||||
Auth_CacheEntryPersistenceMapper: Symbol.for('Auth_CacheEntryPersistenceMapper'),
|
Auth_CacheEntryPersistenceMapper: Symbol.for('Auth_CacheEntryPersistenceMapper'),
|
||||||
|
Auth_SharedVaultUserPersistenceMapper: Symbol.for('Auth_SharedVaultUserPersistenceMapper'),
|
||||||
// Controller
|
// Controller
|
||||||
Auth_ControllerContainer: Symbol.for('Auth_ControllerContainer'),
|
Auth_ControllerContainer: Symbol.for('Auth_ControllerContainer'),
|
||||||
Auth_AuthController: Symbol.for('Auth_AuthController'),
|
Auth_AuthController: Symbol.for('Auth_AuthController'),
|
||||||
@@ -36,6 +37,7 @@ const TYPES = {
|
|||||||
Auth_AuthenticatorChallengeRepository: Symbol.for('Auth_AuthenticatorChallengeRepository'),
|
Auth_AuthenticatorChallengeRepository: Symbol.for('Auth_AuthenticatorChallengeRepository'),
|
||||||
Auth_CacheEntryRepository: Symbol.for('Auth_CacheEntryRepository'),
|
Auth_CacheEntryRepository: Symbol.for('Auth_CacheEntryRepository'),
|
||||||
Auth_TransitionStatusRepository: Symbol.for('Auth_TransitionStatusRepository'),
|
Auth_TransitionStatusRepository: Symbol.for('Auth_TransitionStatusRepository'),
|
||||||
|
Auth_SharedVaultUserRepository: Symbol.for('Auth_SharedVaultUserRepository'),
|
||||||
// ORM
|
// ORM
|
||||||
Auth_ORMOfflineSettingRepository: Symbol.for('Auth_ORMOfflineSettingRepository'),
|
Auth_ORMOfflineSettingRepository: Symbol.for('Auth_ORMOfflineSettingRepository'),
|
||||||
Auth_ORMOfflineUserSubscriptionRepository: Symbol.for('Auth_ORMOfflineUserSubscriptionRepository'),
|
Auth_ORMOfflineUserSubscriptionRepository: Symbol.for('Auth_ORMOfflineUserSubscriptionRepository'),
|
||||||
@@ -51,6 +53,7 @@ const TYPES = {
|
|||||||
Auth_ORMAuthenticatorRepository: Symbol.for('Auth_ORMAuthenticatorRepository'),
|
Auth_ORMAuthenticatorRepository: Symbol.for('Auth_ORMAuthenticatorRepository'),
|
||||||
Auth_ORMAuthenticatorChallengeRepository: Symbol.for('Auth_ORMAuthenticatorChallengeRepository'),
|
Auth_ORMAuthenticatorChallengeRepository: Symbol.for('Auth_ORMAuthenticatorChallengeRepository'),
|
||||||
Auth_ORMCacheEntryRepository: Symbol.for('Auth_ORMCacheEntryRepository'),
|
Auth_ORMCacheEntryRepository: Symbol.for('Auth_ORMCacheEntryRepository'),
|
||||||
|
Auth_ORMSharedVaultUserRepository: Symbol.for('Auth_ORMSharedVaultUserRepository'),
|
||||||
// Middleware
|
// Middleware
|
||||||
Auth_RequiredCrossServiceTokenMiddleware: Symbol.for('Auth_RequiredCrossServiceTokenMiddleware'),
|
Auth_RequiredCrossServiceTokenMiddleware: Symbol.for('Auth_RequiredCrossServiceTokenMiddleware'),
|
||||||
Auth_OptionalCrossServiceTokenMiddleware: Symbol.for('Auth_OptionalCrossServiceTokenMiddleware'),
|
Auth_OptionalCrossServiceTokenMiddleware: Symbol.for('Auth_OptionalCrossServiceTokenMiddleware'),
|
||||||
@@ -157,6 +160,8 @@ const TYPES = {
|
|||||||
Auth_UpdateStorageQuotaUsedForUser: Symbol.for('Auth_UpdateStorageQuotaUsedForUser'),
|
Auth_UpdateStorageQuotaUsedForUser: Symbol.for('Auth_UpdateStorageQuotaUsedForUser'),
|
||||||
Auth_UpdateTransitionStatus: Symbol.for('Auth_UpdateTransitionStatus'),
|
Auth_UpdateTransitionStatus: Symbol.for('Auth_UpdateTransitionStatus'),
|
||||||
Auth_GetTransitionStatus: Symbol.for('Auth_GetTransitionStatus'),
|
Auth_GetTransitionStatus: Symbol.for('Auth_GetTransitionStatus'),
|
||||||
|
Auth_AddSharedVaultUser: Symbol.for('Auth_AddSharedVaultUser'),
|
||||||
|
Auth_RemoveSharedVaultUser: Symbol.for('Auth_RemoveSharedVaultUser'),
|
||||||
// Handlers
|
// Handlers
|
||||||
Auth_UserRegisteredEventHandler: Symbol.for('Auth_UserRegisteredEventHandler'),
|
Auth_UserRegisteredEventHandler: Symbol.for('Auth_UserRegisteredEventHandler'),
|
||||||
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
||||||
@@ -186,6 +191,8 @@ const TYPES = {
|
|||||||
Auth_EmailSubscriptionUnsubscribedEventHandler: Symbol.for('Auth_EmailSubscriptionUnsubscribedEventHandler'),
|
Auth_EmailSubscriptionUnsubscribedEventHandler: Symbol.for('Auth_EmailSubscriptionUnsubscribedEventHandler'),
|
||||||
Auth_PaymentsAccountDeletedEventHandler: Symbol.for('Auth_PaymentsAccountDeletedEventHandler'),
|
Auth_PaymentsAccountDeletedEventHandler: Symbol.for('Auth_PaymentsAccountDeletedEventHandler'),
|
||||||
Auth_TransitionStatusUpdatedEventHandler: Symbol.for('Auth_TransitionStatusUpdatedEventHandler'),
|
Auth_TransitionStatusUpdatedEventHandler: Symbol.for('Auth_TransitionStatusUpdatedEventHandler'),
|
||||||
|
Auth_UserAddedToSharedVaultEventHandler: Symbol.for('Auth_UserAddedToSharedVaultEventHandler'),
|
||||||
|
Auth_UserRemovedFromSharedVaultEventHandler: Symbol.for('Auth_UserRemovedFromSharedVaultEventHandler'),
|
||||||
// Services
|
// Services
|
||||||
Auth_DeviceDetector: Symbol.for('Auth_DeviceDetector'),
|
Auth_DeviceDetector: Symbol.for('Auth_DeviceDetector'),
|
||||||
Auth_SessionService: Symbol.for('Auth_SessionService'),
|
Auth_SessionService: Symbol.for('Auth_SessionService'),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
StatisticPersistenceRequestedEvent,
|
StatisticPersistenceRequestedEvent,
|
||||||
SessionCreatedEvent,
|
SessionCreatedEvent,
|
||||||
SessionRefreshedEvent,
|
SessionRefreshedEvent,
|
||||||
|
TransitionRequestedEvent,
|
||||||
} from '@standardnotes/domain-events'
|
} from '@standardnotes/domain-events'
|
||||||
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
|
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
|
||||||
import { TimerInterface } from '@standardnotes/time'
|
import { TimerInterface } from '@standardnotes/time'
|
||||||
@@ -32,6 +33,21 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
|
|||||||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||||
constructor(@inject(TYPES.Auth_Timer) private timer: TimerInterface) {}
|
constructor(@inject(TYPES.Auth_Timer) private timer: TimerInterface) {}
|
||||||
|
|
||||||
|
createTransitionRequestedEvent(dto: { userUuid: string; type: 'items' | 'revisions' }): TransitionRequestedEvent {
|
||||||
|
return {
|
||||||
|
type: 'TRANSITION_REQUESTED',
|
||||||
|
createdAt: this.timer.getUTCDate(),
|
||||||
|
meta: {
|
||||||
|
correlation: {
|
||||||
|
userIdentifier: dto.userUuid,
|
||||||
|
userIdentifierType: 'uuid',
|
||||||
|
},
|
||||||
|
origin: DomainEventService.Auth,
|
||||||
|
},
|
||||||
|
payload: dto,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent {
|
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent {
|
||||||
return {
|
return {
|
||||||
type: 'SESSION_CREATED',
|
type: 'SESSION_CREATED',
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
StatisticPersistenceRequestedEvent,
|
StatisticPersistenceRequestedEvent,
|
||||||
SessionCreatedEvent,
|
SessionCreatedEvent,
|
||||||
SessionRefreshedEvent,
|
SessionRefreshedEvent,
|
||||||
|
TransitionRequestedEvent,
|
||||||
} from '@standardnotes/domain-events'
|
} from '@standardnotes/domain-events'
|
||||||
import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType'
|
import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType'
|
||||||
|
|
||||||
@@ -89,4 +90,5 @@ export interface DomainEventFactoryInterface {
|
|||||||
}): StatisticPersistenceRequestedEvent
|
}): StatisticPersistenceRequestedEvent
|
||||||
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent
|
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent
|
||||||
createSessionRefreshedEvent(dto: { userUuid: string }): SessionRefreshedEvent
|
createSessionRefreshedEvent(dto: { userUuid: string }): SessionRefreshedEvent
|
||||||
|
createTransitionRequestedEvent(dto: { userUuid: string; type: 'items' | 'revisions' }): TransitionRequestedEvent
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { DomainEventHandlerInterface, UserAddedToSharedVaultEvent } from '@standardnotes/domain-events'
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
import { AddSharedVaultUser } from '../UseCase/AddSharedVaultUser/AddSharedVaultUser'
|
||||||
|
|
||||||
|
export class UserAddedToSharedVaultEventHandler implements DomainEventHandlerInterface {
|
||||||
|
constructor(
|
||||||
|
private addSharedVaultUser: AddSharedVaultUser,
|
||||||
|
private logger: Logger,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async handle(event: UserAddedToSharedVaultEvent): Promise<void> {
|
||||||
|
const result = await this.addSharedVaultUser.execute({
|
||||||
|
userUuid: event.payload.userUuid,
|
||||||
|
sharedVaultUuid: event.payload.sharedVaultUuid,
|
||||||
|
permission: event.payload.permission,
|
||||||
|
createdAt: event.payload.createdAt,
|
||||||
|
updatedAt: event.payload.updatedAt,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.isFailed()) {
|
||||||
|
this.logger.error(`Failed to add user to shared vault: ${result.getError()}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { DomainEventHandlerInterface, UserRemovedFromSharedVaultEvent } from '@standardnotes/domain-events'
|
||||||
|
import { Logger } from 'winston'
|
||||||
|
|
||||||
|
import { RemoveSharedVaultUser } from '../UseCase/RemoveSharedVaultUser/RemoveSharedVaultUser'
|
||||||
|
|
||||||
|
export class UserRemovedFromSharedVaultEventHandler implements DomainEventHandlerInterface {
|
||||||
|
constructor(
|
||||||
|
private removeSharedVaultUser: RemoveSharedVaultUser,
|
||||||
|
private logger: Logger,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async handle(event: UserRemovedFromSharedVaultEvent): Promise<void> {
|
||||||
|
const result = await this.removeSharedVaultUser.execute({
|
||||||
|
userUuid: event.payload.userUuid,
|
||||||
|
sharedVaultUuid: event.payload.sharedVaultUuid,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.isFailed()) {
|
||||||
|
this.logger.error(`Failed to remove user from shared vault: ${result.getError()}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { SharedVaultUser, Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
export interface SharedVaultUserRepositoryInterface {
|
||||||
|
findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultUser | null>
|
||||||
|
findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]>
|
||||||
|
save(sharedVaultUser: SharedVaultUser): Promise<void>
|
||||||
|
remove(sharedVault: SharedVaultUser): Promise<void>
|
||||||
|
}
|
||||||
@@ -1,5 +1,15 @@
|
|||||||
export interface TransitionStatusRepositoryInterface {
|
export interface TransitionStatusRepositoryInterface {
|
||||||
updateStatus(userUuid: string, transitionType: 'items' | 'revisions', status: 'STARTED' | 'FAILED'): Promise<void>
|
updateStatus(
|
||||||
|
userUuid: string,
|
||||||
|
transitionType: 'items' | 'revisions',
|
||||||
|
status: 'STARTED' | 'IN_PROGRESS' | 'FAILED',
|
||||||
|
): Promise<void>
|
||||||
removeStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<void>
|
removeStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<void>
|
||||||
getStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<'STARTED' | 'FAILED' | null>
|
getStatus(
|
||||||
|
userUuid: string,
|
||||||
|
transitionType: 'items' | 'revisions',
|
||||||
|
): Promise<'STARTED' | 'IN_PROGRESS' | 'FAILED' | null>
|
||||||
|
getStatuses(
|
||||||
|
transitionType: 'items' | 'revisions',
|
||||||
|
): Promise<Array<{ userUuid: string; status: 'STARTED' | 'IN_PROGRESS' | 'FAILED' }>>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import { Result, SharedVaultUser } from '@standardnotes/domain-core'
|
||||||
|
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||||
|
import { AddSharedVaultUser } from './AddSharedVaultUser'
|
||||||
|
|
||||||
|
describe('AddSharedVaultUser', () => {
|
||||||
|
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||||
|
|
||||||
|
const createUseCase = () => new AddSharedVaultUser(sharedVaultUserRepository)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||||
|
sharedVaultUserRepository.save = jest.fn()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should save shared vault user', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
permission: 'read',
|
||||||
|
createdAt: 1,
|
||||||
|
updatedAt: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
expect(sharedVaultUserRepository.save).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fail when user uuid is invalid', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
userUuid: 'invalid',
|
||||||
|
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
permission: 'read',
|
||||||
|
createdAt: 1,
|
||||||
|
updatedAt: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(sharedVaultUserRepository.save).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fail when shared vault uuid is invalid', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
sharedVaultUuid: 'invalid',
|
||||||
|
permission: 'read',
|
||||||
|
createdAt: 1,
|
||||||
|
updatedAt: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(sharedVaultUserRepository.save).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fail when permission is invalid', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
permission: 'invalid',
|
||||||
|
createdAt: 1,
|
||||||
|
updatedAt: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(sharedVaultUserRepository.save).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fail when timestamps are invalid', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
permission: 'read',
|
||||||
|
createdAt: 'invalid' as unknown as number,
|
||||||
|
updatedAt: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(sharedVaultUserRepository.save).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fail when shared vault user is invalid', async () => {
|
||||||
|
const mock = jest.spyOn(SharedVaultUser, 'create')
|
||||||
|
mock.mockReturnValue(Result.fail('Oops'))
|
||||||
|
|
||||||
|
const result = await createUseCase().execute({
|
||||||
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
permission: 'read',
|
||||||
|
createdAt: 2,
|
||||||
|
updatedAt: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
|
||||||
|
mock.mockRestore()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
Result,
|
||||||
|
SharedVaultUser,
|
||||||
|
SharedVaultUserPermission,
|
||||||
|
Timestamps,
|
||||||
|
UseCaseInterface,
|
||||||
|
Uuid,
|
||||||
|
} from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||||
|
import { AddSharedVaultUserDTO } from './AddSharedVaultUserDTO'
|
||||||
|
|
||||||
|
export class AddSharedVaultUser implements UseCaseInterface<void> {
|
||||||
|
constructor(private sharedVaultUserRepository: SharedVaultUserRepositoryInterface) {}
|
||||||
|
|
||||||
|
async execute(dto: AddSharedVaultUserDTO): Promise<Result<void>> {
|
||||||
|
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
|
return Result.fail(userUuidOrError.getError())
|
||||||
|
}
|
||||||
|
const userUuid = userUuidOrError.getValue()
|
||||||
|
|
||||||
|
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||||
|
if (sharedVaultUuidOrError.isFailed()) {
|
||||||
|
return Result.fail(sharedVaultUuidOrError.getError())
|
||||||
|
}
|
||||||
|
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||||
|
|
||||||
|
const permissionOrError = SharedVaultUserPermission.create(dto.permission)
|
||||||
|
if (permissionOrError.isFailed()) {
|
||||||
|
return Result.fail(permissionOrError.getError())
|
||||||
|
}
|
||||||
|
const permission = permissionOrError.getValue()
|
||||||
|
|
||||||
|
const timestampsOrError = Timestamps.create(dto.createdAt, dto.updatedAt)
|
||||||
|
if (timestampsOrError.isFailed()) {
|
||||||
|
return Result.fail(timestampsOrError.getError())
|
||||||
|
}
|
||||||
|
const timestamps = timestampsOrError.getValue()
|
||||||
|
|
||||||
|
const sharedVaultUserOrError = SharedVaultUser.create({
|
||||||
|
userUuid,
|
||||||
|
sharedVaultUuid,
|
||||||
|
permission,
|
||||||
|
timestamps,
|
||||||
|
})
|
||||||
|
if (sharedVaultUserOrError.isFailed()) {
|
||||||
|
return Result.fail(sharedVaultUserOrError.getError())
|
||||||
|
}
|
||||||
|
const sharedVaultUser = sharedVaultUserOrError.getValue()
|
||||||
|
|
||||||
|
await this.sharedVaultUserRepository.save(sharedVaultUser)
|
||||||
|
|
||||||
|
return Result.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export interface AddSharedVaultUserDTO {
|
||||||
|
sharedVaultUuid: string
|
||||||
|
userUuid: string
|
||||||
|
permission: string
|
||||||
|
createdAt: number
|
||||||
|
updatedAt: number
|
||||||
|
}
|
||||||
+50
-2
@@ -9,8 +9,9 @@ import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
|||||||
|
|
||||||
import { CreateCrossServiceToken } from './CreateCrossServiceToken'
|
import { CreateCrossServiceToken } from './CreateCrossServiceToken'
|
||||||
import { GetSetting } from '../GetSetting/GetSetting'
|
import { GetSetting } from '../GetSetting/GetSetting'
|
||||||
import { Result } from '@standardnotes/domain-core'
|
import { Result, SharedVaultUser, SharedVaultUserPermission, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||||
|
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||||
|
|
||||||
describe('CreateCrossServiceToken', () => {
|
describe('CreateCrossServiceToken', () => {
|
||||||
let userProjector: ProjectorInterface<User>
|
let userProjector: ProjectorInterface<User>
|
||||||
@@ -20,6 +21,7 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
let userRepository: UserRepositoryInterface
|
let userRepository: UserRepositoryInterface
|
||||||
let getSettingUseCase: GetSetting
|
let getSettingUseCase: GetSetting
|
||||||
let transitionStatusRepository: TransitionStatusRepositoryInterface
|
let transitionStatusRepository: TransitionStatusRepositoryInterface
|
||||||
|
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||||
const jwtTTL = 60
|
const jwtTTL = 60
|
||||||
|
|
||||||
let session: Session
|
let session: Session
|
||||||
@@ -36,6 +38,7 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
jwtTTL,
|
jwtTTL,
|
||||||
getSettingUseCase,
|
getSettingUseCase,
|
||||||
transitionStatusRepository,
|
transitionStatusRepository,
|
||||||
|
sharedVaultUserRepository,
|
||||||
)
|
)
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -70,6 +73,16 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
|
|
||||||
transitionStatusRepository = {} as jest.Mocked<TransitionStatusRepositoryInterface>
|
transitionStatusRepository = {} as jest.Mocked<TransitionStatusRepositoryInterface>
|
||||||
transitionStatusRepository.getStatus = jest.fn().mockReturnValue('TO-DO')
|
transitionStatusRepository.getStatus = jest.fn().mockReturnValue('TO-DO')
|
||||||
|
|
||||||
|
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||||
|
sharedVaultUserRepository.findByUserUuid = jest.fn().mockReturnValue([
|
||||||
|
SharedVaultUser.create({
|
||||||
|
permission: SharedVaultUserPermission.create('read').getValue(),
|
||||||
|
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||||
|
timestamps: Timestamps.create(123456789, 123456789).getValue(),
|
||||||
|
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||||
|
}).getValue(),
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should create a cross service token for user', async () => {
|
it('should create a cross service token for user', async () => {
|
||||||
@@ -86,6 +99,12 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
uuid: '1-3-4',
|
uuid: '1-3-4',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
belongs_to_shared_vaults: [
|
||||||
|
{
|
||||||
|
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
permission: 'read',
|
||||||
|
},
|
||||||
|
],
|
||||||
session: {
|
session: {
|
||||||
test: 'test',
|
test: 'test',
|
||||||
},
|
},
|
||||||
@@ -94,13 +113,14 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
uuid: '00000000-0000-0000-0000-000000000000',
|
uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
},
|
},
|
||||||
ongoing_transition: false,
|
ongoing_transition: false,
|
||||||
|
ongoing_revisions_transition: false,
|
||||||
},
|
},
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should create a cross service token for user that has an ongoing transaction', async () => {
|
it('should create a cross service token for user that has an ongoing transaction', async () => {
|
||||||
transitionStatusRepository.getStatus = jest.fn().mockReturnValue('STARTED')
|
transitionStatusRepository.getStatus = jest.fn().mockReturnValue('IN_PROGRESS')
|
||||||
|
|
||||||
await createUseCase().execute({
|
await createUseCase().execute({
|
||||||
user,
|
user,
|
||||||
@@ -115,6 +135,12 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
uuid: '1-3-4',
|
uuid: '1-3-4',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
belongs_to_shared_vaults: [
|
||||||
|
{
|
||||||
|
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
permission: 'read',
|
||||||
|
},
|
||||||
|
],
|
||||||
session: {
|
session: {
|
||||||
test: 'test',
|
test: 'test',
|
||||||
},
|
},
|
||||||
@@ -123,6 +149,7 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
uuid: '00000000-0000-0000-0000-000000000000',
|
uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
},
|
},
|
||||||
ongoing_transition: true,
|
ongoing_transition: true,
|
||||||
|
ongoing_revisions_transition: true,
|
||||||
},
|
},
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
@@ -141,11 +168,18 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
uuid: '1-3-4',
|
uuid: '1-3-4',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
belongs_to_shared_vaults: [
|
||||||
|
{
|
||||||
|
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
permission: 'read',
|
||||||
|
},
|
||||||
|
],
|
||||||
user: {
|
user: {
|
||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
uuid: '00000000-0000-0000-0000-000000000000',
|
uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
},
|
},
|
||||||
ongoing_transition: false,
|
ongoing_transition: false,
|
||||||
|
ongoing_revisions_transition: false,
|
||||||
},
|
},
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
@@ -164,11 +198,18 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
uuid: '1-3-4',
|
uuid: '1-3-4',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
belongs_to_shared_vaults: [
|
||||||
|
{
|
||||||
|
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
permission: 'read',
|
||||||
|
},
|
||||||
|
],
|
||||||
user: {
|
user: {
|
||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
uuid: '00000000-0000-0000-0000-000000000000',
|
uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
},
|
},
|
||||||
ongoing_transition: false,
|
ongoing_transition: false,
|
||||||
|
ongoing_revisions_transition: false,
|
||||||
},
|
},
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
@@ -208,6 +249,12 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
uuid: '1-3-4',
|
uuid: '1-3-4',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
belongs_to_shared_vaults: [
|
||||||
|
{
|
||||||
|
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
permission: 'read',
|
||||||
|
},
|
||||||
|
],
|
||||||
session: {
|
session: {
|
||||||
test: 'test',
|
test: 'test',
|
||||||
},
|
},
|
||||||
@@ -218,6 +265,7 @@ describe('CreateCrossServiceToken', () => {
|
|||||||
email: 'test@test.te',
|
email: 'test@test.te',
|
||||||
uuid: '00000000-0000-0000-0000-000000000000',
|
uuid: '00000000-0000-0000-0000-000000000000',
|
||||||
},
|
},
|
||||||
|
ongoing_revisions_transition: false,
|
||||||
ongoing_transition: false,
|
ongoing_transition: false,
|
||||||
},
|
},
|
||||||
60,
|
60,
|
||||||
|
|||||||
+14
-1
@@ -13,6 +13,7 @@ import { CreateCrossServiceTokenDTO } from './CreateCrossServiceTokenDTO'
|
|||||||
import { GetSetting } from '../GetSetting/GetSetting'
|
import { GetSetting } from '../GetSetting/GetSetting'
|
||||||
import { SettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/settings'
|
||||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||||
|
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class CreateCrossServiceToken implements UseCaseInterface<string> {
|
export class CreateCrossServiceToken implements UseCaseInterface<string> {
|
||||||
@@ -27,6 +28,7 @@ export class CreateCrossServiceToken implements UseCaseInterface<string> {
|
|||||||
private getSettingUseCase: GetSetting,
|
private getSettingUseCase: GetSetting,
|
||||||
@inject(TYPES.Auth_TransitionStatusRepository)
|
@inject(TYPES.Auth_TransitionStatusRepository)
|
||||||
private transitionStatusRepository: TransitionStatusRepositoryInterface,
|
private transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||||
|
@inject(TYPES.Auth_SharedVaultUserRepository) private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: CreateCrossServiceTokenDTO): Promise<Result<string>> {
|
async execute(dto: CreateCrossServiceTokenDTO): Promise<Result<string>> {
|
||||||
@@ -46,14 +48,25 @@ export class CreateCrossServiceToken implements UseCaseInterface<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const transitionStatus = await this.transitionStatusRepository.getStatus(user.uuid, 'items')
|
const transitionStatus = await this.transitionStatusRepository.getStatus(user.uuid, 'items')
|
||||||
|
const revisionsTransitionStatus = await this.transitionStatusRepository.getStatus(user.uuid, 'revisions')
|
||||||
|
|
||||||
const roles = await user.roles
|
const roles = await user.roles
|
||||||
|
|
||||||
|
const sharedVaultAssociations = await this.sharedVaultUserRepository.findByUserUuid(
|
||||||
|
Uuid.create(user.uuid).getValue(),
|
||||||
|
)
|
||||||
|
|
||||||
const authTokenData: CrossServiceTokenData = {
|
const authTokenData: CrossServiceTokenData = {
|
||||||
user: this.projectUser(user),
|
user: this.projectUser(user),
|
||||||
roles: this.projectRoles(roles),
|
roles: this.projectRoles(roles),
|
||||||
shared_vault_owner_context: undefined,
|
shared_vault_owner_context: undefined,
|
||||||
ongoing_transition: transitionStatus === 'STARTED',
|
ongoing_transition: transitionStatus === 'IN_PROGRESS',
|
||||||
|
ongoing_revisions_transition:
|
||||||
|
revisionsTransitionStatus === 'STARTED' || revisionsTransitionStatus === 'IN_PROGRESS',
|
||||||
|
belongs_to_shared_vaults: sharedVaultAssociations.map((association) => ({
|
||||||
|
shared_vault_uuid: association.props.sharedVaultUuid.value,
|
||||||
|
permission: association.props.permission.value,
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dto.sharedVaultOwnerContext !== undefined) {
|
if (dto.sharedVaultOwnerContext !== undefined) {
|
||||||
|
|||||||
@@ -4,13 +4,17 @@ import { GetTransitionStatusDTO } from './GetTransitionStatusDTO'
|
|||||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||||
|
|
||||||
export class GetTransitionStatus implements UseCaseInterface<'TO-DO' | 'STARTED' | 'FINISHED' | 'FAILED'> {
|
export class GetTransitionStatus
|
||||||
|
implements UseCaseInterface<'TO-DO' | 'STARTED' | 'IN_PROGRESS' | 'FINISHED' | 'FAILED'>
|
||||||
|
{
|
||||||
constructor(
|
constructor(
|
||||||
private transitionStatusRepository: TransitionStatusRepositoryInterface,
|
private transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||||
private userRepository: UserRepositoryInterface,
|
private userRepository: UserRepositoryInterface,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(dto: GetTransitionStatusDTO): Promise<Result<'TO-DO' | 'STARTED' | 'FINISHED' | 'FAILED'>> {
|
async execute(
|
||||||
|
dto: GetTransitionStatusDTO,
|
||||||
|
): Promise<Result<'TO-DO' | 'STARTED' | 'IN_PROGRESS' | 'FINISHED' | 'FAILED'>> {
|
||||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||||
if (userUuidOrError.isFailed()) {
|
if (userUuidOrError.isFailed()) {
|
||||||
return Result.fail(userUuidOrError.getError())
|
return Result.fail(userUuidOrError.getError())
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { SharedVaultUser } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||||
|
import { RemoveSharedVaultUser } from './RemoveSharedVaultUser'
|
||||||
|
|
||||||
|
describe('RemoveSharedVaultUser', () => {
|
||||||
|
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||||
|
|
||||||
|
const createUseCase = () => new RemoveSharedVaultUser(sharedVaultUserRepository)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||||
|
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue({} as jest.Mocked<SharedVaultUser>)
|
||||||
|
sharedVaultUserRepository.remove = jest.fn()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should remove shared vault user', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
sharedVaultUuid: '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()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
userUuid: 'invalid',
|
||||||
|
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(sharedVaultUserRepository.remove).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fail when shared vault uuid is invalid', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
sharedVaultUuid: 'invalid',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(sharedVaultUserRepository.remove).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should fail when shared vault user is not found', async () => {
|
||||||
|
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(null)
|
||||||
|
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeTruthy()
|
||||||
|
expect(sharedVaultUserRepository.remove).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||||
|
import { RemoveSharedVaultUserDTO } from './RemoveSharedVaultUserDTO'
|
||||||
|
|
||||||
|
export class RemoveSharedVaultUser implements UseCaseInterface<void> {
|
||||||
|
constructor(private sharedVaultUserRepository: SharedVaultUserRepositoryInterface) {}
|
||||||
|
|
||||||
|
async execute(dto: RemoveSharedVaultUserDTO): Promise<Result<void>> {
|
||||||
|
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
|
return Result.fail(userUuidOrError.getError())
|
||||||
|
}
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.sharedVaultUserRepository.remove(sharedVaultUser)
|
||||||
|
|
||||||
|
return Result.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface RemoveSharedVaultUserDTO {
|
||||||
|
sharedVaultUuid: string
|
||||||
|
userUuid: string
|
||||||
|
}
|
||||||
+17
@@ -39,6 +39,23 @@ describe('UpdateTransitionStatus', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should remove transition status', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute({
|
||||||
|
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||||
|
status: 'FINISHED',
|
||||||
|
transitionType: 'revisions',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBeFalsy()
|
||||||
|
expect(transitionStatusRepository.removeStatus).toHaveBeenCalledWith(
|
||||||
|
'00000000-0000-0000-0000-000000000000',
|
||||||
|
'revisions',
|
||||||
|
)
|
||||||
|
expect(roleService.addRoleToUser).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
it('should update transition status', async () => {
|
it('should update transition status', async () => {
|
||||||
const useCase = createUseCase()
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ export class UpdateTransitionStatus implements UseCaseInterface<void> {
|
|||||||
if (dto.status === 'FINISHED') {
|
if (dto.status === 'FINISHED') {
|
||||||
await this.transitionStatusRepository.removeStatus(dto.userUuid, dto.transitionType)
|
await this.transitionStatusRepository.removeStatus(dto.userUuid, dto.transitionType)
|
||||||
|
|
||||||
await this.roleService.addRoleToUser(userUuid, RoleName.create(RoleName.NAMES.TransitionUser).getValue())
|
if (dto.transitionType === 'items') {
|
||||||
|
await this.roleService.addRoleToUser(userUuid, RoleName.create(RoleName.NAMES.TransitionUser).getValue())
|
||||||
|
}
|
||||||
|
|
||||||
return Result.ok()
|
return Result.ok()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
export interface UpdateTransitionStatusDTO {
|
export interface UpdateTransitionStatusDTO {
|
||||||
userUuid: string
|
userUuid: string
|
||||||
transitionType: 'items' | 'revisions'
|
transitionType: 'items' | 'revisions'
|
||||||
status: 'STARTED' | 'FINISHED' | 'FAILED'
|
status: 'STARTED' | 'IN_PROGRESS' | 'FINISHED' | 'FAILED'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface UserRepositoryInterface {
|
|||||||
streamTeam(memberEmail?: Email): Promise<ReadStream>
|
streamTeam(memberEmail?: Email): Promise<ReadStream>
|
||||||
findOneByUuid(uuid: Uuid): Promise<User | null>
|
findOneByUuid(uuid: Uuid): Promise<User | null>
|
||||||
findOneByUsernameOrEmail(usernameOrEmail: Email | Username): Promise<User | null>
|
findOneByUsernameOrEmail(usernameOrEmail: Email | Username): Promise<User | null>
|
||||||
|
findAllCreatedBetween(start: Date, end: Date): Promise<User[]>
|
||||||
save(user: User): Promise<User>
|
save(user: User): Promise<User>
|
||||||
remove(user: User): Promise<User>
|
remove(user: User): Promise<User>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,24 @@ export class InMemoryTransitionStatusRepository implements TransitionStatusRepos
|
|||||||
private itemStatuses: Map<string, 'STARTED' | 'FAILED'> = new Map()
|
private itemStatuses: Map<string, 'STARTED' | 'FAILED'> = new Map()
|
||||||
private revisionStatuses: Map<string, 'STARTED' | 'FAILED'> = new Map()
|
private revisionStatuses: Map<string, 'STARTED' | 'FAILED'> = new Map()
|
||||||
|
|
||||||
|
async getStatuses(
|
||||||
|
transitionType: 'items' | 'revisions',
|
||||||
|
): Promise<{ userUuid: string; status: 'STARTED' | 'FAILED' | 'IN_PROGRESS' }[]> {
|
||||||
|
const statuses: { userUuid: string; status: 'STARTED' | 'FAILED' | 'IN_PROGRESS' }[] = []
|
||||||
|
|
||||||
|
if (transitionType === 'items') {
|
||||||
|
for (const [userUuid, status] of this.itemStatuses) {
|
||||||
|
statuses.push({ userUuid, status })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const [userUuid, status] of this.revisionStatuses) {
|
||||||
|
statuses.push({ userUuid, status })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return statuses
|
||||||
|
}
|
||||||
|
|
||||||
async updateStatus(
|
async updateStatus(
|
||||||
userUuid: string,
|
userUuid: string,
|
||||||
transitionType: 'items' | 'revisions',
|
transitionType: 'items' | 'revisions',
|
||||||
|
|||||||
@@ -7,21 +7,44 @@ export class RedisTransitionStatusRepository implements TransitionStatusReposito
|
|||||||
|
|
||||||
constructor(private redisClient: IORedis.Redis) {}
|
constructor(private redisClient: IORedis.Redis) {}
|
||||||
|
|
||||||
|
async getStatuses(
|
||||||
|
transitionType: 'items' | 'revisions',
|
||||||
|
): Promise<{ userUuid: string; status: 'STARTED' | 'IN_PROGRESS' | 'FAILED' }[]> {
|
||||||
|
const keys = await this.redisClient.keys(`${this.PREFIX}:${transitionType}:*`)
|
||||||
|
const statuses = await Promise.all(
|
||||||
|
keys.map(async (key) => {
|
||||||
|
const userUuid = key.split(':')[2]
|
||||||
|
const status = (await this.redisClient.get(key)) as 'STARTED' | 'IN_PROGRESS' | 'FAILED'
|
||||||
|
return { userUuid, status }
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
return statuses
|
||||||
|
}
|
||||||
|
|
||||||
async updateStatus(
|
async updateStatus(
|
||||||
userUuid: string,
|
userUuid: string,
|
||||||
transitionType: 'items' | 'revisions',
|
transitionType: 'items' | 'revisions',
|
||||||
status: 'STARTED' | 'FAILED',
|
status: 'STARTED' | 'IN_PROGRESS' | 'FAILED',
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.redisClient.set(`${this.PREFIX}:${transitionType}:${userUuid}`, status)
|
if (status === 'IN_PROGRESS') {
|
||||||
|
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, 7200, status)
|
||||||
|
} else {
|
||||||
|
await this.redisClient.set(`${this.PREFIX}:${transitionType}:${userUuid}`, status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<void> {
|
async removeStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<void> {
|
||||||
await this.redisClient.del(`${this.PREFIX}:${transitionType}:${userUuid}`)
|
await this.redisClient.del(`${this.PREFIX}:${transitionType}:${userUuid}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<'STARTED' | 'FAILED' | null> {
|
async getStatus(
|
||||||
|
userUuid: string,
|
||||||
|
transitionType: 'items' | 'revisions',
|
||||||
|
): Promise<'STARTED' | 'IN_PROGRESS' | 'FAILED' | null> {
|
||||||
const status = (await this.redisClient.get(`${this.PREFIX}:${transitionType}:${userUuid}`)) as
|
const status = (await this.redisClient.get(`${this.PREFIX}:${transitionType}:${userUuid}`)) as
|
||||||
| 'STARTED'
|
| 'STARTED'
|
||||||
|
| 'IN_PROGRESS'
|
||||||
| 'FAILED'
|
| 'FAILED'
|
||||||
| null
|
| null
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||||
|
|
||||||
|
@Entity({ name: 'auth_shared_vault_users' })
|
||||||
|
export class TypeORMSharedVaultUser {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
declare uuid: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'shared_vault_uuid',
|
||||||
|
length: 36,
|
||||||
|
})
|
||||||
|
@Index('shared_vault_uuid_on_auth_shared_vault_users')
|
||||||
|
declare sharedVaultUuid: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'user_uuid',
|
||||||
|
length: 36,
|
||||||
|
})
|
||||||
|
@Index('user_uuid_on_auth_shared_vault_users')
|
||||||
|
declare userUuid: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'permission',
|
||||||
|
type: 'varchar',
|
||||||
|
length: 24,
|
||||||
|
})
|
||||||
|
declare permission: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'created_at_timestamp',
|
||||||
|
type: 'bigint',
|
||||||
|
})
|
||||||
|
declare createdAtTimestamp: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'updated_at_timestamp',
|
||||||
|
type: 'bigint',
|
||||||
|
})
|
||||||
|
declare updatedAtTimestamp: number
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { Repository } from 'typeorm'
|
||||||
|
import { MapperInterface, SharedVaultUser, Uuid } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
import { SharedVaultUserRepositoryInterface } from '../../Domain/SharedVault/SharedVaultUserRepositoryInterface'
|
||||||
|
import { TypeORMSharedVaultUser } from './TypeORMSharedVaultUser'
|
||||||
|
|
||||||
|
export class TypeORMSharedVaultUserRepository implements SharedVaultUserRepositoryInterface {
|
||||||
|
constructor(
|
||||||
|
private ormRepository: Repository<TypeORMSharedVaultUser>,
|
||||||
|
private mapper: MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]> {
|
||||||
|
const persistence = await this.ormRepository
|
||||||
|
.createQueryBuilder('shared_vault_user')
|
||||||
|
.where('shared_vault_user.user_uuid = :userUuid', {
|
||||||
|
userUuid: userUuid.value,
|
||||||
|
})
|
||||||
|
.getMany()
|
||||||
|
|
||||||
|
return persistence.map((p) => this.mapper.toDomain(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByUserUuidAndSharedVaultUuid(dto: {
|
||||||
|
userUuid: Uuid
|
||||||
|
sharedVaultUuid: Uuid
|
||||||
|
}): Promise<SharedVaultUser | null> {
|
||||||
|
const persistence = await this.ormRepository
|
||||||
|
.createQueryBuilder('shared_vault_user')
|
||||||
|
.where('shared_vault_user.user_uuid = :userUuid', {
|
||||||
|
userUuid: dto.userUuid.value,
|
||||||
|
})
|
||||||
|
.andWhere('shared_vault_user.shared_vault_uuid = :sharedVaultUuid', {
|
||||||
|
sharedVaultUuid: dto.sharedVaultUuid.value,
|
||||||
|
})
|
||||||
|
.getOne()
|
||||||
|
|
||||||
|
if (persistence === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.mapper.toDomain(persistence)
|
||||||
|
}
|
||||||
|
|
||||||
|
async save(sharedVaultUser: SharedVaultUser): Promise<void> {
|
||||||
|
const persistence = this.mapper.toProjection(sharedVaultUser)
|
||||||
|
|
||||||
|
await this.ormRepository.save(persistence)
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(sharedVaultUser: SharedVaultUser): Promise<void> {
|
||||||
|
await this.ormRepository.remove(this.mapper.toProjection(sharedVaultUser))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,13 @@ export class TypeORMUserRepository implements UserRepositoryInterface {
|
|||||||
private ormRepository: Repository<User>,
|
private ormRepository: Repository<User>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
async findAllCreatedBetween(start: Date, end: Date): Promise<User[]> {
|
||||||
|
return this.ormRepository
|
||||||
|
.createQueryBuilder('user')
|
||||||
|
.where('user.created_at BETWEEN :start AND :end', { start, end })
|
||||||
|
.getMany()
|
||||||
|
}
|
||||||
|
|
||||||
async save(user: User): Promise<User> {
|
async save(user: User): Promise<User> {
|
||||||
return this.ormRepository.save(user)
|
return this.ormRepository.save(user)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import {
|
||||||
|
Timestamps,
|
||||||
|
MapperInterface,
|
||||||
|
UniqueEntityId,
|
||||||
|
Uuid,
|
||||||
|
SharedVaultUserPermission,
|
||||||
|
SharedVaultUser,
|
||||||
|
} from '@standardnotes/domain-core'
|
||||||
|
|
||||||
|
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||||
|
|
||||||
|
export class SharedVaultUserPersistenceMapper implements MapperInterface<SharedVaultUser, TypeORMSharedVaultUser> {
|
||||||
|
toDomain(projection: TypeORMSharedVaultUser): SharedVaultUser {
|
||||||
|
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||||
|
if (userUuidOrError.isFailed()) {
|
||||||
|
throw new Error(`Failed to create shared vault user from projection: ${userUuidOrError.getError()}`)
|
||||||
|
}
|
||||||
|
const userUuid = userUuidOrError.getValue()
|
||||||
|
|
||||||
|
const sharedVaultUuidOrError = Uuid.create(projection.sharedVaultUuid)
|
||||||
|
if (sharedVaultUuidOrError.isFailed()) {
|
||||||
|
throw new Error(`Failed to create shared vault user from projection: ${sharedVaultUuidOrError.getError()}`)
|
||||||
|
}
|
||||||
|
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||||
|
|
||||||
|
const timestampsOrError = Timestamps.create(projection.createdAtTimestamp, projection.updatedAtTimestamp)
|
||||||
|
if (timestampsOrError.isFailed()) {
|
||||||
|
throw new Error(`Failed to create shared vault user from projection: ${timestampsOrError.getError()}`)
|
||||||
|
}
|
||||||
|
const timestamps = timestampsOrError.getValue()
|
||||||
|
|
||||||
|
const permissionOrError = SharedVaultUserPermission.create(projection.permission)
|
||||||
|
if (permissionOrError.isFailed()) {
|
||||||
|
throw new Error(`Failed to create shared vault user from projection: ${permissionOrError.getError()}`)
|
||||||
|
}
|
||||||
|
const permission = permissionOrError.getValue()
|
||||||
|
|
||||||
|
const sharedVaultUserOrError = SharedVaultUser.create(
|
||||||
|
{
|
||||||
|
userUuid,
|
||||||
|
sharedVaultUuid,
|
||||||
|
permission,
|
||||||
|
timestamps,
|
||||||
|
},
|
||||||
|
new UniqueEntityId(projection.uuid),
|
||||||
|
)
|
||||||
|
if (sharedVaultUserOrError.isFailed()) {
|
||||||
|
throw new Error(`Failed to create shared vault user from projection: ${sharedVaultUserOrError.getError()}`)
|
||||||
|
}
|
||||||
|
const sharedVaultUser = sharedVaultUserOrError.getValue()
|
||||||
|
|
||||||
|
return sharedVaultUser
|
||||||
|
}
|
||||||
|
|
||||||
|
toProjection(domain: SharedVaultUser): TypeORMSharedVaultUser {
|
||||||
|
const typeorm = new TypeORMSharedVaultUser()
|
||||||
|
|
||||||
|
typeorm.uuid = domain.id.toString()
|
||||||
|
typeorm.sharedVaultUuid = domain.props.sharedVaultUuid.value
|
||||||
|
typeorm.userUuid = domain.props.userUuid.value
|
||||||
|
typeorm.permission = domain.props.permission.value
|
||||||
|
typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
|
||||||
|
typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
|
||||||
|
|
||||||
|
return typeorm
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user