Compare commits

..

6 Commits

25 changed files with 193 additions and 77 deletions

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.148.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.148.1...@standardnotes/auth-server@1.148.2) (2023-09-29)
### Bug Fixes
* **auth:** increase ttl for in progress duration of transitions ([681e037](https://github.com/standardnotes/server/commit/681e0378ae6b97c838b0b34ccc630194b304b81a))
## [1.148.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.148.0...@standardnotes/auth-server@1.148.1) (2023-09-29)
### Bug Fixes
* **auth:** disable transitions retriggering if they are in progress ([5ef6c5c](https://github.com/standardnotes/server/commit/5ef6c5c14a9f7a558de7ac9ff0ab99a5f831c127))
# [1.148.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.147.1...@standardnotes/auth-server@1.148.0) (2023-09-28)
### Features

View File

@@ -55,7 +55,7 @@ const requestTransition = async (
let wasTransitionRequested = false
if (itemsTransitionStatus?.value !== TransitionStatus.STATUSES.Verified) {
if (itemsTransitionStatus === null || itemsTransitionStatus.value === TransitionStatus.STATUSES.Failed) {
wasTransitionRequested = true
await transitionStatusRepository.remove(user.uuid, 'items')
@@ -68,7 +68,7 @@ const requestTransition = async (
)
}
if (revisionsTransitionStatus?.value !== TransitionStatus.STATUSES.Verified) {
if (revisionsTransitionStatus === null || revisionsTransitionStatus.value === TransitionStatus.STATUSES.Failed) {
wasTransitionRequested = true
await transitionStatusRepository.remove(user.uuid, 'revisions')

View File

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

View File

@@ -19,8 +19,8 @@ export class RedisTransitionStatusRepository implements TransitionStatusReposito
await this.redisClient.set(`${this.PREFIX}:${transitionType}:${userUuid}`, status.value)
break
case TransitionStatus.STATUSES.InProgress: {
const ttl2Hourse = 7_200
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, ttl2Hourse, status.value)
const ttl24Hours = 86_400
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, ttl24Hours, status.value)
break
}
}

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.16.11](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.10...@standardnotes/home-server@1.16.11) (2023-09-29)
**Note:** Version bump only for package @standardnotes/home-server
## [1.16.10](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.9...@standardnotes/home-server@1.16.10) (2023-09-29)
**Note:** Version bump only for package @standardnotes/home-server
## [1.16.9](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.8...@standardnotes/home-server@1.16.9) (2023-09-28)
**Note:** Version bump only for package @standardnotes/home-server

View File

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

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.38.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.38.2...@standardnotes/revisions-server@1.38.3) (2023-09-29)
### Bug Fixes
* add paging memory to integrity check ([e4ca310](https://github.com/standardnotes/server/commit/e4ca310707b12b1c08073a391e8857ee52acd92b))
## [1.38.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.38.1...@standardnotes/revisions-server@1.38.2) (2023-09-28)
**Note:** Version bump only for package @standardnotes/revisions-server

View File

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

View File

@@ -1,4 +1,6 @@
export interface TransitionRepositoryInterface {
getPagingProgress(userUuid: string): Promise<number>
setPagingProgress(userUuid: string, progress: number): Promise<void>
getIntegrityProgress(userUuid: string): Promise<number>
setIntegrityProgress(userUuid: string, progress: number): Promise<void>
}

View File

@@ -42,7 +42,6 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
if (migrationResult.isFailed()) {
return Result.fail(migrationResult.getError())
}
const revisionsToSkipInIntegrityCheck = migrationResult.getValue()
this.logger.info(`[${dto.userUuid}] Revisions migrated`)
@@ -50,11 +49,11 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
this.logger.info(`[${dto.userUuid}] Checking integrity between primary and secondary database`)
const integrityCheckResult = await this.checkIntegrityBetweenPrimaryAndSecondaryDatabase(
userUuid,
revisionsToSkipInIntegrityCheck,
)
const integrityCheckResult = await this.checkIntegrityBetweenPrimaryAndSecondaryDatabase(userUuid)
if (integrityCheckResult.isFailed()) {
await (this.transitionStatusRepository as TransitionRepositoryInterface).setPagingProgress(userUuid.value, 1)
await (this.transitionStatusRepository as TransitionRepositoryInterface).setIntegrityProgress(userUuid.value, 1)
return Result.fail(integrityCheckResult.getError())
}
@@ -75,7 +74,7 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
return Result.ok()
}
private async migrateRevisionsForUser(userUuid: Uuid): Promise<Result<string[]>> {
private async migrateRevisionsForUser(userUuid: Uuid): Promise<Result<void>> {
try {
const initialPage = await (this.transitionStatusRepository as TransitionRepositoryInterface).getPagingProgress(
userUuid.value,
@@ -85,7 +84,6 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
const totalRevisionsCountForUser = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
const totalPages = Math.ceil(totalRevisionsCountForUser / this.pageSize)
const revisionsToSkipInIntegrityCheck = []
for (let currentPage = initialPage; currentPage <= totalPages; currentPage++) {
await (this.transitionStatusRepository as TransitionRepositoryInterface).setPagingProgress(
userUuid.value,
@@ -113,7 +111,6 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
this.logger.info(
`[${userUuid.value}] Revision ${revision.id.toString()} is older than revision in secondary database`,
)
revisionsToSkipInIntegrityCheck.push(revision.id.toString())
continue
}
@@ -145,7 +142,7 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
}
}
return Result.ok(revisionsToSkipInIntegrityCheck)
return Result.ok()
} catch (error) {
return Result.fail(`Errored when migrating revisions for user ${userUuid.value}: ${(error as Error).message}`)
}
@@ -171,11 +168,14 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
await this.timer.sleep(twoSecondsInMilliseconds)
}
private async checkIntegrityBetweenPrimaryAndSecondaryDatabase(
userUuid: Uuid,
revisionsToSkipInIntegrityCheck: string[],
): Promise<Result<boolean>> {
private async checkIntegrityBetweenPrimaryAndSecondaryDatabase(userUuid: Uuid): Promise<Result<boolean>> {
try {
const initialPage = await (this.transitionStatusRepository as TransitionRepositoryInterface).getIntegrityProgress(
userUuid.value,
)
this.logger.info(`[${userUuid.value}] Checking integrity from page ${initialPage}`)
const totalRevisionsCountForUserInPrimary = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
const totalRevisionsCountForUserInSecondary = await (
this.secondRevisionsRepository as RevisionRepositoryInterface
@@ -188,7 +188,12 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
}
const totalPages = Math.ceil(totalRevisionsCountForUserInPrimary / this.pageSize)
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
for (let currentPage = initialPage; currentPage <= totalPages; currentPage++) {
await (this.transitionStatusRepository as TransitionRepositoryInterface).setIntegrityProgress(
userUuid.value,
currentPage,
)
const query = {
userUuid: userUuid,
offset: (currentPage - 1) * this.pageSize,
@@ -212,17 +217,25 @@ export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements
return Result.fail(`Revision ${revision.id.toString()} not found in secondary database`)
}
if (revisionsToSkipInIntegrityCheck.includes(revision.id.toString())) {
if (revision.isIdenticalTo(revisionInSecondary)) {
continue
}
if (!revision.isIdenticalTo(revisionInSecondary)) {
return Result.fail(
`Revision ${revision.id.toString()} is not identical in primary and secondary database. Revision in primary database: ${JSON.stringify(
revision,
)}, revision in secondary database: ${JSON.stringify(revisionInSecondary)}`,
if (revisionInSecondary.props.dates.updatedAt > revision.props.dates.updatedAt) {
this.logger.info(
`[${
userUuid.value
}] Integrity check of revision ${revision.id.toString()} - is older than revision in secondary database`,
)
continue
}
return Result.fail(
`Revision ${revision.id.toString()} is not identical in primary and secondary database. Revision in primary database: ${JSON.stringify(
revision,
)}, revision in secondary database: ${JSON.stringify(revisionInSecondary)}`,
)
}
}

View File

@@ -3,10 +3,25 @@ import * as IORedis from 'ioredis'
import { TransitionRepositoryInterface } from '../../Domain/Transition/TransitionRepositoryInterface'
export class RedisTransitionRepository implements TransitionRepositoryInterface {
private readonly PREFIX = 'transition-revisions-paging-progress'
private readonly PREFIX = 'transition-revisions-migration-progress'
private readonly INTEGRITY_PREFIX = 'transition-revisions-integrity-progress'
constructor(private redisClient: IORedis.Redis) {}
async getIntegrityProgress(userUuid: string): Promise<number> {
const progress = await this.redisClient.get(`${this.INTEGRITY_PREFIX}:${userUuid}`)
if (progress === null) {
return 1
}
return parseInt(progress)
}
async setIntegrityProgress(userUuid: string, progress: number): Promise<void> {
await this.redisClient.setex(`${this.INTEGRITY_PREFIX}:${userUuid}`, 172_800, progress.toString())
}
async getPagingProgress(userUuid: string): Promise<number> {
const progress = await this.redisClient.get(`${this.PREFIX}:${userUuid}`)

View File

@@ -30,7 +30,7 @@ export class SQLLegacyRevisionRepository implements RevisionRepositoryInterface
const queryBuilder = this.ormRepository
.createQueryBuilder('revision')
.where('revision.user_uuid = :userUuid', { userUuid: dto.userUuid.value })
.orderBy('revision.uuid', 'ASC')
.orderBy('revision.created_at', 'ASC')
if (dto.offset !== undefined) {
queryBuilder.skip(dto.offset)

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.110.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.109.2...@standardnotes/syncing-server@1.110.0) (2023-09-29)
### Bug Fixes
* add paging memory to integrity check ([e4ca310](https://github.com/standardnotes/syncing-server-js/commit/e4ca310707b12b1c08073a391e8857ee52acd92b))
### Features
* **syncing-server:** allow surviving only upon account deletion ([#857](https://github.com/standardnotes/syncing-server-js/issues/857)) ([609e85f](https://github.com/standardnotes/syncing-server-js/commit/609e85f926ebbc2887656c46df18471c68d70185))
## [1.109.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.109.1...@standardnotes/syncing-server@1.109.2) (2023-09-28)
**Note:** Version bump only for package @standardnotes/syncing-server

View File

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

View File

@@ -37,6 +37,7 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
const deletingVaultsResult = await this.deleteSharedVaults.execute({
ownerUuid: event.payload.userUuid,
allowSurviving: true,
})
if (deletingVaultsResult.isFailed()) {
this.logger.error(

View File

@@ -1,4 +1,6 @@
export interface TransitionRepositoryInterface {
getPagingProgress(userUuid: string): Promise<number>
setPagingProgress(userUuid: string, progress: number): Promise<void>
getIntegrityProgress(userUuid: string): Promise<number>
setIntegrityProgress(userUuid: string, progress: number): Promise<void>
}

View File

@@ -96,6 +96,7 @@ describe('DeleteSharedVault', () => {
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
originatorUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: false,
})
expect(result.isFailed()).toBeFalsy()
@@ -111,6 +112,7 @@ describe('DeleteSharedVault', () => {
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
originatorUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: false,
})
expect(result.isFailed()).toBeTruthy()
@@ -125,6 +127,7 @@ describe('DeleteSharedVault', () => {
const result = await useCase.execute({
sharedVaultUuid: 'invalid',
originatorUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: false,
})
expect(result.isFailed()).toBeTruthy()
@@ -139,6 +142,7 @@ describe('DeleteSharedVault', () => {
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
originatorUuid: 'invalid',
allowSurviving: false,
})
expect(result.isFailed()).toBeTruthy()
@@ -159,6 +163,7 @@ describe('DeleteSharedVault', () => {
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
originatorUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: false,
})
expect(result.isFailed()).toBeTruthy()
@@ -174,6 +179,7 @@ describe('DeleteSharedVault', () => {
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
originatorUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: false,
})
expect(result.isFailed()).toBeTruthy()
@@ -188,6 +194,7 @@ describe('DeleteSharedVault', () => {
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
originatorUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: false,
})
expect(result.isFailed()).toBeTruthy()
@@ -207,6 +214,7 @@ describe('DeleteSharedVault', () => {
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
originatorUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: true,
})
expect(result.isFailed()).toBeFalsy()
@@ -223,6 +231,7 @@ describe('DeleteSharedVault', () => {
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
originatorUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: true,
})
expect(result.isFailed()).toBeTruthy()
@@ -239,6 +248,7 @@ describe('DeleteSharedVault', () => {
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
originatorUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: true,
})
expect(result.isFailed()).toBeTruthy()

View File

@@ -56,30 +56,32 @@ export class DeleteSharedVault implements UseCaseInterface<{ status: 'deleted' |
}
}
const sharedVaultDesignatedSurvivor =
await this.sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid)
if (sharedVaultDesignatedSurvivor) {
const result = await this.transferSharedVault.execute({
sharedVaultUid: sharedVaultUuid.value,
fromUserUuid: originatorUuid.value,
toUserUuid: sharedVaultDesignatedSurvivor.props.userUuid.value,
})
if (dto.allowSurviving) {
const sharedVaultDesignatedSurvivor =
await this.sharedVaultUserRepository.findDesignatedSurvivorBySharedVaultUuid(sharedVaultUuid)
if (sharedVaultDesignatedSurvivor) {
const result = await this.transferSharedVault.execute({
sharedVaultUid: sharedVaultUuid.value,
fromUserUuid: originatorUuid.value,
toUserUuid: sharedVaultDesignatedSurvivor.props.userUuid.value,
})
if (result.isFailed()) {
return Result.fail(result.getError())
if (result.isFailed()) {
return Result.fail(result.getError())
}
const removingOwnerFromSharedVaultResult = await this.removeUserFromSharedVault.execute({
originatorUuid: originatorUuid.value,
sharedVaultUuid: sharedVaultUuid.value,
userUuid: originatorUuid.value,
forceRemoveOwner: true,
})
if (removingOwnerFromSharedVaultResult.isFailed()) {
return Result.fail(removingOwnerFromSharedVaultResult.getError())
}
return Result.ok({ status: 'transitioned' })
}
const removingOwnerFromSharedVaultResult = await this.removeUserFromSharedVault.execute({
originatorUuid: originatorUuid.value,
sharedVaultUuid: sharedVaultUuid.value,
userUuid: originatorUuid.value,
forceRemoveOwner: true,
})
if (removingOwnerFromSharedVaultResult.isFailed()) {
return Result.fail(removingOwnerFromSharedVaultResult.getError())
}
return Result.ok({ status: 'transitioned' })
}
const sharedVaultUsers = await this.sharedVaultUserRepository.findBySharedVaultUuid(sharedVaultUuid)

View File

@@ -1,4 +1,5 @@
export interface DeleteSharedVaultDTO {
originatorUuid: string
sharedVaultUuid: string
allowSurviving: boolean
}

View File

@@ -32,6 +32,7 @@ describe('DeleteSharedVaults', () => {
const result = await useCase.execute({
ownerUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: true,
})
expect(result.isFailed()).toBe(false)
@@ -39,6 +40,7 @@ describe('DeleteSharedVaults', () => {
expect(deleteSharedVaultUseCase.execute).toHaveBeenCalledWith({
originatorUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: true,
})
})
@@ -48,6 +50,7 @@ describe('DeleteSharedVaults', () => {
const result = await useCase.execute({
ownerUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: true,
})
expect(result.isFailed()).toBe(true)
@@ -55,6 +58,7 @@ describe('DeleteSharedVaults', () => {
expect(deleteSharedVaultUseCase.execute).toHaveBeenCalledWith({
originatorUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
allowSurviving: true,
})
})
@@ -63,6 +67,7 @@ describe('DeleteSharedVaults', () => {
const result = await useCase.execute({
ownerUuid: 'invalid',
allowSurviving: true,
})
expect(result.isFailed()).toBeTruthy()

View File

@@ -24,6 +24,7 @@ export class DeleteSharedVaults implements UseCaseInterface<Map<Uuid, 'deleted'
const result = await this.deleteSharedVaultUseCase.execute({
originatorUuid: ownerUuid.value,
sharedVaultUuid: sharedVault.id.toString(),
allowSurviving: dto.allowSurviving,
})
if (result.isFailed()) {
return Result.fail(result.getError())

View File

@@ -1,3 +1,4 @@
export interface DeleteSharedVaultsDTO {
ownerUuid: string
allowSurviving: boolean
}

View File

@@ -43,7 +43,6 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
if (migrationResult.isFailed()) {
return Result.fail(migrationResult.getError())
}
const itemsToSkipInIntegrityCheck = migrationResult.getValue()
this.logger.info(`[${dto.userUuid}] Items migrated`)
@@ -51,11 +50,11 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
this.logger.info(`[${dto.userUuid}] Checking integrity between primary and secondary database`)
const integrityCheckResult = await this.checkIntegrityBetweenPrimaryAndSecondaryDatabase(
userUuid,
itemsToSkipInIntegrityCheck,
)
const integrityCheckResult = await this.checkIntegrityBetweenPrimaryAndSecondaryDatabase(userUuid)
if (integrityCheckResult.isFailed()) {
await (this.transitionStatusRepository as TransitionRepositoryInterface).setPagingProgress(userUuid.value, 1)
await (this.transitionStatusRepository as TransitionRepositoryInterface).setIntegrityProgress(userUuid.value, 1)
return Result.fail(integrityCheckResult.getError())
}
@@ -81,7 +80,7 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
await this.timer.sleep(twoSecondsInMilliseconds)
}
private async migrateItemsForUser(userUuid: Uuid): Promise<Result<string[]>> {
private async migrateItemsForUser(userUuid: Uuid): Promise<Result<void>> {
try {
const initialPage = await (this.transitionStatusRepository as TransitionRepositoryInterface).getPagingProgress(
userUuid.value,
@@ -91,7 +90,6 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
const totalItemsCountForUser = await this.primaryItemRepository.countAll({ userUuid: userUuid.value })
const totalPages = Math.ceil(totalItemsCountForUser / this.pageSize)
const itemsToSkipInIntegrityCheck = []
for (let currentPage = initialPage; currentPage <= totalPages; currentPage++) {
await (this.transitionStatusRepository as TransitionRepositoryInterface).setPagingProgress(
userUuid.value,
@@ -102,7 +100,7 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
userUuid: userUuid.value,
offset: (currentPage - 1) * this.pageSize,
limit: this.pageSize,
sortBy: 'uuid',
sortBy: 'created_at_timestamp',
sortOrder: 'ASC',
}
@@ -120,7 +118,6 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
}
if (itemInSecondary.props.timestamps.updatedAt > item.props.timestamps.updatedAt) {
this.logger.info(`[${userUuid.value}] Item ${item.uuid.value} is older than item in secondary database`)
itemsToSkipInIntegrityCheck.push(item.uuid.value)
continue
}
@@ -143,7 +140,7 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
}
}
return Result.ok(itemsToSkipInIntegrityCheck)
return Result.ok()
} catch (error) {
return Result.fail((error as Error).message)
}
@@ -161,11 +158,14 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
}
}
private async checkIntegrityBetweenPrimaryAndSecondaryDatabase(
userUuid: Uuid,
itemsToSkipInIntegrityCheck: string[],
): Promise<Result<boolean>> {
private async checkIntegrityBetweenPrimaryAndSecondaryDatabase(userUuid: Uuid): Promise<Result<boolean>> {
try {
const initialPage = await (this.transitionStatusRepository as TransitionRepositoryInterface).getIntegrityProgress(
userUuid.value,
)
this.logger.info(`[${userUuid.value}] Checking integrity from page ${initialPage}`)
const totalItemsCountForUserInPrimary = await this.primaryItemRepository.countAll({ userUuid: userUuid.value })
const totalItemsCountForUserInSecondary = await (
this.secondaryItemRepository as ItemRepositoryInterface
@@ -180,12 +180,17 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
}
const totalPages = Math.ceil(totalItemsCountForUserInPrimary / this.pageSize)
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
for (let currentPage = initialPage; currentPage <= totalPages; currentPage++) {
await (this.transitionStatusRepository as TransitionRepositoryInterface).setIntegrityProgress(
userUuid.value,
currentPage,
)
const query: ItemQuery = {
userUuid: userUuid.value,
offset: (currentPage - 1) * this.pageSize,
limit: this.pageSize,
sortBy: 'uuid',
sortBy: 'created_at_timestamp',
sortOrder: 'ASC',
}
@@ -197,19 +202,25 @@ export class TransitionItemsFromPrimaryToSecondaryDatabaseForUser implements Use
return Result.fail(`Item ${item.uuid.value} not found in secondary database`)
}
if (itemsToSkipInIntegrityCheck.includes(item.id.toString())) {
if (item.isIdenticalTo(itemInSecondary)) {
continue
}
if (!item.isIdenticalTo(itemInSecondary)) {
return Result.fail(
`Item ${
item.uuid.value
} is not identical in primary and secondary database. Item in primary database: ${JSON.stringify(
item,
)}, item in secondary database: ${JSON.stringify(itemInSecondary)}`,
if (itemInSecondary.props.timestamps.updatedAt > item.props.timestamps.updatedAt) {
this.logger.info(
`[${userUuid.value}] Integrity check of Item ${item.uuid.value} - is older than item in secondary database`,
)
continue
}
return Result.fail(
`Item ${
item.uuid.value
} is not identical in primary and secondary database. Item in primary database: ${JSON.stringify(
item,
)}, item in secondary database: ${JSON.stringify(itemInSecondary)}`,
)
}
}

View File

@@ -92,6 +92,7 @@ export class BaseSharedVaultsController extends BaseHttpController {
const result = await this.deleteSharedVaultUseCase.execute({
sharedVaultUuid: request.params.sharedVaultUuid,
originatorUuid: response.locals.user.uuid,
allowSurviving: false,
})
if (result.isFailed()) {

View File

@@ -3,10 +3,25 @@ import * as IORedis from 'ioredis'
import { TransitionRepositoryInterface } from '../../Domain/Transition/TransitionRepositoryInterface'
export class RedisTransitionRepository implements TransitionRepositoryInterface {
private readonly PREFIX = 'transition-items-paging-progress'
private readonly PREFIX = 'transition-items-migration-progress'
private readonly INTEGRITY_PREFIX = 'transition-items-integrity-progress'
constructor(private redisClient: IORedis.Redis) {}
async getIntegrityProgress(userUuid: string): Promise<number> {
const progress = await this.redisClient.get(`${this.INTEGRITY_PREFIX}:${userUuid}`)
if (progress === null) {
return 1
}
return parseInt(progress)
}
async setIntegrityProgress(userUuid: string, progress: number): Promise<void> {
await this.redisClient.setex(`${this.INTEGRITY_PREFIX}:${userUuid}`, 172_800, progress.toString())
}
async getPagingProgress(userUuid: string): Promise<number> {
const progress = await this.redisClient.get(`${this.PREFIX}:${userUuid}`)