managed-outbox.service.ts raw

   1  import { normalizeUrl } from '@/lib/url'
   2  import relayStatsService from './relay-stats.service'
   3  import storage from './local-storage.service'
   4  import type { TRelayDirection, TOutboxMode, TRelayEntry } from '@/types/relay-management'
   5  
   6  class ManagedOutboxService {
   7    filterRelayUrls(
   8      urls: string[],
   9      direction: TRelayDirection,
  10      ownRelays?: Set<string>
  11    ): string[] {
  12      const mode = storage.getOutboxMode() as TOutboxMode
  13      const normalizedOwn = ownRelays ? new Set([...ownRelays].map(normalizeUrl)) : undefined
  14      const allowed: string[] = []
  15  
  16      for (let url of urls) {
  17        url = normalizeUrl(url)
  18        if (normalizedOwn?.has(url)) {
  19          allowed.push(url)
  20          continue
  21        }
  22  
  23        const entry = relayStatsService.getEntry(url)
  24  
  25        if (entry?.manualExclude) continue
  26        if (relayStatsService.isAutoDisabled(url)) continue
  27  
  28        if (mode === 'automatic') {
  29          if (!entry) {
  30            this.addPending(url, direction)
  31          }
  32          allowed.push(url)
  33          continue
  34        }
  35  
  36        // mode === 'managed'
  37        if (entry?.status === 'approved') {
  38          allowed.push(url)
  39        } else if (entry?.status === 'rejected') {
  40          // blocked
  41        } else {
  42          // pending or no entry
  43          this.addPending(url, direction)
  44        }
  45      }
  46  
  47      return allowed
  48    }
  49  
  50    addPending(url: string, direction: TRelayDirection, reason?: string): void {
  51      const existing = relayStatsService.getEntry(url)
  52      if (!existing) {
  53        const entry = relayStatsService.getOrCreateEntry(url)
  54        entry.direction = direction
  55        if (reason) entry.reason = reason
  56        // status defaults to 'pending' from getOrCreateEntry
  57        return
  58      }
  59      if (existing.direction !== direction && existing.direction !== 'both') {
  60        relayStatsService.updateEntry(url, { direction: 'both' })
  61      }
  62      if (reason && !existing.reason) {
  63        relayStatsService.updateEntry(url, { reason })
  64      }
  65    }
  66  
  67    approve(url: string): void {
  68      relayStatsService.updateEntry(url, { status: 'approved' })
  69    }
  70  
  71    reject(url: string): void {
  72      relayStatsService.updateEntry(url, { status: 'rejected' })
  73    }
  74  
  75    resetStatus(url: string): void {
  76      relayStatsService.updateEntry(url, { status: 'pending' })
  77    }
  78  
  79    setManualExclude(url: string, excluded: boolean): void {
  80      relayStatsService.updateEntry(url, { manualExclude: excluded })
  81    }
  82  
  83    bulkApprove(urls: string[]): void {
  84      for (const url of urls) this.approve(url)
  85    }
  86  
  87    bulkReject(urls: string[]): void {
  88      for (const url of urls) this.reject(url)
  89    }
  90  
  91    getPendingRelays(): TRelayEntry[] {
  92      return relayStatsService.getAllEntries().filter((e) => e.status === 'pending')
  93    }
  94  
  95    getApprovedRelays(): TRelayEntry[] {
  96      return relayStatsService.getAllEntries().filter((e) => e.status === 'approved')
  97    }
  98  
  99    getRejectedRelays(): TRelayEntry[] {
 100      return relayStatsService.getAllEntries().filter((e) => e.status === 'rejected')
 101    }
 102  
 103    getExcludedRelays(): TRelayEntry[] {
 104      return relayStatsService.getAllEntries().filter((e) => e.manualExclude)
 105    }
 106  
 107    getAutoDisabledRelays(): TRelayEntry[] {
 108      return relayStatsService
 109        .getAllEntries()
 110        .filter((e) => relayStatsService.isAutoDisabled(e.url))
 111    }
 112  }
 113  
 114  const managedOutboxService = new ManagedOutboxService()
 115  export default managedOutboxService
 116