import { FastifyInstance , FastifyReply , FastifyRequest } from "fastify" ; import controllers from "../controller" ; import AppError from "../common/AppError" ; import { IocFactory } from "./decorator/IocDecorator" ; import { CONTROLLER_METADATA , PARAM_METADATA , ROUTE_METADATA , ParamType , } from "./decorator/RouterDecorator" ; import WebsocketEmitter from "./WebsocketEmitter" ; function selfish ( target ) { const cache = new WeakMap ( ) ; const handler = { get ( self , key ) { const value = Reflect . get ( self , key ) ; if ( typeof value !== "function" ) { return value ; } if ( ! cache . has ( value ) ) { cache . set ( value , value . bind ( self ) ) ; } return cache . get ( value ) ; } , } ; const proxy = new Proxy ( target , handler ) ; return proxy ; } export default class Router { private server : FastifyInstance ; private wss ? : WebsocketEmitter ; static instance : Router ; static getInstance ( app ) { if ( ! Router . instance ) { Router . instance = new Router ( app ) ; } return Router . instance ; } constructor ( server ) { this . server = server ; controllers . forEach ( ( controller ) => { const instance = IocFactory ( controller ) ; const controllerMetadata = Reflect . getMetadata ( CONTROLLER_METADATA , controller ) ; const proto = Object . getPrototypeOf ( instance ) ; const routeNameArr = Object . getOwnPropertyNames ( proto ) . filter ( ( n ) => n !== "constructor" && typeof proto [ n ] === "function" ) ; routeNameArr . forEach ( ( routeName ) => { const routeMetadata = Reflect . getMetadata ( ROUTE_METADATA , proto [ routeName ] ) ; if ( ! routeMetadata ) return ; const { type , path } = routeMetadata ; const self = selfish ( instance ) ; const urlPath = typeof path === "string" ? controllerMetadata + path : path ; if ( type === "ws" ) { if ( ! this . wss ) { this . wss = new WebsocketEmitter ( ) ; } this . wss . on ( urlPath , self [ routeName ] ) ; return ; } const handler = this . handlerFactory ( self [ routeName ] , Reflect . getMetadata ( PARAM_METADATA , instance , routeName ) ) ; this . server . route ( { method : type . toUpperCase ( ) , url : urlPath , handler , } ) ; console . info ( `Add {${ urlPath }, ${ type . toUpperCase ( ) }} route` . blue ) ; } ) ; } ) ; if ( this . wss ) { this . wss . listen ( { server : server . server } ) ; } } param func param paramList handlerFactory ( func , paramList ) { return async ( req , res ) => { const ctx = { req , res } ; const args = await this . extractParameters ( ctx , paramList ) ; const rel = await func ( ... args ) ; res . send ( rel ) ; } ; } async extractParameters ( ctx , paramArr = [ ] ) { if ( ! paramArr . length ) return [ ctx ] ; const { req } = ctx ; const args = [ ] ; const checkArgs = [ ] ; for ( const param of paramArr ) { const { key , index , type , validator , paramType } = param ; let obj ; switch ( type ) { case "query" : obj = req . query as any ; args [ index ] = key && obj ? obj [ key ] : obj ; break ; case "body" : obj = req . body as any ; args [ index ] = key && obj ? obj [ key ] : obj ; break ; case "headers" : obj = req . headers as any ; args [ index ] = key && obj ? obj [ key ] : obj ; break ; case "uploadFile" : obj = req as any ; args [ index ] = await obj . file ( ) ; break ; default : args [ index ] = undefined ; } if ( validator ) { checkArgs [ index ] = await validator . check ( paramType , args [ index ] ) ; } else { if ( key && ( args [ index ] === undefined || args [ index ] === "" ) ) { AppError . assert ( `${ key }不能为空` ) ; } checkArgs [ index ] = args [ index ] ; } } checkArgs . push ( ctx ) ; return checkArgs ; } }