[enh] add BMA watcher + reorganize code
This commit is contained in:
4
app.yml
4
app.yml
@@ -18,6 +18,10 @@ ws2pServers:
|
|||||||
passwd: test
|
passwd: test
|
||||||
currency: g1-test # Ensure we connect to the correct currency
|
currency: g1-test # Ensure we connect to the correct currency
|
||||||
|
|
||||||
|
bmaServers:
|
||||||
|
- address: https://g1-test.cgeek.fr
|
||||||
|
frequency: 60000 # 1'
|
||||||
|
|
||||||
mail:
|
mail:
|
||||||
enabled: false
|
enabled: false
|
||||||
host: smtp.sparkpostmail.com
|
host: smtp.sparkpostmail.com
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/minimist": "^1.2.0",
|
"@types/minimist": "^1.2.0",
|
||||||
|
"axios": "^0.19.0",
|
||||||
"duniter": "^1.8.0",
|
"duniter": "^1.8.0",
|
||||||
"js-yaml": "^3.12.1",
|
"js-yaml": "^3.12.1",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
"typescript": "^3.4.3"
|
"typescript": "^3.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/axios": "^0.14.0",
|
||||||
"@types/js-yaml": "^3.12.0",
|
"@types/js-yaml": "^3.12.0",
|
||||||
"@types/node": "~8.9.4",
|
"@types/node": "~8.9.4",
|
||||||
"@types/nodemailer": "^6.1.1"
|
"@types/nodemailer": "^6.1.1"
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ process.on('unhandledRejection', (err) => {
|
|||||||
await dwatch(argv.conf || path.join(__dirname, '../app.yml'))
|
await dwatch(argv.conf || path.join(__dirname, '../app.yml'))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
} finally {
|
|
||||||
console.log('Finished')
|
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import {Conf} from './conf'
|
import {Conf} from './types/conf'
|
||||||
import * as yaml from 'js-yaml';
|
import * as yaml from 'js-yaml';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import {ws2pWatcher} from "./ws2p/ws2p-watcher";
|
import {ws2pWatcher} from "./watchers/ws2p/ws2p-watcher";
|
||||||
|
import {bmaWatcher} from "./watchers/bma/bma-watcher";
|
||||||
|
|
||||||
export async function dwatch(confFile: string) {
|
export async function dwatch(confFile: string) {
|
||||||
|
|
||||||
const yml = fs.readFileSync(confFile, 'utf8')
|
const yml = fs.readFileSync(confFile, 'utf8')
|
||||||
const conf = yaml.load(yml) as Conf
|
const conf = yaml.load(yml) as Conf
|
||||||
|
|
||||||
await Promise.all(conf.ws2pServers.map(ws2pWatcher(conf)))
|
await Promise.all((conf.ws2pServers || []).map(ws2pWatcher(conf)))
|
||||||
|
await Promise.all((conf.bmaServers || []).map(bmaWatcher(conf)))
|
||||||
}
|
}
|
||||||
|
|||||||
69
src/lib/mail.ts
Normal file
69
src/lib/mail.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import {moment} from "duniter/app/lib/common-libs/moment";
|
||||||
|
import {Conf} from "./types/conf";
|
||||||
|
import {ConfMail} from './types/conf'
|
||||||
|
import * as nodemailer from 'nodemailer'
|
||||||
|
|
||||||
|
export async function sendMail(conf: ConfMail, subject: string, html: string) {
|
||||||
|
|
||||||
|
if (conf.enabled) {
|
||||||
|
let transporter = nodemailer.createTransport({
|
||||||
|
host: conf.host,
|
||||||
|
port: conf.port,
|
||||||
|
secure: false, // true for 465, false for other ports
|
||||||
|
auth: {
|
||||||
|
user: conf.username, // generated ethereal user
|
||||||
|
pass: conf.apikey // generated ethereal password
|
||||||
|
},
|
||||||
|
authMethod: conf.auth,
|
||||||
|
requireTLS: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// send mail with defined transport object
|
||||||
|
let info = await transporter.sendMail({
|
||||||
|
from: conf.from,
|
||||||
|
to: conf.to,
|
||||||
|
subject,
|
||||||
|
html
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mail = {
|
||||||
|
|
||||||
|
onEstablished: (conf: Conf, target: string) => {
|
||||||
|
return async () => {
|
||||||
|
console.log('Connection established')
|
||||||
|
await sendMail(conf.mail, '[dwatcher] Connection established', `
|
||||||
|
<p>
|
||||||
|
Connection to ${target} established on ${moment().format('DD-MM-YYYY HH:mm:ss')}.
|
||||||
|
</p>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onDisconnect: (conf: Conf, target: string) => {
|
||||||
|
return async (waitingDelay: number) => {
|
||||||
|
console.log('Connection closed')
|
||||||
|
console.log('Waiting %s seconds...', waitingDelay)
|
||||||
|
await sendMail(conf.mail, '[dwatcher] Connection closed', `
|
||||||
|
<p>
|
||||||
|
Connection to ${target} was lost on ${moment().format('dd-MM-YYYY HH:mm:ss')}.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Waiting ${(waitingDelay / 1000).toFixed(0)} seconds before trying to reconnect.
|
||||||
|
</p>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onRestartSuccess: (conf: Conf, target: string) => {
|
||||||
|
return async () => {
|
||||||
|
console.log('Connection recovered')
|
||||||
|
await sendMail(conf.mail, '[dwatcher] Connection recovered', `
|
||||||
|
<p>
|
||||||
|
Connection to ${target} was lost on ${moment().format('dd-MM-YYYY HH:mm:ss')}.
|
||||||
|
</p>
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import * as nodemailer from 'nodemailer'
|
|
||||||
import {ConfMail} from './conf'
|
|
||||||
|
|
||||||
export async function sendMail(conf: ConfMail, subject: string, html: string) {
|
|
||||||
|
|
||||||
if (conf.enabled) {
|
|
||||||
let transporter = nodemailer.createTransport({
|
|
||||||
host: conf.host,
|
|
||||||
port: conf.port,
|
|
||||||
secure: false, // true for 465, false for other ports
|
|
||||||
auth: {
|
|
||||||
user: conf.username, // generated ethereal user
|
|
||||||
pass: conf.apikey // generated ethereal password
|
|
||||||
},
|
|
||||||
authMethod: conf.auth,
|
|
||||||
requireTLS: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// send mail with defined transport object
|
|
||||||
let info = await transporter.sendMail({
|
|
||||||
from: conf.from,
|
|
||||||
to: conf.to,
|
|
||||||
subject,
|
|
||||||
html
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
export interface Conf {
|
export interface Conf {
|
||||||
connectionTimeout: number
|
connectionTimeout: number
|
||||||
reconnectionDelays: number[]
|
reconnectionDelays: number[]
|
||||||
ws2pServers: ConfServers[]
|
ws2pServers: ConfWS2P[]
|
||||||
|
bmaServers: ConfBMA[]
|
||||||
mail: ConfMail
|
mail: ConfMail
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfServers {
|
export interface ConfWS2P {
|
||||||
address: string
|
address: string
|
||||||
expectedKey: string
|
expectedKey: string
|
||||||
salt: string
|
salt: string
|
||||||
@@ -13,6 +14,11 @@ export interface ConfServers {
|
|||||||
currency: string
|
currency: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ConfBMA {
|
||||||
|
address: string
|
||||||
|
frequency: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface ConfMail {
|
export interface ConfMail {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
host: string
|
host: string
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
import {WS2PConnection} from 'duniter/app/modules/ws2p/lib/WS2PConnection'
|
export async function watcherLoop(
|
||||||
|
|
||||||
export async function processHandler(
|
|
||||||
connect: () => Promise<void>,
|
connect: () => Promise<void>,
|
||||||
onConnectionClosed: () => Promise<void>,
|
onConnectionClosed: () => Promise<void>,
|
||||||
reconnectionDelays: number[],
|
reconnectionDelays: number[],
|
||||||
@@ -12,7 +10,6 @@ export async function processHandler(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
let hasStarted = false
|
let hasStarted = false
|
||||||
let connecting = await connect()
|
|
||||||
|
|
||||||
;(async () => {
|
;(async () => {
|
||||||
|
|
||||||
@@ -23,11 +20,10 @@ export async function processHandler(
|
|||||||
|
|
||||||
if (hasStarted) {
|
if (hasStarted) {
|
||||||
await onRestart()
|
await onRestart()
|
||||||
connecting = await connect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection trial
|
// Connection trial
|
||||||
await connecting
|
await connect()
|
||||||
|
|
||||||
if (!hasStarted) {
|
if (!hasStarted) {
|
||||||
hasStarted = true
|
hasStarted = true
|
||||||
51
src/lib/watchers/bma/bma-watcher.ts
Normal file
51
src/lib/watchers/bma/bma-watcher.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import {watcherLoop} from "../../watcherLoop";
|
||||||
|
import {Conf, ConfBMA} from "../../types/conf";
|
||||||
|
import Axios from "axios";
|
||||||
|
import {mail} from "../../mail";
|
||||||
|
|
||||||
|
export function bmaWatcher(conf: Conf) {
|
||||||
|
|
||||||
|
const URL_PATH = '/blockchain/current'
|
||||||
|
|
||||||
|
return async (bmaServer: ConfBMA) => {
|
||||||
|
|
||||||
|
let nodeDownRes: () => void
|
||||||
|
let nodeDownPromise: Promise<void> = new Promise(res => nodeDownRes = res)
|
||||||
|
|
||||||
|
await watcherLoop(
|
||||||
|
async () => {
|
||||||
|
await Axios.get(bmaServer.address + URL_PATH)
|
||||||
|
let interval = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
await Axios.get(bmaServer.address + URL_PATH)
|
||||||
|
} catch (e) {
|
||||||
|
clearInterval(interval)
|
||||||
|
nodeDownRes()
|
||||||
|
// Re-create down promise for future connection trial
|
||||||
|
nodeDownPromise = new Promise(res => nodeDownRes = res)
|
||||||
|
}
|
||||||
|
}, bmaServer.frequency)
|
||||||
|
},
|
||||||
|
|
||||||
|
() => nodeDownPromise,
|
||||||
|
|
||||||
|
conf.reconnectionDelays,
|
||||||
|
|
||||||
|
mail.onEstablished(conf, bmaServer.address),
|
||||||
|
|
||||||
|
// When a disconnection is detected
|
||||||
|
mail.onDisconnect(conf, bmaServer.address),
|
||||||
|
|
||||||
|
async () => {
|
||||||
|
console.log('Trying to connect to %s', bmaServer.address)
|
||||||
|
},
|
||||||
|
|
||||||
|
mail.onRestartSuccess(conf, bmaServer.address),
|
||||||
|
|
||||||
|
async (e) => {
|
||||||
|
console.error(e.message)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/lib/watchers/ws2p/ws2p-watcher.ts
Normal file
62
src/lib/watchers/ws2p/ws2p-watcher.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import {WS2PConnection, WS2PPubkeyLocalAuth, WS2PPubkeyRemoteAuth} from "duniter/app/modules/ws2p/lib/WS2PConnection";
|
||||||
|
import {watcherLoop} from "../../watcherLoop";
|
||||||
|
import {MessageHandler} from "./message-handler";
|
||||||
|
import {Conf, ConfWS2P} from "../../types/conf";
|
||||||
|
import {Scrypt} from "duniter/app/modules/keypair/lib/scrypt";
|
||||||
|
import {Key} from "duniter/app/lib/common-libs/crypto/keyring";
|
||||||
|
import {mail} from "../../mail";
|
||||||
|
|
||||||
|
export function ws2pWatcher(conf: Conf) {
|
||||||
|
|
||||||
|
let c: WS2PConnection
|
||||||
|
|
||||||
|
return async (wserver: ConfWS2P) => {
|
||||||
|
|
||||||
|
const keys = await Scrypt(wserver.salt, wserver.passwd)
|
||||||
|
const keypair = new Key(keys.pub, keys.sec)
|
||||||
|
|
||||||
|
await watcherLoop(
|
||||||
|
async () => {
|
||||||
|
|
||||||
|
const localAuth = new WS2PPubkeyLocalAuth(wserver.currency, keypair, "", async () => true)
|
||||||
|
const remoteAuth = new WS2PPubkeyRemoteAuth(wserver.currency, keypair, async () => true)
|
||||||
|
|
||||||
|
c = WS2PConnection.newConnectionToAddress(
|
||||||
|
1,
|
||||||
|
wserver.address,
|
||||||
|
new MessageHandler(),
|
||||||
|
localAuth,
|
||||||
|
remoteAuth,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
connectionTimeout: conf.connectionTimeout,
|
||||||
|
requestTimeout: 0 // No request anyway
|
||||||
|
},
|
||||||
|
wserver.expectedKey
|
||||||
|
)
|
||||||
|
|
||||||
|
await c.connectAsInitiator()
|
||||||
|
},
|
||||||
|
|
||||||
|
() => c.closed,
|
||||||
|
|
||||||
|
conf.reconnectionDelays,
|
||||||
|
|
||||||
|
mail.onEstablished(conf, wserver.expectedKey),
|
||||||
|
|
||||||
|
// When a disconnection is detected
|
||||||
|
mail.onDisconnect(conf, wserver.expectedKey),
|
||||||
|
|
||||||
|
async () => {
|
||||||
|
console.log('Trying to connect to %s', wserver.expectedKey)
|
||||||
|
},
|
||||||
|
|
||||||
|
mail.onRestartSuccess(conf, wserver.expectedKey),
|
||||||
|
|
||||||
|
async (e) => {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
import {WS2PConnection, WS2PPubkeyLocalAuth, WS2PPubkeyRemoteAuth} from "duniter/app/modules/ws2p/lib/WS2PConnection";
|
|
||||||
import {processHandler} from "../processHandler";
|
|
||||||
import {MessageHandler} from "../message-handler";
|
|
||||||
import {sendMail} from "../sendMail";
|
|
||||||
import {moment} from "duniter/app/lib/common-libs/moment";
|
|
||||||
import {Conf, ConfServers} from "../conf";
|
|
||||||
import {Scrypt} from "duniter/app/modules/keypair/lib/scrypt";
|
|
||||||
import {Key} from "duniter/app/lib/common-libs/crypto/keyring";
|
|
||||||
|
|
||||||
export function ws2pWatcher(conf: Conf) {
|
|
||||||
|
|
||||||
return async (wserver: ConfServers) => {
|
|
||||||
|
|
||||||
const keys = await Scrypt(wserver.salt, wserver.passwd)
|
|
||||||
const keypair = new Key(keys.pub, keys.sec)
|
|
||||||
|
|
||||||
let c: WS2PConnection
|
|
||||||
|
|
||||||
await processHandler(
|
|
||||||
async () => {
|
|
||||||
|
|
||||||
const localAuth = new WS2PPubkeyLocalAuth(wserver.currency, keypair, "", async () => true)
|
|
||||||
const remoteAuth = new WS2PPubkeyRemoteAuth(wserver.currency, keypair, async () => true)
|
|
||||||
|
|
||||||
c = WS2PConnection.newConnectionToAddress(
|
|
||||||
1,
|
|
||||||
wserver.address,
|
|
||||||
new MessageHandler(),
|
|
||||||
localAuth,
|
|
||||||
remoteAuth,
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
connectionTimeout: conf.connectionTimeout,
|
|
||||||
requestTimeout: 0 // No request anyway
|
|
||||||
},
|
|
||||||
wserver.expectedKey
|
|
||||||
)
|
|
||||||
|
|
||||||
await c.connectAsInitiator()
|
|
||||||
},
|
|
||||||
|
|
||||||
() => c.closed,
|
|
||||||
|
|
||||||
conf.reconnectionDelays,
|
|
||||||
|
|
||||||
async () => {
|
|
||||||
console.log('Connection established')
|
|
||||||
await sendMail(conf.mail, '[dwatcher] Connection established', `
|
|
||||||
<p>
|
|
||||||
Connection to ${c.pubkey} established on ${moment().format('DD-MM-YYYY HH:mm:ss')}.
|
|
||||||
</p>
|
|
||||||
`)
|
|
||||||
},
|
|
||||||
|
|
||||||
// When a disconnection is detected
|
|
||||||
async (waitingDelay) => {
|
|
||||||
console.log('Connection closed')
|
|
||||||
console.log('Waiting %s seconds...', waitingDelay)
|
|
||||||
await sendMail(conf.mail, '[dwatcher] Connection closed', `
|
|
||||||
<p>
|
|
||||||
Connection to ${c.pubkey} was lost on ${moment().format('dd-MM-YYYY HH:mm:ss')}.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Waiting ${(waitingDelay / 1000).toFixed(0)} seconds before trying to reconnect.
|
|
||||||
</p>
|
|
||||||
`)
|
|
||||||
},
|
|
||||||
|
|
||||||
async () => {
|
|
||||||
console.log('Trying to connect to %s', c.pubkey)
|
|
||||||
},
|
|
||||||
|
|
||||||
async () => {
|
|
||||||
console.log('Connection recovered')
|
|
||||||
await sendMail(conf.mail, '[dwatcher] Connection recovered', `
|
|
||||||
<p>
|
|
||||||
Connection to ${c.pubkey} was lost on ${moment().format('dd-MM-YYYY HH:mm:ss')}.
|
|
||||||
</p>
|
|
||||||
`)
|
|
||||||
},
|
|
||||||
|
|
||||||
async (e) => {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user