diff --git a/packages/server/src/application/Application.ts b/packages/server/src/application/Application.ts deleted file mode 100644 index 43fd9c0..0000000 --- a/packages/server/src/application/Application.ts +++ /dev/null @@ -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(): AsyncRouter { - return new AsyncRouter() - } - - createTransactionalRouter(): AsyncRouter { - return new TransactionalRouter(this.transactionManager) - } -} diff --git a/packages/server/src/application/Bootstrap.ts b/packages/server/src/application/Bootstrap.ts index bd33014..c34830d 100644 --- a/packages/server/src/application/Bootstrap.ts +++ b/packages/server/src/application/Bootstrap.ts @@ -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 diff --git a/packages/server/src/application/IApplicationConfig.ts b/packages/server/src/application/IApplicationConfig.ts index 6f46be4..70fd916 100644 --- a/packages/server/src/application/IApplicationConfig.ts +++ b/packages/server/src/application/IApplicationConfig.ts @@ -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 - readonly framework: IFramework + readonly globalErrorHandler: ErrorRequestHandler + readonly framework: Record } diff --git a/packages/server/src/application/configureApplication.ts b/packages/server/src/application/configureApplication.ts index 7e9d968..0a65711 100644 --- a/packages/server/src/application/configureApplication.ts +++ b/packages/server/src/application/configureApplication.ts @@ -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 = () => new TransactionalRouter(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, + }, }, } } diff --git a/packages/server/src/application/createApplication.ts b/packages/server/src/application/createApplication.ts index e7f0587..a133340 100644 --- a/packages/server/src/application/createApplication.ts +++ b/packages/server/src/application/createApplication.ts @@ -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, diff --git a/packages/server/src/application/index.ts b/packages/server/src/application/index.ts index 611f50d..2d53cf9 100644 --- a/packages/server/src/application/index.ts +++ b/packages/server/src/application/index.ts @@ -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'