UNPKG

2.46 kBPlain TextView Raw
1import { create as createLogger } from '../common/log'
2const log = createLogger('alert-middleware')
3import { Middleware, MiddlewareCallback, Pipelines } from '../types/middleware'
4import * as IlpPacket from 'ilp-packet'
5
6const { T04_INSUFFICIENT_LIQUIDITY } = IlpPacket.Errors.codes
7
8export interface Alert {
9 id: number
10 accountId: string
11 triggeredBy: string
12 message: string
13 count: number
14 createdAt: Date
15 updatedAt: Date
16}
17
18export default class AlertMiddleware implements Middleware {
19 private alerts: {[id: number]: Alert} = {}
20 private nextAlertId: number = Date.now()
21
22 async applyToPipelines (pipelines: Pipelines, accountId: string) {
23 pipelines.outgoingData.insertLast({
24 name: 'alert',
25 method: async (data: Buffer, next: MiddlewareCallback<Buffer, Buffer>) => {
26 const result = await next(data)
27 if (result[0] !== IlpPacket.Type.TYPE_ILP_REJECT) return result
28
29 const rejectPacket = IlpPacket.deserializeIlpReject(result)
30 if (rejectPacket.code !== T04_INSUFFICIENT_LIQUIDITY) return result
31
32 // The peer rejected a packet which, according to the local balance, should
33 // have succeeded. This can happen when our local connector owes the peer
34 // money but restarted before it was settled.
35 if (rejectPacket.message !== 'exceeded maximum balance.') return result
36
37 const { triggeredBy } = rejectPacket
38 log.warn('generating alert for account=%s triggeredBy=%s message="%s"', accountId, triggeredBy, rejectPacket.message)
39 this.addAlert(accountId, triggeredBy, rejectPacket.message)
40
41 return result
42 }
43 })
44 }
45
46 getAlerts (): Alert[] {
47 return Object.keys(this.alerts)
48 .map((id) => this.alerts[id])
49 .sort((a, b) => a.id - b.id)
50 }
51
52 dismissAlert (id: number) {
53 delete this.alerts[id]
54 }
55
56 private addAlert (accountId: string, triggeredBy: string, message: string) {
57 const alert = Object.keys(this.alerts)
58 .map((alertId) => this.alerts[alertId])
59 .find((alert) =>
60 alert.accountId === accountId &&
61 alert.triggeredBy === triggeredBy &&
62 alert.message === message)
63 if (alert) {
64 alert.count++
65 alert.updatedAt = new Date()
66 return
67 }
68
69 const id = this.nextAlertId++
70 const now = new Date()
71 this.alerts[id] = {
72 id,
73 accountId,
74 triggeredBy,
75 message,
76 count: 1,
77 createdAt: now,
78 updatedAt: now
79 }
80 }
81}