diff --git a/myteamwallet_backend/src/database/logging/log-retention.scheduler.spec.ts b/myteamwallet_backend/src/database/logging/log-retention.scheduler.spec.ts index b3382ca..70aafd9 100644 --- a/myteamwallet_backend/src/database/logging/log-retention.scheduler.spec.ts +++ b/myteamwallet_backend/src/database/logging/log-retention.scheduler.spec.ts @@ -4,7 +4,7 @@ import { LogRetentionScheduler } from './log-retention.scheduler'; describe('LogRetentionScheduler', () => { const repository = { delete: jest.fn() }; const configService = { get: jest.fn() }; - const logger = { info: jest.fn() }; + const logger = { info: jest.fn(), error: jest.fn() }; let scheduler: LogRetentionScheduler; beforeEach(() => { @@ -49,4 +49,17 @@ describe('LogRetentionScheduler', () => { userId: -1, }); }); + + it('logs and does not rethrow when the delete fails', async () => { + repository.delete.mockRejectedValue(new Error('connection reset')); + + await expect(scheduler.cleanupOldLogs()).resolves.toBeUndefined(); + + expect(logger.error).toHaveBeenCalledWith({ + event: 'log_retention_cleanup_run_fail', + details: 'connection reset', + userId: -1, + }); + expect(logger.info).not.toHaveBeenCalled(); + }); }); diff --git a/myteamwallet_backend/src/database/logging/log-retention.scheduler.ts b/myteamwallet_backend/src/database/logging/log-retention.scheduler.ts index e5eb333..79f320e 100644 --- a/myteamwallet_backend/src/database/logging/log-retention.scheduler.ts +++ b/myteamwallet_backend/src/database/logging/log-retention.scheduler.ts @@ -21,12 +21,21 @@ export class LogRetentionScheduler { const cutoff = new Date(); cutoff.setUTCDate(cutoff.getUTCDate() - retentionDays); - const result = await this.repository.delete({ createdAt: LessThan(cutoff) }); + try { + const result = await this.repository.delete({ createdAt: LessThan(cutoff) }); - await this.logger.info({ - event: 'log_retention_cleanup_run', - details: `deletedCount=${result.affected ?? 0} retentionDays=${retentionDays}`, - userId: -1, - }); + await this.logger.info({ + event: 'log_retention_cleanup_run', + details: `deletedCount=${result.affected ?? 0} retentionDays=${retentionDays}`, + userId: -1, + }); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + await this.logger.error({ + event: 'log_retention_cleanup_run_fail', + details: errorMessage, + userId: -1, + }); + } } } diff --git a/myteamwallet_backend/src/database/logging/logging.service.spec.ts b/myteamwallet_backend/src/database/logging/logging.service.spec.ts index 515e238..573d64d 100644 --- a/myteamwallet_backend/src/database/logging/logging.service.spec.ts +++ b/myteamwallet_backend/src/database/logging/logging.service.spec.ts @@ -85,7 +85,12 @@ describe('LoggingService.findLogs', () => { await service.findLogs({ page: 1, limit: 20, from: '2026-01-01', to: '2026-01-31' }); expect(query.andWhere).toHaveBeenCalledWith('log.createdAt >= :from', { from: '2026-01-01' }); - expect(query.andWhere).toHaveBeenCalledWith('log.createdAt <= :to', { to: '2026-01-31' }); + // `to` is a plain date (e.g. from a ); comparing it + // as-is would parse to midnight and exclude the whole last day, so it + // must be widened to the end of that day to be genuinely inclusive. + expect(query.andWhere).toHaveBeenCalledWith('log.createdAt <= :to', { + to: new Date('2026-01-31T23:59:59.999Z'), + }); }); it('does not add level/event/date filters when omitted', async () => { diff --git a/myteamwallet_backend/src/database/logging/logging.service.ts b/myteamwallet_backend/src/database/logging/logging.service.ts index 787b85b..2cb1115 100644 --- a/myteamwallet_backend/src/database/logging/logging.service.ts +++ b/myteamwallet_backend/src/database/logging/logging.service.ts @@ -115,7 +115,9 @@ export class LoggingService { if (query.level) builder.andWhere('log.level = :level', { level: query.level }); if (query.event) builder.andWhere('log.event = :event', { event: query.event }); if (query.from) builder.andWhere('log.createdAt >= :from', { from: query.from }); - if (query.to) builder.andWhere('log.createdAt <= :to', { to: query.to }); + if (query.to) { + builder.andWhere('log.createdAt <= :to', { to: new Date(`${query.to}T23:59:59.999Z`) }); + } const term = query.search?.trim().toLocaleLowerCase(); if (term) { builder.andWhere('LOWER(log.details) LIKE :search', { search: `%${term}%` }); diff --git a/myteamwallet_backend/src/database/logging/model/logging-event.type.ts b/myteamwallet_backend/src/database/logging/model/logging-event.type.ts index 8738b53..f413d68 100644 --- a/myteamwallet_backend/src/database/logging/model/logging-event.type.ts +++ b/myteamwallet_backend/src/database/logging/model/logging-event.type.ts @@ -37,7 +37,8 @@ export type LOGEVENT = | 'cashbox_export_subscription_update' | 'cashbox_export_subscription_run' | 'cashbox_export_subscription_run_fail' - | 'log_retention_cleanup_run'; + | 'log_retention_cleanup_run' + | 'log_retention_cleanup_run_fail'; export const LOGEVENT_VALUES: LOGEVENT[] = [ 'user_create', @@ -78,6 +79,7 @@ export const LOGEVENT_VALUES: LOGEVENT[] = [ 'cashbox_export_subscription_run', 'cashbox_export_subscription_run_fail', 'log_retention_cleanup_run', + 'log_retention_cleanup_run_fail', ]; export type LOGLEVEL = 'FATAL' | 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' | 'TRACE';