import assert from 'assert' ; import { Collection , DeleteWriteOpResultObject , MongoClient , UpdateWriteOpResult } from 'mongodb' ; const COLLECTION = 'registry' ; /**
 * Contains ingester metadata to keep track of sync status for an address
 */ export interface IngesterMetadata { block ? : number ; syncing ? : { key ? : string ; startTime : number ; endTime : number ; } ; } /**
 * Contains registry info and is used for un/registering addresses
 */ export interface RegistryDocument { client_id : string ; ingester_meta ? : Record < string , IngesterMetadata > ; registration : { addresses ? : string [ ] ; pubkey ? : string ; } ; } export class RegistryService { private client ? : MongoClient ; private collection ? : Collection ; constructor ( url , db , poolsize ? ) { MongoClient . connect ( url , { useUnifiedTopology : true , poolSize : poolsize } , ( err , client ) => { assert . strictEqual ( null , err ) ; this . client = client ; this . collection = this . client . db ( db ) . collection ( COLLECTION ) ; this . collection . createIndex ( { client_id : 1 , 'registration.pubkey' : 1 } , { unique : true } ) ; this . collection . createIndex ( { client_id : 1 , 'registration.addresses' : 1 } ) ; } ) ; } disconnect ( ) { this . client ?. close ( ) ; } async getByAddress ( address ) { address = address . toLowerCase ( ) ; const cursors = this . collection ?. find < RegistryDocument > ( { 'registration.addresses' : { $in : [ address ] } } , { } ) ; return cursors ?. toArray ( ) ; } async updateBlock ( address , block , client_id ) { address = address . toLowerCase ( ) ; return this . collection ?. updateOne ( { client_id : client_id , 'registration.addresses' : { $in : [ address ] } } , { $set : { [ `ingester_meta.${ address }.block` ] : block } } ) ; } async updateSyncing ( address , client_id , key ? ) { address = address . toLowerCase ( ) ; return this . collection ?. updateOne ( { client_id : client_id , 'registration.addresses' : { $in : [ address ] } } , { $set : { [ `ingester_meta.${ address }.syncing.key` ] : key , [ `ingester_meta.${ address }.syncing.startTime` ] : key ? Date . now ( ) : 0 , [ `ingester_meta.${ address }.syncing.endTime` ] : key ? 0 : Date . now ( ) , } , } ) ; } async add ( document ) { document = this . sanitizeDocument ( document ) ; const addresses = document . registration . addresses ?? [ ] ; return this . collection ?. updateOne ( { client_id : document . client_id , 'registration.pubkey' : document . registration . pubkey , } , { $set : { client_id : document . client_id , 'registration.pubkey' : document . registration . pubkey , ... addresses . reduce ( ( prev , address ) => { const key = `ingester_meta.${ address }.block` ; const block = document . ingester_meta ?. [ address ] . block ?? 0 ; return { ... prev , [ key ] : block } ; } , { } ) , } , $addToSet : { 'registration.addresses' : { $each : addresses } , } , } , { upsert : true } ) ; } async remove ( document ) { document = this . sanitizeDocument ( document ) ; return this . collection ?. updateOne ( { client_id : document . client_id , 'registration.pubkey' : document . registration . pubkey , } , { $pull : { 'registration.addresses' : { $in : document . registration . addresses } , } , } ) ; } async delete ( document ) { document = this . sanitizeDocument ( document ) ; return this . collection ?. deleteOne ( { client_id : document . client_id , 'registration.pubkey' : document . registration . pubkey , } ) ; } sanitizeDocument ( document ) { const { client_id , registration } = document ; const { addresses } = registration ; return { client_id , registration : { pubkey : registration . pubkey , addresses : addresses ?. map ( ( a ) => a . toLowerCase ( ) ) , } , } ; } }