Replace Application class w/ configureApplication
This commit is contained in:
parent
64f5f2f642
commit
8ca6107ce6
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
}
|
||||
|
||||
@ -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,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user