Skip to content

Commit 95b9566

Browse files
authored
Merge pull request #12 from BitGo/WP-4678-refactor-routes
chore(mbe, ebe): refactor routes into their own files
2 parents e3e494c + 805a6f9 commit 95b9566

File tree

8 files changed

+169
-161
lines changed

8 files changed

+169
-161
lines changed

src/__tests__/routes.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import express from 'express';
22
import request from 'supertest';
3-
import { setupRoutes } from '../routes';
3+
import { setupRoutes } from '../routes/enclaved';
44

55
describe('Routes', () => {
66
let app: express.Application;

src/app.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { determineAppMode, AppMode } from './config';
22
import * as enclavedApp from './enclavedApp';
33
import * as masterExpressApp from './masterExpressApp';
4+
import logger from './logger';
45

56
/**
67
* Main application entry point that determines the mode and starts the appropriate app
@@ -9,10 +10,10 @@ export async function init(): Promise<void> {
910
const appMode = determineAppMode();
1011

1112
if (appMode === AppMode.ENCLAVED) {
12-
console.log('Starting in Enclaved mode...');
13+
logger.info('Starting in Enclaved mode...');
1314
await enclavedApp.init();
1415
} else if (appMode === AppMode.MASTER_EXPRESS) {
15-
console.log('Starting in Master Express mode...');
16+
logger.info('Starting in Master Express mode...');
1617
await masterExpressApp.init();
1718
} else {
1819
throw new Error(`Unknown app mode: ${appMode}`);

src/enclavedApp.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import morgan from 'morgan';
55
import { SSL_OP_NO_TLSv1, SSL_OP_NO_TLSv1_1 } from 'constants';
66

77
import { EnclavedConfig, config, TlsMode, isEnclavedConfig } from './config';
8-
import * as routes from './routes';
8+
import { setupRoutes } from './routes/enclaved';
99
import {
1010
setupLogging,
1111
setupCommonMiddleware,
@@ -108,7 +108,7 @@ export function app(cfg: EnclavedConfig): express.Application {
108108
}
109109

110110
// Setup routes
111-
routes.setupRoutes(app);
111+
setupRoutes(app);
112112

113113
// Add error handler
114114
app.use(createErrorHandler());

src/kms/kmsClient.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export class KmsClient {
2323
}
2424

2525
async postKey(params: PostKeyParams): Promise<PostKeyResponse> {
26-
console.log('postKey', params);
2726
debugLogger('Posting key to KMS: %O', params);
2827

2928
let kmsResponse: any;

src/masterExpressApp.ts

Lines changed: 3 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,20 @@
11
import express from 'express';
22
import https from 'https';
33
import http from 'http';
4-
import superagent from 'superagent';
5-
import { BitGo, BitGoOptions } from 'bitgo';
6-
import { BitGoBase } from '@bitgo/sdk-core';
7-
import { version } from 'bitgo/package.json';
84
import { SSL_OP_NO_TLSv1, SSL_OP_NO_TLSv1_1 } from 'constants';
95

106
import { MasterExpressConfig, config, isMasterExpressConfig, TlsMode } from './config';
11-
import { BitGoRequest } from './types/request';
127
import {
138
setupLogging,
149
setupCommonMiddleware,
1510
createErrorHandler,
1611
createHttpServer,
1712
configureServerTimeouts,
1813
prepareIpc,
19-
setupHealthCheckRoutes,
2014
createMtlsMiddleware,
2115
} from './shared/appUtils';
22-
import bodyParser from 'body-parser';
23-
import { promiseWrapper } from './routes';
24-
import pjson from '../package.json';
25-
import { handleGenerateWalletOnPrem } from './masterBitgoExpress/generateWallet';
2616
import logger from './logger';
27-
28-
const BITGOEXPRESS_USER_AGENT = `BitGoExpress/${pjson.version} BitGoJS/${version}`;
17+
import { setupRoutes } from './routes/master';
2918

3019
/**
3120
* Create a startup function which will be run upon server initialization
@@ -53,57 +42,6 @@ function isTLS(config: MasterExpressConfig): boolean {
5342
return Boolean((keyPath && crtPath) || (tlsKey && tlsCert));
5443
}
5544

56-
const expressJSONParser = bodyParser.json({ limit: '20mb' });
57-
58-
/**
59-
* Perform body parsing here only on routes we want
60-
*/
61-
function parseBody(req: express.Request, res: express.Response, next: express.NextFunction) {
62-
// Set the default Content-Type, in case the client doesn't set it. If
63-
// Content-Type isn't specified, Express silently refuses to parse the
64-
// request body.
65-
req.headers['content-type'] = req.headers['content-type'] || 'application/json';
66-
return expressJSONParser(req, res, next);
67-
}
68-
69-
/**
70-
* Create the bitgo object in the request
71-
* @param config
72-
*/
73-
function prepareBitGo(config: MasterExpressConfig) {
74-
const { env, customRootUri } = config;
75-
76-
return function prepBitGo(
77-
req: express.Request,
78-
res: express.Response,
79-
next: express.NextFunction,
80-
) {
81-
// Get access token
82-
let accessToken;
83-
if (req.headers.authorization) {
84-
const authSplit = req.headers.authorization.split(' ');
85-
if (authSplit.length === 2 && authSplit[0].toLowerCase() === 'bearer') {
86-
accessToken = authSplit[1];
87-
}
88-
}
89-
const userAgent = req.headers['user-agent']
90-
? BITGOEXPRESS_USER_AGENT + ' ' + req.headers['user-agent']
91-
: BITGOEXPRESS_USER_AGENT;
92-
93-
const bitgoConstructorParams: BitGoOptions = {
94-
env,
95-
customRootURI: customRootUri,
96-
accessToken,
97-
userAgent,
98-
};
99-
100-
(req as BitGoRequest).bitgo = new BitGo(bitgoConstructorParams) as unknown as BitGoBase;
101-
(req as BitGoRequest).config = config;
102-
103-
next();
104-
};
105-
}
106-
10745
async function createHttpsServer(
10846
app: express.Application,
10947
config: MasterExpressConfig,
@@ -145,70 +83,6 @@ export function createBaseUri(config: MasterExpressConfig): string {
14583
return `http${ssl ? 's' : ''}://${bind}${!isStandardPort ? ':' + port : ''}`;
14684
}
14785

148-
/**
149-
* Setup master express specific routes
150-
*/
151-
function setupMasterExpressRoutes(app: express.Application, cfg: MasterExpressConfig): void {
152-
// Setup common health check routes
153-
setupHealthCheckRoutes(app, 'master express');
154-
155-
// Add enclaved express ping route
156-
app.post('/ping/enclavedExpress', async (req, res) => {
157-
try {
158-
logger.debug('Pinging enclaved express');
159-
160-
let response;
161-
if (cfg.tlsMode === TlsMode.MTLS) {
162-
// Use Master Express's own certificate as client cert when connecting to Enclaved Express
163-
const httpsAgent = new https.Agent({
164-
rejectUnauthorized: !cfg.allowSelfSigned,
165-
ca: cfg.enclavedExpressCert,
166-
// Provide client certificate for mTLS
167-
key: cfg.tlsKey,
168-
cert: cfg.tlsCert,
169-
});
170-
171-
response = await superagent
172-
.post(`${cfg.enclavedExpressUrl}/ping`)
173-
.ca(cfg.enclavedExpressCert)
174-
.agent(httpsAgent)
175-
.send();
176-
} else {
177-
// When TLS is disabled, use plain HTTP without any TLS configuration
178-
response = await superagent.post(`${cfg.enclavedExpressUrl}/ping`).send();
179-
}
180-
181-
res.json({
182-
status: 'Successfully pinged enclaved express',
183-
enclavedResponse: response.body,
184-
});
185-
} catch (error) {
186-
logger.error('Failed to ping enclaved express:', { error });
187-
res.status(500).json({
188-
error: 'Failed to ping enclaved express',
189-
details: error instanceof Error ? error.message : String(error),
190-
});
191-
}
192-
});
193-
194-
// TODO: Add api-ts to these new API routes
195-
app.post(
196-
'/api/:coin/wallet/generate',
197-
parseBody,
198-
prepareBitGo(cfg),
199-
promiseWrapper(handleGenerateWalletOnPrem),
200-
);
201-
202-
// Add a catch-all for unsupported routes
203-
app.use('*', (_req, res) => {
204-
res.status(404).json({
205-
error: 'Route not found or not supported in master express mode',
206-
});
207-
});
208-
209-
logger.debug('Master express routes configured');
210-
}
211-
21286
/**
21387
* Create and configure the express application for master express mode
21488
*/
@@ -228,7 +102,8 @@ export function app(cfg: MasterExpressConfig): express.Application {
228102
}
229103

230104
// Setup master express routes
231-
setupMasterExpressRoutes(app, cfg);
105+
setupRoutes(app, cfg);
106+
setupRoutes(app, cfg);
232107

233108
// Add error handler
234109
app.use(createErrorHandler());

src/routes.ts renamed to src/routes/enclaved.ts

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import express from 'express';
22
import debug from 'debug';
3-
import pjson from '../package.json';
3+
import pjson from '../../package.json';
44
import type { BitGoOptions } from 'bitgo';
5-
import { postIndependentKey } from './api/enclaved/postIndependentKey';
5+
import { postIndependentKey } from '../api/enclaved/postIndependentKey';
6+
import { promiseWrapper } from './utils';
67

78
const debugLogger = debug('enclaved:routes');
89

@@ -72,28 +73,3 @@ export function setupRoutes(app: express.Application): void {
7273

7374
debugLogger('All routes configured');
7475
}
75-
76-
// promiseWrapper implementation
77-
export function promiseWrapper(promiseRequestHandler: any) {
78-
return async function promWrapper(
79-
req: express.Request,
80-
res: express.Response,
81-
next: express.NextFunction,
82-
) {
83-
debugLogger(`handle: ${req.method} ${req.originalUrl}`);
84-
try {
85-
const result = await promiseRequestHandler(req, res, next);
86-
if (result && typeof result === 'object') {
87-
if ('status' in result && 'body' in result) {
88-
const { status, body } = result as { status: number; body: unknown };
89-
return res.status(status).json(body);
90-
}
91-
return res.status(200).json(result);
92-
}
93-
return res.status(200).json(result);
94-
} catch (e) {
95-
const err = e as Error;
96-
return res.status(500).json({ error: err.message || String(err) });
97-
}
98-
};
99-
}

src/routes/master.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import express from 'express';
2+
import superagent from 'superagent';
3+
import https from 'https';
4+
import { BitGo, BitGoOptions } from 'bitgo';
5+
import { BitGoBase } from '@bitgo/sdk-core';
6+
import { version } from 'bitgo/package.json';
7+
import pjson from '../../package.json';
8+
import { MasterExpressConfig, TlsMode } from '../config';
9+
import { BitGoRequest } from '../types/request';
10+
import { handleGenerateWalletOnPrem } from '../masterBitgoExpress/generateWallet';
11+
import { setupHealthCheckRoutes } from '../shared/appUtils';
12+
import { promiseWrapper } from './utils';
13+
import logger from '../logger';
14+
15+
const BITGOEXPRESS_USER_AGENT = `BitGoExpress/${pjson.version} BitGoJS/${version}`;
16+
17+
/**
18+
* Perform body parsing here only on routes we want
19+
*/
20+
function parseBody(req: express.Request, res: express.Response, next: express.NextFunction) {
21+
// Set the default Content-Type, in case the client doesn't set it. If
22+
// Content-Type isn't specified, Express silently refuses to parse the
23+
// request body.
24+
req.headers['content-type'] = req.headers['content-type'] || 'application/json';
25+
return express.json({ limit: '20mb' })(req, res, next);
26+
}
27+
28+
/**
29+
* Create the bitgo object in the request
30+
* @param config
31+
*/
32+
function prepareBitGo(config: MasterExpressConfig) {
33+
const { env, customRootUri } = config;
34+
35+
return function prepBitGo(
36+
req: express.Request,
37+
res: express.Response,
38+
next: express.NextFunction,
39+
) {
40+
// Get access token
41+
let accessToken;
42+
if (req.headers.authorization) {
43+
const authSplit = req.headers.authorization.split(' ');
44+
if (authSplit.length === 2 && authSplit[0].toLowerCase() === 'bearer') {
45+
accessToken = authSplit[1];
46+
}
47+
}
48+
const userAgent = req.headers['user-agent']
49+
? BITGOEXPRESS_USER_AGENT + ' ' + req.headers['user-agent']
50+
: BITGOEXPRESS_USER_AGENT;
51+
52+
const bitgoConstructorParams: BitGoOptions = {
53+
env,
54+
customRootURI: customRootUri,
55+
accessToken,
56+
userAgent,
57+
};
58+
59+
(req as BitGoRequest).bitgo = new BitGo(bitgoConstructorParams) as unknown as BitGoBase;
60+
(req as BitGoRequest).config = config;
61+
62+
next();
63+
};
64+
}
65+
66+
/**
67+
* Setup master express specific routes
68+
*/
69+
export function setupRoutes(app: express.Application, cfg: MasterExpressConfig): void {
70+
// Setup common health check routes
71+
setupHealthCheckRoutes(app, 'master express');
72+
73+
// Add enclaved express ping route
74+
app.post('/ping/enclavedExpress', async (req, res) => {
75+
try {
76+
logger.debug('Pinging enclaved express');
77+
78+
let response;
79+
if (cfg.tlsMode === TlsMode.MTLS) {
80+
// Use Master Express's own certificate as client cert when connecting to Enclaved Express
81+
const httpsAgent = new https.Agent({
82+
rejectUnauthorized: !cfg.allowSelfSigned,
83+
ca: cfg.enclavedExpressCert,
84+
// Provide client certificate for mTLS
85+
key: cfg.tlsKey,
86+
cert: cfg.tlsCert,
87+
});
88+
89+
response = await superagent
90+
.post(`${cfg.enclavedExpressUrl}/ping`)
91+
.ca(cfg.enclavedExpressCert)
92+
.agent(httpsAgent)
93+
.send();
94+
} else {
95+
// When TLS is disabled, use plain HTTP without any TLS configuration
96+
response = await superagent.post(`${cfg.enclavedExpressUrl}/ping`).send();
97+
}
98+
99+
res.json({
100+
status: 'Successfully pinged enclaved express',
101+
enclavedResponse: response.body,
102+
});
103+
} catch (error) {
104+
logger.error('Failed to ping enclaved express:', { error });
105+
res.status(500).json({
106+
error: 'Failed to ping enclaved express',
107+
details: error instanceof Error ? error.message : String(error),
108+
});
109+
}
110+
});
111+
112+
// TODO: Add api-ts to these new API routes
113+
app.post(
114+
'/api/:coin/wallet/generate',
115+
parseBody,
116+
prepareBitGo(cfg),
117+
promiseWrapper(handleGenerateWalletOnPrem),
118+
);
119+
120+
// Add a catch-all for unsupported routes
121+
app.use('*', (_req, res) => {
122+
res.status(404).json({
123+
error: 'Route not found or not supported in master express mode',
124+
});
125+
});
126+
127+
logger.debug('Master express routes configured');
128+
}

0 commit comments

Comments
 (0)