Replace Application class w/ configureApplication

This commit is contained in:
Jerko Steiner 2019-08-31 14:14:11 +07:00
parent 64f5f2f642
commit 8ca6107ce6
6 changed files with 82 additions and 243 deletions

View File

@ -1,155 +0,0 @@
import * as middleware from '../middleware'
import * as routes from '../routes'
import * as rpc from '../rpc'
import * as services from '../services'
import * as team from '../team'
import * as user from '../user'
import cookieParser from 'cookie-parser'
import express from 'express'
import {keys} from 'ts-transformer-keys'
import {AsyncRouter, TransactionalRouter} from '../router'
import {IApplication} from './IApplication'
import {IConfig} from './IConfig'
import {IDatabase} from '../database/IDatabase'
import {ILogger} from '../logger/ILogger'
import {IRoutes, IContext} from '@rondo.dev/common'
import {IServices} from './IServices'
import {ITransactionManager} from '../database/ITransactionManager'
import {loggerFactory} from '../logger'
import {ILoggerFactory} from '@rondo.dev/logger'
import {json} from 'body-parser'
import {jsonrpc} from '@rondo.dev/jsonrpc'
export class Application implements IApplication {
readonly transactionManager: ITransactionManager
readonly server: express.Application
readonly services: IServices
readonly authenticator: middleware.Authenticator
readonly loggerFactory: ILoggerFactory = loggerFactory
constructor(readonly config: IConfig, readonly database: IDatabase) {
this.transactionManager = database.transactionManager
this.services = this.configureServices()
this.authenticator = new middleware.Authenticator(this.services.userService)
this.server = this.createServer()
}
protected configureServices(): IServices {
return {
userService: new services.UserService(this.database),
teamService: new team.TeamService(this.database),
userPermissions: new user.UserPermissions(this.database),
}
}
protected getApiLogger(): ILogger {
return this.loggerFactory.getLogger('api')
}
protected createServer() {
const server = express()
server.set('trust proxy', 1)
server.disable('x-powered-by')
const router = express.Router()
this.configureMiddleware(router)
this.configureRouter(router)
this.configureRPC(router)
this.configureApiErrorHandling(router)
this.configureFrontend(router)
server.use(this.config.app.context, router)
this.configureGlobalErrorHandling(server)
return server
}
protected configureMiddleware(router: express.Router) {
const {transactionManager} = this
const apiLogger = this.getApiLogger()
router.use(new middleware.SessionMiddleware({
transactionManager,
baseUrl: this.config.app.baseUrl,
sessionName: this.config.app.session.name,
sessionSecret: this.config.app.session.secret,
}).handle)
router.use(new middleware.RequestLogger(apiLogger).handle)
router.use(json())
router.use(cookieParser(this.config.app.session.secret))
router.use(new middleware.CSRFMiddleware({
baseUrl: this.config.app.baseUrl,
cookieName: this.config.app.session.name + '_csrf',
}).handle)
router.use(new middleware.Transaction(this.database.namespace).handle)
router.use(this.authenticator.handle)
}
protected configureRouter(router: express.Router) {
// TODO use /api for LoginRoutes
router.use('/app', routes.application)
router.use('/api', new routes.LoginRoutes(
this.services.userService,
this.authenticator,
this.createTransactionalRouter(),
).handle)
router.use('/api', new routes.UserRoutes(
this.services.userService,
this.createTransactionalRouter(),
).handle)
router.use('/api', new team.TeamRoutes(
this.services.teamService,
this.services.userPermissions,
this.createTransactionalRouter(),
).handle)
}
protected getContext(req: express.Request): IContext {
return {user: req.user}
}
protected jsonrpc() {
return jsonrpc(
req => this.getContext(req),
this.getApiLogger(),
(path, service, callback) => this
.database
.transactionManager
.doInNewTransaction(() => callback()),
)
}
protected configureRPC(router: express.Router) {
// Override this method
}
protected configureApiErrorHandling(router: express.Router) {
const apiLogger = this.getApiLogger()
router.use('/api', new middleware.ErrorApiHandler(apiLogger).handle)
}
protected configureFrontend(router: express.Router) {
// Override this method
}
protected configureGlobalErrorHandling(server: express.Application) {
const apiLogger = this.getApiLogger()
server.use(new middleware.ErrorPageHandler(apiLogger).handle)
}
createAsyncRouter<T extends IRoutes>(): AsyncRouter<T> {
return new AsyncRouter<T>()
}
createTransactionalRouter<T extends IRoutes>(): AsyncRouter<T> {
return new TransactionalRouter<T>(this.transactionManager)
}
}

View File

