From 147d8fd9af89d2b97cd68eefac36e53d14d511bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Mon, 19 Dec 2022 12:49:52 +0100 Subject: [PATCH] feat(auth): add session traces cleanup procedure --- packages/auth/bin/cleanup.ts | 38 +++++++++++++++++++ packages/auth/docker/entrypoint.sh | 5 +++ packages/auth/package.json | 1 + packages/auth/src/Bootstrap/Container.ts | 5 +++ packages/auth/src/Bootstrap/Types.ts | 1 + .../SessionTraceRepositoryInterface.ts | 1 + .../CleanupSessionTraces.spec.ts | 19 ++++++++++ .../CleanupSessionTraces.ts | 14 +++++++ .../CleanupSessionTracesDTO.ts | 3 ++ .../MySQL/MySQLSessionTraceRepository.ts | 8 ++++ 10 files changed, 95 insertions(+) create mode 100644 packages/auth/bin/cleanup.ts create mode 100644 packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces.spec.ts create mode 100644 packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces.ts create mode 100644 packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTracesDTO.ts diff --git a/packages/auth/bin/cleanup.ts b/packages/auth/bin/cleanup.ts new file mode 100644 index 000000000..22434e56b --- /dev/null +++ b/packages/auth/bin/cleanup.ts @@ -0,0 +1,38 @@ +import 'reflect-metadata' + +import 'newrelic' + +import { Logger } from 'winston' + +import { ContainerConfigLoader } from '../src/Bootstrap/Container' +import TYPES from '../src/Bootstrap/Types' +import { Env } from '../src/Bootstrap/Env' +import { CleanupSessionTraces } from '../src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces' + +const container = new ContainerConfigLoader() +void container.load().then((container) => { + const env: Env = new Env() + env.load() + + const logger: Logger = container.get(TYPES.Logger) + + logger.info('Starting session traces cleanup') + + const cleanupSessionTraces: CleanupSessionTraces = container.get(TYPES.CleanupSessionTraces) + + Promise.resolve( + cleanupSessionTraces.execute({ + date: new Date(), + }), + ) + .then(() => { + logger.info('Expired session traces cleaned.') + + process.exit(0) + }) + .catch((error) => { + logger.error(`Could not clean session traces: ${error.message}`) + + process.exit(1) + }) +}) diff --git a/packages/auth/docker/entrypoint.sh b/packages/auth/docker/entrypoint.sh index 29c5eaa88..2a89ed4f0 100755 --- a/packages/auth/docker/entrypoint.sh +++ b/packages/auth/docker/entrypoint.sh @@ -19,6 +19,11 @@ case "$COMMAND" in yarn workspace @standardnotes/auth-server worker ;; + 'cleanup' ) + echo "[Docker] Starting Cleanup..." + yarn workspace @standardnotes/auth-server cleanup + ;; + 'email-daily-backup' ) echo "[Docker] Starting Email Daily Backup..." yarn workspace @standardnotes/auth-server daily-backup:email diff --git a/packages/auth/package.json b/packages/auth/package.json index 980c1726c..b40336e85 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -19,6 +19,7 @@ "test": "jest --coverage --config=./jest.config.js --maxWorkers=50%", "start": "yarn node dist/bin/server.js", "worker": "yarn node dist/bin/worker.js", + "cleanup": "yarn node dist/bin/cleanup.js", "daily-backup:email": "yarn node dist/bin/backup.js email daily", "user-email-backup": "yarn node dist/bin/user_email_backup.js", "daily-backup:dropbox": "yarn node dist/bin/backup.js dropbox daily", diff --git a/packages/auth/src/Bootstrap/Container.ts b/packages/auth/src/Bootstrap/Container.ts index f80efb03c..ba785e38f 100644 --- a/packages/auth/src/Bootstrap/Container.ts +++ b/packages/auth/src/Bootstrap/Container.ts @@ -201,6 +201,7 @@ import { SessionTracePersistenceMapper } from '../Mapping/SessionTracePersistenc import { SessionTrace } from '../Domain/Session/SessionTrace' import { TypeORMSessionTrace } from '../Infra/TypeORM/TypeORMSessionTrace' import { TraceSession } from '../Domain/UseCase/TraceSession/TraceSession' +import { CleanupSessionTraces } from '../Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces' // eslint-disable-next-line @typescript-eslint/no-var-requires const newrelicFormatter = require('@newrelic/winston-enricher') @@ -499,6 +500,10 @@ export class ContainerConfigLoader { container.get(TYPES.SESSION_TRACE_DAYS_TTL), ), ) + + container + .bind(TYPES.CleanupSessionTraces) + .toConstantValue(new CleanupSessionTraces(container.get(TYPES.SessionTraceRepository))) container.bind(TYPES.AuthenticateUser).to(AuthenticateUser) container.bind(TYPES.AuthenticateRequest).to(AuthenticateRequest) container.bind(TYPES.RefreshSessionToken).to(RefreshSessionToken) diff --git a/packages/auth/src/Bootstrap/Types.ts b/packages/auth/src/Bootstrap/Types.ts index 664007281..834056481 100644 --- a/packages/auth/src/Bootstrap/Types.ts +++ b/packages/auth/src/Bootstrap/Types.ts @@ -125,6 +125,7 @@ const TYPES = { CreateCrossServiceToken: Symbol.for('CreateCrossServiceToken'), ProcessUserRequest: Symbol.for('ProcessUserRequest'), TraceSession: Symbol.for('TraceSession'), + CleanupSessionTraces: Symbol.for('CleanupSessionTraces'), // Handlers UserRegisteredEventHandler: Symbol.for('UserRegisteredEventHandler'), AccountDeletionRequestedEventHandler: Symbol.for('AccountDeletionRequestedEventHandler'), diff --git a/packages/auth/src/Domain/Session/SessionTraceRepositoryInterface.ts b/packages/auth/src/Domain/Session/SessionTraceRepositoryInterface.ts index 51a598d8b..2820e2397 100644 --- a/packages/auth/src/Domain/Session/SessionTraceRepositoryInterface.ts +++ b/packages/auth/src/Domain/Session/SessionTraceRepositoryInterface.ts @@ -4,5 +4,6 @@ import { SessionTrace } from './SessionTrace' export interface SessionTraceRepositoryInterface { save(sessionTrace: SessionTrace): Promise + removeExpiredBefore(date: Date): Promise findOneByUserUuidAndDate(userUuid: Uuid, date: Date): Promise } diff --git a/packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces.spec.ts b/packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces.spec.ts new file mode 100644 index 000000000..02ccff978 --- /dev/null +++ b/packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces.spec.ts @@ -0,0 +1,19 @@ +import { SessionTraceRepositoryInterface } from '../../Session/SessionTraceRepositoryInterface' +import { CleanupSessionTraces } from './CleanupSessionTraces' + +describe('CleanupSessionTraces', () => { + let sessionTracesRepository: SessionTraceRepositoryInterface + + const createUseCase = () => new CleanupSessionTraces(sessionTracesRepository) + + beforeEach(() => { + sessionTracesRepository = {} as jest.Mocked + sessionTracesRepository.removeExpiredBefore = jest.fn() + }) + + it('should remove stale session traces', async () => { + await createUseCase().execute({ date: new Date() }) + + expect(sessionTracesRepository.removeExpiredBefore).toHaveBeenCalled() + }) +}) diff --git a/packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces.ts b/packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces.ts new file mode 100644 index 000000000..fb0b0a1fa --- /dev/null +++ b/packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTraces.ts @@ -0,0 +1,14 @@ +import { Result, UseCaseInterface } from '@standardnotes/domain-core' + +import { SessionTraceRepositoryInterface } from '../../Session/SessionTraceRepositoryInterface' +import { CleanupSessionTracesDTO } from './CleanupSessionTracesDTO' + +export class CleanupSessionTraces implements UseCaseInterface { + constructor(private sessionTracesRepository: SessionTraceRepositoryInterface) {} + + async execute(dto: CleanupSessionTracesDTO): Promise> { + await this.sessionTracesRepository.removeExpiredBefore(dto.date) + + return Result.ok('Session traces removed') + } +} diff --git a/packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTracesDTO.ts b/packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTracesDTO.ts new file mode 100644 index 000000000..d1b2d5258 --- /dev/null +++ b/packages/auth/src/Domain/UseCase/CleanupSessionTraces/CleanupSessionTracesDTO.ts @@ -0,0 +1,3 @@ +export interface CleanupSessionTracesDTO { + date: Date +} diff --git a/packages/auth/src/Infra/MySQL/MySQLSessionTraceRepository.ts b/packages/auth/src/Infra/MySQL/MySQLSessionTraceRepository.ts index 0350cbcc8..9f568e762 100644 --- a/packages/auth/src/Infra/MySQL/MySQLSessionTraceRepository.ts +++ b/packages/auth/src/Infra/MySQL/MySQLSessionTraceRepository.ts @@ -10,6 +10,14 @@ export class MySQLSessionTraceRepository implements SessionTraceRepositoryInterf private mapper: MapperInterface, ) {} + async removeExpiredBefore(date: Date): Promise { + await this.ormRepository + .createQueryBuilder() + .delete() + .where('expires_at < :date', { date: date.toISOString() }) + .execute() + } + async findOneByUserUuidAndDate(userUuid: Uuid, date: Date): Promise { const typeOrm = await this.ormRepository .createQueryBuilder('trace')