import * as Koa from 'koa' ; import * as json from 'koa-json' ; import * as compress from 'koa-compress' ; import * as bodyParser from 'koa-bodyparser' ; import { Server } from 'http' ; import { Kysely , PostgresDialect } from 'kysely' ; import { Config } from './config' ; import { Context , ContextExtension } from './context' ; import { Database } from './database' ; import { Router } from './router' ; import { userController } from './user/user.controller' ; import { ControllerError } from './util/errors' ; import { isObject } from './util/object' ; export class App { #config : Config ; #koa : Koa < any , ContextExtension > ; #router : Router ; #db : Kysely < Database > ; #server ? : Server ; constructor ( config ) { this . #config = config ; this . #koa = new Koa ( ) ; this . #router = new Router ( ) ; this . #db = new Kysely < Database > ( { dialect : new PostgresDialect ( this . #config . database ) , } ) ; this . #koa . use ( compress ( ) ) ; this . #koa . use ( bodyParser ( ) ) ; this . #koa . use ( json ( ) ) ; this . #koa . use ( this . errorHandler ) ; this . #koa . use ( this . decorateContext ) ; userController ( this . #router ) ; this . #koa . use ( this . #router . routes ( ) ) ; this . #koa . use ( this . #router . allowedMethods ( ) ) ; } get db ( ) : Kysely < Database > { return this . #db ; } async start ( ) { return new Promise ( ( resolve ) => { this . #server = this . #koa . listen ( this . #config . port , resolve ) ; } ) ; } async stop ( ) { await new Promise < void > ( ( resolve , reject ) => { this . #server ?. close ( ( err ) => { if ( err ) { reject ( err ) ; } else { resolve ( ) ; } } ) ; } ) ; await this . #db ?. destroy ( ) ; } private readonly errorHandler = async ( ctx , next ) : Promise < void > => { try { await next ( ) ; } catch ( error ) { if ( error instanceof ControllerError ) { respondError ( ctx , error ) ; } else { respondError ( ctx , createUnknownError ( error ) ) ; } } } ; private readonly decorateContext = async ( ctx , next ) : Promise < void > => { ctx . db = this . #db ! ; ctx . throwError = ( status , code , message , data ) : never => { throw new ControllerError ( status , code , message , data ) ; } ; await next ( ) ; } ; } function respondError ( ctx , error ) { ctx . status = error . status ; ctx . body = error . toJSON ( ) ; } function createUnknownError ( error ) { return new ControllerError ( 500 , 'UnknownError' , ( isObject ( error ) ? error . message : undefined ) ?? 'unknown error' ) ; }