-
Notifications
You must be signed in to change notification settings - Fork 0
Wp 4622/api ts mbe #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,95 @@ | ||||||||||||||
| import * as t from 'io-ts'; | ||||||||||||||
| import { apiSpec, httpRoute, httpRequest, HttpResponse } from '@api-ts/io-ts-http'; | ||||||||||||||
| import { createRouter, type WrappedRouter } from '@api-ts/typed-express-router'; | ||||||||||||||
| import { Response } from '@api-ts/response'; | ||||||||||||||
| import https from 'https'; | ||||||||||||||
| import superagent from 'superagent'; | ||||||||||||||
| import { MasterExpressConfig, TlsMode } from '../../config'; | ||||||||||||||
| import logger from '../../logger'; | ||||||||||||||
| import { withResponseHandler } from '../../shared/responseHandler'; | ||||||||||||||
|
|
||||||||||||||
| // Response type for /ping/enclavedExpress endpoint | ||||||||||||||
| const PingEnclavedResponse: HttpResponse = { | ||||||||||||||
| 200: t.type({ | ||||||||||||||
| status: t.string, | ||||||||||||||
| // TODO: Move to common definition between enclavedExpress and masterExpress | ||||||||||||||
| enclavedResponse: t.type({ | ||||||||||||||
| message: t.string, | ||||||||||||||
| timestamp: t.string, | ||||||||||||||
| }), | ||||||||||||||
| }), | ||||||||||||||
| 500: t.type({ | ||||||||||||||
| error: t.string, | ||||||||||||||
| details: t.string, | ||||||||||||||
| }), | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| // API Specification | ||||||||||||||
| export const EnclavedExpressApiSpec = apiSpec({ | ||||||||||||||
| 'v1.enclaved.ping': { | ||||||||||||||
| post: httpRoute({ | ||||||||||||||
| method: 'POST', | ||||||||||||||
| path: '/ping/enclavedExpress', | ||||||||||||||
| request: httpRequest({}), | ||||||||||||||
| response: PingEnclavedResponse, | ||||||||||||||
| description: 'Ping the enclaved express server', | ||||||||||||||
| }), | ||||||||||||||
| }, | ||||||||||||||
| }); | ||||||||||||||
|
|
||||||||||||||
| // Create router with handlers | ||||||||||||||
| export function createEnclavedExpressRouter( | ||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is master express API router |
||||||||||||||
| cfg: MasterExpressConfig, | ||||||||||||||
| ): WrappedRouter<typeof EnclavedExpressApiSpec> { | ||||||||||||||
| const router = createRouter(EnclavedExpressApiSpec, { | ||||||||||||||
| onDecodeError: (err, _req, _res) => { | ||||||||||||||
| logger.error('Decode error:', { error: err }); | ||||||||||||||
|
||||||||||||||
| logger.error('Decode error:', { error: err }); | |
| logger.error('Decode error:', { error: err }); | |
| _res.status(400).json({ error: 'Bad Request', details: err.message }); |
Copilot
AI
Jun 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onEncodeError handler logs the error but does not send a response or call next(). Consider returning a 500 Internal Server Error response.
| logger.error('Encode error:', { error: err }); | |
| logger.error('Encode error:', { error: err }); | |
| _res.status(500).send({ | |
| error: 'Internal Server Error', | |
| details: err instanceof Error ? err.message : String(err), | |
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| import * as t from 'io-ts'; | ||
| import { apiSpec, httpRoute, httpRequest, HttpResponse } from '@api-ts/io-ts-http'; | ||
| import { createRouter, type WrappedRouter } from '@api-ts/typed-express-router'; | ||
| import { Response } from '@api-ts/response'; | ||
| import pjson from '../../../package.json'; | ||
| import { withResponseHandler } from '../../shared/responseHandler'; | ||
|
|
||
| // Response type for /ping endpoint | ||
| const PingResponse: HttpResponse = { | ||
| 200: t.type({ | ||
| status: t.string, | ||
| timestamp: t.string, | ||
| }), | ||
| }; | ||
|
|
||
| // Response type for /version endpoint | ||
| const VersionResponse: HttpResponse = { | ||
| 200: t.type({ | ||
| version: t.string, | ||
| name: t.string, | ||
| }), | ||
| }; | ||
|
|
||
| // API Specification | ||
| export const HealthCheckApiSpec = apiSpec({ | ||
| 'v1.health.ping': { | ||
| post: httpRoute({ | ||
| method: 'POST', | ||
| path: '/ping', | ||
| request: httpRequest({}), | ||
| response: PingResponse, | ||
| description: 'Health check endpoint that returns server status', | ||
| }), | ||
| }, | ||
| 'v1.health.version': { | ||
| get: httpRoute({ | ||
| method: 'GET', | ||
| path: '/version', | ||
| request: httpRequest({}), | ||
| response: VersionResponse, | ||
| description: 'Returns the current version of the server', | ||
| }), | ||
| }, | ||
| }); | ||
|
|
||
| // Create router with handlers | ||
| export function createHealthCheckRouter( | ||
| serverType: string, | ||
| ): WrappedRouter<typeof HealthCheckApiSpec> { | ||
| const router = createRouter(HealthCheckApiSpec, { | ||
| onDecodeError: (_err, _req, _res) => { | ||
| console.log(_err); | ||
|
||
| }, | ||
| onEncodeError: (_err, _req, _res) => { | ||
| console.log(_err); | ||
|
||
| }, | ||
| }); | ||
|
|
||
| // Ping endpoint handler | ||
| router.post('v1.health.ping', [ | ||
| withResponseHandler(() => | ||
| Response.ok({ | ||
| status: `${serverType} server is ok!`, | ||
| timestamp: new Date().toISOString(), | ||
| }), | ||
| ), | ||
| ]); | ||
|
|
||
| // Version endpoint handler | ||
| router.get('v1.health.version', [ | ||
| withResponseHandler(() => | ||
| Response.ok({ | ||
| version: pjson.version, | ||
| name: pjson.name, | ||
| }), | ||
| ), | ||
| ]); | ||
|
|
||
| return router; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| import * as t from 'io-ts'; | ||
| import { apiSpec, httpRoute, httpRequest, HttpResponse } from '@api-ts/io-ts-http'; | ||
| import { createRouter, type WrappedRouter } from '@api-ts/typed-express-router'; | ||
| import { Response } from '@api-ts/response'; | ||
| import express, { Request } from 'express'; | ||
| import { BitGo } from 'bitgo'; | ||
| import { BitGoRequest, isBitGoRequest } from '../../types/request'; | ||
| import { MasterExpressConfig } from '../../config'; | ||
| import { handleGenerateWalletOnPrem } from '../generateWallet'; | ||
| import { withResponseHandler } from '../../shared/responseHandler'; | ||
|
|
||
| // Middleware functions | ||
| export function parseBody(req: express.Request, res: express.Response, next: express.NextFunction) { | ||
| req.headers['content-type'] = req.headers['content-type'] || 'application/json'; | ||
| return express.json({ limit: '20mb' })(req, res, next); | ||
| } | ||
|
|
||
| export function prepareBitGo(config: MasterExpressConfig) { | ||
| const { env, customRootUri } = config; | ||
| const BITGOEXPRESS_USER_AGENT = `BitGoExpress/${process.env.npm_package_version}`; | ||
|
|
||
| return function prepBitGo( | ||
| req: express.Request, | ||
| res: express.Response, | ||
| next: express.NextFunction, | ||
| ) { | ||
| let accessToken; | ||
| if (req.headers.authorization) { | ||
| const authSplit = req.headers.authorization.split(' '); | ||
| if (authSplit.length === 2 && authSplit[0].toLowerCase() === 'bearer') { | ||
| accessToken = authSplit[1]; | ||
| } | ||
| } | ||
| const userAgent = req.headers['user-agent'] | ||
| ? BITGOEXPRESS_USER_AGENT + ' ' + req.headers['user-agent'] | ||
| : BITGOEXPRESS_USER_AGENT; | ||
|
|
||
| const bitgoConstructorParams = { | ||
| env, | ||
| customRootURI: customRootUri, | ||
| accessToken, | ||
| userAgent, | ||
| }; | ||
|
|
||
| (req as BitGoRequest).bitgo = new BitGo(bitgoConstructorParams); | ||
| (req as BitGoRequest).config = config; | ||
|
|
||
| next(); | ||
| }; | ||
| } | ||
|
|
||
| // Response type for /generate endpoint | ||
| const GenerateWalletResponse: HttpResponse = { | ||
| // TODO: Get type from public types repo | ||
| 200: t.any, | ||
| 500: t.type({ | ||
| error: t.string, | ||
| details: t.string, | ||
| }), | ||
| }; | ||
|
|
||
| // Request type for /generate endpoint | ||
| const GenerateWalletRequest = { | ||
| label: t.string, | ||
| multisigType: t.union([t.undefined, t.literal('onchain'), t.literal('tss')]), | ||
| enterprise: t.string, | ||
| disableTransactionNotifications: t.union([t.undefined, t.boolean]), | ||
| isDistributedCustody: t.union([t.undefined, t.boolean]), | ||
| }; | ||
|
|
||
| // API Specification | ||
| export const MasterApiSpec = apiSpec({ | ||
| 'v1.wallet.generate': { | ||
| post: httpRoute({ | ||
| method: 'POST', | ||
| path: '/{coin}/wallet/generate', | ||
| request: httpRequest({ | ||
| params: { | ||
| coin: t.string, | ||
| }, | ||
| body: GenerateWalletRequest, | ||
| }), | ||
| response: GenerateWalletResponse, | ||
| description: 'Generate a new wallet', | ||
| }), | ||
| }, | ||
| }); | ||
|
|
||
| // Create router with handlers | ||
| export function createMasterApiRouter( | ||
| cfg: MasterExpressConfig, | ||
| ): WrappedRouter<typeof MasterApiSpec> { | ||
| const router = createRouter(MasterApiSpec); | ||
|
|
||
| // Add middleware to all routes | ||
| router.use(parseBody); | ||
| router.use(prepareBitGo(cfg)); | ||
|
|
||
| // Generate wallet endpoint handler | ||
| router.post('v1.wallet.generate', [ | ||
| withResponseHandler(async (req: BitGoRequest | Request) => { | ||
| if (!isBitGoRequest(req)) { | ||
| throw new Error('Invalid request type'); | ||
| } | ||
| const result = await handleGenerateWalletOnPrem(req); | ||
| return Response.ok(result); | ||
| }), | ||
| ]); | ||
|
|
||
| return router; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.