UNPKG

4.64 kBPlain TextView Raw
1import reduct, { Injector } from 'reduct'
2import { partial } from 'lodash'
3import { create as createLogger } from './common/log'
4const log = createLogger('app')
5
6import Config from './services/config'
7import RouteBuilder from './services/route-builder'
8import RouteBroadcaster from './services/route-broadcaster'
9import Accounts from './services/accounts'
10import RateBackend from './services/rate-backend'
11import Store from './services/store'
12import MiddlewareManager from './services/middleware-manager'
13import AdminApi from './services/admin-api'
14import * as Prometheus from 'prom-client'
15
16const version = require('../package.json').version
17
18function listen (
19 config: Config,
20 accounts: Accounts,
21 backend: RateBackend,
22 store: Store,
23 routeBuilder: RouteBuilder,
24 routeBroadcaster: RouteBroadcaster,
25 middlewareManager: MiddlewareManager,
26 adminApi: AdminApi
27) {
28 // Start a coroutine that connects to the backend and
29 // subscribes to all the accounts in the background
30 return (async function () {
31 adminApi.listen()
32
33 try {
34 await backend.connect()
35 } catch (error) {
36 log.error(error)
37 process.exit(1)
38 }
39
40 await middlewareManager.setup()
41
42 // If we have no configured ILP address, try to get one via ILDCP
43 await accounts.loadIlpAddress()
44
45 if (config.routeBroadcastEnabled) {
46 routeBroadcaster.start()
47 }
48
49 // Connect other plugins, give up after initialConnectTimeout
50 await new Promise((resolve, reject) => {
51 const connectTimeout = setTimeout(() => {
52 log.warn('one or more accounts failed to connect within the time limit, continuing anyway.')
53 resolve()
54 }, config.initialConnectTimeout)
55 accounts.connect({ timeout: config.initialConnectTimeout })
56 .then(() => {
57 clearTimeout(connectTimeout)
58 resolve()
59 }, reject)
60 })
61
62 await middlewareManager.startup()
63
64 if (config.collectDefaultMetrics) {
65 Prometheus.collectDefaultMetrics()
66 }
67
68 log.info('connector ready (republic attitude). address=%s version=%s', accounts.getOwnAddress(), version)
69 })().catch((err) => log.error(err))
70}
71
72async function addPlugin (
73 config: Config,
74 accounts: Accounts,
75 backend: RateBackend,
76 routeBroadcaster: RouteBroadcaster,
77 middlewareManager: MiddlewareManager,
78
79 id: string,
80 options: any
81) {
82 accounts.add(id, options)
83 const plugin = accounts.getPlugin(id)
84 await middlewareManager.addPlugin(id, plugin)
85
86 await plugin.connect({ timeout: Infinity })
87 routeBroadcaster.track(id)
88 routeBroadcaster.reloadLocalRoutes()
89}
90
91async function removePlugin (
92 config: Config,
93 accounts: Accounts,
94 backend: RateBackend,
95 routeBroadcaster: RouteBroadcaster,
96 middlewareManager: MiddlewareManager,
97
98 id: string
99) {
100 const plugin = accounts.getPlugin(id)
101 await middlewareManager.removePlugin(id, plugin)
102 await plugin.disconnect()
103 routeBroadcaster.untrack(id)
104 accounts.remove(id)
105 routeBroadcaster.reloadLocalRoutes()
106}
107
108function getPlugin (
109 accounts: Accounts,
110
111 id: string
112) {
113 return accounts.getPlugin(id)
114}
115
116function shutdown (
117 accounts: Accounts,
118 routeBroadcaster: RouteBroadcaster
119) {
120 routeBroadcaster.stop()
121 return accounts.disconnect()
122}
123
124export default function createApp (opts?: object, container?: Injector) {
125 const deps = container || reduct()
126
127 const config = deps(Config)
128
129 try {
130 if (opts) {
131 config.loadFromOpts(opts)
132 } else {
133 config.loadFromEnv()
134 }
135 } catch (err) {
136 if (err.name === 'InvalidJsonBodyError') {
137 log.warn('config validation error.')
138 err.debugPrint(log.warn.bind(log))
139 log.error('invalid configuration, shutting down.')
140 throw new Error('failed to initialize due to invalid configuration.')
141 }
142
143 throw err
144 }
145
146 const accounts = deps(Accounts)
147 const routeBuilder = deps(RouteBuilder)
148 const routeBroadcaster = deps(RouteBroadcaster)
149 const backend = deps(RateBackend)
150 const store = deps(Store)
151 const middlewareManager = deps(MiddlewareManager)
152 const adminApi = deps(AdminApi)
153
154 const credentials = config.accounts
155 for (let id of Object.keys(credentials)) {
156 accounts.add(id, credentials[id])
157 }
158
159 return {
160 config,
161 listen: partial(listen, config, accounts, backend, store, routeBuilder, routeBroadcaster, middlewareManager, adminApi),
162 addPlugin: partial(addPlugin, config, accounts, backend, routeBroadcaster, middlewareManager),
163 removePlugin: partial(removePlugin, config, accounts, backend, routeBroadcaster, middlewareManager),
164 getPlugin: partial(getPlugin, accounts),
165 shutdown: partial(shutdown, accounts, routeBroadcaster)
166 }
167}