@bitpay-labs/bitcore-wallet-service 11.8.0 → 11.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/package.json +6 -6
  2. package/src/bws.ts +2 -0
  3. package/src/externalservices/ramp.ts +0 -1
  4. package/src/lib/common/utils.ts +6 -0
  5. package/src/lib/expressapp.ts +63 -2378
  6. package/src/lib/logger.ts +5 -74
  7. package/src/lib/model/txproposal.ts +14 -10
  8. package/src/lib/routes/advertisements.ts +151 -0
  9. package/src/lib/routes/blockchain.ts +152 -0
  10. package/src/lib/routes/context.ts +149 -0
  11. package/src/lib/routes/maintenance.ts +111 -0
  12. package/src/lib/routes/moralis.ts +89 -0
  13. package/src/lib/routes/notifications.ts +135 -0
  14. package/src/lib/routes/services.ts +528 -0
  15. package/src/lib/routes/setup.ts +86 -0
  16. package/src/lib/routes/transactions.ts +196 -0
  17. package/src/lib/routes/walletdata.ts +167 -0
  18. package/src/lib/routes/wallets.ts +289 -0
  19. package/src/lib/server.ts +29 -4
  20. package/src/lib/storage.ts +3 -0
  21. package/test/integration/externalservices/ramp.test.ts +0 -4
  22. package/ts_build/package.json +5 -5
  23. package/ts_build/src/bws.js +3 -0
  24. package/ts_build/src/bws.js.map +1 -1
  25. package/ts_build/src/externalservices/ramp.d.ts.map +1 -1
  26. package/ts_build/src/externalservices/ramp.js +0 -2
  27. package/ts_build/src/externalservices/ramp.js.map +1 -1
  28. package/ts_build/src/lib/common/index.d.ts +1 -0
  29. package/ts_build/src/lib/common/index.d.ts.map +1 -1
  30. package/ts_build/src/lib/common/utils.d.ts +1 -0
  31. package/ts_build/src/lib/common/utils.d.ts.map +1 -1
  32. package/ts_build/src/lib/common/utils.js +6 -0
  33. package/ts_build/src/lib/common/utils.js.map +1 -1
  34. package/ts_build/src/lib/expressapp.d.ts.map +1 -1
  35. package/ts_build/src/lib/expressapp.js +64 -2224
  36. package/ts_build/src/lib/expressapp.js.map +1 -1
  37. package/ts_build/src/lib/logger.d.ts +5 -5
  38. package/ts_build/src/lib/logger.d.ts.map +1 -1
  39. package/ts_build/src/lib/logger.js +6 -91
  40. package/ts_build/src/lib/logger.js.map +1 -1
  41. package/ts_build/src/lib/model/txproposal.d.ts +1 -4
  42. package/ts_build/src/lib/model/txproposal.d.ts.map +1 -1
  43. package/ts_build/src/lib/model/txproposal.js +13 -9
  44. package/ts_build/src/lib/model/txproposal.js.map +1 -1
  45. package/ts_build/src/lib/routes/advertisements.d.ts +11 -0
  46. package/ts_build/src/lib/routes/advertisements.d.ts.map +1 -0
  47. package/ts_build/src/lib/routes/advertisements.js +119 -0
  48. package/ts_build/src/lib/routes/advertisements.js.map +1 -0
  49. package/ts_build/src/lib/routes/blockchain.d.ts +13 -0
  50. package/ts_build/src/lib/routes/blockchain.d.ts.map +1 -0
  51. package/ts_build/src/lib/routes/blockchain.js +159 -0
  52. package/ts_build/src/lib/routes/blockchain.js.map +1 -0
  53. package/ts_build/src/lib/routes/context.d.ts +12 -0
  54. package/ts_build/src/lib/routes/context.d.ts.map +1 -0
  55. package/ts_build/src/lib/routes/context.js +115 -0
  56. package/ts_build/src/lib/routes/context.js.map +1 -0
  57. package/ts_build/src/lib/routes/maintenance.d.ts +10 -0
  58. package/ts_build/src/lib/routes/maintenance.d.ts.map +1 -0
  59. package/ts_build/src/lib/routes/maintenance.js +131 -0
  60. package/ts_build/src/lib/routes/maintenance.js.map +1 -0
  61. package/ts_build/src/lib/routes/moralis.d.ts +9 -0
  62. package/ts_build/src/lib/routes/moralis.d.ts.map +1 -0
  63. package/ts_build/src/lib/routes/moralis.js +78 -0
  64. package/ts_build/src/lib/routes/moralis.js.map +1 -0
  65. package/ts_build/src/lib/routes/notifications.d.ts +10 -0
  66. package/ts_build/src/lib/routes/notifications.d.ts.map +1 -0
  67. package/ts_build/src/lib/routes/notifications.js +120 -0
  68. package/ts_build/src/lib/routes/notifications.js.map +1 -0
  69. package/ts_build/src/lib/routes/services.d.ts +11 -0
  70. package/ts_build/src/lib/routes/services.d.ts.map +1 -0
  71. package/ts_build/src/lib/routes/services.js +430 -0
  72. package/ts_build/src/lib/routes/services.js.map +1 -0
  73. package/ts_build/src/lib/routes/setup.d.ts +4 -0
  74. package/ts_build/src/lib/routes/setup.d.ts.map +1 -0
  75. package/ts_build/src/lib/routes/setup.js +79 -0
  76. package/ts_build/src/lib/routes/setup.js.map +1 -0
  77. package/ts_build/src/lib/routes/transactions.d.ts +9 -0
  78. package/ts_build/src/lib/routes/transactions.d.ts.map +1 -0
  79. package/ts_build/src/lib/routes/transactions.js +188 -0
  80. package/ts_build/src/lib/routes/transactions.js.map +1 -0
  81. package/ts_build/src/lib/routes/walletdata.d.ts +10 -0
  82. package/ts_build/src/lib/routes/walletdata.d.ts.map +1 -0
  83. package/ts_build/src/lib/routes/walletdata.js +186 -0
  84. package/ts_build/src/lib/routes/walletdata.js.map +1 -0
  85. package/ts_build/src/lib/routes/wallets.d.ts +13 -0
  86. package/ts_build/src/lib/routes/wallets.d.ts.map +1 -0
  87. package/ts_build/src/lib/routes/wallets.js +292 -0
  88. package/ts_build/src/lib/routes/wallets.js.map +1 -0
  89. package/ts_build/src/lib/server.d.ts +5 -3
  90. package/ts_build/src/lib/server.d.ts.map +1 -1
  91. package/ts_build/src/lib/server.js +19 -1
  92. package/ts_build/src/lib/server.js.map +1 -1
  93. package/ts_build/src/lib/storage.d.ts.map +1 -1
  94. package/ts_build/src/lib/storage.js +3 -0
  95. package/ts_build/src/lib/storage.js.map +1 -1
  96. package/ts_build/test/integration/externalservices/ramp.test.js +0 -4
  97. package/ts_build/test/integration/externalservices/ramp.test.js.map +1 -1
