import { DomainError } from '../errors/domain.error'; export enum BackupShareStatus { ACTIVE = 'ACTIVE', REVOKED = 'REVOKED', ROTATED = 'ROTATED', } export interface BackupShareProps { shareId: bigint | null; userId: bigint; accountSequence: bigint; publicKey: string; partyIndex: number; threshold: number; totalParties: number; encryptedShareData: string; encryptionKeyId: string; status: BackupShareStatus; accessCount: number; lastAccessedAt: Date | null; createdAt: Date; updatedAt: Date; revokedAt: Date | null; } export class BackupShare { private _shareId: bigint | null; private readonly _userId: bigint; private readonly _accountSequence: bigint; private readonly _publicKey: string; private readonly _partyIndex: number; private readonly _threshold: number; private readonly _totalParties: number; private _encryptedShareData: string; private _encryptionKeyId: string; private _status: BackupShareStatus; private _accessCount: number; private _lastAccessedAt: Date | null; private readonly _createdAt: Date; private _updatedAt: Date; private _revokedAt: Date | null; private constructor(props: BackupShareProps) { this._shareId = props.shareId; this._userId = props.userId; this._accountSequence = props.accountSequence; this._publicKey = props.publicKey; this._partyIndex = props.partyIndex; this._threshold = props.threshold; this._totalParties = props.totalParties; this._encryptedShareData = props.encryptedShareData; this._encryptionKeyId = props.encryptionKeyId; this._status = props.status; this._accessCount = props.accessCount; this._lastAccessedAt = props.lastAccessedAt; this._createdAt = props.createdAt; this._updatedAt = props.updatedAt; this._revokedAt = props.revokedAt; } static create(params: { userId: bigint; accountSequence: bigint; publicKey: string; encryptedShareData: string; encryptionKeyId: string; threshold?: number; totalParties?: number; }): BackupShare { const now = new Date(); return new BackupShare({ shareId: null, userId: params.userId, accountSequence: params.accountSequence, publicKey: params.publicKey, partyIndex: 2, // Backup = Party 2 threshold: params.threshold ?? 2, totalParties: params.totalParties ?? 3, encryptedShareData: params.encryptedShareData, encryptionKeyId: params.encryptionKeyId, status: BackupShareStatus.ACTIVE, accessCount: 0, lastAccessedAt: null, createdAt: now, updatedAt: now, revokedAt: null, }); } static reconstitute(props: BackupShareProps): BackupShare { return new BackupShare(props); } recordAccess(): void { if (this._status !== BackupShareStatus.ACTIVE) { throw new DomainError('Cannot access revoked or rotated share'); } this._accessCount++; this._lastAccessedAt = new Date(); this._updatedAt = new Date(); } revoke(reason: string): void { if (this._status === BackupShareStatus.REVOKED) { throw new DomainError('Share already revoked'); } this._status = BackupShareStatus.REVOKED; this._revokedAt = new Date(); this._updatedAt = new Date(); } rotate(newEncryptedData: string, newKeyId: string): void { if (this._status === BackupShareStatus.REVOKED) { throw new DomainError('Cannot rotate revoked share'); } this._encryptedShareData = newEncryptedData; this._encryptionKeyId = newKeyId; this._status = BackupShareStatus.ACTIVE; this._updatedAt = new Date(); } isActive(): boolean { return this._status === BackupShareStatus.ACTIVE; } // Getters get shareId(): bigint | null { return this._shareId; } get userId(): bigint { return this._userId; } get accountSequence(): bigint { return this._accountSequence; } get publicKey(): string { return this._publicKey; } get partyIndex(): number { return this._partyIndex; } get threshold(): number { return this._threshold; } get totalParties(): number { return this._totalParties; } get encryptedShareData(): string { return this._encryptedShareData; } get encryptionKeyId(): string { return this._encryptionKeyId; } get status(): BackupShareStatus { return this._status; } get accessCount(): number { return this._accessCount; } get lastAccessedAt(): Date | null { return this._lastAccessedAt; } get createdAt(): Date { return this._createdAt; } get updatedAt(): Date { return this._updatedAt; } get revokedAt(): Date | null { return this._revokedAt; } // For persistence setShareId(id: bigint): void { if (this._shareId !== null) { throw new DomainError('Share ID already set'); } this._shareId = id; } toProps(): BackupShareProps { return { shareId: this._shareId, userId: this._userId, accountSequence: this._accountSequence, publicKey: this._publicKey, partyIndex: this._partyIndex, threshold: this._threshold, totalParties: this._totalParties, encryptedShareData: this._encryptedShareData, encryptionKeyId: this._encryptionKeyId, status: this._status, accessCount: this._accessCount, lastAccessedAt: this._lastAccessedAt, createdAt: this._createdAt, updatedAt: this._updatedAt, revokedAt: this._revokedAt, }; } }