Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
5c100e7bca chore(deps): bump docker/build-push-action from 4 to 5
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 12:19:19 +00:00
164 changed files with 793 additions and 2924 deletions

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: analytics
workspace_name: "@standardnotes/analytics"
deploy_web: false
package_path: packages/analytics
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: api-gateway
workspace_name: "@standardnotes/api-gateway"
deploy_worker: false
package_path: packages/api-gateway
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: auth
workspace_name: "@standardnotes/auth-server"
package_path: packages/auth
secrets: inherit

View File

@@ -9,9 +9,6 @@ on:
package_path:
required: true
type: string
workspace_name:
required: true
type: string
secrets:
DOCKER_USERNAME:
required: true
@@ -33,14 +30,6 @@ jobs:
with:
python-version: '3.11'
- name: Cache build
id: cache-build
uses: actions/cache@v3
with:
path: |
packages/**/dist
key: ${{ runner.os }}-${{ inputs.service_name }}-build-${{ github.sha }}
- name: Set up Node
uses: actions/setup-node@v3
with:
@@ -50,10 +39,6 @@ jobs:
- name: Install dependencies
run: yarn install --immutable
- name: Build
if: steps.cache-build.outputs.cache-hit != 'true'
run: yarn build
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
@@ -81,7 +66,7 @@ jobs:
uses: docker/setup-buildx-action@master
- name: Publish Docker image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .

View File

@@ -24,6 +24,7 @@ jobs:
fail-fast: false
matrix:
secondary_db_enabled: [true, false]
transition_mode_enabled: [true, false]
runs-on: ubuntu-latest
services:
@@ -50,6 +51,7 @@ jobs:
DB_TYPE: mysql
CACHE_TYPE: redis
SECONDARY_DB_ENABLED: ${{ matrix.secondary_db_enabled }}
TRANSITION_MODE_ENABLED: ${{ matrix.transition_mode_enabled }}
- name: Wait for server to start
run: docker/is-available.sh http://localhost:3123 $(pwd)/logs
@@ -73,6 +75,7 @@ jobs:
db_type: [mysql, sqlite]
cache_type: [redis, memory]
secondary_db_enabled: [true, false]
transition_mode_enabled: [true, false]
runs-on: ubuntu-latest
@@ -142,6 +145,7 @@ jobs:
echo "REDIS_URL=redis://localhost:6379" >> packages/home-server/.env
echo "CACHE_TYPE=${{ matrix.cache_type }}" >> packages/home-server/.env
echo "SECONDARY_DB_ENABLED=${{ matrix.secondary_db_enabled }}" >> packages/home-server/.env
echo "TRANSITION_MODE_ENABLED=${{ matrix.transition_mode_enabled }}" >> packages/home-server/.env
echo "MONGO_HOST=localhost" >> packages/home-server/.env
echo "MONGO_PORT=27017" >> packages/home-server/.env
echo "MONGO_DATABASE=standardnotes" >> packages/home-server/.env

View File

@@ -35,7 +35,7 @@ jobs:
uses: docker/setup-buildx-action@master
- name: Publish Docker image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: .

View File

@@ -6,9 +6,6 @@ on:
service_name:
required: true
type: string
workspace_name:
required: true
type: string
deploy_web:
required: false
default: true
@@ -39,7 +36,6 @@ jobs:
with:
service_name: ${{ inputs.service_name }}
package_path: ${{ inputs.package_path }}
workspace_name: ${{ inputs.workspace_name }}
secrets: inherit
deploy-web:

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: files
workspace_name: "@standardnotes/files-server"
package_path: packages/files
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: revisions
workspace_name: "@standardnotes/revisions-server"
package_path: packages/revisions
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: scheduler
workspace_name: "@standardnotes/scheduler-server"
deploy_web: false
package_path: packages/scheduler
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: syncing-server-js
workspace_name: "@standardnotes/syncing-server"
package_path: packages/syncing-server
secrets: inherit

View File

@@ -16,7 +16,6 @@ jobs:
uses: standardnotes/server/.github/workflows/common-server-application.yml@main
with:
service_name: websockets
workspace_name: "@standardnotes/websockets-server"
package_path: packages/websockets
secrets: inherit

View File

@@ -24,6 +24,7 @@ services:
DB_TYPE: "${DB_TYPE}"
CACHE_TYPE: "${CACHE_TYPE}"
SECONDARY_DB_ENABLED: "${SECONDARY_DB_ENABLED}"
TRANSITION_MODE_ENABLED: "${TRANSITION_MODE_ENABLED}"
container_name: server-ci
ports:
- 3123:3000

View File

@@ -68,6 +68,9 @@ fi
if [ -z "$SECONDARY_DB_ENABLED" ]; then
export SECONDARY_DB_ENABLED=false
fi
if [ -z "$TRANSITION_MODE_ENABLED" ]; then
export TRANSITION_MODE_ENABLED=false
fi
export DB_MIGRATIONS_PATH="dist/migrations/*.js"
#########

View File

@@ -3,8 +3,6 @@ module.exports = {
testEnvironment: 'node',
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.ts$',
testTimeout: 20000,
coverageReporters: ['text'],
reporters: ['summary'],
coverageThreshold: {
global: {
branches: 100,

View File

@@ -3,22 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.26.20](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.19...@standardnotes/analytics@2.26.20) (2023-09-21)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.19](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.18...@standardnotes/analytics@2.26.19) (2023-09-20)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.18](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.17...@standardnotes/analytics@2.26.18) (2023-09-20)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.17](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.16...@standardnotes/analytics@2.26.17) (2023-09-19)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.16](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.15...@standardnotes/analytics@2.26.16) (2023-09-18)
**Note:** Version bump only for package @standardnotes/analytics

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.26.20",
"version": "2.26.16",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,24 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.75.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.17...@standardnotes/api-gateway@1.75.0) (2023-09-21)
### Features
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/api-gateway/issues/841)) ([230c96d](https://github.com/standardnotes/api-gateway/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
## [1.74.17](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.16...@standardnotes/api-gateway@1.74.17) (2023-09-20)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.74.16](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.15...@standardnotes/api-gateway@1.74.16) (2023-09-20)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.74.15](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.14...@standardnotes/api-gateway@1.74.15) (2023-09-19)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.74.14](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.74.13...@standardnotes/api-gateway@1.74.14) (2023-09-18)
**Note:** Version bump only for package @standardnotes/api-gateway

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.75.0",
"version": "1.74.14",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -1,6 +1,6 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
import { BaseHttpController, controller, httpDelete, httpGet } from 'inversify-express-utils'
import { TYPES } from '../../Bootstrap/Types'
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
@@ -42,19 +42,4 @@ export class SharedVaultUsersController extends BaseHttpController {
request.body,
)
}
@httpPost('/:userUuid/designate-survivor')
async designateSurvivor(request: Request, response: Response): Promise<void> {
await this.httpService.callSyncingServer(
request,
response,
this.endpointResolver.resolveEndpointOrMethodIdentifier(
'POST',
'shared-vaults/:sharedVaultUuid/users/:userUuid/designate-survivor',
request.params.sharedVaultUuid,
request.params.userUuid,
),
request.body,
)
}
}

View File