package/src/lib/logger.ts CHANGED
@@ -1,77 +1,8 @@
1
- import path from 'path';
2
- import * as winston from 'winston';
1
+ import { createLogger, formatTimestamp, timestamp } from '@bitpay-labs/bitcore-logging';
3
2
 
4
- // const logLevel = args.DEBUG ? 'debug' : 'info';
5
- const logLevel = process.env.BWS_LOG_LEVEL || 'debug';
6
-
7
- export const transports: winston.transport[] = [
8
- new winston.transports.Console({
9
- level: logLevel,
10
- format: winston.format.combine(
11
- winston.format.colorize(),
12
- winston.format.prettyPrint(),
13
- winston.format.splat(),
14
- winston.format.simple(),
15
- winston.format.printf(function(info) {
16
- // fallback in case the above formatters don't work.
17
- // eg: logger.log({ some: 'object' })
18
- if (typeof info.message === 'object') {
19
- info.message = JSON.stringify(info.message, null, 4);
20
- }
21
- return `${info.level} :: ${new Date().toISOString()} :: ${info.message}`;
22
- })
23
- ),
24
- })
25
- ];
26
-
27
- if (process.env.BWS_LOG_HTTP_HOST) {
28
- transports.push(new winston.transports.Http({
29
- level: process.env.BWS_LOG_HTTP_LEVEL || logLevel,
30
- host: process.env.BWS_LOG_HTTP_HOST,
31
- port: parseInt(process.env.BWS_LOG_HTTP_PORT) || undefined,
32
- path: process.env.BWS_LOG_HTTP_PATH || ('bws.' + path.parse(process.argv[1]).name),
33
- headers: {
34
- 'Content-Type': 'application/json'
35
- },
36
- format: winston.format.combine(
37
- winston.format.splat(),
38
- winston.format.simple(),
39
- winston.format.printf(info => {
40
- // fallback in case the above formatters don't work.
41
- // eg: logger.log({ some: 'object' })
42
- if (typeof info.message === 'object') {
43
- info.message = JSON.stringify(info.message, null, 4);
44
- }
45
- return JSON.stringify({
46
- tag: process.env.BWS_LOG_HTTP_TAG || ('bws.' + path.parse(process.argv[1]).name),
47
- ...info
48
- });
49
- }),
50
- )
51
- }));
52
- }
53
-
54
- export const logger = winston.createLogger({ transports });
55
-
56
- export const formatTimestamp = (date: Date): string =>
57
- `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date
58
- .getDate()
59
- .toString()
60
- .padStart(2, '0')} ${date
61
- .getHours()
62
- .toString()
63
- .padStart(2, '0')}:${date
64
- .getMinutes()
65
- .toString()
66
- .padStart(2, '0')}:${date
67
- .getSeconds()
68
- .toString()
69
- .padStart(2, '0')}.${date
70
- .getMilliseconds()
71
- .toString()
72
- // .padEnd(3, '0')} ${timezone}`;
73
- .padEnd(3, '0')}`;
74
-
75
- export const timestamp = () => formatTimestamp(new Date());
3
+ type BwsLogger = ReturnType<typeof createLogger>;
76
4
 
5
+ export const logger: BwsLogger = createLogger({ prefix: 'BWS', defaultLevel: 'debug' });
6
+ export const transports: BwsLogger['transports'] = logger.transports;
7
+ export { formatTimestamp, timestamp };
77
8
  export default logger;
@@ -424,16 +424,20 @@ export class TxProposal implements ITxProposal {
424
424
  }
425
425
 
426
426
  getCurrentSignatures() {
427
- const acceptedActions = _.filter(this.actions, a => {
428
- return a.type == 'accept';
429
- });
430
-
431
- return _.map(acceptedActions, x => {
432
- return {
433
- signatures: x.signatures,
434
- xpub: x.xpub
435
- };
436
- });
427
+ const acceptedActions = this.actions.filter(a => a.type == 'accept');
428
+
429
+ const uniqueXpubs = new Set();
430
+ const signatures = [];
431
+ for (const a of acceptedActions) {
432
+ // TSS will have multiple accept actions with the same xpub/signature (different copayerIds)
433
+ if (uniqueXpubs.has(a.xpub)) continue;
434
+ uniqueXpubs.add(a.xpub);
435
+ signatures.push({
436
+ signatures: a.signatures,
437
+ xpub: a.xpub
438
+ });
439
+ }
440
+ return signatures;
437
441
  }
438
442
 
439
443
  getRawTx() {
@@ -0,0 +1,151 @@
1
+ import express from 'express';
2
+ import type * as Types from '../../types/expressapp';
3
+
4
+ interface RouteContext {
5
+ getServer: Types.GetServerFn;
6
+ getServerWithAuth: Types.GetServerWithAuthFn;
7
+ setPublicCache: (res: express.Response, seconds: number) => void;
8
+ returnError: Types.ReturnErrorFn;
9
+ }
10
+
11
+ export function registerAdvertisementRoutes(router: express.Router, context: RouteContext) {
12
+ const { getServer, getServerWithAuth, setPublicCache, returnError } = context;
13
+ const ONE_MINUTE = 60;
14
+
15
+ router.post('/v1/advertisements/', (req, res) => {
16
+ getServerWithAuth(
17
+ req,
18
+ res,
19
+ {
20
+ onlyMarketingStaff: true
21
+ },
22
+ server => {
23
+ server.createAdvert(req.body, (err, advert) => {
24
+ if (err) {
25
+ return returnError(err, res, req);
26
+ }
27
+ if (advert) res.json(advert);
28
+ });
29
+ }
30
+ );
31
+ });
32
+
33
+ router.get('/v1/advertisements/', (req, res) => {
34
+ const testing = req.query.testing;
35
+
36
+ let server;
37
+ try {
38
+ server = getServer(req, res);
39
+ } catch (ex) {
40
+ return returnError(ex, res, req);
41
+ }
42
+
43
+ if (testing) {
44
+ server.getTestingAdverts(req.body, (err, ads) => {
45
+ if (err) return returnError(err, res, req);
46
+ res.json(ads);
47
+ });
48
+ return;
49
+ }
50
+
51
+ setPublicCache(res, 5 * ONE_MINUTE);
52
+ server.getAdverts(req.body, (err, ads) => {
53
+ if (err) return returnError(err, res, req);
54
+ res.json(ads);
55
+ });
56
+ });
57
+
58
+ router.get('/v1/advertisements/:adId/', (req, res) => {
59
+ let server;
60
+
61
+ try {
62
+ server = getServer(req, res);
63
+ } catch (ex) {
64
+ return returnError(ex, res, req);
65
+ }
66
+
67
+ const opts = { adId: req.params['adId'] };
68
+
69
+ if (req.params['adId']) {
70
+ server.getAdvert(opts, (err, ad) => {
71
+ if (err) return returnError(err, res, req);
72
+ res.json(ad);
73
+ });
74
+ }
75
+ });
76
+
77
+ router.get('/v1/advertisements/country/:country', (req, res) => {
78
+ const opts = { country: req.params['country'] };
79
+
80
+ let server;
81
+ try {
82
+ server = getServer(req, res);
83
+ } catch (ex) {
84
+ return returnError(ex, res, req);
85
+ }
86
+
87
+ server.getAdvertsByCountry(opts, (err, ads) => {
88
+ if (err) return returnError(err, res, req);
89
+ res.json(ads);
90
+ });
91
+ });
92
+
93
+ router.post('/v1/advertisements/:adId/activate', (req, res) => {
94
+ const opts = { adId: req.params['adId'] };
95
+
96
+ getServerWithAuth(
97
+ req,
98
+ res,
99
+ {
100
+ onlyMarketingStaff: true
101
+ },
102
+ server => {
103
+ if (req.params['adId']) {
104
+ server.activateAdvert(opts, err => {
105
+ if (err) return returnError(err, res, req);
106
+ res.json({ advertisementId: opts.adId, message: 'advert activated' });
107
+ });
108
+ }
109
+ }
110
+ );
111
+ });
112
+
113
+ router.post('/v1/advertisements/:adId/deactivate', (req, res) => {
114
+ const opts = { adId: req.params['adId'] };
115
+
116
+ getServerWithAuth(
117
+ req,
118
+ res,
119
+ {
120
+ onlyMarketingStaff: true
121
+ },
122
+ server => {
123
+ if (req.params['adId']) {
124
+ server.deactivateAdvert(opts, err => {
125
+ if (err) return returnError(err, res, req);
126
+ res.json({ advertisementId: opts.adId, message: 'advert deactivated' });
127
+ });
128
+ }
129
+ }
130
+ );
131
+ });
132
+
133
+ router.delete('/v1/advertisements/:adId/', (req, res) => {
134
+ getServerWithAuth(
135
+ req,
136
+ res,
137
+ {
138
+ onlyMarketingStaff: true
139
+ },
140
+ server => {
141
+ req.body.adId = req.params['adId'];
142
+ server.removeAdvert(req.body, (err, removedAd) => {
143
+ if (err) return returnError(err, res, req);
144
+ if (removedAd) {
145
+ res.json(removedAd);
146
+ }
147
+ });
148
+ }
149
+ );
150
+ });
151
+ }
@@ -0,0 +1,152 @@
1
+ import express from 'express';
2
+ import * as _ from 'lodash';
3
+ import type * as Types from '../../types/expressapp';
4
+ import type { WalletService } from '../server';
5
+
6
+ interface RouteContext {
7
+ estimateFeeLimiter: express.Handler;
8
+ getServer: Types.GetServerFn;
9
+ getServerWithAuth: Types.GetServerWithAuthFn;
10
+ logDeprecated: Types.LogDeprecatedFn;
11
+ setPublicCache: (res: express.Response, seconds: number) => void;
12
+ returnError: Types.ReturnErrorFn;
13
+ }
14
+
15
+ type RouteHandler = (server: WalletService) => Promise<any> | any;
16
+
17
+ const ONE_MINUTE = 60;
18
+
19
+ function callbackResult<T>(fn: (cb: (err?: any, data?: T) => void) => void): Promise<T> {
20
+ return new Promise((resolve, reject) => {
21
+ fn((err, data) => {
22
+ if (err) return reject(err);
23
+ return resolve(data as T);
24
+ });
25
+ });
26
+ }
27
+
28
+ function getServerOrReturnError(req, res, context: RouteContext): WalletService | null {
29
+ try {
30
+ return context.getServer(req, res);
31
+ } catch (err) {
32
+ context.returnError(err, res, req);
33
+ return null;
34
+ }
35
+ }
36
+
37
+ function respondWithPublicServer(req, res, context: RouteContext, handler: RouteHandler) {
38
+ const server = getServerOrReturnError(req, res, context);
39
+ if (!server) return;
40
+
41
+ Promise.resolve()
42
+ .then(() => handler(server))
43
+ .then(response => {
44
+ res.json(response);
45
+ })
46
+ .catch(err => {
47
+ context.returnError(err ?? 'unknown', res, req);
48
+ });
49
+ }
50
+
51
+ function respondWithAuthServer(req, res, context: RouteContext, handler: RouteHandler) {
52
+ context.getServerWithAuth(req, res, server => {
53
+ Promise.resolve()
54
+ .then(() => handler(server))
55
+ .then(response => {
56
+ res.json(response);
57
+ })
58
+ .catch(err => {
59
+ context.returnError(err ?? 'unknown', res, req);
60
+ });
61
+ });
62
+ }
63
+
64
+ export function registerBlockchainRoutes(router: express.Router, context: RouteContext) {
65
+ const { estimateFeeLimiter, logDeprecated, setPublicCache } = context;
66
+
67
+ router.get('/v1/feelevels/', estimateFeeLimiter, (req, res) => {
68
+ setPublicCache(res, 1 * ONE_MINUTE);
69
+ logDeprecated(req);
70
+
71
+ respondWithPublicServer(req, res, context, server =>
72
+ callbackResult(resolve => {
73
+ const opts: { network?: string } = {};
74
+ if (req.query.network) opts.network = req.query.network as string;
75
+ server.getFeeLevels(opts, resolve);
76
+ }).then((feeLevels: any[]) => {
77
+ _.each(feeLevels, feeLevel => {
78
+ feeLevel.feePerKB = feeLevel.feePerKb;
79
+ delete feeLevel.feePerKb;
80
+ });
81
+ return feeLevels;
82
+ })
83
+ );
84
+ });
85
+
86
+ router.get('/v2/feelevels/', (req, res) => {
87
+ setPublicCache(res, 1 * ONE_MINUTE);
88
+
89
+ respondWithPublicServer(req, res, context, server =>
90
+ callbackResult(resolve => {
91
+ const opts: { coin?: string; network?: string; chain?: string } = {};
92
+ if (req.query.coin) opts.coin = req.query.coin as string;
93
+ if (req.query.chain || req.query.coin) opts.chain = (req.query.chain || req.query.coin) as string;
94
+ if (req.query.network) opts.network = req.query.network as string;
95
+ server.getFeeLevels(opts, resolve);
96
+ })
97
+ );
98
+ });
99
+
100
+ router.post('/v3/estimateGas/', (req, res) => {
101
+ respondWithAuthServer(req, res, context, server => {
102
+ return server.estimateGas(req.body);
103
+ });
104
+ });
105
+
106
+ router.post('/v1/ethmultisig/', (req, res) => {
107
+ respondWithAuthServer(req, res, context, server => {
108
+ return server.getMultisigContractInstantiationInfo(req.body);
109
+ });
110
+ });
111
+
112
+ router.post('/v1/ethmultisig/info', (req, res) => {
113
+ respondWithAuthServer(req, res, context, server => {
114
+ return server.getMultisigContractInfo(req.body);
115
+ });
116
+ });
117
+
118
+ router.post('/v1/multisig/', (req, res) => {
119
+ respondWithAuthServer(req, res, context, server => {
120
+ return server.getMultisigContractInstantiationInfo(req.body);
121
+ });
122
+ });
123
+
124
+ router.post('/v1/multisig/info', (req, res) => {
125
+ respondWithAuthServer(req, res, context, server => {
126
+ return server.getMultisigContractInfo(req.body);
127
+ });
128
+ });
129
+
130
+ router.post('/v1/token/info', (req, res) => {
131
+ respondWithAuthServer(req, res, context, server => {
132
+ return server.getTokenContractInfo(req.body);
133
+ });
134
+ });
135
+
136
+ router.post('/v1/token/allowance', (req, res) => {
137
+ respondWithPublicServer(req, res, context, server => {
138
+ return server.getTokenAllowance(req.body);
139
+ });
140
+ });
141
+
142
+ router.get('/v1/nonce/:address', (req, res) => {
143
+ respondWithAuthServer(req, res, context, server => {
144
+ return server.getNonce({
145
+ coin: req.query.coin || 'eth',
146
+ chain: req.query.chain,
147
+ network: req.query.network || 'livenet',
148
+ address: req.params['address']
149
+ });
150
+ });
151
+ });
152
+ }
@@ -0,0 +1,149 @@
1
+ import express from 'express';
2
+ import * as Types from '../../types/expressapp';
3
+ import { Errors } from '../errors/errordefinitions';
4
+ import { logger } from '../logger';
5
+ import { WalletService } from '../server';
6
+
7
+ interface RouteHelpers {
8
+ getServer: Types.GetServerFn;
9
+ getServerWithAuth: Types.GetServerWithAuthFn;
10
+ getServerWithMultiAuth: Types.GetServerWithMultiAuthFn;
11
+ logDeprecated: Types.LogDeprecatedFn;
12
+ setPublicCache: (res: express.Response, seconds: number) => void;
13
+ }
14
+
15
+ export function createRouteHelpers(returnError: Types.ReturnErrorFn): RouteHelpers {
16
+ const logDeprecated: Types.LogDeprecatedFn = req => {
17
+ logger.warn('DEPRECATED', req.method, req.url, '(' + req.header('x-client-version') + ')');
18
+ };
19
+
20
+ const getCredentials: Types.GetCredentialsFn = req => {
21
+ const identity = req.header('x-identity');
22
+ if (!identity) return;
23
+
24
+ return {
25
+ copayerId: identity,
26
+ signature: req.header('x-signature'),
27
+ session: req.header('x-session')
28
+ };
29
+ };
30
+
31
+ const getServer: Types.GetServerFn = (req, _res) => {
32
+ const opts = {
33
+ clientVersion: req.header('x-client-version'),
34
+ userAgent: req.header('user-agent')
35
+ };
36
+ return WalletService.getInstance(opts);
37
+ };
38
+
39
+ const getServerWithAuth: Types.GetServerWithAuthFn = async (req, res, opts, cb) => {
40
+ if (typeof opts === 'function') {
41
+ cb = opts;
42
+ opts = {};
43
+ }
44
+ opts = (opts || {}) as Types.ServerOpts;
45
+
46
+ const credentials = getCredentials(req);
47
+ if (!credentials) {
48
+ return returnError(Errors.NOT_AUTHORIZED, res, req);
49
+ }
50
+
51
+ const reqUrl = req.redirectedUrl || req.url;
52
+ const auth = {
53
+ copayerId: credentials.copayerId,
54
+ message: req.method.toLowerCase() + '|' + reqUrl + '|' + JSON.stringify(req.body),
55
+ signature: credentials.signature,
56
+ clientVersion: req.header('x-client-version'),
57
+ userAgent: req.header('user-agent'),
58
+ walletId: req.header('x-wallet-id'),
59
+ session: undefined
60
+ };
61
+
62
+ if (opts.allowSession) {
63
+ auth.session = credentials.session;
64
+ }
65
+
66
+ try {
67
+ const server: WalletService = await new Promise((resolve, reject) => {
68
+ WalletService.getInstanceWithAuth(auth, (err, server) => {
69
+ if (err) {
70
+ return reject(err);
71
+ }
72
+
73
+ if (opts.onlySupportStaff && !server.copayerIsSupportStaff) {
74
+ return reject(Errors.NOT_AUTHORIZED);
75
+ }
76
+
77
+ if (server.copayerIsSupportStaff) {
78
+ req.isSupportStaff = true;
79
+ }
80
+
81
+ if (opts.onlyMarketingStaff && !server.copayerIsMarketingStaff) {
82
+ return reject(Errors.NOT_AUTHORIZED);
83
+ }
84
+
85
+ req.walletId = server.walletId;
86
+ req.copayerId = server.copayerId;
87
+
88
+ return resolve(server);
89
+ });
90
+ });
91
+
92
+ if (cb) {
93
+ return cb(server);
94
+ }
95
+ return server;
96
+ } catch (err) {
97
+ if (opts.silentFailure) {
98
+ if (cb) {
99
+ return cb(null, err);
100
+ }
101
+ throw err;
102
+ }
103
+ return returnError(err, res, req);
104
+ }
105
+ };
106
+
107
+ const getServerWithMultiAuth: Types.GetServerWithMultiAuthFn = (req, res, opts = {}) => {
108
+ const identitiesHeader = req.headers['x-identities'];
109
+ const identitiesHeaderValue = Array.isArray(identitiesHeader) ? identitiesHeader.join(',') : identitiesHeader;
110
+ const identities = identitiesHeaderValue ? identitiesHeaderValue.split(',') : false;
111
+ const signature = req.headers['x-signature'];
112
+ if (!identities || !signature) {
113
+ throw Errors.NOT_AUTHORIZED;
114
+ }
115
+
116
+ if (!Array.isArray(identities)) {
117
+ throw Errors.NOT_AUTHORIZED;
118
+ }
119
+
120
+ return identities.map(
121
+ id =>
122
+ new Promise((resolve, reject) =>
123
+ getServerWithAuth(
124
+ Object.assign(req, {
125
+ headers: {
126
+ ...req.headers,
127
+ 'x-identity': id
128
+ }
129
+ }),
130
+ res,
131
+ opts,
132
+ (server, err) => (err ? reject(err) : resolve(server))
133
+ )
134
+ )
135
+ );
136
+ };
137
+
138
+ const setPublicCache = (res: express.Response, seconds: number) => {
139
+ res.setHeader('Cache-Control', `public, max-age=${seconds}, stale-if-error=${10 * seconds}`);
140
+ };
141
+
142
+ return {
143
+ getServer,
144
+ getServerWithAuth,
145
+ getServerWithMultiAuth,
146
+ logDeprecated,
147
+ setPublicCache
148
+ };
149
+ }
@@ -0,0 +1,111 @@
1
+ import express from 'express';
2
+ import * as _ from 'lodash';
3
+ import { WalletService } from '../server';
4
+ import { Stats } from '../stats';
5
+ import type * as Types from '../../types/expressapp';
6
+
7
+ interface RouteContext {
8
+ getServerWithAuth: Types.GetServerWithAuthFn;
9
+ setPublicCache: (res: express.Response, seconds: number) => void;
10
+ returnError: Types.ReturnErrorFn;
11
+ }
12
+
13
+ const ONE_MINUTE = 60;
14
+
15
+ export function registerMaintenanceRoutes(router: express.Router, context: RouteContext) {
16
+ const { getServerWithAuth, setPublicCache, returnError } = context;
17
+
18
+ router.post('/v1/addresses/scan/', (req, res) => {
19
+ getServerWithAuth(req, res, server => {
20
+ req.body = req.body || {};
21
+ req.body.startIdx = server.copayerIsSupportStaff ? Number(req.body.startIdx) : null;
22
+ server.startScan(req.body, (err, started) => {
23
+ if (err) return returnError(err, res, req);
24
+ res.json(started);
25
+ res.end();
26
+ });
27
+ });
28
+ });
29
+
30
+ router.get('/v1/stats/', (req, res) => {
31
+ setPublicCache(res, 1 * ONE_MINUTE);
32
+ const opts: {
33
+ network?: string;
34
+ coin?: string;
35
+ from?: string;
36
+ to?: string;
37
+ } = {};
38
+
39
+ if (req.query.network) opts.network = req.query.network as string;
40
+ if (req.query.coin) opts.coin = req.query.coin as string;
41
+ if (req.query.from) opts.from = req.query.from as string;
42
+ if (req.query.to) opts.to = req.query.to as string;
43
+
44
+ const stats = new Stats(opts);
45
+ stats.run((err, data) => {
46
+ if (err) return returnError(err, res, req);
47
+ res.json(data);
48
+ res.end();
49
+ });
50
+ });
51
+
52
+ router.get('/v1/version/', (req, res) => {
53
+ setPublicCache(res, 1 * ONE_MINUTE);
54
+ res.json({
55
+ serviceVersion: WalletService.getServiceVersion()
56
+ });
57
+ res.end();
58
+ });
59
+
60
+ router.get('/v1/txnotes/:txid', (req, res) => {
61
+ getServerWithAuth(req, res, server => {
62
+ server.getTxNote(
63
+ {
64
+ txid: req.params['txid']
65
+ },
66
+ (err, note) => {
67
+ if (err) return returnError(err, res, req);
68
+ res.json(note);
69
+ }
70
+ );
71
+ });
72
+ });
73
+
74
+ router.put('/v1/txnotes/:txid/', (req, res) => {
75
+ req.body.txid = req.params['txid'];
76
+ getServerWithAuth(req, res, server => {
77
+ server.editTxNote(req.body, (err, note) => {
78
+ if (err) return returnError(err, res, req);
79
+ res.json(note);
80
+ });
81
+ });
82
+ });
83
+
84
+ router.get('/v1/txnotes/', (req, res) => {
85
+ getServerWithAuth(req, res, server => {
86
+ const opts: { minTs?: number } = {};
87
+ if (req.query.minTs && _.isNumber(+req.query.minTs)) {
88
+ opts.minTs = +req.query.minTs;
89
+ }
90
+ server.getTxNotes(opts, (err, notes) => {
91
+ if (err) return returnError(err, res, req);
92
+ res.json(notes);
93
+ });
94
+ });
95
+ });
96
+
97
+ router.post('/v1/clearcache/', (req, res) => {
98
+ getServerWithAuth(req, res, server => {
99
+ server
100
+ .clearWalletCache(req.query)
101
+ .then(val => {
102
+ if (val) {
103
+ res.sendStatus(200);
104
+ } else {
105
+ res.sendStatus(500);
106
+ }
107
+ })
108
+ .catch(err => returnError(err, res, req));
109
+ });
110
+ });
111
+ }