@vex-chat/libvex 6.2.3 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -349,6 +349,7 @@ export class SqliteStorage extends EventEmitter implements Storage {
349
349
  )
350
350
  .execute();
351
351
  await this.ensureSessionRatchetColumns();
352
+ await this.ensureRetentionHintColumn();
352
353
 
353
354
  await this.db.schema
354
355
  .createTable("preKeys")
@@ -416,6 +417,46 @@ export class SqliteStorage extends EventEmitter implements Storage {
416
417
 
417
418
  // ── PreKeys / OneTimeKeys ────────────────────────────────────────────────
418
419
 
420
+ async pruneExpiredLocalMessages(
421
+ clientMaxRetentionDays: number,
422
+ ): Promise<void> {
423
+ await this.untilReady();
424
+ if (this.closing) {
425
+ return;
426
+ }
427
+ const cap = Math.min(
428
+ 30,
429
+ Math.max(1, Math.round(clientMaxRetentionDays)),
430
+ );
431
+ const rows = await this.db
432
+ .selectFrom("messages")
433
+ .select(["mailID", "timestamp", "retentionHintDays"])
434
+ .execute();
435
+ const now = Date.now();
436
+ const msPerDay = 86_400_000;
437
+ const toDelete: string[] = [];
438
+ for (const r of rows) {
439
+ const hintDays = r.retentionHintDays ?? 30;
440
+ const maxDays = Math.min(30, cap, hintDays);
441
+ const ts = new Date(r.timestamp).getTime();
442
+ if (!Number.isFinite(ts)) {
443
+ continue;
444
+ }
445
+ if (now - ts > maxDays * msPerDay) {
446
+ toDelete.push(r.mailID);
447
+ }
448
+ }
449
+ if (toDelete.length === 0) {
450
+ return;
451
+ }
452
+ for (const mailID of toDelete) {
453
+ await this.db
454
+ .deleteFrom("messages")
455
+ .where("mailID", "=", mailID)
456
+ .execute();
457
+ }
458
+ }
459
+
419
460
  async purgeHistory(): Promise<void> {
420
461
  await this.db.deleteFrom("messages").execute();
421
462
  }
@@ -489,6 +530,16 @@ export class SqliteStorage extends EventEmitter implements Storage {
489
530
  nonce: message.nonce,
490
531
  readerID: message.readerID,
491
532
  recipient: message.recipient,
533
+ retentionHintDays:
534
+ message.retentionHintDays === undefined
535
+ ? null
536
+ : Math.min(
537
+ 30,
538
+ Math.max(
539
+ 1,
540
+ Math.round(message.retentionHintDays),
541
+ ),
542
+ ),
492
543
  sender: message.sender,
493
544
  timestamp: message.timestamp,
494
545
  })
@@ -638,7 +689,7 @@ export class SqliteStorage extends EventEmitter implements Storage {
638
689
  }
639
690
  const direction =
640
691
  msg.direction === "incoming" ? "incoming" : "outgoing";
641
- out.push({
692
+ const rowMessage: Message = {
642
693
  authorID: msg.authorID,
643
694
  decrypted: decryptedFlag,
644
695
  direction,
@@ -651,7 +702,11 @@ export class SqliteStorage extends EventEmitter implements Storage {
651
702
  recipient: msg.recipient,
652
703
  sender: msg.sender,
653
704
  timestamp: msg.timestamp,
654
- });
705
+ };
706
+ if (msg.retentionHintDays != null) {
707
+ rowMessage.retentionHintDays = msg.retentionHintDays;
708
+ }
709
+ out.push(rowMessage);
655
710
  }
656
711
  return out;
657
712
  }
@@ -669,6 +724,18 @@ export class SqliteStorage extends EventEmitter implements Storage {
669
724
  };
670
725
  }
671
726
 
727
+ private async ensureRetentionHintColumn(): Promise<void> {
728
+ try {
729
+ await sql
730
+ .raw(
731
+ "ALTER TABLE messages ADD COLUMN retentionHintDays integer",
732
+ )
733
+ .execute(this.db);
734
+ } catch {
735
+ // Existing databases may already have this column.
736
+ }
737
+ }
738
+
672
739
  private async ensureSessionRatchetColumns(): Promise<void> {
673
740
  const add = async (
674
741
  column: string,