@@ -89,10 +89,6 @@ export class EndpointResolver implements EndpointResolverInterface {
// Shared Vault Users Controller
['[GET]:shared-vaults/:sharedVaultUuid/users', 'sync.shared-vault-users.get-users'],
['[DELETE]:shared-vaults/:sharedVaultUuid/users/:userUuid', 'sync.shared-vault-users.remove-user'],
[
'[POST]:shared-vaults/:sharedVaultUuid/users/:userUuid/designate-survivor',
'sync.shared-vault-users.designate-survivor',
],
])
resolveEndpointOrMethodIdentifier(method: string, endpoint: string, ...params: string[]): string {

View File

@@ -3,52 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.146.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.145.0...@standardnotes/auth-server@1.146.0) (2023-09-22)
### Bug Fixes
* **auth:** register specs ([f9b1f40](https://github.com/standardnotes/server/commit/f9b1f40ddf2d733d106ea64b9a7c4b38c5ec43ce))
### Features
* remove transition mode from code ([5001496](https://github.com/standardnotes/server/commit/5001496c7bc1df9e20c2d88ebf52ed77f868110c))
# [1.145.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.144.0...@standardnotes/auth-server@1.145.0) (2023-09-22)
### Features
* remove user from all shared vaults upon account deletion ([#843](https://github.com/standardnotes/server/issues/843)) ([dc77ff3](https://github.com/standardnotes/server/commit/dc77ff3e45983d231bc9c132802428e77b4be431))
# [1.144.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.9...@standardnotes/auth-server@1.144.0) (2023-09-21)
### Features
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/server/issues/841)) ([230c96d](https://github.com/standardnotes/server/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
## [1.143.9](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.8...@standardnotes/auth-server@1.143.9) (2023-09-20)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.143.8](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.7...@standardnotes/auth-server@1.143.8) (2023-09-20)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.143.7](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.6...@standardnotes/auth-server@1.143.7) (2023-09-19)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.143.6](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.4...@standardnotes/auth-server@1.143.6) (2023-09-19)
### Bug Fixes
* **auth:** bump version ([b91cd7e](https://github.com/standardnotes/server/commit/b91cd7e2329d68026665f61d9bd17ba918c57563))
## [1.143.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.3...@standardnotes/auth-server@1.143.4) (2023-09-19)
### Bug Fixes
* **auth:** add debug logs to transition ([bf855bb](https://github.com/standardnotes/server/commit/bf855bb26e8a3618113bd2f5801c260782566815))
## [1.143.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.143.2...@standardnotes/auth-server@1.143.3) (2023-09-18)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -53,10 +53,7 @@ const requestTransition = async (
continue
}
let wasTransitionRequested = false
if (itemsTransitionStatus?.value !== TransitionStatus.STATUSES.Verified) {
wasTransitionRequested = true
await transitionStatusRepository.remove(user.uuid, 'items')
await domainEventPublisher.publish(
@@ -69,7 +66,6 @@ const requestTransition = async (
}
if (revisionsTransitionStatus?.value !== TransitionStatus.STATUSES.Verified) {
wasTransitionRequested = true
await transitionStatusRepository.remove(user.uuid, 'revisions')
await domainEventPublisher.publish(
@@ -82,12 +78,6 @@ const requestTransition = async (
}
usersTriggered += 1
if (wasTransitionRequested) {
logger.info(
`[TRANSITION ${timestamp}] Transition requested for user ${user.uuid} - items status: ${itemsTransitionStatus?.value}, revisions status: ${revisionsTransitionStatus?.value}, has transition role: ${userHasTransitionRole}`,
)
}
}
logger.info(

Binary file not shown.

View File

@@ -1,15 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddDesignatedSurvivor1695283870612 implements MigrationInterface {
name = 'AddDesignatedSurvivor1695283870612'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
'ALTER TABLE `auth_shared_vault_users` ADD `is_designated_survivor` tinyint NOT NULL DEFAULT 0',
)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `auth_shared_vault_users` DROP COLUMN `is_designated_survivor`')
}
}

View File

@@ -1,43 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddDesignatedSurvivor1695283961201 implements MigrationInterface {
name = 'AddDesignatedSurvivor1695283961201'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('DROP INDEX "user_uuid_on_auth_shared_vault_users"')
await queryRunner.query('DROP INDEX "shared_vault_uuid_on_auth_shared_vault_users"')
await queryRunner.query(
'CREATE TABLE "temporary_auth_shared_vault_users" ("uuid" varchar PRIMARY KEY NOT NULL, "shared_vault_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36) NOT NULL, "permission" varchar(24) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL, "is_designated_survivor" boolean NOT NULL DEFAULT (0))',
)
await queryRunner.query(
'INSERT INTO "temporary_auth_shared_vault_users"("uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp" FROM "auth_shared_vault_users"',
)
await queryRunner.query('DROP TABLE "auth_shared_vault_users"')
await queryRunner.query('ALTER TABLE "temporary_auth_shared_vault_users" RENAME TO "auth_shared_vault_users"')
await queryRunner.query(
'CREATE INDEX "user_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("user_uuid") ',
)
await queryRunner.query(
'CREATE INDEX "shared_vault_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("shared_vault_uuid") ',
)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('DROP INDEX "shared_vault_uuid_on_auth_shared_vault_users"')
await queryRunner.query('DROP INDEX "user_uuid_on_auth_shared_vault_users"')
await queryRunner.query('ALTER TABLE "auth_shared_vault_users" RENAME TO "temporary_auth_shared_vault_users"')
await queryRunner.query(
'CREATE TABLE "auth_shared_vault_users" ("uuid" varchar PRIMARY KEY NOT NULL, "shared_vault_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36) NOT NULL, "permission" varchar(24) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
)
await queryRunner.query(
'INSERT INTO "auth_shared_vault_users"("uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp" FROM "temporary_auth_shared_vault_users"',
)
await queryRunner.query('DROP TABLE "temporary_auth_shared_vault_users"')
await queryRunner.query(
'CREATE INDEX "shared_vault_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("shared_vault_uuid") ',
)
await queryRunner.query(
'CREATE INDEX "user_uuid_on_auth_shared_vault_users" ON "auth_shared_vault_users" ("user_uuid") ',
)
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.146.0",
"version": "1.143.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -271,8 +271,6 @@ import { AddSharedVaultUser } from '../Domain/UseCase/AddSharedVaultUser/AddShar
import { RemoveSharedVaultUser } from '../Domain/UseCase/RemoveSharedVaultUser/RemoveSharedVaultUser'
import { UserAddedToSharedVaultEventHandler } from '../Domain/Handler/UserAddedToSharedVaultEventHandler'
import { UserRemovedFromSharedVaultEventHandler } from '../Domain/Handler/UserRemovedFromSharedVaultEventHandler'
import { DesignateSurvivor } from '../Domain/UseCase/DesignateSurvivor/DesignateSurvivor'
import { UserDesignatedAsSurvivorInSharedVaultEventHandler } from '../Domain/Handler/UserDesignatedAsSurvivorInSharedVaultEventHandler'
export class ContainerConfigLoader {
constructor(private mode: 'server' | 'worker' = 'server') {}
@@ -595,6 +593,9 @@ export class ContainerConfigLoader {
container
.bind(TYPES.Auth_READONLY_USERS)
.toConstantValue(env.get('READONLY_USERS', true) ? env.get('READONLY_USERS', true).split(',') : [])
container
.bind(TYPES.Auth_TRANSITION_MODE_ENABLED)
.toConstantValue(env.get('TRANSITION_MODE_ENABLED', true) === 'true')
if (isConfiguredForInMemoryCache) {
container
@@ -956,14 +957,6 @@ export class ContainerConfigLoader {
container.get<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository),
),
)
container
.bind<DesignateSurvivor>(TYPES.Auth_DesignateSurvivor)
.toConstantValue(
new DesignateSurvivor(
container.get<SharedVaultUserRepositoryInterface>(TYPES.Auth_SharedVaultUserRepository),
container.get<TimerInterface>(TYPES.Auth_Timer),
),
)
// Controller
container
@@ -1005,16 +998,7 @@ export class ContainerConfigLoader {
container.bind<UserRegisteredEventHandler>(TYPES.Auth_UserRegisteredEventHandler).to(UserRegisteredEventHandler)
container
.bind<AccountDeletionRequestedEventHandler>(TYPES.Auth_AccountDeletionRequestedEventHandler)
.toConstantValue(
new AccountDeletionRequestedEventHandler(
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
container.get<SessionRepositoryInterface>(TYPES.Auth_SessionRepository),
container.get<EphemeralSessionRepositoryInterface>(TYPES.Auth_EphemeralSessionRepository),
container.get<RevokedSessionRepositoryInterface>(TYPES.Auth_RevokedSessionRepository),
container.get<RemoveSharedVaultUser>(TYPES.Auth_RemoveSharedVaultUser),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
.to(AccountDeletionRequestedEventHandler)
container
.bind<SubscriptionPurchasedEventHandler>(TYPES.Auth_SubscriptionPurchasedEventHandler)
.to(SubscriptionPurchasedEventHandler)
@@ -1138,16 +1122,6 @@ export class ContainerConfigLoader {
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
container
.bind<UserDesignatedAsSurvivorInSharedVaultEventHandler>(
TYPES.Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler,
)
.toConstantValue(
new UserDesignatedAsSurvivorInSharedVaultEventHandler(
container.get<DesignateSurvivor>(TYPES.Auth_DesignateSurvivor),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
['USER_REGISTERED', container.get(TYPES.Auth_UserRegisteredEventHandler)],
@@ -1182,10 +1156,6 @@ export class ContainerConfigLoader {
['TRANSITION_STATUS_UPDATED', container.get(TYPES.Auth_TransitionStatusUpdatedEventHandler)],
['USER_ADDED_TO_SHARED_VAULT', container.get(TYPES.Auth_UserAddedToSharedVaultEventHandler)],
['USER_REMOVED_FROM_SHARED_VAULT', container.get(TYPES.Auth_UserRemovedFromSharedVaultEventHandler)],
[
'USER_DESIGNATED_AS_SURVIVOR_IN_SHARED_VAULT',
container.get(TYPES.Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler),
],
])
if (isConfiguredForHomeServer) {

View File

@@ -105,6 +105,7 @@ const TYPES = {
Auth_U2F_EXPECTED_ORIGIN: Symbol.for('Auth_U2F_EXPECTED_ORIGIN'),
Auth_U2F_REQUIRE_USER_VERIFICATION: Symbol.for('Auth_U2F_REQUIRE_USER_VERIFICATION'),
Auth_READONLY_USERS: Symbol.for('Auth_READONLY_USERS'),
Auth_TRANSITION_MODE_ENABLED: Symbol.for('Auth_TRANSITION_MODE_ENABLED'),
// use cases
Auth_AuthenticateUser: Symbol.for('Auth_AuthenticateUser'),
Auth_AuthenticateRequest: Symbol.for('Auth_AuthenticateRequest'),
@@ -160,7 +161,6 @@ const TYPES = {
Auth_UpdateTransitionStatus: Symbol.for('Auth_UpdateTransitionStatus'),
Auth_AddSharedVaultUser: Symbol.for('Auth_AddSharedVaultUser'),
Auth_RemoveSharedVaultUser: Symbol.for('Auth_RemoveSharedVaultUser'),
Auth_DesignateSurvivor: Symbol.for('Auth_DesignateSurvivor'),
// Handlers
Auth_UserRegisteredEventHandler: Symbol.for('Auth_UserRegisteredEventHandler'),
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
@@ -192,9 +192,6 @@ const TYPES = {
Auth_TransitionStatusUpdatedEventHandler: Symbol.for('Auth_TransitionStatusUpdatedEventHandler'),
Auth_UserAddedToSharedVaultEventHandler: Symbol.for('Auth_UserAddedToSharedVaultEventHandler'),
Auth_UserRemovedFromSharedVaultEventHandler: Symbol.for('Auth_UserRemovedFromSharedVaultEventHandler'),
Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler: Symbol.for(
'Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler',
),
// Services
Auth_DeviceDetector: Symbol.for('Auth_DeviceDetector'),
Auth_SessionService: Symbol.for('Auth_SessionService'),

View File

@@ -0,0 +1,114 @@
import 'reflect-metadata'
import { AccountDeletionRequestedEvent } from '@standardnotes/domain-events'
import { Logger } from 'winston'
import { EphemeralSession } from '../Session/EphemeralSession'
import { EphemeralSessionRepositoryInterface } from '../Session/EphemeralSessionRepositoryInterface'
import { RevokedSession } from '../Session/RevokedSession'
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
import { Session } from '../Session/Session'
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
import { User } from '../User/User'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
describe('AccountDeletionRequestedEventHandler', () => {
let userRepository: UserRepositoryInterface
let sessionRepository: SessionRepositoryInterface
let ephemeralSessionRepository: EphemeralSessionRepositoryInterface
let revokedSessionRepository: RevokedSessionRepositoryInterface
let logger: Logger
let session: Session
let ephemeralSession: EphemeralSession
let revokedSession: RevokedSession
let user: User
let event: AccountDeletionRequestedEvent
const createHandler = () =>
new AccountDeletionRequestedEventHandler(
userRepository,
sessionRepository,
ephemeralSessionRepository,
revokedSessionRepository,
logger,
)
beforeEach(() => {
user = {} as jest.Mocked<User>
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
userRepository.remove = jest.fn()
session = {
uuid: '1-2-3',
} as jest.Mocked<Session>
sessionRepository = {} as jest.Mocked<SessionRepositoryInterface>
sessionRepository.findAllByUserUuid = jest.fn().mockReturnValue([session])
sessionRepository.remove = jest.fn()
ephemeralSession = {
uuid: '2-3-4',
userUuid: '00000000-0000-0000-0000-000000000000',
} as jest.Mocked<EphemeralSession>
ephemeralSessionRepository = {} as jest.Mocked<EphemeralSessionRepositoryInterface>
ephemeralSessionRepository.findAllByUserUuid = jest.fn().mockReturnValue([ephemeralSession])
ephemeralSessionRepository.deleteOne = jest.fn()
revokedSession = {
uuid: '3-4-5',
} as jest.Mocked<RevokedSession>
revokedSessionRepository = {} as jest.Mocked<RevokedSessionRepositoryInterface>
revokedSessionRepository.findAllByUserUuid = jest.fn().mockReturnValue([revokedSession])
revokedSessionRepository.remove = jest.fn()
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
event.createdAt = new Date(1)
event.payload = {
userUuid: '00000000-0000-0000-0000-000000000000',
userCreatedAtTimestamp: 1,
regularSubscriptionUuid: '2-3-4',
roleNames: ['CORE_USER'],
}
logger = {} as jest.Mocked<Logger>
logger.info = jest.fn()
logger.warn = jest.fn()
})
it('should remove a user', async () => {
await createHandler().handle(event)
expect(userRepository.remove).toHaveBeenCalledWith(user)
})
it('should not remove a user with invalid uuid', async () => {
event.payload.userUuid = 'invalid'
await createHandler().handle(event)
expect(userRepository.remove).not.toHaveBeenCalled()
})
it('should not remove a user if one does not exist', async () => {
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
await createHandler().handle(event)
expect(userRepository.remove).not.toHaveBeenCalled()
expect(sessionRepository.remove).not.toHaveBeenCalled()
expect(revokedSessionRepository.remove).not.toHaveBeenCalled()
expect(ephemeralSessionRepository.deleteOne).not.toHaveBeenCalled()
})
it('should remove all user sessions', async () => {
await createHandler().handle(event)
expect(sessionRepository.remove).toHaveBeenCalledWith(session)
expect(revokedSessionRepository.remove).toHaveBeenCalledWith(revokedSession)
expect(ephemeralSessionRepository.deleteOne).toHaveBeenCalledWith('2-3-4', '00000000-0000-0000-0000-000000000000')
})
})

View File

@@ -1,21 +1,22 @@
import { AccountDeletionRequestedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
import { Uuid } from '@standardnotes/domain-core'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { EphemeralSessionRepositoryInterface } from '../Session/EphemeralSessionRepositoryInterface'
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { RemoveSharedVaultUser } from '../UseCase/RemoveSharedVaultUser/RemoveSharedVaultUser'
import { Uuid } from '@standardnotes/domain-core'
@injectable()
export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
constructor(
private userRepository: UserRepositoryInterface,
private sessionRepository: SessionRepositoryInterface,
@inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface,
@inject(TYPES.Auth_SessionRepository) private sessionRepository: SessionRepositoryInterface,
@inject(TYPES.Auth_EphemeralSessionRepository)
private ephemeralSessionRepository: EphemeralSessionRepositoryInterface,
private revokedSessionRepository: RevokedSessionRepositoryInterface,
private removeSharedVaultUser: RemoveSharedVaultUser,
private logger: Logger,
@inject(TYPES.Auth_RevokedSessionRepository) private revokedSessionRepository: RevokedSessionRepositoryInterface,
@inject(TYPES.Auth_Logger) private logger: Logger,
) {}
async handle(event: AccountDeletionRequestedEvent): Promise<void> {
@@ -37,13 +38,6 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
await this.removeSessions(userUuid.value)
const result = await this.removeSharedVaultUser.execute({
userUuid: userUuid.value,
})
if (result.isFailed()) {
this.logger.error(`Could not remove shared vault user: ${result.getError()}`)
}
await this.userRepository.remove(user)
this.logger.info(`Finished account cleanup for user: ${userUuid.value}`)

View File

@@ -1,26 +0,0 @@
import { DomainEventHandlerInterface, UserDesignatedAsSurvivorInSharedVaultEvent } from '@standardnotes/domain-events'
import { Logger } from 'winston'
import { DesignateSurvivor } from '../UseCase/DesignateSurvivor/DesignateSurvivor'
export class UserDesignatedAsSurvivorInSharedVaultEventHandler implements DomainEventHandlerInterface {
constructor(
private designateSurvivorUseCase: DesignateSurvivor,
private logger: Logger,
) {}
async handle(event: UserDesignatedAsSurvivorInSharedVaultEvent): Promise<void> {
const result = await this.designateSurvivorUseCase.execute({
sharedVaultUuid: event.payload.sharedVaultUuid,
userUuid: event.payload.userUuid,
timestamp: event.payload.timestamp,
})
if (result.isFailed()) {
this.logger.error(
`Failed designate survivor for user ${event.payload.userUuid} and shared vault ${
event.payload.sharedVaultUuid
}: ${result.getError()}`,
)
}
}
}

View File

@@ -10,12 +10,6 @@ export class UserRemovedFromSharedVaultEventHandler implements DomainEventHandle
) {}
async handle(event: UserRemovedFromSharedVaultEvent): Promise<void> {
if (!event.payload.sharedVaultUuid) {
this.logger.error(`Shared vault uuid is missing from event: ${JSON.stringify(event)}`)
return
}
const result = await this.removeSharedVaultUser.execute({
userUuid: event.payload.userUuid,
sharedVaultUuid: event.payload.sharedVaultUuid,

View File

@@ -3,7 +3,6 @@ import { SharedVaultUser, Uuid } from '@standardnotes/domain-core'
export interface SharedVaultUserRepositoryInterface {
findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultUser | null>
findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]>
findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultUser | null>
save(sharedVaultUser: SharedVaultUser): Promise<void>
remove(sharedVault: SharedVaultUser): Promise<void>
}

View File

@@ -43,7 +43,6 @@ export class AddSharedVaultUser implements UseCaseInterface<void> {
sharedVaultUuid,
permission,
timestamps,
isDesignatedSurvivor: false,
})
if (sharedVaultUserOrError.isFailed()) {
return Result.fail(sharedVaultUserOrError.getError())

View File

@@ -90,7 +90,6 @@ describe('CreateCrossServiceToken', () => {
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
timestamps: Timestamps.create(123456789, 123456789).getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
isDesignatedSurvivor: false,
}).getValue(),
])
})

View File

@@ -1,156 +0,0 @@
import { SharedVaultUser, SharedVaultUserPermission, Timestamps, Uuid } from '@standardnotes/domain-core'
import { DesignateSurvivor } from './DesignateSurvivor'
import { TimerInterface } from '@standardnotes/time'
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
describe('DesignateSurvivor', () => {
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
let sharedVaultUser: SharedVaultUser
let timer: TimerInterface
const createUseCase = () => new DesignateSurvivor(sharedVaultUserRepository, timer)
beforeEach(() => {
timer = {} as jest.Mocked<TimerInterface>
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123)
sharedVaultUser = SharedVaultUser.create({
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
timestamps: Timestamps.create(123, 123).getValue(),
isDesignatedSurvivor: false,
}).getValue()
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(null)
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
sharedVaultUserRepository.save = jest.fn()
})
it('should fail if shared vault uuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: 'invalid',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 123,
})
expect(result.isFailed()).toBe(true)
})
it('should fail if user uuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: 'invalid',
timestamp: 123,
})
expect(result.isFailed()).toBe(true)
})
it('should fail if shared vault user is not found', async () => {
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(null)
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 123,
})
expect(result.isFailed()).toBe(true)
})
it('should designate a survivor if the user is a member', async () => {
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 123,
})
expect(result.isFailed()).toBe(false)
expect(sharedVaultUser.props.isDesignatedSurvivor).toBe(true)
expect(sharedVaultUserRepository.save).toBeCalledTimes(1)
})
it('should designate a survivor if the user is a member and there is already a survivor', async () => {
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(
SharedVaultUser.create({
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(),
timestamps: Timestamps.create(123, 123).getValue(),
isDesignatedSurvivor: true,
}).getValue(),
)
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 123,
})
expect(result.isFailed()).toBe(false)
expect(sharedVaultUser.props.isDesignatedSurvivor).toBe(true)
expect(sharedVaultUserRepository.save).toBeCalledTimes(2)
})
it('should fail if the timestamp is older than the existing survivor', async () => {
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(
SharedVaultUser.create({
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(),
timestamps: Timestamps.create(123, 123).getValue(),
isDesignatedSurvivor: true,
}).getValue(),
)
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 122,
})
expect(result.isFailed()).toBe(true)
})
it('should do nothing if the user is already a survivor', async () => {
sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid = jest.fn().mockReturnValue(
SharedVaultUser.create({
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
timestamps: Timestamps.create(123, 123).getValue(),
isDesignatedSurvivor: true,
}).getValue(),
)
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockReturnValue(sharedVaultUser)
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
userUuid: '00000000-0000-0000-0000-000000000000',
timestamp: 200,
})
expect(result.isFailed()).toBe(false)
})
})

View File

@@ -1,66 +0,0 @@
import { TimerInterface } from '@standardnotes/time'
import { Result, Timestamps, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
import { DesignateSurvivorDTO } from './DesignateSurvivorDTO'
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
export class DesignateSurvivor implements UseCaseInterface<void> {
constructor(
private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
private timer: TimerInterface,
) {}
async execute(dto: DesignateSurvivorDTO): Promise<Result<void>> {
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
if (sharedVaultUuidOrError.isFailed()) {
return Result.fail(sharedVaultUuidOrError.getError())
}
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return Result.fail(userUuidOrError.getError())
}
const userUuid = userUuidOrError.getValue()
const existingSurvivor =
await this.sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid)
if (existingSurvivor) {
if (existingSurvivor.props.timestamps.updatedAt > dto.timestamp) {
return Result.fail(
'Cannot designate survivor to a previous version of the shared vault. Most probably a race condition.',
)
}
if (existingSurvivor.props.userUuid.value === userUuid.value) {
return Result.ok()
}
existingSurvivor.props.isDesignatedSurvivor = false
existingSurvivor.props.timestamps = Timestamps.create(
existingSurvivor.props.timestamps.createdAt,
this.timer.getTimestampInMicroseconds(),
).getValue()
await this.sharedVaultUserRepository.save(existingSurvivor)
}
const toBeDesignatedAsASurvivor = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
userUuid,
sharedVaultUuid,
})
if (!toBeDesignatedAsASurvivor) {
return Result.fail('User is not a member of the shared vault')
}
toBeDesignatedAsASurvivor.props.isDesignatedSurvivor = true
toBeDesignatedAsASurvivor.props.timestamps = Timestamps.create(
toBeDesignatedAsASurvivor.props.timestamps.createdAt,
this.timer.getTimestampInMicroseconds(),
).getValue()
await this.sharedVaultUserRepository.save(toBeDesignatedAsASurvivor)
return Result.ok()
}
}

View File

@@ -1,5 +0,0 @@
export interface DesignateSurvivorDTO {
sharedVaultUuid: string
userUuid: string
timestamp: number
}

View File

@@ -21,9 +21,19 @@ describe('Register', () => {
let user: User
let crypter: CrypterInterface
let timer: TimerInterface
let transitionModeEnabled = false
const createUseCase = () =>
new Register(userRepository, roleRepository, authResponseFactory, crypter, false, settingService, timer)
new Register(
userRepository,
roleRepository,
authResponseFactory,
crypter,
false,
settingService,
timer,
transitionModeEnabled,
)
beforeEach(() => {
userRepository = {} as jest.Mocked<UserRepositoryInterface>
@@ -84,7 +94,45 @@ describe('Register', () => {
expect(settingService.applyDefaultSettingsUponRegistration).toHaveBeenCalled()
})
it('should register a new user with default role', async () => {
const role = new Role()
role.name = 'role1'
roleRepository.findOneByName = jest.fn().mockReturnValue(role)
expect(
await createUseCase().execute({
email: 'test@test.te',
password: 'asdzxc',
updatedWithUserAgent: 'Mozilla',
apiVersion: '20200115',
ephemeralSession: false,
version: '004',
pwCost: 11,
pwSalt: 'qweqwe',
pwNonce: undefined,
}),
).toEqual({ success: true, authResponse: { foo: 'bar' } })
expect(userRepository.save).toHaveBeenCalledWith({
email: 'test@test.te',
encryptedPassword: expect.any(String),
encryptedServerKey: 'test',
serverEncryptionVersion: 1,
pwCost: 11,
pwNonce: undefined,
pwSalt: 'qweqwe',
updatedWithUserAgent: 'Mozilla',
uuid: expect.any(String),
version: '004',
createdAt: new Date(1),
updatedAt: new Date(1),
roles: Promise.resolve([role]),
})
})
it('should register a new user with default role and transition role', async () => {
transitionModeEnabled = true
const role = new Role()
role.name = RoleName.NAMES.CoreUser
@@ -201,6 +249,7 @@ describe('Register', () => {
true,
settingService,
timer,
transitionModeEnabled,
).execute({
email: 'test@test.te',
password: 'asdzxc',

View File

@@ -27,6 +27,7 @@ export class Register implements UseCaseInterface {
@inject(TYPES.Auth_DISABLE_USER_REGISTRATION) private disableUserRegistration: boolean,
@inject(TYPES.Auth_SettingService) private settingService: SettingServiceInterface,
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
@inject(TYPES.Auth_TRANSITION_MODE_ENABLED) private transitionModeEnabled: boolean,
) {}
async execute(dto: RegisterDTO): Promise<RegisterResponse> {
@@ -77,9 +78,11 @@ export class Register implements UseCaseInterface {
if (defaultRole) {
roles.push(defaultRole)
}
const transitionRole = await this.roleRepository.findOneByName(RoleName.NAMES.TransitionUser)
if (transitionRole) {
roles.push(transitionRole)
if (this.transitionModeEnabled) {
const transitionRole = await this.roleRepository.findOneByName(RoleName.NAMES.TransitionUser)
if (transitionRole) {
roles.push(transitionRole)
}
}
user.roles = Promise.resolve(roles)

View File

@@ -13,7 +13,6 @@ describe('RemoveSharedVaultUser', () => {
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
.fn()
.mockReturnValue({} as jest.Mocked<SharedVaultUser>)
sharedVaultUserRepository.findByUserUuid = jest.fn().mockReturnValue([{} as jest.Mocked<SharedVaultUser>])
sharedVaultUserRepository.remove = jest.fn()
})
@@ -29,17 +28,6 @@ describe('RemoveSharedVaultUser', () => {
expect(sharedVaultUserRepository.remove).toHaveBeenCalled()
})
it('should remove all shared vault users', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
userUuid: '00000000-0000-0000-0000-000000000000',
})
expect(result.isFailed()).toBeFalsy()
expect(sharedVaultUserRepository.remove).toHaveBeenCalled()
})
it('should fail when user uuid is invalid', async () => {
const useCase = createUseCase()

View File

@@ -13,31 +13,21 @@ export class RemoveSharedVaultUser implements UseCaseInterface<void> {
}
const userUuid = userUuidOrError.getValue()
let sharedVaultUuid: Uuid | undefined
if (dto.sharedVaultUuid !== undefined) {
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
if (sharedVaultUuidOrError.isFailed()) {
return Result.fail(sharedVaultUuidOrError.getError())
}
sharedVaultUuid = sharedVaultUuidOrError.getValue()
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')
}
if (sharedVaultUuid) {
const sharedVaultUser = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
userUuid,
sharedVaultUuid,
})
if (!sharedVaultUser) {
return Result.fail('Shared vault user not found')
}
await this.sharedVaultUserRepository.remove(sharedVaultUser)
} else {
const sharedVaultUsers = await this.sharedVaultUserRepository.findByUserUuid(userUuid)
for (const sharedVaultUser of sharedVaultUsers) {
await this.sharedVaultUserRepository.remove(sharedVaultUser)
}
}
await this.sharedVaultUserRepository.remove(sharedVaultUser)
return Result.ok()
}

View File

@@ -1,4 +1,4 @@
export interface RemoveSharedVaultUserDTO {
sharedVaultUuid?: string
sharedVaultUuid: string
userUuid: string
}

View File

@@ -26,13 +26,6 @@ export class TypeORMSharedVaultUser {
})
declare permission: string
@Column({
name: 'is_designated_survivor',
type: 'boolean',
default: false,
})
declare isDesignatedSurvivor: boolean
@Column({
name: 'created_at_timestamp',
type: 'bigint',

View File

@@ -10,24 +10,6 @@ export class TypeORMSharedVaultUserRepository implements SharedVaultUserReposito
private mapper: MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>,
) {}
async findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid: Uuid): Promise<SharedVaultUser | null> {
const persistence = await this.ormRepository
.createQueryBuilder('shared_vault_user')
.where('shared_vault_user.shared_vault_uuid = :sharedVaultUuid', {
sharedVaultUuid: sharedVaultUuid.value,
})
.andWhere('shared_vault_user.is_designated_survivor = :isDesignatedSurvivor', {
isDesignatedSurvivor: true,
})
.getOne()
if (persistence === null) {
return null
}
return this.mapper.toDomain(persistence)
}
async findByUserUuid(userUuid: Uuid): Promise<SharedVaultUser[]> {
const persistence = await this.ormRepository
.createQueryBuilder('shared_vault_user')

View File

@@ -41,7 +41,6 @@ export class SharedVaultUserPersistenceMapper implements MapperInterface<SharedV
sharedVaultUuid,
permission,
timestamps,
isDesignatedSurvivor: !!projection.isDesignatedSurvivor,
},
new UniqueEntityId(projection.uuid),
)
@@ -62,7 +61,6 @@ export class SharedVaultUserPersistenceMapper implements MapperInterface<SharedV
typeorm.permission = domain.props.permission.value
typeorm.createdAtTimestamp = domain.props.timestamps.createdAt
typeorm.updatedAtTimestamp = domain.props.timestamps.updatedAt
typeorm.isDesignatedSurvivor = !!domain.props.isDesignatedSurvivor
return typeorm
}

View File

@@ -3,30 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.33.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.32.0...@standardnotes/domain-core@1.33.0) (2023-09-21)
### Features
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/server/issues/841)) ([230c96d](https://github.com/standardnotes/server/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
# [1.32.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.31.0...@standardnotes/domain-core@1.32.0) (2023-09-20)
### Features
* **syncing-server:** distinct notifications upon user removal from shared vault ([#840](https://github.com/standardnotes/server/issues/840)) ([41e2136](https://github.com/standardnotes/server/commit/41e2136bc07312974701a70652528d304105e0f9))
# [1.31.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.30.1...@standardnotes/domain-core@1.31.0) (2023-09-20)
### Features
* **syncing-server:** add notification for user upon declined shared vault invitation ([#837](https://github.com/standardnotes/server/issues/837)) ([31e7aaf](https://github.com/standardnotes/server/commit/31e7aaf253029a951d8b943d6cffd655cd5ca765))
## [1.30.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.30.0...@standardnotes/domain-core@1.30.1) (2023-09-19)
### Bug Fixes
* **domain-core:** allow any version and variant of the UUID format ([de081fe](https://github.com/standardnotes/server/commit/de081fe78658bdd36c8c5d86b70a16a2880aa3d1))
# [1.30.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.29.0...@standardnotes/domain-core@1.30.0) (2023-09-18)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-core",
"version": "1.33.0",
"version": "1.30.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -5,7 +5,6 @@ describe('Validator', () => {
'2221101c-1da9-4d2b-9b32-b8be2a8d1c82',
'c08f2f29-a74b-42b4-aefd-98af9832391c',
'b453fa64-1493-443b-b5bb-bca7b9c696c7',
'fa7350b3-77cf-8c0c-40b2-6046b13254fe',
]
const invalidUuids = [

View File

@@ -1,13 +1,12 @@
import { Result } from './Result'
export class Validator {
private static readonly UUID_ANY_VERSION_AND_VARIANT_REGEX =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
private static readonly UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i
private static readonly EMAIL_REGEX =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
static isValidUuid(value: string): Result<string> {
const matchesUuidRegex = String(value).toLowerCase().match(Validator.UUID_ANY_VERSION_AND_VARIANT_REGEX) !== null
const matchesUuidRegex = String(value).toLowerCase().match(Validator.UUID_REGEX) !== null
if (matchesUuidRegex) {
return Result.ok()
}

View File

@@ -5,10 +5,8 @@ import { NotificationTypeProps } from './NotificationTypeProps'
export class NotificationType extends ValueObject<NotificationTypeProps> {
static readonly TYPES = {
SharedVaultItemRemoved: 'shared_vault_item_removed',
SelfRemovedFromSharedVault: 'self_removed_from_shared_vault',
UserRemovedFromSharedVault: 'user_removed_from_shared_vault',
RemovedFromSharedVault: 'removed_from_shared_vault',
UserAddedToSharedVault: 'user_added_to_shared_vault',
SharedVaultInviteDeclined: 'shared_vault_invite_declined',
SharedVaultFileUploaded: 'shared_vault_file_uploaded',
SharedVaultFileRemoved: 'shared_vault_file_removed',
}

View File

@@ -10,7 +10,6 @@ describe('SharedVaultUser', () => {
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
timestamps: Timestamps.create(123456789, 123456789).getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
isDesignatedSurvivor: false,
})
expect(entityOrError.isFailed()).toBeFalsy()

View File

@@ -6,6 +6,5 @@ export interface SharedVaultUserProps {
sharedVaultUuid: Uuid
userUuid: Uuid
permission: SharedVaultUserPermission
isDesignatedSurvivor: boolean
timestamps: Timestamps
}

View File

@@ -3,14 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.12.32](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.31...@standardnotes/domain-events-infra@1.12.32) (2023-09-21)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.12.31](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.30...@standardnotes/domain-events-infra@1.12.31) (2023-09-20)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.12.30](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.29...@standardnotes/domain-events-infra@1.12.30) (2023-09-15)
**Note:** Version bump only for package @standardnotes/domain-events-infra

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events-infra",
"version": "1.12.32",
"version": "1.12.30",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,18 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.129.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.128.0...@standardnotes/domain-events@2.129.0) (2023-09-21)
### Features
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/server/issues/841)) ([230c96d](https://github.com/standardnotes/server/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
# [2.128.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.127.0...@standardnotes/domain-events@2.128.0) (2023-09-20)
### Features
* add unassigning items and revisions upon shared vault removal ([#839](https://github.com/standardnotes/server/issues/839)) ([378eced](https://github.com/standardnotes/server/commit/378ecedfcc4fb23475c2329fb37479edb3b48a39))
# [2.127.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.126.0...@standardnotes/domain-events@2.127.0) (2023-09-15)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events",
"version": "2.129.0",
"version": "2.127.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -2,4 +2,5 @@ export interface ItemRemovedFromSharedVaultEventPayload {
userUuid: string
itemUuid: string
sharedVaultUuid: string
roleNames: string[]
}

View File

@@ -1,7 +0,0 @@
import { DomainEventInterface } from './DomainEventInterface'
import { SharedVaultRemovedEventPayload } from './SharedVaultRemovedEventPayload'
export interface SharedVaultRemovedEvent extends DomainEventInterface {
type: 'SHARED_VAULT_REMOVED'
payload: SharedVaultRemovedEventPayload
}

View File

@@ -1,3 +0,0 @@
export interface SharedVaultRemovedEventPayload {
sharedVaultUuid: string
}

View File

@@ -1,7 +0,0 @@
import { DomainEventInterface } from './DomainEventInterface'
import { UserDesignatedAsSurvivorInSharedVaultEventPayload } from './UserDesignatedAsSurvivorInSharedVaultEventPayload'
export interface UserDesignatedAsSurvivorInSharedVaultEvent extends DomainEventInterface {
type: 'USER_DESIGNATED_AS_SURVIVOR_IN_SHARED_VAULT'
payload: UserDesignatedAsSurvivorInSharedVaultEventPayload
}

View File

@@ -1,5 +0,0 @@
export interface UserDesignatedAsSurvivorInSharedVaultEventPayload {
userUuid: string
sharedVaultUuid: string
timestamp: number
}

View File

@@ -76,8 +76,6 @@ export * from './Event/SharedVaultFileRemovedEvent'
export * from './Event/SharedVaultFileRemovedEventPayload'
export * from './Event/SharedVaultFileUploadedEvent'
export * from './Event/SharedVaultFileUploadedEventPayload'
export * from './Event/SharedVaultRemovedEvent'
export * from './Event/SharedVaultRemovedEventPayload'
export * from './Event/StatisticPersistenceRequestedEvent'
export * from './Event/StatisticPersistenceRequestedEventPayload'
export * from './Event/SubscriptionCancelledEvent'
@@ -104,8 +102,6 @@ export * from './Event/TransitionStatusUpdatedEvent'
export * from './Event/TransitionStatusUpdatedEventPayload'
export * from './Event/UserAddedToSharedVaultEvent'
export * from './Event/UserAddedToSharedVaultEventPayload'
export * from './Event/UserDesignatedAsSurvivorInSharedVaultEvent'
export * from './Event/UserDesignatedAsSurvivorInSharedVaultEventPayload'
export * from './Event/UserDisabledSessionUserAgentLoggingEvent'
export * from './Event/UserDisabledSessionUserAgentLoggingEventPayload'
export * from './Event/UserEmailChangedEvent'

View File

@@ -3,22 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.11.48](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.47...@standardnotes/event-store@1.11.48) (2023-09-21)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.47](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.46...@standardnotes/event-store@1.11.47) (2023-09-20)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.46](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.45...@standardnotes/event-store@1.11.46) (2023-09-20)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.45](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.44...@standardnotes/event-store@1.11.45) (2023-09-19)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.44](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.43...@standardnotes/event-store@1.11.44) (2023-09-18)
**Note:** Version bump only for package @standardnotes/event-store

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/event-store",
"version": "1.11.48",
"version": "1.11.44",
"description": "Event Store Service",
"private": true,
"main": "dist/src/index.js",

View File

@@ -3,22 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.22.27](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.26...@standardnotes/files-server@1.22.27) (2023-09-21)
**Note:** Version bump only for package @standardnotes/files-server
## [1.22.26](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.25...@standardnotes/files-server@1.22.26) (2023-09-20)
**Note:** Version bump only for package @standardnotes/files-server
## [1.22.25](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.24...@standardnotes/files-server@1.22.25) (2023-09-20)
**Note:** Version bump only for package @standardnotes/files-server
## [1.22.24](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.23...@standardnotes/files-server@1.22.24) (2023-09-19)
**Note:** Version bump only for package @standardnotes/files-server
## [1.22.23](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.22...@standardnotes/files-server@1.22.23) (2023-09-18)
**Note:** Version bump only for package @standardnotes/files-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.22.27",
"version": "1.22.23",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -16,3 +16,5 @@ MONGO_PORT=27017
MONGO_USERNAME=standardnotes
MONGO_PASSWORD=standardnotes
MONGO_DATABASE=standardnotes
TRANSITION_MODE_ENABLED=false

View File

@@ -3,88 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.16.0](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.80...@standardnotes/home-server@1.16.0) (2023-09-22)
### Features
* remove transition mode from code ([5001496](https://github.com/standardnotes/server/commit/5001496c7bc1df9e20c2d88ebf52ed77f868110c))
## [1.15.80](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.79...@standardnotes/home-server@1.15.80) (2023-09-22)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.79](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.78...@standardnotes/home-server@1.15.79) (2023-09-22)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.78](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.77...@standardnotes/home-server@1.15.78) (2023-09-22)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.77](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.76...@standardnotes/home-server@1.15.77) (2023-09-22)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.76](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.75...@standardnotes/home-server@1.15.76) (2023-09-21)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.75](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.74...@standardnotes/home-server@1.15.75) (2023-09-21)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.74](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.73...@standardnotes/home-server@1.15.74) (2023-09-21)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.73](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.72...@standardnotes/home-server@1.15.73) (2023-09-21)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.72](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.71...@standardnotes/home-server@1.15.72) (2023-09-21)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.71](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.70...@standardnotes/home-server@1.15.71) (2023-09-20)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.70](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.69...@standardnotes/home-server@1.15.70) (2023-09-20)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.69](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.68...@standardnotes/home-server@1.15.69) (2023-09-20)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.68](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.67...@standardnotes/home-server@1.15.68) (2023-09-19)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.67](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.66...@standardnotes/home-server@1.15.67) (2023-09-19)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.66](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.65...@standardnotes/home-server@1.15.66) (2023-09-19)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.65](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.64...@standardnotes/home-server@1.15.65) (2023-09-19)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.64](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.63...@standardnotes/home-server@1.15.64) (2023-09-19)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.63](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.62...@standardnotes/home-server@1.15.63) (2023-09-19)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.62](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.61...@standardnotes/home-server@1.15.62) (2023-09-19)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.61](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.60...@standardnotes/home-server@1.15.61) (2023-09-18)
**Note:** Version bump only for package @standardnotes/home-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.16.0",
"version": "1.15.61",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,85 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.5...@standardnotes/revisions-server@1.36.6) (2023-09-22)
### Bug Fixes
* add more logs to transition process ([0562b0a](https://github.com/standardnotes/server/commit/0562b0a621eb878026fbdc0346b6170e815b64bf))
* remove excessive logs ([15ed1fd](https://github.com/standardnotes/server/commit/15ed1fd789aba306cbec6a23e88d5c1f837dabc0))
## [1.36.5](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.4...@standardnotes/revisions-server@1.36.5) (2023-09-22)
### Bug Fixes
* disable cleaning secondary database on transition ([4f4443a](https://github.com/standardnotes/server/commit/4f4443a882f69c2e76ef831ef36347c9c54f31cd))
* integrity check during transition ([921c30f](https://github.com/standardnotes/server/commit/921c30f6415ef122a7d1af83ffa3f6840a42edba))
* processing migration optimization ([22540ee](https://github.com/standardnotes/server/commit/22540ee83436b986949127a6923285a702162706))
## [1.36.4](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.3...@standardnotes/revisions-server@1.36.4) (2023-09-21)
### Bug Fixes
* **revisions:** add log info about skipping already existing revision ([d0dba1b](https://github.com/standardnotes/server/commit/d0dba1b66df0fb4ab64ede8f0d4e1c4e2a23ad3c))
## [1.36.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.2...@standardnotes/revisions-server@1.36.3) (2023-09-21)
**Note:** Version bump only for package @standardnotes/revisions-server
## [1.36.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.1...@standardnotes/revisions-server@1.36.2) (2023-09-21)
### Bug Fixes
* **revisions:** rename revisions table to all users stuck mid-migration process - fixes [#836](https://github.com/standardnotes/server/issues/836) ([#842](https://github.com/standardnotes/server/issues/842)) ([a621cf1](https://github.com/standardnotes/server/commit/a621cf1e3b891c450272e9762e4a71a199ea2932))
## [1.36.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.36.0...@standardnotes/revisions-server@1.36.1) (2023-09-21)
### Bug Fixes
* secondary database catch up time ([880db10](https://github.com/standardnotes/server/commit/880db1038a39d4610a2593489a18e207487347a2))
# [1.36.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.8...@standardnotes/revisions-server@1.36.0) (2023-09-20)
### Features
* add unassigning items and revisions upon shared vault removal ([#839](https://github.com/standardnotes/server/issues/839)) ([378eced](https://github.com/standardnotes/server/commit/378ecedfcc4fb23475c2329fb37479edb3b48a39))
## [1.35.8](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.7...@standardnotes/revisions-server@1.35.8) (2023-09-20)
**Note:** Version bump only for package @standardnotes/revisions-server
## [1.35.7](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.6...@standardnotes/revisions-server@1.35.7) (2023-09-19)
### Bug Fixes
* skip removing already existing content in secondary to pick up where the transition left of ([857c6af](https://github.com/standardnotes/server/commit/857c6af9468ec829ff4dce9a96ba7bf9c14d55a5))
## [1.35.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.5...@standardnotes/revisions-server@1.35.6) (2023-09-19)
### Bug Fixes
* increase timeout for secondary database to catch up for indexes to be rebuilt ([b265a39](https://github.com/standardnotes/server/commit/b265a39b635373c36ee8c3d8e09f0631159b3574))
* logs verbosity during transitions ([e589029](https://github.com/standardnotes/server/commit/e589029722ab9f4debc8aa6cc78913f877eda2e3))
## [1.35.5](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.4...@standardnotes/revisions-server@1.35.5) (2023-09-19)
### Bug Fixes
* add checking for secondary items logs ([a1a3e9f](https://github.com/standardnotes/server/commit/a1a3e9f586358b943b1b490a1382e42f081f7d06))
* logs for removing already existing content and paging through diff of the content ([a40b17b](https://github.com/standardnotes/server/commit/a40b17b141f1d5954e1a45b969d5a941386c68d0))
## [1.35.4](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.3...@standardnotes/revisions-server@1.35.4) (2023-09-19)
### Bug Fixes
* logs formatting during transition for better readability ([0ae028d](https://github.com/standardnotes/server/commit/0ae028db739decec8c50321b18b0af515e00bd23))
## [1.35.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.2...@standardnotes/revisions-server@1.35.3) (2023-09-19)
### Bug Fixes
* **syncing-server:** paging through already existing items ([e4fcd73](https://github.com/standardnotes/server/commit/e4fcd738c35a4dc96e57db6ca08383a5647d61ad))
## [1.35.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.35.1...@standardnotes/revisions-server@1.35.2) (2023-09-18)
**Note:** Version bump only for package @standardnotes/revisions-server

View File

@@ -4,8 +4,6 @@ export class removeDateIndexes1669636497932 implements MigrationInterface {
name = 'removeDateIndexes1669636497932'
public async up(queryRunner: QueryRunner): Promise<void> {
await this.renameRevisionsTable(queryRunner)
const indexRevisionsOnCreatedAt = await queryRunner.manager.query(
'SHOW INDEX FROM `revisions_revisions` where `key_name` = "created_at"',
)
@@ -27,14 +25,4 @@ export class removeDateIndexes1669636497932 implements MigrationInterface {
await queryRunner.query('CREATE INDEX `creation_date` ON `revisions_revisions` (`creation_date`)')
await queryRunner.query('CREATE INDEX `created_at` ON `revisions_revisions` (`created_at`)')
}
private async renameRevisionsTable(queryRunner: QueryRunner) {
const revisionsTableExistsQueryResult = await queryRunner.manager.query(
'SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = "revisions"',
)
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
if (revisionsTableExists) {
await queryRunner.query('RENAME TABLE `revisions` TO `revisions_revisions`')
}
}
}

View File

@@ -4,22 +4,10 @@ export class makeUserUuidNullable1669735585016 implements MigrationInterface {
name = 'makeUserUuidNullable1669735585016'
public async up(queryRunner: QueryRunner): Promise<void> {
await this.renameRevisionsTable(queryRunner)
await queryRunner.query('ALTER TABLE `revisions_revisions` CHANGE `user_uuid` `user_uuid` varchar(36) NULL')
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `revisions_revisions` CHANGE `user_uuid` `user_uuid` varchar(36) NOT NULL')
}
private async renameRevisionsTable(queryRunner: QueryRunner) {
const revisionsTableExistsQueryResult = await queryRunner.manager.query(
'SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = "revisions"',
)
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
if (revisionsTableExists) {
await queryRunner.query('RENAME TABLE `revisions` TO `revisions_revisions`')
}
}
}

View File

@@ -2,8 +2,6 @@ import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddSharedVaultInformation1693915383950 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await this.renameRevisionsTable(queryRunner)
await queryRunner.query('ALTER TABLE `revisions_revisions` ADD `edited_by` varchar(36) NULL')
await queryRunner.query('ALTER TABLE `revisions_revisions` ADD `shared_vault_uuid` varchar(36) NULL')
await queryRunner.query('ALTER TABLE `revisions_revisions` ADD `key_system_identifier` varchar(36) NULL')
@@ -18,14 +16,4 @@ export class AddSharedVaultInformation1693915383950 implements MigrationInterfac
await queryRunner.query('ALTER TABLE `revisions_revisions` DROP COLUMN `shared_vault_uuid`')
await queryRunner.query('ALTER TABLE `revisions_revisions` DROP COLUMN `last_edited_by`')
}
private async renameRevisionsTable(queryRunner: QueryRunner) {
const revisionsTableExistsQueryResult = await queryRunner.manager.query(
'SELECT COUNT(*) as count FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = "revisions"',
)
const revisionsTableExists = revisionsTableExistsQueryResult[0].count === 1
if (revisionsTableExists) {
await queryRunner.query('RENAME TABLE `revisions` TO `revisions_revisions`')
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/revisions-server",
"version": "1.36.6",
"version": "1.35.2",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -67,7 +67,6 @@ import { SQLRevisionPersistenceMapper } from '../Mapping/Persistence/SQL/SQLRevi
import { RemoveRevisionsFromSharedVault } from '../Domain/UseCase/RemoveRevisionsFromSharedVault/RemoveRevisionsFromSharedVault'
import { ItemRemovedFromSharedVaultEventHandler } from '../Domain/Handler/ItemRemovedFromSharedVaultEventHandler'
import { TransitionRequestedEventHandler } from '../Domain/Handler/TransitionRequestedEventHandler'
import { SharedVaultRemovedEventHandler } from '../Domain/Handler/SharedVaultRemovedEventHandler'
export class ContainerConfigLoader {
constructor(private mode: 'server' | 'worker' = 'server') {}
@@ -357,9 +356,7 @@ export class ContainerConfigLoader {
.bind<RemoveRevisionsFromSharedVault>(TYPES.Revisions_RemoveRevisionsFromSharedVault)
.toConstantValue(
new RemoveRevisionsFromSharedVault(
isSecondaryDatabaseEnabled
? container.get<RevisionRepositoryInterface>(TYPES.Revisions_MongoDBRevisionRepository)
: container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository),
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
),
)
@@ -451,14 +448,6 @@ export class ContainerConfigLoader {
container.get<winston.Logger>(TYPES.Revisions_Logger),
),
)
container
.bind<SharedVaultRemovedEventHandler>(TYPES.Revisions_SharedVaultRemovedEventHandler)
.toConstantValue(
new SharedVaultRemovedEventHandler(
container.get<RemoveRevisionsFromSharedVault>(TYPES.Revisions_RemoveRevisionsFromSharedVault),
container.get<winston.Logger>(TYPES.Revisions_Logger),
),
)
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
['ITEM_DUMPED', container.get(TYPES.Revisions_ItemDumpedEventHandler)],
@@ -466,7 +455,6 @@ export class ContainerConfigLoader {
['REVISIONS_COPY_REQUESTED', container.get(TYPES.Revisions_RevisionsCopyRequestedEventHandler)],
['ITEM_REMOVED_FROM_SHARED_VAULT', container.get(TYPES.Revisions_ItemRemovedFromSharedVaultEventHandler)],
['TRANSITION_REQUESTED', container.get(TYPES.Revisions_TransitionRequestedEventHandler)],
['SHARED_VAULT_REMOVED', container.get(TYPES.Revisions_SharedVaultRemovedEventHandler)],
])
if (isConfiguredForHomeServer) {

View File

@@ -57,7 +57,6 @@ const TYPES = {
Revisions_RevisionsCopyRequestedEventHandler: Symbol.for('Revisions_RevisionsCopyRequestedEventHandler'),
Revisions_ItemRemovedFromSharedVaultEventHandler: Symbol.for('Revisions_ItemRemovedFromSharedVaultEventHandler'),
Revisions_TransitionRequestedEventHandler: Symbol.for('Revisions_TransitionRequestedEventHandler'),
Revisions_SharedVaultRemovedEventHandler: Symbol.for('Revisions_SharedVaultRemovedEventHandler'),
// Services
Revisions_CrossServiceTokenDecoder: Symbol.for('Revisions_CrossServiceTokenDecoder'),
Revisions_DomainEventSubscriberFactory: Symbol.for('Revisions_DomainEventSubscriberFactory'),

View File

@@ -9,15 +9,10 @@ export class ItemRemovedFromSharedVaultEventHandler implements DomainEventHandle
) {}
async handle(event: ItemRemovedFromSharedVaultEvent): Promise<void> {
if (!event.payload.itemUuid) {
this.logger.error('ItemRemovedFromSharedVaultEvent is missing itemUuid')
return
}
const result = await this.removeRevisionsFromSharedVault.execute({
sharedVaultUuid: event.payload.sharedVaultUuid,
itemUuid: event.payload.itemUuid,
roleNames: event.payload.roleNames,
})
if (result.isFailed()) {

View File

@@ -1,21 +0,0 @@
import { DomainEventHandlerInterface, SharedVaultRemovedEvent } from '@standardnotes/domain-events'
import { Logger } from 'winston'
import { RemoveRevisionsFromSharedVault } from '../UseCase/RemoveRevisionsFromSharedVault/RemoveRevisionsFromSharedVault'
export class SharedVaultRemovedEventHandler implements DomainEventHandlerInterface {
constructor(
private removeRevisionsFromSharedVault: RemoveRevisionsFromSharedVault,
private logger: Logger,
) {}
async handle(event: SharedVaultRemovedEvent): Promise<void> {
const result = await this.removeRevisionsFromSharedVault.execute({
sharedVaultUuid: event.payload.sharedVaultUuid,
})
if (result.isFailed()) {
this.logger.error(`Failed to remove revisions from shared vault: ${result.getError()}`)
}
}
}

View File

@@ -29,7 +29,7 @@ export class TransitionRequestedEventHandler implements DomainEventHandlerInterf
}
if (await this.isAlreadyMigrated(userUuid)) {
this.logger.info(`[${event.payload.userUuid}] User already migrated.`)
this.logger.info(`User ${event.payload.userUuid} already migrated.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createTransitionStatusUpdatedEvent({
@@ -43,7 +43,7 @@ export class TransitionRequestedEventHandler implements DomainEventHandlerInterf
return
}
this.logger.info(`[${event.payload.userUuid}] Handling transition requested event`)
this.logger.info(`Handling transition requested event for user ${event.payload.userUuid}`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createTransitionStatusUpdatedEvent({
@@ -59,7 +59,7 @@ export class TransitionRequestedEventHandler implements DomainEventHandlerInterf
})
if (result.isFailed()) {
this.logger.error(`[${event.payload.userUuid}] Failed to transition: ${result.getError()}`)
this.logger.error(`Failed to transition for user ${event.payload.userUuid}`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createTransitionStatusUpdatedEvent({
@@ -88,7 +88,7 @@ export class TransitionRequestedEventHandler implements DomainEventHandlerInterf
if (totalRevisionsCountForUserInPrimary > 0) {
this.logger.info(
`[${userUuid.value}] User has ${totalRevisionsCountForUserInPrimary} revisions in primary database.`,
`User ${userUuid.value} has ${totalRevisionsCountForUserInPrimary} revisions in primary database.`,
)
}
@@ -98,7 +98,9 @@ export class TransitionRequestedEventHandler implements DomainEventHandlerInterf
private async getUserUuidFromEvent(event: TransitionRequestedEvent): Promise<Uuid | null> {
const userUuidOrError = Uuid.create(event.payload.userUuid)
if (userUuidOrError.isFailed()) {
this.logger.error(`[${event.payload.userUuid}] Failed to transition revisions: ${userUuidOrError.getError()}`)
this.logger.error(
`Failed to transition revisions for user ${event.payload.userUuid}: ${userUuidOrError.getError()}`,
)
await this.domainEventPublisher.publish(
this.domainEventFactory.createTransitionStatusUpdatedEvent({
userUuid: event.payload.userUuid,

View File

@@ -13,5 +13,5 @@ export interface RevisionRepositoryInterface {
updateUserUuid(itemUuid: Uuid, userUuid: Uuid): Promise<void>
findByUserUuid(dto: { userUuid: Uuid; offset?: number; limit?: number }): Promise<Array<Revision>>
insert(revision: Revision): Promise<boolean>
clearSharedVaultAndKeySystemAssociations(dto: { itemUuid?: Uuid; sharedVaultUuid: Uuid }): Promise<void>
clearSharedVaultAndKeySystemAssociations(itemUuid: Uuid, sharedVaultUuid: Uuid): Promise<void>
}

View File

@@ -1,14 +1,19 @@
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
import { RemoveRevisionsFromSharedVault } from './RemoveRevisionsFromSharedVault'
describe('RemoveRevisionsFromSharedVault', () => {
let revisionRepositoryResolver: RevisionRepositoryResolverInterface
let revisionRepository: RevisionRepositoryInterface
const createUseCase = () => new RemoveRevisionsFromSharedVault(revisionRepository)
const createUseCase = () => new RemoveRevisionsFromSharedVault(revisionRepositoryResolver)
beforeEach(() => {
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
revisionRepository.clearSharedVaultAndKeySystemAssociations = jest.fn()
revisionRepositoryResolver = {} as jest.Mocked<RevisionRepositoryResolverInterface>
revisionRepositoryResolver.resolve = jest.fn().mockReturnValue(revisionRepository)
})
it('should clear shared vault and key system associations', async () => {
@@ -17,16 +22,7 @@ describe('RemoveRevisionsFromSharedVault', () => {
await useCase.execute({
itemUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultUuid: '00000000-0000-0000-0000-000000000001',
})
expect(revisionRepository.clearSharedVaultAndKeySystemAssociations).toHaveBeenCalled()
})
it('should clear shared vault and key system associations for all items in a vault when item uuid is not provided', async () => {
const useCase = createUseCase()
await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000001',
roleNames: ['CORE_USER'],
})
expect(revisionRepository.clearSharedVaultAndKeySystemAssociations).toHaveBeenCalled()
@@ -38,6 +34,7 @@ describe('RemoveRevisionsFromSharedVault', () => {
const result = await useCase.execute({
itemUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultUuid: 'invalid',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBe(true)
@@ -49,6 +46,19 @@ describe('RemoveRevisionsFromSharedVault', () => {
const result = await useCase.execute({
itemUuid: 'invalid',
sharedVaultUuid: '00000000-0000-0000-0000-000000000001',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBe(true)
})
it('should return error when role names are invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
itemUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultUuid: '00000000-0000-0000-0000-000000000001',
roleNames: ['invalid'],
})
expect(result.isFailed()).toBe(true)

View File

@@ -1,10 +1,9 @@
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
import { Result, RoleNameCollection, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
import { RemoveRevisionsFromSharedVaultDTO } from './RemoveRevisionsFromSharedVaultDTO'
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
export class RemoveRevisionsFromSharedVault implements UseCaseInterface<void> {
constructor(private revisionRepository: RevisionRepositoryInterface) {}
constructor(private revisionRepositoryResolver: RevisionRepositoryResolverInterface) {}
async execute(dto: RemoveRevisionsFromSharedVaultDTO): Promise<Result<void>> {
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
@@ -13,19 +12,21 @@ export class RemoveRevisionsFromSharedVault implements UseCaseInterface<void> {
}
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
let itemUuid: Uuid | undefined
if (dto.itemUuid !== undefined) {
const itemUuidOrError = Uuid.create(dto.itemUuid)
if (itemUuidOrError.isFailed()) {
return Result.fail(itemUuidOrError.getError())
}
itemUuid = itemUuidOrError.getValue()
const itemUuidOrError = Uuid.create(dto.itemUuid)
if (itemUuidOrError.isFailed()) {
return Result.fail(itemUuidOrError.getError())
}
const itemUuid = itemUuidOrError.getValue()
await this.revisionRepository.clearSharedVaultAndKeySystemAssociations({
itemUuid,
sharedVaultUuid,
})
const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
if (roleNamesOrError.isFailed()) {
return Result.fail(roleNamesOrError.getError())
}
const roleNames = roleNamesOrError.getValue()
const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
await revisionRepository.clearSharedVaultAndKeySystemAssociations(itemUuid, sharedVaultUuid)
return Result.ok()
}

View File

@@ -1,4 +1,5 @@
export interface RemoveRevisionsFromSharedVaultDTO {
itemUuid?: string
itemUuid: string
sharedVaultUuid: string
roleNames: string[]
}

View File

@@ -5,6 +5,7 @@ import { Logger } from 'winston'
import { TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO } from './TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO'
import { RevisionRepositoryInterface } from '../../../Revision/RevisionRepositoryInterface'
import { Revision } from '../../../Revision/Revision'
export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements UseCaseInterface<void> {
constructor(
@@ -16,7 +17,7 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
) {}
async execute(dto: TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO): Promise<Result<void>> {
this.logger.info(`[${dto.userUuid}] Transitioning revisions for user`)
this.logger.info(`Transitioning revisions for user ${dto.userUuid}`)
if (this.secondRevisionsRepository === null) {
return Result.fail('Secondary revision repository is not set')
@@ -28,33 +29,84 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
}
const userUuid = userUuidOrError.getValue()
const migrationTimeStart = this.timer.getTimestampInMicroseconds()
let newRevisionsInSecondaryCount = 0
let updatedRevisionsInSecondary: Revision[] = []
if (await this.hasAlreadyDataInSecondaryDatabase(userUuid)) {
const { alreadyExistingInPrimary, newRevisionsInSecondary, updatedInSecondary } =
await this.getNewRevisionsCreatedInSecondaryDatabase(userUuid)
this.logger.info(`[${dto.userUuid}] Migrating revisions`)
for (const existingRevision of alreadyExistingInPrimary) {
this.logger.info(`Removing revision ${existingRevision.id.toString()} from secondary database`)
await (this.secondRevisionsRepository as RevisionRepositoryInterface).removeOneByUuid(
Uuid.create(existingRevision.id.toString()).getValue(),
userUuid,
)
}
const migrationResult = await this.migrateRevisionsForUser(userUuid)
if (migrationResult.isFailed()) {
return Result.fail(migrationResult.getError())
if (newRevisionsInSecondary.length > 0) {
this.logger.info(
`Found ${newRevisionsInSecondary.length} new revisions in secondary database for user ${userUuid.value}`,
)
}
newRevisionsInSecondaryCount = newRevisionsInSecondary.length
if (updatedInSecondary.length > 0) {
this.logger.info(
`Found ${updatedInSecondary.length} updated revisions in secondary database for user ${userUuid.value}`,
)
}
updatedRevisionsInSecondary = updatedInSecondary
}
const revisionsToSkipInIntegrityCheck = migrationResult.getValue()
this.logger.info(`[${dto.userUuid}] Revisions migrated`)
const updatedRevisionsInSecondaryCount = updatedRevisionsInSecondary.length
await this.allowForSecondaryDatabaseToCatchUp()
this.logger.info(`[${dto.userUuid}] Checking integrity between primary and secondary database`)
const migrationTimeStart = this.timer.getTimestampInMicroseconds()
this.logger.debug(`Transitioning revisions for user ${userUuid.value}`)
const migrationResult = await this.migrateRevisionsForUser(userUuid, updatedRevisionsInSecondary)
if (migrationResult.isFailed()) {
if (newRevisionsInSecondaryCount === 0 && updatedRevisionsInSecondaryCount === 0) {
const cleanupResult = await this.deleteRevisionsForUser(userUuid, this.secondRevisionsRepository)
if (cleanupResult.isFailed()) {
this.logger.error(
`Failed to clean up secondary database revisions for user ${userUuid.value}: ${cleanupResult.getError()}`,
)
}
}
return Result.fail(migrationResult.getError())
}
await this.allowForSecondaryDatabaseToCatchUp()
const integrityCheckResult = await this.checkIntegrityBetweenPrimaryAndSecondaryDatabase(
userUuid,
revisionsToSkipInIntegrityCheck,
newRevisionsInSecondaryCount,
updatedRevisionsInSecondary,
)
if (integrityCheckResult.isFailed()) {
if (newRevisionsInSecondaryCount === 0 && updatedRevisionsInSecondaryCount === 0) {
const cleanupResult = await this.deleteRevisionsForUser(userUuid, this.secondRevisionsRepository)
if (cleanupResult.isFailed()) {
this.logger.error(
`Failed to clean up secondary database revisions for user ${userUuid.value}: ${cleanupResult.getError()}`,
)
}
}
return Result.fail(integrityCheckResult.getError())
}
const cleanupResult = await this.deleteRevisionsForUser(userUuid, this.primaryRevisionsRepository)
if (cleanupResult.isFailed()) {
this.logger.error(`[${dto.userUuid}] Failed to clean up primary database revisions: ${cleanupResult.getError()}`)
this.logger.error(
`Failed to clean up primary database revisions for user ${userUuid.value}: ${cleanupResult.getError()}`,
)
}
const migrationTimeEnd = this.timer.getTimestampInMicroseconds()
@@ -63,17 +115,20 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
const migrationDurationTimeStructure = this.timer.convertMicrosecondsToTimeStructure(migrationDuration)
this.logger.info(
`[${dto.userUuid}] Transitioned revisions in ${migrationDurationTimeStructure.hours}h ${migrationDurationTimeStructure.minutes}m ${migrationDurationTimeStructure.seconds}s ${migrationDurationTimeStructure.milliseconds}ms`,
`Transitioned revisions for user ${userUuid.value} in ${migrationDurationTimeStructure.hours}h ${migrationDurationTimeStructure.minutes}m ${migrationDurationTimeStructure.seconds}s ${migrationDurationTimeStructure.milliseconds}ms`,
)
return Result.ok()
}
private async migrateRevisionsForUser(userUuid: Uuid): Promise<Result<string[]>> {
private async migrateRevisionsForUser(
userUuid: Uuid,
updatedRevisionsInSecondary: Revision[],
): Promise<Result<void>> {
try {
const totalRevisionsCountForUser = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
let totalRevisionsCountTransitionedToSecondary = 0
const totalPages = Math.ceil(totalRevisionsCountForUser / this.pageSize)
const revisionsToSkipInIntegrityCheck = []
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
const query = {
userUuid: userUuid,
@@ -82,44 +137,34 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
}
const revisions = await this.primaryRevisionsRepository.findByUserUuid(query)
for (const revision of revisions) {
try {
const revisionInSecondary = await (
this.secondRevisionsRepository as RevisionRepositoryInterface
).findOneByUuid(Uuid.create(revision.id.toString()).getValue(), revision.props.userUuid as Uuid, [])
if (revisionInSecondary !== null) {
if (revisionInSecondary.isIdenticalTo(revision)) {
continue
}
if (revisionInSecondary.props.dates.updatedAt > revision.props.dates.updatedAt) {
this.logger.info(
`[${userUuid.value}] Revision ${revision.id.toString()} is older than revision in secondary database`,
)
revisionsToSkipInIntegrityCheck.push(revision.id.toString())
continue
}
if (
updatedRevisionsInSecondary.find(
(updatedRevision) => updatedRevision.id.toString() === revision.id.toString(),
)
) {
this.logger.info(
`[${
userUuid.value
}] Removing revision ${revision.id.toString()} in secondary database as it is not identical to revision in primary database`,
`Skipping saving revision ${revision.id.toString()} as it was updated in secondary database`,
)
await (this.secondRevisionsRepository as RevisionRepositoryInterface).removeOneByUuid(
Uuid.create(revisionInSecondary.id.toString()).getValue(),
revisionInSecondary.props.userUuid as Uuid,
)
await this.allowForSecondaryDatabaseToCatchUp()
continue
}
this.logger.debug(
`Transitioning revision #${
totalRevisionsCountTransitionedToSecondary + 1
}: ${revision.id.toString()} to secondary database`,
)
const didSave = await (this.secondRevisionsRepository as RevisionRepositoryInterface).insert(revision)
if (!didSave) {
this.logger.error(`Failed to save revision ${revision.id.toString()} to secondary database`)
return Result.fail(`Failed to save revision ${revision.id.toString()} to secondary database`)
}
totalRevisionsCountTransitionedToSecondary++
} catch (error) {
this.logger.error(
return Result.fail(
`Errored when saving revision ${revision.id.toString()} to secondary database: ${
(error as Error).message
}`,
@@ -128,7 +173,9 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
}
}
return Result.ok(revisionsToSkipInIntegrityCheck)
this.logger.debug(`Transitioned ${totalRevisionsCountTransitionedToSecondary} revisions to secondary database`)
return Result.ok()
} catch (error) {
return Result.fail(`Errored when migrating revisions for user ${userUuid.value}: ${(error as Error).message}`)
}
@@ -139,8 +186,6 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
revisionRepository: RevisionRepositoryInterface,
): Promise<Result<void>> {
try {
this.logger.info(`[${userUuid.value}] Deleting all revisions from primary database`)
await revisionRepository.removeByUserUuid(userUuid)
return Result.ok()
@@ -154,21 +199,101 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
await this.timer.sleep(twoSecondsInMilliseconds)
}
private async hasAlreadyDataInSecondaryDatabase(userUuid: Uuid): Promise<boolean> {
const totalRevisionsCountForUserInSecondary = await (
this.secondRevisionsRepository as RevisionRepositoryInterface
).countByUserUuid(userUuid)
const hasAlreadyDataInSecondaryDatabase = totalRevisionsCountForUserInSecondary > 0
if (hasAlreadyDataInSecondaryDatabase) {
this.logger.info(
`User ${userUuid.value} has already ${totalRevisionsCountForUserInSecondary} revisions in secondary database`,
)
}
return hasAlreadyDataInSecondaryDatabase
}
private async getNewRevisionsCreatedInSecondaryDatabase(userUuid: Uuid): Promise<{
alreadyExistingInPrimary: Revision[]
newRevisionsInSecondary: Revision[]
updatedInSecondary: Revision[]
}> {
const revisions = await (this.secondRevisionsRepository as RevisionRepositoryInterface).findByUserUuid({
userUuid: userUuid,
})
const alreadyExistingInPrimary: Revision[] = []
const newRevisionsInSecondary: Revision[] = []
const updatedInSecondary: Revision[] = []
for (const revision of revisions) {
const { revisionInPrimary, newerRevisionInSecondary } =
await this.checkIfRevisionExistsInPrimaryDatabase(revision)
if (revisionInPrimary !== null) {
alreadyExistingInPrimary.push(revision)
continue
}
if (newerRevisionInSecondary !== null) {
updatedInSecondary.push(newerRevisionInSecondary)
continue
}
if (revisionInPrimary === null && newerRevisionInSecondary === null) {
newRevisionsInSecondary.push(revision)
continue
}
}
return {
alreadyExistingInPrimary: alreadyExistingInPrimary,
newRevisionsInSecondary: newRevisionsInSecondary,
updatedInSecondary: updatedInSecondary,
}
}
private async checkIfRevisionExistsInPrimaryDatabase(
revision: Revision,
): Promise<{ revisionInPrimary: Revision | null; newerRevisionInSecondary: Revision | null }> {
const revisionInPrimary = await this.primaryRevisionsRepository.findOneByUuid(
Uuid.create(revision.id.toString()).getValue(),
revision.props.userUuid as Uuid,
[],
)
if (revisionInPrimary === null) {
return {
revisionInPrimary: null,
newerRevisionInSecondary: null,
}
}
if (!revision.isIdenticalTo(revisionInPrimary)) {
this.logger.error(
`Revision ${revision.id.toString()} is not identical in primary and secondary database. Revision in secondary database: ${JSON.stringify(
revision,
)}, revision in primary database: ${JSON.stringify(revisionInPrimary)}`,
)
return {
revisionInPrimary: null,
newerRevisionInSecondary:
revision.props.dates.updatedAt > revisionInPrimary.props.dates.updatedAt ? revision : null,
}
}
return {
revisionInPrimary: revisionInPrimary,
newerRevisionInSecondary: null,
}
}
private async checkIntegrityBetweenPrimaryAndSecondaryDatabase(
userUuid: Uuid,
revisionsToSkipInIntegrityCheck: string[],
newRevisionsInSecondaryCount: number,
updatedRevisionsInSecondary: Revision[],
): Promise<Result<boolean>> {
try {
const totalRevisionsCountForUserInPrimary = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
const totalRevisionsCountForUserInSecondary = await (
this.secondRevisionsRepository as RevisionRepositoryInterface
).countByUserUuid(userUuid)
if (totalRevisionsCountForUserInPrimary > totalRevisionsCountForUserInSecondary) {
return Result.fail(
`Total revisions count for user ${userUuid.value} in primary database (${totalRevisionsCountForUserInPrimary}) does not match total revisions count in secondary database (${totalRevisionsCountForUserInSecondary})`,
)
}
const totalPages = Math.ceil(totalRevisionsCountForUserInPrimary / this.pageSize)
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
@@ -195,7 +320,14 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
return Result.fail(`Revision ${revision.id.toString()} not found in secondary database`)
}
if (revisionsToSkipInIntegrityCheck.includes(revision.id.toString())) {
if (
updatedRevisionsInSecondary.find(
(updatedRevision) => updatedRevision.id.toString() === revision.id.toString(),
)
) {
this.logger.info(
`Skipping integrity check for revision ${revision.id.toString()} as it was updated in secondary database`,
)
continue
}
@@ -209,6 +341,19 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
}
}
const totalRevisionsCountForUserInSecondary = await (
this.secondRevisionsRepository as RevisionRepositoryInterface
).countByUserUuid(userUuid)
if (
totalRevisionsCountForUserInPrimary + newRevisionsInSecondaryCount !==
totalRevisionsCountForUserInSecondary
) {
return Result.fail(
`Total revisions count for user ${userUuid.value} in primary database (${totalRevisionsCountForUserInPrimary} + ${newRevisionsInSecondaryCount}) does not match total revisions count in secondary database (${totalRevisionsCountForUserInSecondary})`,
)
}
return Result.ok()
} catch (error) {
return Result.fail(

View File

@@ -1,5 +1,5 @@
import { MapperInterface, Uuid } from '@standardnotes/domain-core'
import { MongoRepository, ObjectLiteral } from 'typeorm'
import { MongoRepository } from 'typeorm'
import { BSON } from 'mongodb'
import { Logger } from 'winston'
@@ -16,25 +16,19 @@ export class MongoDBRevisionRepository implements RevisionRepositoryInterface {
private logger: Logger,
) {}
async clearSharedVaultAndKeySystemAssociations(dto: { itemUuid?: Uuid; sharedVaultUuid: Uuid }): Promise<void> {
let query: ObjectLiteral
if (dto.itemUuid !== undefined) {
query = {
itemUuid: { $eq: dto.itemUuid.value },
sharedVaultUuid: { $eq: dto.sharedVaultUuid.value },
}
} else {
query = {
sharedVaultUuid: { $eq: dto.sharedVaultUuid.value },
}
}
await this.mongoRepository.updateMany(query, {
$set: {
sharedVaultUuid: null,
keySystemIdentifier: null,
async clearSharedVaultAndKeySystemAssociations(itemUuid: Uuid, sharedVaultUuid: Uuid): Promise<void> {
await this.mongoRepository.updateMany(
{
itemUuid: { $eq: itemUuid.value },
sharedVaultUuid: { $eq: sharedVaultUuid.value },
},
})
{
$set: {
sharedVaultUuid: null,
keySystemIdentifier: null,
},
},
)
}
async countByUserUuid(userUuid: Uuid): Promise<number> {

View File

@@ -15,7 +15,7 @@ export class SQLLegacyRevisionRepository implements RevisionRepositoryInterface
protected logger: Logger,
) {}
async clearSharedVaultAndKeySystemAssociations(_dto: { itemUuid?: Uuid; sharedVaultUuid: Uuid }): Promise<void> {
async clearSharedVaultAndKeySystemAssociations(_itemUuid: Uuid, _sharedVaultUuid: Uuid): Promise<void> {
this.logger.error('Method clearSharedVaultAndKeySystemAssociations not implemented.')
}

View File

@@ -66,27 +66,19 @@ export class SQLRevisionRepository extends SQLLegacyRevisionRepository {
return this.revisionMapper.toDomain(sqlRevision)
}
override async clearSharedVaultAndKeySystemAssociations(dto: {
itemUuid?: Uuid
sharedVaultUuid: Uuid
}): Promise<void> {
const queryBuilder = this.ormRepository.createQueryBuilder().update().set({
sharedVaultUuid: null,
keySystemIdentifier: null,
})
if (dto.itemUuid !== undefined) {
queryBuilder.where('item_uuid = :itemUuid AND shared_vault_uuid = :sharedVaultUuid', {
itemUuid: dto.itemUuid.value,
sharedVaultUuid: dto.sharedVaultUuid.value,
override async clearSharedVaultAndKeySystemAssociations(itemUuid: Uuid, sharedVaultUuid: Uuid): Promise<void> {
await this.ormRepository
.createQueryBuilder()
.update()
.set({
sharedVaultUuid: null,
keySystemIdentifier: null,
})
} else {
queryBuilder.where('shared_vault_uuid = :sharedVaultUuid', {
sharedVaultUuid: dto.sharedVaultUuid.value,
.where('item_uuid = :itemUuid AND shared_vault_uuid = :sharedVaultUuid', {
itemUuid: itemUuid.value,
sharedVaultUuid: sharedVaultUuid.value,
})
}
await queryBuilder.execute()
.execute()
}
override async findMetadataByItemId(

View File

@@ -3,22 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.20.52](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.51...@standardnotes/scheduler-server@1.20.52) (2023-09-21)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.51](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.50...@standardnotes/scheduler-server@1.20.51) (2023-09-20)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.50](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.49...@standardnotes/scheduler-server@1.20.50) (2023-09-20)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.49](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.48...@standardnotes/scheduler-server@1.20.49) (2023-09-19)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.48](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.47...@standardnotes/scheduler-server@1.20.48) (2023-09-18)
**Note:** Version bump only for package @standardnotes/scheduler-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.20.52",
"version": "1.20.48",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,22 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.37](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.36...@standardnotes/settings@1.21.37) (2023-09-21)
**Note:** Version bump only for package @standardnotes/settings
## [1.21.36](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.35...@standardnotes/settings@1.21.36) (2023-09-20)
**Note:** Version bump only for package @standardnotes/settings
## [1.21.35](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.34...@standardnotes/settings@1.21.35) (2023-09-20)
**Note:** Version bump only for package @standardnotes/settings
## [1.21.34](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.33...@standardnotes/settings@1.21.34) (2023-09-19)
**Note:** Version bump only for package @standardnotes/settings
## [1.21.33](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.32...@standardnotes/settings@1.21.33) (2023-09-18)
**Note:** Version bump only for package @standardnotes/settings

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/settings",
"version": "1.21.37",
"version": "1.21.33",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,112 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.105.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.104.0...@standardnotes/syncing-server@1.105.0) (2023-09-22)
### Bug Fixes
* add more logs to transition process ([0562b0a](https://github.com/standardnotes/syncing-server-js/commit/0562b0a621eb878026fbdc0346b6170e815b64bf))
* remove excessive logs ([15ed1fd](https://github.com/standardnotes/syncing-server-js/commit/15ed1fd789aba306cbec6a23e88d5c1f837dabc0))
### Features
* **syncing-server:** transfer shared vault ownership to designated survivor upon account deletion ([#845](https://github.com/standardnotes/syncing-server-js/issues/845)) ([0a1080c](https://github.com/standardnotes/syncing-server-js/commit/0a1080ce2a0fb021309a960de2c40193acab46eb))
# [1.104.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.103.1...@standardnotes/syncing-server@1.104.0) (2023-09-22)
### Features
* **syncing-server:** add designated survivors in fetching shared vaults response ([#844](https://github.com/standardnotes/syncing-server-js/issues/844)) ([bcd95cd](https://github.com/standardnotes/syncing-server-js/commit/bcd95cdbe9054d4ca39d5dc0486b6a0c0b6f52da))
## [1.103.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.103.0...@standardnotes/syncing-server@1.103.1) (2023-09-22)
### Bug Fixes
* disable cleaning secondary database on transition ([4f4443a](https://github.com/standardnotes/syncing-server-js/commit/4f4443a882f69c2e76ef831ef36347c9c54f31cd))
* integrity check during transition ([921c30f](https://github.com/standardnotes/syncing-server-js/commit/921c30f6415ef122a7d1af83ffa3f6840a42edba))
* processing migration optimization ([22540ee](https://github.com/standardnotes/syncing-server-js/commit/22540ee83436b986949127a6923285a702162706))
# [1.103.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.102.2...@standardnotes/syncing-server@1.103.0) (2023-09-22)
### Features
* remove user from all shared vaults upon account deletion ([#843](https://github.com/standardnotes/syncing-server-js/issues/843)) ([dc77ff3](https://github.com/standardnotes/syncing-server-js/commit/dc77ff3e45983d231bc9c132802428e77b4be431))
## [1.102.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.102.1...@standardnotes/syncing-server@1.102.2) (2023-09-22)
### Bug Fixes
* **syncing-server:** error message ([d0fd6b9](https://github.com/standardnotes/syncing-server-js/commit/d0fd6b98df58f6bd2050ff415515c692ecd32bef))
## [1.102.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.102.0...@standardnotes/syncing-server@1.102.1) (2023-09-21)
### Bug Fixes
* **syncing-server:** add missing binding ([a5da42b](https://github.com/standardnotes/syncing-server-js/commit/a5da42bdddc9bad0d641ad9a50932133b76a546a))
# [1.102.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.101.1...@standardnotes/syncing-server@1.102.0) (2023-09-21)
### Features
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/syncing-server-js/issues/841)) ([230c96d](https://github.com/standardnotes/syncing-server-js/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
## [1.101.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.101.0...@standardnotes/syncing-server@1.101.1) (2023-09-21)
### Bug Fixes
* secondary database catch up time ([880db10](https://github.com/standardnotes/syncing-server-js/commit/880db1038a39d4610a2593489a18e207487347a2))
# [1.101.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.100.0...@standardnotes/syncing-server@1.101.0) (2023-09-20)
### Features
* add unassigning items and revisions upon shared vault removal ([#839](https://github.com/standardnotes/syncing-server-js/issues/839)) ([378eced](https://github.com/standardnotes/syncing-server-js/commit/378ecedfcc4fb23475c2329fb37479edb3b48a39))
* **syncing-server:** distinct notifications upon user removal from shared vault ([#840](https://github.com/standardnotes/syncing-server-js/issues/840)) ([41e2136](https://github.com/standardnotes/syncing-server-js/commit/41e2136bc07312974701a70652528d304105e0f9))
# [1.100.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.99.0...@standardnotes/syncing-server@1.100.0) (2023-09-20)
### Features
* **syncing-server:** remove owned shared vaults upon account deletion ([#838](https://github.com/standardnotes/syncing-server-js/issues/838)) ([22a8cc9](https://github.com/standardnotes/syncing-server-js/commit/22a8cc90f1232fd5f5646f613c80bd7c60186670))
# [1.99.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.98.6...@standardnotes/syncing-server@1.99.0) (2023-09-20)
### Features
* **syncing-server:** add notification for user upon declined shared vault invitation ([#837](https://github.com/standardnotes/syncing-server-js/issues/837)) ([31e7aaf](https://github.com/standardnotes/syncing-server-js/commit/31e7aaf253029a951d8b943d6cffd655cd5ca765))
## [1.98.6](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.98.5...@standardnotes/syncing-server@1.98.6) (2023-09-19)
### Bug Fixes
* skip removing already existing content in secondary to pick up where the transition left of ([857c6af](https://github.com/standardnotes/syncing-server-js/commit/857c6af9468ec829ff4dce9a96ba7bf9c14d55a5))
## [1.98.5](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.98.4...@standardnotes/syncing-server@1.98.5) (2023-09-19)
### Bug Fixes
* increase timeout for secondary database to catch up for indexes to be rebuilt ([b265a39](https://github.com/standardnotes/syncing-server-js/commit/b265a39b635373c36ee8c3d8e09f0631159b3574))
* logs verbosity during transitions ([e589029](https://github.com/standardnotes/syncing-server-js/commit/e589029722ab9f4debc8aa6cc78913f877eda2e3))
## [1.98.4](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.98.3...@standardnotes/syncing-server@1.98.4) (2023-09-19)
### Bug Fixes
* add checking for secondary items logs ([a1a3e9f](https://github.com/standardnotes/syncing-server-js/commit/a1a3e9f586358b943b1b490a1382e42f081f7d06))
* logs for removing already existing content and paging through diff of the content ([a40b17b](https://github.com/standardnotes/syncing-server-js/commit/a40b17b141f1d5954e1a45b969d5a941386c68d0))
## [1.98.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.98.2...@standardnotes/syncing-server@1.98.3) (2023-09-19)
### Bug Fixes
* logs formatting during transition for better readability ([0ae028d](https://github.com/standardnotes/syncing-server-js/commit/0ae028db739decec8c50321b18b0af515e00bd23))
## [1.98.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.98.1...@standardnotes/syncing-server@1.98.2) (2023-09-19)
### Bug Fixes
* **syncing-server:** paging through already existing items ([e4fcd73](https://github.com/standardnotes/syncing-server-js/commit/e4fcd738c35a4dc96e57db6ca08383a5647d61ad))
## [1.98.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.98.0...@standardnotes/syncing-server@1.98.1) (2023-09-18)
**Note:** Version bump only for package @standardnotes/syncing-server

View File

@@ -1,13 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddDesignatedSurvivor1695284084365 implements MigrationInterface {
name = 'AddDesignatedSurvivor1695284084365'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `shared_vault_users` ADD `is_designated_survivor` tinyint NOT NULL DEFAULT 0')
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `shared_vault_users` DROP COLUMN `is_designated_survivor`')
}
}

View File

@@ -1,13 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddDesignatedSurvivor1695284084365 implements MigrationInterface {
name = 'AddDesignatedSurvivor1695284084365'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `shared_vault_users` ADD `is_designated_survivor` tinyint NOT NULL DEFAULT 0')
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `shared_vault_users` DROP COLUMN `is_designated_survivor`')
}
}

View File

@@ -1,39 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddDesignatedSurvivor1695284249461 implements MigrationInterface {
name = 'AddDesignatedSurvivor1695284249461'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('DROP INDEX "user_uuid_on_shared_vault_users"')
await queryRunner.query('DROP INDEX "shared_vault_uuid_on_shared_vault_users"')
await queryRunner.query(
'CREATE TABLE "temporary_shared_vault_users" ("uuid" varchar PRIMARY KEY NOT NULL, "shared_vault_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36) NOT NULL, "permission" varchar(24) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL, "is_designated_survivor" boolean NOT NULL DEFAULT (0))',
)
await queryRunner.query(
'INSERT INTO "temporary_shared_vault_users"("uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp" FROM "shared_vault_users"',
)
await queryRunner.query('DROP TABLE "shared_vault_users"')
await queryRunner.query('ALTER TABLE "temporary_shared_vault_users" RENAME TO "shared_vault_users"')
await queryRunner.query('CREATE INDEX "user_uuid_on_shared_vault_users" ON "shared_vault_users" ("user_uuid") ')
await queryRunner.query(
'CREATE INDEX "shared_vault_uuid_on_shared_vault_users" ON "shared_vault_users" ("shared_vault_uuid") ',
)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('DROP INDEX "shared_vault_uuid_on_shared_vault_users"')
await queryRunner.query('DROP INDEX "user_uuid_on_shared_vault_users"')
await queryRunner.query('ALTER TABLE "shared_vault_users" RENAME TO "temporary_shared_vault_users"')
await queryRunner.query(
'CREATE TABLE "shared_vault_users" ("uuid" varchar PRIMARY KEY NOT NULL, "shared_vault_uuid" varchar(36) NOT NULL, "user_uuid" varchar(36) NOT NULL, "permission" varchar(24) NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
)
await queryRunner.query(
'INSERT INTO "shared_vault_users"("uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "shared_vault_uuid", "user_uuid", "permission", "created_at_timestamp", "updated_at_timestamp" FROM "temporary_shared_vault_users"',
)
await queryRunner.query('DROP TABLE "temporary_shared_vault_users"')
await queryRunner.query(
'CREATE INDEX "shared_vault_uuid_on_shared_vault_users" ON "shared_vault_users" ("shared_vault_uuid") ',
)
await queryRunner.query('CREATE INDEX "user_uuid_on_shared_vault_users" ON "shared_vault_users" ("user_uuid") ')
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.105.0",
"version": "1.98.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -165,12 +165,6 @@ import { SQLItemPersistenceMapper } from '../Mapping/Persistence/SQLItemPersiste
import { SQLItemRepository } from '../Infra/TypeORM/SQLItemRepository'
import { SendEventToClient } from '../Domain/UseCase/Syncing/SendEventToClient/SendEventToClient'
import { TransitionRequestedEventHandler } from '../Domain/Handler/TransitionRequestedEventHandler'
import { DeleteSharedVaults } from '../Domain/UseCase/SharedVaults/DeleteSharedVaults/DeleteSharedVaults'
import { RemoveItemsFromSharedVault } from '../Domain/UseCase/SharedVaults/RemoveItemsFromSharedVault/RemoveItemsFromSharedVault'
import { SharedVaultRemovedEventHandler } from '../Domain/Handler/SharedVaultRemovedEventHandler'
import { DesignateSurvivor } from '../Domain/UseCase/SharedVaults/DesignateSurvivor/DesignateSurvivor'
import { RemoveUserFromSharedVaults } from '../Domain/UseCase/SharedVaults/RemoveUserFromSharedVaults/RemoveUserFromSharedVaults'
import { TransferSharedVault } from '../Domain/UseCase/SharedVaults/TransferSharedVault/TransferSharedVault'
export class ContainerConfigLoader {
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -729,12 +723,7 @@ export class ContainerConfigLoader {
)
container
.bind<DeclineInviteToSharedVault>(TYPES.Sync_DeclineInviteToSharedVault)
.toConstantValue(
new DeclineInviteToSharedVault(
container.get<SharedVaultInviteRepositoryInterface>(TYPES.Sync_SharedVaultInviteRepository),
container.get<AddNotificationForUser>(TYPES.Sync_AddNotificationForUser),
),
)
.toConstantValue(new DeclineInviteToSharedVault(container.get(TYPES.Sync_SharedVaultInviteRepository)))
container
.bind<DeleteSharedVaultInvitesToUser>(TYPES.Sync_DeleteSharedVaultInvitesToUser)
.toConstantValue(
@@ -769,7 +758,6 @@ export class ContainerConfigLoader {
container.get<SharedVaultUserRepositoryInterface>(TYPES.Sync_SharedVaultUserRepository),
container.get<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository),
container.get<AddNotificationsForUsers>(TYPES.Sync_AddNotificationsForUsers),
container.get<AddNotificationForUser>(TYPES.Sync_AddNotificationForUser),
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
),
@@ -783,6 +771,16 @@ export class ContainerConfigLoader {
container.get(TYPES.Sync_Timer),
),
)
container
.bind<DeleteSharedVault>(TYPES.Sync_DeleteSharedVault)
.toConstantValue(
new DeleteSharedVault(
container.get(TYPES.Sync_SharedVaultRepository),
container.get(TYPES.Sync_SharedVaultUserRepository),
container.get(TYPES.Sync_SharedVaultInviteRepository),
container.get(TYPES.Sync_RemoveSharedVaultUser),
),
)
container
.bind<CreateSharedVaultFileValetToken>(TYPES.Sync_CreateSharedVaultFileValetToken)
.toConstantValue(
@@ -838,66 +836,6 @@ export class ContainerConfigLoader {
env.get('MIGRATION_BATCH_SIZE', true) ? +env.get('MIGRATION_BATCH_SIZE', true) : 100,
),
)
container
.bind<RemoveItemsFromSharedVault>(TYPES.Sync_RemoveItemsFromSharedVault)
.toConstantValue(
new RemoveItemsFromSharedVault(
isSecondaryDatabaseEnabled
? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository)
: container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
),
)
container
.bind<DesignateSurvivor>(TYPES.Sync_DesignateSurvivor)
.toConstantValue(
new DesignateSurvivor(
container.get<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository),
container.get<SharedVaultUserRepositoryInterface>(TYPES.Sync_SharedVaultUserRepository),
container.get<TimerInterface>(TYPES.Sync_Timer),
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
),
)
container
.bind<RemoveUserFromSharedVaults>(TYPES.Sync_RemoveUserFromSharedVaults)
.toConstantValue(
new RemoveUserFromSharedVaults(
container.get<SharedVaultUserRepositoryInterface>(TYPES.Sync_SharedVaultUserRepository),
container.get<RemoveUserFromSharedVault>(TYPES.Sync_RemoveSharedVaultUser),
container.get<Logger>(TYPES.Sync_Logger),
),
)
container
.bind<TransferSharedVault>(TYPES.Sync_TransferSharedVault)
.toConstantValue(
new TransferSharedVault(
container.get<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository),
container.get<SharedVaultUserRepositoryInterface>(TYPES.Sync_SharedVaultUserRepository),
container.get<TimerInterface>(TYPES.Sync_Timer),
),
)
container
.bind<DeleteSharedVault>(TYPES.Sync_DeleteSharedVault)
.toConstantValue(
new DeleteSharedVault(
container.get<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository),
container.get<SharedVaultUserRepositoryInterface>(TYPES.Sync_SharedVaultUserRepository),
container.get<SharedVaultInviteRepositoryInterface>(TYPES.Sync_SharedVaultInviteRepository),
container.get<RemoveUserFromSharedVault>(TYPES.Sync_RemoveSharedVaultUser),
container.get<DeclineInviteToSharedVault>(TYPES.Sync_DeclineInviteToSharedVault),
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
container.get<TransferSharedVault>(TYPES.Sync_TransferSharedVault),
),
)
container
.bind<DeleteSharedVaults>(TYPES.Sync_DeleteSharedVaults)
.toConstantValue(
new DeleteSharedVaults(
container.get<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository),
container.get<DeleteSharedVault>(TYPES.Sync_DeleteSharedVault),
),
)
// Services
container
@@ -959,8 +897,6 @@ export class ContainerConfigLoader {
.toConstantValue(
new AccountDeletionRequestedEventHandler(
container.get<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver),
container.get<DeleteSharedVaults>(TYPES.Sync_DeleteSharedVaults),
container.get<RemoveUserFromSharedVaults>(TYPES.Sync_RemoveUserFromSharedVaults),
container.get<Logger>(TYPES.Sync_Logger),
),
)
@@ -1014,14 +950,6 @@ export class ContainerConfigLoader {
container.get<Logger>(TYPES.Sync_Logger),
),
)
container
.bind<SharedVaultRemovedEventHandler>(TYPES.Sync_SharedVaultRemovedEventHandler)
.toConstantValue(
new SharedVaultRemovedEventHandler(
container.get<RemoveItemsFromSharedVault>(TYPES.Sync_RemoveItemsFromSharedVault),
container.get<Logger>(TYPES.Sync_Logger),
),
)
// Services
container.bind<ContentDecoder>(TYPES.Sync_ContentDecoder).toDynamicValue(() => new ContentDecoder())
@@ -1060,10 +988,6 @@ export class ContainerConfigLoader {
'TRANSITION_REQUESTED',
container.get<TransitionRequestedEventHandler>(TYPES.Sync_TransitionRequestedEventHandler),
],
[
'SHARED_VAULT_REMOVED',
container.get<SharedVaultRemovedEventHandler>(TYPES.Sync_SharedVaultRemovedEventHandler),
],
])
if (!isConfiguredForHomeServer) {
container.bind(TYPES.Sync_AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL'))
@@ -1166,13 +1090,10 @@ export class ContainerConfigLoader {
.bind<BaseSharedVaultUsersController>(TYPES.Sync_BaseSharedVaultUsersController)
.toConstantValue(
new BaseSharedVaultUsersController(
container.get<GetSharedVaultUsers>(TYPES.Sync_GetSharedVaultUsers),
container.get<RemoveUserFromSharedVault>(TYPES.Sync_RemoveSharedVaultUser),
container.get<DesignateSurvivor>(TYPES.Sync_DesignateSurvivor),
container.get<MapperInterface<SharedVaultUser, SharedVaultUserHttpRepresentation>>(
TYPES.Sync_SharedVaultUserHttpMapper,
),
container.get<ControllerContainerInterface>(TYPES.Sync_ControllerContainer),
container.get(TYPES.Sync_GetSharedVaultUsers),
container.get(TYPES.Sync_RemoveSharedVaultUser),
container.get(TYPES.Sync_SharedVaultUserHttpMapper),
container.get(TYPES.Sync_ControllerContainer),
),
)
container

View File

@@ -54,7 +54,6 @@ const TYPES = {
Sync_GetSharedVaults: Symbol.for('Sync_GetSharedVaults'),
Sync_CreateSharedVault: Symbol.for('Sync_CreateSharedVault'),
Sync_DeleteSharedVault: Symbol.for('Sync_DeleteSharedVault'),
Sync_DeleteSharedVaults: Symbol.for('Sync_DeleteSharedVaults'),
Sync_CreateSharedVaultFileValetToken: Symbol.for('Sync_CreateSharedVaultFileValetToken'),
Sync_GetSharedVaultUsers: Symbol.for('Sync_GetSharedVaultUsers'),
Sync_AddUserToSharedVault: Symbol.for('Sync_AddUserToSharedVault'),
@@ -86,10 +85,6 @@ const TYPES = {
'Sync_TransitionItemsFromPrimaryToSecondaryDatabaseForUser',
),
Sync_SendEventToClient: Symbol.for('Sync_SendEventToClient'),
Sync_RemoveItemsFromSharedVault: Symbol.for('Sync_RemoveItemsFromSharedVault'),
Sync_DesignateSurvivor: Symbol.for('Sync_DesignateSurvivor'),
Sync_RemoveUserFromSharedVaults: Symbol.for('Sync_RemoveUserFromSharedVaults'),
Sync_TransferSharedVault: Symbol.for('Sync_TransferSharedVault'),
// Handlers
Sync_AccountDeletionRequestedEventHandler: Symbol.for('Sync_AccountDeletionRequestedEventHandler'),
Sync_DuplicateItemSyncedEventHandler: Symbol.for('Sync_DuplicateItemSyncedEventHandler'),
@@ -99,7 +94,6 @@ const TYPES = {
Sync_SharedVaultFileUploadedEventHandler: Symbol.for('Sync_SharedVaultFileUploadedEventHandler'),
Sync_SharedVaultFileMovedEventHandler: Symbol.for('Sync_SharedVaultFileMovedEventHandler'),
Sync_TransitionRequestedEventHandler: Symbol.for('Sync_TransitionRequestedEventHandler'),
Sync_SharedVaultRemovedEventHandler: Symbol.for('Sync_SharedVaultRemovedEventHandler'),
// Services
Sync_ContentDecoder: Symbol.for('Sync_ContentDecoder'),
Sync_DomainEventPublisher: Symbol.for('Sync_DomainEventPublisher'),

Some files were not shown because too many files have changed in this diff Show More