Skip to content

Commit

Permalink
Merge branch 'main' into feat/middleware-error-wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
Evert De Spiegeleer committed Jan 12, 2024
2 parents 71634d6 + 2abbf65 commit 561f81f
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 187 deletions.
4 changes: 3 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
{
"root": true,
"env": {
"browser": true,
"es2021": true
},
"extends": "standard-with-typescript",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
"sourceType": "module",
"project": ["./tsconfig.json"]
},
"rules": {
"@typescript-eslint/explicit-function-return-type": "off"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"build": "tsc -p tsconfig.build.json",
"test": "node --test --test-reporter spec --test-reporter-destination stdout --test-reporter junit --test-reporter-destination=./test-report.xml --loader ts-node/esm ./src/**/*.test.ts",
"prepare": "husky install",
"lint": "eslint ./src/**/*.ts",
"lint": "yarn eslint 'src/**/*.ts'",
"lint:fix": "yarn run lint --fix"
},
"dependencies": {
Expand Down
144 changes: 72 additions & 72 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,133 +1,133 @@
/* eslint-disable @typescript-eslint/ban-types */
import express, { Application } from 'express';
import { Server as NodeHttpServer, createServer } from 'node:http';
import cors from 'cors';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import { Controller, bindControllerToApp } from './util/controller.js';
import { Middleware, MiddlewareTypes } from './util/middleware.js';
import { makeErrorHandlerMiddleware } from './middleware/errorHandler.js';
import { metricMiddleware } from './middleware/metrics.js';
import { OASInfo, Oas } from './oas.js';
import { BadRequestError } from './util/errors.js';
import { ILogger, defaultLogger } from './util/logger.js';
import express, { type Application } from 'express'
import { type Server as NodeHttpServer, createServer } from 'node:http'
import cors from 'cors'
import bodyParser from 'body-parser'
import cookieParser from 'cookie-parser'
import { type Controller, bindControllerToApp } from './util/controller.js'
import { type Middleware, MiddlewareTypes } from './util/middleware.js'
import { makeErrorHandlerMiddleware } from './middleware/errorHandler.js'
import { metricMiddleware } from './middleware/metrics.js'
import { type OASInfo, Oas } from './oas.js'
import { BadRequestError } from './util/errors.js'
import { type ILogger, defaultLogger } from './util/logger.js'

interface RoutingOptions {
controllers?: Controller[];
middlewares?: Middleware[];
controllers?: Controller[]
middlewares?: Middleware[]
}

interface IHTTPOptions {
port?: number;
allowedOrigins?: string[];
bypassAllowedOrigins?: boolean;
trustProxy?: boolean;
oasInfo?: OASInfo;
logger?: ILogger;
port?: number
allowedOrigins?: string[]
bypassAllowedOrigins?: boolean
trustProxy?: boolean
oasInfo?: OASInfo
logger?: ILogger
}

export let oasInstance: Oas;
export let oasInstance: Oas

export class Server {
private app: Application;
private httpServer: NodeHttpServer;
private logger: ILogger;
private loggerInstance;

constructor(
private options: RoutingOptions = {},
private httpOptions: IHTTPOptions = {},
private readonly app: Application
private httpServer: NodeHttpServer
private readonly logger: ILogger
private readonly loggerInstance

constructor (
private readonly options: RoutingOptions = {},
private readonly httpOptions: IHTTPOptions = {}
) {
this.logger = httpOptions.logger ?? defaultLogger;
this.loggerInstance = this.logger('zhttp');
this.logger = httpOptions.logger ?? defaultLogger
this.loggerInstance = this.logger('zhttp')

oasInstance = new Oas(httpOptions.oasInfo);
oasInstance = new Oas(httpOptions.oasInfo)

// Apply defaults
this.httpOptions = {
trustProxy: true,
...this.httpOptions,
};
...this.httpOptions
}

this.app = express();
this.httpServer = createServer(this.app);
this.app = express()
this.httpServer = createServer(this.app)

this.app.set('trust proxy', this.httpOptions.trustProxy);
this.app.use(bodyParser.json());
this.app.set('trust proxy', this.httpOptions.trustProxy)
this.app.use(bodyParser.json())
this.app.use(
cors({
credentials: true,
origin: (origin: string | undefined, callback: CallableFunction) => {
if (!origin || origin === 'null') return callback(null, true);
const allowedOrigins = this.httpOptions.allowedOrigins ?? [];
if (origin == null || origin === 'null') return callback(null, true)
const allowedOrigins = this.httpOptions.allowedOrigins ?? []
if (
!origin ||
origin == null ||
allowedOrigins.includes(origin) ||
this.httpOptions.bypassAllowedOrigins
this.httpOptions.bypassAllowedOrigins === true
) {
callback(null, true);
callback(null, true)
} else {
this.loggerInstance.warn(`Origin ${origin} not allowed`);
callback(new BadRequestError('Not allowed by CORS'));
this.loggerInstance.warn(`Origin ${origin} not allowed`)
callback(new BadRequestError('Not allowed by CORS'))
}
},
}),
);
this.app.use(cookieParser());
}
})
)
this.app.use(cookieParser())

// Set default middlewares
this.options.middlewares = [
...(this.options.middlewares ?? []),
metricMiddleware,
makeErrorHandlerMiddleware(this.logger),
];
makeErrorHandlerMiddleware(this.logger)
]

// run all global before middlewares
this.options.middlewares
?.filter((m) => m.type === MiddlewareTypes.BEFORE)
.forEach((middleware) => {
this.app.use(middleware.handler);
});
this.app.use(middleware.handler)
})

// Bind all controllers
this.options.controllers?.forEach((controller) => {
// Bind all controllers to the express app
bindControllerToApp(controller, this.app);
oasInstance.addController(controller);
});
bindControllerToApp(controller, this.app)
oasInstance.addController(controller)
})

// run all global after middlewares
this.options.middlewares
?.filter((m) => m.type === MiddlewareTypes.AFTER)
.forEach((middleware) => {
this.app.use(middleware.handler);
});
this.app.use(middleware.handler)
})
}

get expressInstance() {
return this.app;
get expressInstance () {
return this.app
}

get oasInstance() {
return oasInstance;
get oasInstance () {
return oasInstance
}

get server() {
return this.httpServer;
get server () {
return this.httpServer
}

async start() {
async start () {
this.httpServer = this.httpServer.listen(this.httpOptions.port, () => {
this.loggerInstance.info(
`HTTP server listening on port ${this.httpOptions.port}`,
);
});
`HTTP server listening on port ${this.httpOptions.port}`
)
})
}

async stop() {
if (this.httpServer) {
this.httpServer.close();
this.loggerInstance.info('HTTP server stopped');
async stop () {
if (this.httpServer != null) {
this.httpServer.close()
this.loggerInstance.info('HTTP server stopped')
}
}
}
14 changes: 7 additions & 7 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { Server } from './app.js';
export { Server } from './app.js'
export {
endpoint,
type Method,
Expand All @@ -7,15 +7,15 @@ export {
post,
del,
type InputValidationSchema,
type ResponseValidationSchema,
} from './util/endpoint.js';
type ResponseValidationSchema
} from './util/endpoint.js'

export { openapiController } from './controllers/openapi.js'

export { controller } from './util/controller.js';
export { controller } from './util/controller.js'

export * from './util/middleware.js';
export * from './util/middleware.js'
export * as errors from './util/errors.js'

export { apiResponse, zApiOutput } from './util/apiResponse.js';
export { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
export { apiResponse, zApiOutput } from './util/apiResponse.js'
export { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'
Loading

0 comments on commit 561f81f

Please sign in to comment.