Compare commits

..

2 Commits

41 changed files with 304 additions and 30 deletions
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.33.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.33.2...@standardnotes/analytics@2.33.3) (2023-11-22)
**Note:** Version bump only for package @standardnotes/analytics
## [2.33.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.33.1...@standardnotes/analytics@2.33.2) (2023-11-13)
**Note:** Version bump only for package @standardnotes/analytics
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.33.2",
"version": "2.33.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.86.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.86.3...@standardnotes/api-gateway@1.86.4) (2023-11-22)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.86.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.86.2...@standardnotes/api-gateway@1.86.3) (2023-11-21)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.86.3",
"version": "1.86.4",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+6
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.173.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.172.2...@standardnotes/auth-server@1.173.0) (2023-11-22)
### Features
* add verifiying if user has no items before mass deleting spam accounts ([#936](https://github.com/standardnotes/server/issues/936)) ([c11abe1](https://github.com/standardnotes/server/commit/c11abe1bd36de7c0fb9850c20a8157c066fa9379))
## [1.172.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.172.1...@standardnotes/auth-server@1.172.2) (2023-11-20)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.172.2",
"version": "1.173.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+13 -1
View File
@@ -280,6 +280,7 @@ import { TriggerEmailBackupForAllUsers } from '../Domain/UseCase/TriggerEmailBac
import { CSVFileReaderInterface } from '../Domain/CSV/CSVFileReaderInterface'
import { S3CsvFileReader } from '../Infra/S3/S3CsvFileReader'
import { DeleteAccountsFromCSVFile } from '../Domain/UseCase/DeleteAccountsFromCSVFile/DeleteAccountsFromCSVFile'
import { AccountDeletionVerificationPassedEventHandler } from '../Domain/Handler/AccountDeletionVerificationPassedEventHandler'
export class ContainerConfigLoader {
constructor(private mode: 'server' | 'worker' = 'server') {}
@@ -1274,7 +1275,9 @@ export class ContainerConfigLoader {
.toConstantValue(
new DeleteAccountsFromCSVFile(
container.get<CSVFileReaderInterface>(TYPES.Auth_CSVFileReader),
container.get<DeleteAccount>(TYPES.Auth_DeleteAccount),
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
@@ -1328,6 +1331,14 @@ export class ContainerConfigLoader {
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
container
.bind<AccountDeletionVerificationPassedEventHandler>(TYPES.Auth_AccountDeletionVerificationPassedEventHandler)
.toConstantValue(
new AccountDeletionVerificationPassedEventHandler(
container.get<DeleteAccount>(TYPES.Auth_DeleteAccount),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
container
.bind<SubscriptionPurchasedEventHandler>(TYPES.Auth_SubscriptionPurchasedEventHandler)
.toConstantValue(
@@ -1516,6 +1527,7 @@ export class ContainerConfigLoader {
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.Auth_AccountDeletionRequestedEventHandler)],
['ACCOUNT_DELETION_VERIFICATION_PASSED', container.get(TYPES.Auth_AccountDeletionVerificationPassedEventHandler)],
['SUBSCRIPTION_PURCHASED', container.get(TYPES.Auth_SubscriptionPurchasedEventHandler)],
['SUBSCRIPTION_CANCELLED', container.get(TYPES.Auth_SubscriptionCancelledEventHandler)],
['SUBSCRIPTION_RENEWED', container.get(TYPES.Auth_SubscriptionRenewedEventHandler)],
+1
View File
@@ -171,6 +171,7 @@ const TYPES = {
Auth_DeleteAccountsFromCSVFile: Symbol.for('Auth_DeleteAccountsFromCSVFile'),
// Handlers
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
Auth_AccountDeletionVerificationPassedEventHandler: Symbol.for('Auth_AccountDeletionVerificationPassedEventHandler'),
Auth_SubscriptionPurchasedEventHandler: Symbol.for('Auth_SubscriptionPurchasedEventHandler'),
Auth_SubscriptionCancelledEventHandler: Symbol.for('Auth_SubscriptionCancelledEventHandler'),
Auth_SubscriptionReassignedEventHandler: Symbol.for('Auth_SubscriptionReassignedEventHandler'),
@@ -20,6 +20,7 @@ import {
StatisticPersistenceRequestedEvent,
SessionCreatedEvent,
SessionRefreshedEvent,
AccountDeletionVerificationRequestedEvent,
} from '@standardnotes/domain-events'
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
import { TimerInterface } from '@standardnotes/time'
@@ -33,6 +34,24 @@ import { KeyParamsData } from '@standardnotes/responses'
export class DomainEventFactory implements DomainEventFactoryInterface {
constructor(@inject(TYPES.Auth_Timer) private timer: TimerInterface) {}
createAccountDeletionVerificationRequestedEvent(dto: {
userUuid: string
email: string
}): AccountDeletionVerificationRequestedEvent {
return {
type: 'ACCOUNT_DELETION_VERIFICATION_REQUESTED',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: dto.userUuid,
userIdentifierType: 'uuid',
},
origin: DomainEventService.Auth,
},
payload: dto,
}
}
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent {
return {
type: 'SESSION_CREATED',
@@ -18,6 +18,7 @@ import {
StatisticPersistenceRequestedEvent,
SessionCreatedEvent,
SessionRefreshedEvent,
AccountDeletionVerificationRequestedEvent,
} from '@standardnotes/domain-events'
import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType'
import { KeyParamsData } from '@standardnotes/responses'
@@ -56,6 +57,10 @@ export interface DomainEventFactoryInterface {
ownerUuid: string
}
}): AccountDeletionRequestedEvent
createAccountDeletionVerificationRequestedEvent(dto: {
userUuid: string
email: string
}): AccountDeletionVerificationRequestedEvent
createUserRolesChangedEvent(userUuid: string, email: string, currentRoles: string[]): UserRolesChangedEvent
createUserEmailChangedEvent(userUuid: string, fromEmail: string, toEmail: string): UserEmailChangedEvent
createUserDisabledSessionUserAgentLoggingEvent(dto: {
@@ -0,0 +1,21 @@
import { AccountDeletionVerificationPassedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
import { Logger } from 'winston'
import { DeleteAccount } from '../UseCase/DeleteAccount/DeleteAccount'
export class AccountDeletionVerificationPassedEventHandler implements DomainEventHandlerInterface {
constructor(
private deleteAccount: DeleteAccount,
private logger: Logger,
) {}
async handle(event: AccountDeletionVerificationPassedEvent): Promise<void> {
const result = await this.deleteAccount.execute({
userUuid: event.payload.userUuid,
})
if (result.isFailed()) {
this.logger.error(`AccountDeletionVerificationPassedEventHandler failed: ${result.getError()}`)
}
}
}
@@ -1,33 +1,51 @@
import { Logger } from 'winston'
import { Result } from '@standardnotes/domain-core'
import { AccountDeletionVerificationRequestedEvent, DomainEventPublisherInterface } from '@standardnotes/domain-events'
import { CSVFileReaderInterface } from '../../CSV/CSVFileReaderInterface'
import { DeleteAccount } from '../DeleteAccount/DeleteAccount'
import { DeleteAccountsFromCSVFile } from './DeleteAccountsFromCSVFile'
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
import { User } from '../../User/User'
describe('DeleteAccountsFromCSVFile', () => {
let csvFileReader: CSVFileReaderInterface
let deleteAccount: DeleteAccount
let userRepository: UserRepositoryInterface
let domainEventPublisher: DomainEventPublisherInterface
let domainEventFactory: DomainEventFactoryInterface
let logger: Logger
const createUseCase = () => new DeleteAccountsFromCSVFile(csvFileReader, deleteAccount, logger)
const createUseCase = () =>
new DeleteAccountsFromCSVFile(csvFileReader, domainEventPublisher, domainEventFactory, userRepository, logger)
beforeEach(() => {
const user = {} as jest.Mocked<User>
csvFileReader = {} as jest.Mocked<CSVFileReaderInterface>
csvFileReader.getValues = jest.fn().mockResolvedValue(Result.ok(['email1']))
deleteAccount = {} as jest.Mocked<DeleteAccount>
deleteAccount.execute = jest.fn().mockResolvedValue(Result.ok(''))
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findAllByUsernameOrEmail = jest.fn().mockResolvedValue([user])
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
domainEventPublisher.publish = jest.fn()
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
domainEventFactory.createAccountDeletionVerificationRequestedEvent = jest
.fn()
.mockReturnValue({} as jest.Mocked<AccountDeletionVerificationRequestedEvent>)
logger = {} as jest.Mocked<Logger>
logger.info = jest.fn()
})
it('should delete accounts', async () => {
it('should request account deletion verification', async () => {
const useCase = createUseCase()
const result = await useCase.execute({ fileName: 'test.csv', dryRun: false })
expect(domainEventPublisher.publish).toHaveBeenCalled()
expect(result.isFailed()).toBeFalsy()
})
@@ -56,12 +74,12 @@ describe('DeleteAccountsFromCSVFile', () => {
const result = await useCase.execute({ fileName: 'test.csv', dryRun: true })
expect(deleteAccount.execute).not.toHaveBeenCalled()
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
expect(result.isFailed()).toBeFalsy()
})
it('should return error if delete account fails', async () => {
deleteAccount.execute = jest.fn().mockResolvedValue(Result.fail('Oops'))
it('should return error username is invalid', async () => {
csvFileReader.getValues = jest.fn().mockResolvedValue(Result.ok(['']))
const useCase = createUseCase()
@@ -69,4 +87,15 @@ describe('DeleteAccountsFromCSVFile', () => {
expect(result.isFailed()).toBeTruthy()
})
it('should do nothing if users could not be found', async () => {
userRepository.findAllByUsernameOrEmail = jest.fn().mockResolvedValue([])
const useCase = createUseCase()
const result = await useCase.execute({ fileName: 'test.csv', dryRun: false })
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
expect(result.isFailed()).toBeFalsy()
})
})
@@ -1,14 +1,18 @@
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
import { Result, UseCaseInterface, Username } from '@standardnotes/domain-core'
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
import { Logger } from 'winston'
import { DeleteAccount } from '../DeleteAccount/DeleteAccount'
import { CSVFileReaderInterface } from '../../CSV/CSVFileReaderInterface'
import { DeleteAccountsFromCSVFileDTO } from './DeleteAccountsFromCSVFileDTO'
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
export class DeleteAccountsFromCSVFile implements UseCaseInterface<void> {
constructor(
private csvFileReader: CSVFileReaderInterface,
private deleteAccount: DeleteAccount,
private domainEventPublisher: DomainEventPublisherInterface,
private domainEventFactory: DomainEventFactoryInterface,
private userRepository: UserRepositoryInterface,
private logger: Logger,
) {}
@@ -33,12 +37,20 @@ export class DeleteAccountsFromCSVFile implements UseCaseInterface<void> {
}
for (const email of emails) {
const deleteAccountOrError = await this.deleteAccount.execute({
username: email,
})
const usernameOrError = Username.create(email)
if (usernameOrError.isFailed()) {
return Result.fail(usernameOrError.getError())
}
const username = usernameOrError.getValue()
if (deleteAccountOrError.isFailed()) {
return Result.fail(deleteAccountOrError.getError())
const users = await this.userRepository.findAllByUsernameOrEmail(username)
for (const user of users) {
await this.domainEventPublisher.publish(
this.domainEventFactory.createAccountDeletionVerificationRequestedEvent({
userUuid: user.uuid,
email: user.email,
}),
)
}
}
@@ -8,6 +8,7 @@ export interface UserRepositoryInterface {
streamTeam(memberEmail?: Email): Promise<ReadStream>
findOneByUuid(uuid: Uuid): Promise<User | null>
findOneByUsernameOrEmail(usernameOrEmail: Email | Username): Promise<User | null>
findAllByUsernameOrEmail(usernameOrEmail: Email | Username): Promise<User[]>
findAllCreatedBetween(dto: { start: Date; end: Date; offset: number; limit: number }): Promise<User[]>
countAllCreatedBetween(start: Date, end: Date): Promise<number>
save(user: User): Promise<User>
@@ -69,7 +69,13 @@ export class TypeORMUserRepository implements UserRepositoryInterface {
return this.ormRepository
.createQueryBuilder('user')
.where('user.email = :email', { email: usernameOrEmail.value })
.cache(`user_email_${usernameOrEmail.value}`, 60000)
.getOne()
}
async findAllByUsernameOrEmail(usernameOrEmail: Email | Username): Promise<User[]> {
return this.ormRepository
.createQueryBuilder('user')
.where('user.email = :email', { email: usernameOrEmail.value })
.getMany()
}
}
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.21.2...@standardnotes/domain-events-infra@1.21.3) (2023-11-22)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.21.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.21.1...@standardnotes/domain-events-infra@1.21.2) (2023-11-13)
**Note:** Version bump only for package @standardnotes/domain-events-infra
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events-infra",
"version": "1.21.2",
"version": "1.21.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+6
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.
# [2.135.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.134.2...@standardnotes/domain-events@2.135.0) (2023-11-22)
### Features
* add verifiying if user has no items before mass deleting spam accounts ([#936](https://github.com/standardnotes/server/issues/936)) ([c11abe1](https://github.com/standardnotes/server/commit/c11abe1bd36de7c0fb9850c20a8157c066fa9379))
## [2.134.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.134.1...@standardnotes/domain-events@2.134.2) (2023-11-13)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events",
"version": "2.134.2",
"version": "2.135.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -0,0 +1,7 @@
import { DomainEventInterface } from './DomainEventInterface'
import { AccountDeletionVerificationPassedEventPayload } from './AccountDeletionVerificationPassedEventPayload'
export interface AccountDeletionVerificationPassedEvent extends DomainEventInterface {
type: 'ACCOUNT_DELETION_VERIFICATION_PASSED'
payload: AccountDeletionVerificationPassedEventPayload
}
@@ -0,0 +1,4 @@
export interface AccountDeletionVerificationPassedEventPayload {
userUuid: string
email: string
}
@@ -0,0 +1,7 @@
import { DomainEventInterface } from './DomainEventInterface'
import { AccountDeletionVerificationRequestedEventPayload } from './AccountDeletionVerificationRequestedEventPayload'
export interface AccountDeletionVerificationRequestedEvent extends DomainEventInterface {
type: 'ACCOUNT_DELETION_VERIFICATION_REQUESTED'
payload: AccountDeletionVerificationRequestedEventPayload
}
@@ -0,0 +1,4 @@
export interface AccountDeletionVerificationRequestedEventPayload {
userUuid: string
email: string
}
@@ -1,5 +1,9 @@
export * from './Event/AccountDeletionRequestedEvent'
export * from './Event/AccountDeletionRequestedEventPayload'
export * from './Event/AccountDeletionVerificationPassedEvent'
export * from './Event/AccountDeletionVerificationPassedEventPayload'
export * from './Event/AccountDeletionVerificationRequestedEvent'
export * from './Event/AccountDeletionVerificationRequestedEventPayload'
export * from './Event/DiscountApplyRequestedEvent'
export * from './Event/DiscountApplyRequestedEventPayload'
export * from './Event/DiscountWithdrawRequestedEvent'
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.35.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.35.0...@standardnotes/files-server@1.35.1) (2023-11-22)
**Note:** Version bump only for package @standardnotes/files-server
# [1.35.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.34.2...@standardnotes/files-server@1.35.0) (2023-11-16)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.35.0",
"version": "1.35.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.9](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.8...@standardnotes/home-server@1.21.9) (2023-11-22)
**Note:** Version bump only for package @standardnotes/home-server
## [1.21.8](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.21.7...@standardnotes/home-server@1.21.8) (2023-11-21)
**Note:** Version bump only for package @standardnotes/home-server
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.21.8",
"version": "1.21.9",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.50.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.50.0...@standardnotes/revisions-server@1.50.1) (2023-11-22)
**Note:** Version bump only for package @standardnotes/revisions-server
# [1.50.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.49.2...@standardnotes/revisions-server@1.50.0) (2023-11-16)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/revisions-server",
"version": "1.50.0",
"version": "1.50.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.3](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.27.2...@standardnotes/scheduler-server@1.27.3) (2023-11-22)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.27.2](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.27.1...@standardnotes/scheduler-server@1.27.2) (2023-11-13)
**Note:** Version bump only for package @standardnotes/scheduler-server
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.27.2",
"version": "1.27.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
+6
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.125.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.124.3...@standardnotes/syncing-server@1.125.0) (2023-11-22)
### Features
* add verifiying if user has no items before mass deleting spam accounts ([#936](https://github.com/standardnotes/syncing-server-js/issues/936)) ([c11abe1](https://github.com/standardnotes/syncing-server-js/commit/c11abe1bd36de7c0fb9850c20a8157c066fa9379))
## [1.124.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.124.2...@standardnotes/syncing-server@1.124.3) (2023-11-21)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.124.3",
"version": "1.125.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -160,6 +160,7 @@ import { DumpItem } from '../Domain/UseCase/Syncing/DumpItem/DumpItem'
import { SyncResponse20200115 } from '../Domain/Item/SyncResponse/SyncResponse20200115'
import { SyncResponse } from '@standardnotes/grpc'
import { SyncResponseGRPCMapper } from '../Mapping/gRPC/SyncResponseGRPCMapper'
import { AccountDeletionVerificationRequestedEventHandler } from '../Domain/Handler/AccountDeletionVerificationRequestedEventHandler'
export class ContainerConfigLoader {
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -907,6 +908,18 @@ export class ContainerConfigLoader {
container.get<Logger>(TYPES.Sync_Logger),
),
)
container
.bind<AccountDeletionVerificationRequestedEventHandler>(
TYPES.Sync_AccountDeletionVerificationRequestedEventHandler,
)
.toConstantValue(
new AccountDeletionVerificationRequestedEventHandler(
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
container.get<Logger>(TYPES.Sync_Logger),
),
)
container
.bind<ItemRevisionCreationRequestedEventHandler>(TYPES.Sync_ItemRevisionCreationRequestedEventHandler)
.toConstantValue(
@@ -957,6 +970,10 @@ export class ContainerConfigLoader {
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
['DUPLICATE_ITEM_SYNCED', container.get(TYPES.Sync_DuplicateItemSyncedEventHandler)],
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.Sync_AccountDeletionRequestedEventHandler)],
[
'ACCOUNT_DELETION_VERIFICATION_REQUESTED',
container.get(TYPES.Sync_AccountDeletionVerificationRequestedEventHandler),
],
['ITEM_REVISION_CREATION_REQUESTED', container.get(TYPES.Sync_ItemRevisionCreationRequestedEventHandler)],
[
'SHARED_VAULT_FILE_UPLOADED',
@@ -85,6 +85,9 @@ const TYPES = {
Sync_DumpItem: Symbol.for('Sync_DumpItem'),
// Handlers
Sync_AccountDeletionRequestedEventHandler: Symbol.for('Sync_AccountDeletionRequestedEventHandler'),
Sync_AccountDeletionVerificationRequestedEventHandler: Symbol.for(
'Sync_AccountDeletionVerificationRequestedEventHandler',
),
Sync_DuplicateItemSyncedEventHandler: Symbol.for('Sync_DuplicateItemSyncedEventHandler'),
Sync_EmailBackupRequestedEventHandler: Symbol.for('Sync_EmailBackupRequestedEventHandler'),
Sync_ItemRevisionCreationRequestedEventHandler: Symbol.for('Sync_ItemRevisionCreationRequestedEventHandler'),
@@ -1,5 +1,6 @@
/* istanbul ignore file */
import {
AccountDeletionVerificationPassedEvent,
DomainEventService,
DuplicateItemSyncedEvent,
EmailRequestedEvent,
@@ -22,6 +23,24 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
export class DomainEventFactory implements DomainEventFactoryInterface {
constructor(private timer: TimerInterface) {}
createAccountDeletionVerificationPassedEvent(dto: {
userUuid: string
email: string
}): AccountDeletionVerificationPassedEvent {
return {
type: 'ACCOUNT_DELETION_VERIFICATION_PASSED',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: dto.userUuid,
userIdentifierType: 'uuid',
},
origin: DomainEventService.SyncingServer,
},
payload: dto,
}
}
createUserDesignatedAsSurvivorInSharedVaultEvent(dto: {
sharedVaultUuid: string
userUuid: string
@@ -1,4 +1,5 @@
import {
AccountDeletionVerificationPassedEvent,
DuplicateItemSyncedEvent,
EmailRequestedEvent,
ItemDumpedEvent,
@@ -93,4 +94,8 @@ export interface DomainEventFactoryInterface {
userUuid: string
timestamp: number
}): UserDesignatedAsSurvivorInSharedVaultEvent
createAccountDeletionVerificationPassedEvent(dto: {
userUuid: string
email: string
}): AccountDeletionVerificationPassedEvent
}
@@ -0,0 +1,48 @@
import {
AccountDeletionVerificationRequestedEvent,
DomainEventHandlerInterface,
DomainEventPublisherInterface,
} from '@standardnotes/domain-events'
import { Uuid } from '@standardnotes/domain-core'
import { Logger } from 'winston'
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
export class AccountDeletionVerificationRequestedEventHandler implements DomainEventHandlerInterface {
constructor(
private itemRepository: ItemRepositoryInterface,
private domainEventPublisher: DomainEventPublisherInterface,
private domainEventFactory: DomainEventFactoryInterface,
private logger: Logger,
) {}
async handle(event: AccountDeletionVerificationRequestedEvent): Promise<void> {
const userUuidOrError = Uuid.create(event.payload.userUuid)
if (userUuidOrError.isFailed()) {
this.logger.error(`AccountDeletionVerificationRequestedEventHandler failed: ${userUuidOrError.getError()}`)
return
}
const userUuid = userUuidOrError.getValue()
const itemsCount = await this.itemRepository.countAll({
userUuid: userUuid.value,
})
if (itemsCount !== 0) {
this.logger.warn(
`AccountDeletionVerificationRequestedEventHandler: User ${userUuid.value} has ${itemsCount} items and cannot be deleted.`,
)
return
}
await this.domainEventPublisher.publish(
this.domainEventFactory.createAccountDeletionVerificationPassedEvent({
userUuid: userUuid.value,
email: event.payload.email,
}),
)
}
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.20.1](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.20.0...@standardnotes/websockets-server@1.20.1) (2023-11-22)
**Note:** Version bump only for package @standardnotes/websockets-server
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.19.2...@standardnotes/websockets-server@1.20.0) (2023-11-16)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/websockets-server",
"version": "1.20.0",
"version": "1.20.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},