Adds audit logging and sync management infrastructure

Introduces comprehensive audit trail system with:
- AuditService to track entity changes
- SyncManager for background sync of audit entries
- New CoreEvents for entity and audit tracking
- Simplified sync architecture with event-driven approach

Prepares system for enhanced compliance and change tracking
This commit is contained in:
Janus C. H. Knudsen 2025-11-21 23:23:04 +01:00
parent dcd76836bd
commit 9ea98e3a04
18 changed files with 469 additions and 414 deletions

View file

@ -2,6 +2,9 @@ import { ISync, EntityType, SyncStatus } from '../types/CalendarTypes';
import { IEntityService } from './IEntityService';
import { SyncPlugin } from './SyncPlugin';
import { IndexedDBContext } from './IndexedDBContext';
import { IEventBus } from '../types/CalendarTypes';
import { CoreEvents } from '../constants/CoreEvents';
import { diff } from 'json-diff-ts';
/**
* BaseEntityService<T extends ISync> - Abstract base class for all entity services
@ -42,11 +45,16 @@ export abstract class BaseEntityService<T extends ISync> implements IEntityServi
// IndexedDB context - provides database connection
private context: IndexedDBContext;
// EventBus for emitting entity events
protected eventBus: IEventBus;
/**
* @param context - IndexedDBContext instance (injected dependency)
* @param eventBus - EventBus for emitting entity events
*/
constructor(context: IndexedDBContext) {
constructor(context: IndexedDBContext, eventBus: IEventBus) {
this.context = context;
this.eventBus = eventBus;
this.syncPlugin = new SyncPlugin<T>(this);
}
@ -132,10 +140,28 @@ export abstract class BaseEntityService<T extends ISync> implements IEntityServi
/**
* Save an entity (create or update)
* Emits ENTITY_SAVED event with operation type and changes
*
* @param entity - Entity to save
*/
async save(entity: T): Promise<void> {
const entityId = (entity as any).id;
// Check if entity exists to determine create vs update
const existingEntity = await this.get(entityId);
const isCreate = existingEntity === null;
// Calculate changes: full entity for create, diff for update
let changes: any;
if (isCreate) {
changes = entity;
} else {
// Calculate diff between existing and new entity
const existingSerialized = this.serialize(existingEntity);
const newSerialized = this.serialize(entity);
changes = diff(existingSerialized, newSerialized);
}
const serialized = this.serialize(entity);
return new Promise((resolve, reject) => {
@ -144,17 +170,26 @@ export abstract class BaseEntityService<T extends ISync> implements IEntityServi
const request = store.put(serialized);
request.onsuccess = () => {
// Emit ENTITY_SAVED event
this.eventBus.emit(CoreEvents.ENTITY_SAVED, {
entityType: this.entityType,
entityId,
operation: isCreate ? 'create' : 'update',
changes,
timestamp: Date.now()
});
resolve();
};
request.onerror = () => {
reject(new Error(`Failed to save ${this.entityType} ${(entity as any).id}: ${request.error}`));
reject(new Error(`Failed to save ${this.entityType} ${entityId}: ${request.error}`));
};
});
}
/**
* Delete an entity
* Emits ENTITY_DELETED event
*
* @param id - Entity ID to delete
*/
@ -165,6 +200,13 @@ export abstract class BaseEntityService<T extends ISync> implements IEntityServi
const request = store.delete(id);
request.onsuccess = () => {
// Emit ENTITY_DELETED event
this.eventBus.emit(CoreEvents.ENTITY_DELETED, {
entityType: this.entityType,
entityId: id,
operation: 'delete',
timestamp: Date.now()
});
resolve();
};