@ -1,16 +1,15 @@
import assert from 'assert'
import {AddressInfo} from 'net'
import {Application} from './Application'
import {Database} from '../database/Database'
import {IApplication} from './IApplication'
import {IBootstrap} from './IBootstrap'
import {IConfig} from './IConfig'
import {IDatabase} from '../database/IDatabase'
import {Server} from 'http'
import {SqlLogger, loggerFactory} from '../logger'
import {createNamespace, Namespace} from 'cls-hooked'
import { createNamespace, Namespace } from 'cls-hooked'
import { Server } from 'http'
import { AddressInfo } from 'net'
import { Database } from '../database/Database'
import { IDatabase } from '../database/IDatabase'
import { loggerFactory, SqlLogger } from '../logger'
import { configureApplication } from './configureApplication'
import { createApplication } from './createApplication'
import { IApplication } from './IApplication'
import { IBootstrap } from './IBootstrap'
import { IConfig } from './IConfig'
export class Bootstrap implements IBootstrap {
protected server?: Server

View File

@ -4,21 +4,10 @@ import { ILogger } from '@rondo.dev/logger'
import { IServices } from './IServices'
import { RequestHandlerParams, ErrorRequestHandler } from 'express-serve-static-core'
export interface IFramework {
readonly middleware: RequestHandlerParams[]
// TODO remove app, i believe this is used in tests
readonly app: RequestHandlerParams[]
readonly api: RequestHandlerParams[]
readonly apiError: ErrorRequestHandler
readonly frontend: RequestHandlerParams[]
readonly error: ErrorRequestHandler
}
export interface IFrameworkPaths {
readonly middleware: string
readonly app: string
readonly api: string
readonly frontend: string
export interface IApplicationMiddleware {
path: string
handle: RequestHandlerParams[]
error?: ErrorRequestHandler
}
export interface IApplicationConfig {
@ -26,6 +15,6 @@ export interface IApplicationConfig {
readonly database: IDatabase
readonly logger: ILogger
readonly services: IServices
readonly paths?: Partial<IFrameworkPaths>
readonly framework: IFramework
readonly globalErrorHandler: ErrorRequestHandler
readonly framework: Record<string, IApplicationMiddleware>
}

View File

@ -14,10 +14,14 @@ import { TransactionalRouter } from '../router'
import { IRoutes, IContext } from '@rondo.dev/common'
import { Express } from 'express-serve-static-core'
export function configureApplication(
export type AppConfigurator<
T extends IApplicationConfig = IApplicationConfig
> = (
config: IConfig,
database: IDatabase,
): IApplicationConfig {
) => T
export const configureApplication: AppConfigurator = (config, database) => {
const logger = loggerFactory.getLogger('api')
@ -33,51 +37,59 @@ export function configureApplication(
const createTransactionalRouter = <T extends IRoutes>() =>
new TransactionalRouter<T>(transactionManager)
const getContext = (req: Express.Request): IContext => ({user: req.user})
const globalErrorHandler = new Middleware.ErrorPageHandler(logger).handle
return {
config,
database,
logger,
services,
globalErrorHandler,
framework: {
middleware: [
new Middleware.SessionMiddleware({
transactionManager,
baseUrl: config.app.baseUrl,
sessionName: config.app.session.name,
sessionSecret: config.app.session.secret,
}).handle,
new Middleware.RequestLogger(logger).handle,
json(),
cookieParser(config.app.session.secret),
new Middleware.CSRFMiddleware({
baseUrl: config.app.baseUrl,
cookieName: config.app.session.name + '_csrf',
}).handle,
new Middleware.Transaction(database.namespace).handle,
authenticator.handle,
],
app: [routes.application],
api: [
new routes.LoginRoutes(
services.userService,
authenticator,
createTransactionalRouter(),
).handle,
new routes.UserRoutes(
services.userService,
createTransactionalRouter(),
).handle,
new Team.TeamRoutes(
services.teamService,
services.userPermissions,
createTransactionalRouter(),
).handle,
],
apiError: new Middleware.ErrorApiHandler(logger).handle,
frontend: [],
error: new Middleware.ErrorPageHandler(logger).handle,
middleware: {
path: '/',
handle: [
new Middleware.SessionMiddleware({
transactionManager,
baseUrl: config.app.baseUrl,
sessionName: config.app.session.name,
sessionSecret: config.app.session.secret,
}).handle,
new Middleware.RequestLogger(logger).handle,
json(),
cookieParser(config.app.session.secret),
new Middleware.CSRFMiddleware({
baseUrl: config.app.baseUrl,
cookieName: config.app.session.name + '_csrf',
}).handle,
new Middleware.Transaction(database.namespace).handle,
authenticator.handle,
],
},
app: {
path: '/app',
handle: [routes.application],
},
api: {
path: '/api',
handle: [
new routes.LoginRoutes(
services.userService,
authenticator,
createTransactionalRouter(),
).handle,
new routes.UserRoutes(
services.userService,
createTransactionalRouter(),
).handle,
new Team.TeamRoutes(
services.teamService,
services.userPermissions,
createTransactionalRouter(),
).handle,
],
error: new Middleware.ErrorApiHandler(logger).handle,
},
},
}
}

View File

@ -1,37 +1,28 @@
import { IApplicationConfig, IFrameworkPaths } from './IApplicationConfig'
import { IApplicationConfig } from './IApplicationConfig'
import { IApplication } from './IApplication'
import express from 'express'
export const defaultPaths: IFrameworkPaths = {
middleware: '/',
app: '/app',
api: '/api',
frontend: '/',
}
export function createApplication(appConfig: IApplicationConfig): IApplication {
const {config, database, framework} = appConfig
const server = express()
const paths = {
...defaultPaths,
...(appConfig.paths || {}),
}
server.set('trust proxy', 1)
server.disable('x-powered-by')
const router = express.Router()
router.use(paths.middleware, ...framework.middleware)
router.use(paths.app, ...framework.app)
router.use(paths.api, ...framework.api)
router.use(paths.api, framework.apiError)
if (framework.frontend.length) {
router.use(paths.frontend, ...framework.frontend)
}
Object.keys(framework)
.forEach(name => {
const {path, handle, error} = framework[name]
router.use(path, ...handle)
if (error) {
router.use(path, error)
}
})
server.use(config.app.context, router)
server.use(framework.error)
server.use(appConfig.globalErrorHandler)
return {
server,
database,

View File

@ -1,5 +1,8 @@
export * from './Application'
export * from './Bootstrap'
export * from './IApplication'
export * from './IApplicationConfig'
export * from './IConfig'
export * from './IServices'
export * from './configureApplication'
export * from './createApplication'