You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
3.3 KiB
105 lines
3.3 KiB
import { Singleton, Inject } from "@extollo/di"
|
|
import { Unit, Logging, Config } from "@extollo/lib"
|
|
import * as firebase from "firebase-admin"
|
|
|
|
export type RTDBRef = 'peers' | 'transaction' | 'block' | 'exposure' | 'locks'
|
|
|
|
/**
|
|
* FirebaseUnit Unit
|
|
* ---------------------------------------
|
|
* Fetch credentials from config and setup the firebase-admin connection.
|
|
*/
|
|
@Singleton()
|
|
export class FirebaseUnit extends Unit {
|
|
protected _firebase = firebase
|
|
|
|
@Inject()
|
|
protected readonly logging!: Logging
|
|
|
|
@Inject()
|
|
protected readonly config!: Config
|
|
|
|
/** Get the underlying Firebase library. */
|
|
get() {
|
|
return this._firebase
|
|
}
|
|
|
|
/** Get a realtime-database Reference using our internal aliases. */
|
|
ref(name: RTDBRef): firebase.database.Reference {
|
|
return this._firebase.database().ref(
|
|
String(this.config.get(`app.firebase.rtdb.refs.${name}`))
|
|
)
|
|
}
|
|
|
|
/** Get the realtime database object directly. */
|
|
db(): firebase.database.Database {
|
|
return this._firebase.database()
|
|
}
|
|
|
|
/**
|
|
* Try to lock the given database ref alias.
|
|
* Promise will sleep if lock is held, and will resolve once lock is acquired.
|
|
* @param name
|
|
* @param description
|
|
*/
|
|
async trylock(name: RTDBRef, description: string): Promise<any> {
|
|
return this._firebase.database()
|
|
.ref(`${this.config.get('app.firebase.rtdb.refs.locks')}/${name}`)
|
|
.transaction(current => {
|
|
if ( !current || current.time < 1 ) {
|
|
return {
|
|
time: (new Date).getTime(),
|
|
description,
|
|
}
|
|
}
|
|
}, undefined, false).then(async result => {
|
|
if ( result.committed ) {
|
|
this.logging.debug(`Lock acquired: ${name}`)
|
|
return Promise.resolve()
|
|
}
|
|
|
|
this.logging.debug(`Unable to acquire lock: ${name} - ${description}. Trying again soon...`)
|
|
await this.sleep(500)
|
|
return this.trylock(name, description)
|
|
})
|
|
.catch(async reason => {
|
|
this.logging.debug(`Unable to acquire lock: ${name} - ${description}. Trying again soon...`)
|
|
await this.sleep(500)
|
|
return this.trylock(name, description)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Release the lock on the given database ref.
|
|
* @param name
|
|
*/
|
|
async unlock(name: RTDBRef) {
|
|
await this._firebase.database()
|
|
.ref(`${this.config.get('app.firebase.rtdb.refs.locks')}/${name}`)
|
|
.set({time: 0, description: 'none'}, err => {
|
|
if ( err ) this.logging.error(err)
|
|
})
|
|
}
|
|
|
|
/** Called on app start. */
|
|
public async up() {
|
|
this.logging.info('Initializing Firebase application credentials...')
|
|
this._firebase.initializeApp({
|
|
credential: firebase.credential.cert(this.config.get('app.firebase.credentials')),
|
|
databaseURL: this.config.get('app.firebase.rtdb.default'),
|
|
})
|
|
}
|
|
|
|
/** Called on app shutdown. */
|
|
public async down() {
|
|
|
|
}
|
|
|
|
/** Sleep for (roughly) the given number of milliseconds. */
|
|
async sleep(ms: number) {
|
|
await new Promise<void>(res => {
|
|
setTimeout(res, ms)
|
|
})
|
|
}
|
|
}
|