You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
4.3 KiB
127 lines
4.3 KiB
import {Singleton, Inject} from '../di'
|
|
import {UniversalPath, Collection, Pipe, universalPath} from '../util'
|
|
import {Unit} from '../lifecycle/Unit'
|
|
import {Logging} from './Logging'
|
|
import {Route} from '../http/routing/Route'
|
|
import {HTTPMethod} from '../http/lifecycle/Request'
|
|
import {ViewEngineFactory} from '../views/ViewEngineFactory'
|
|
import {ViewEngine} from '../views/ViewEngine'
|
|
import {lib} from '../lib'
|
|
import {Config} from './Config'
|
|
|
|
/**
|
|
* Application unit that loads the various route files from `app/http/routes` and pre-compiles the route handlers.
|
|
*/
|
|
@Singleton()
|
|
export class Routing extends Unit {
|
|
@Inject()
|
|
protected readonly logging!: Logging
|
|
|
|
@Inject()
|
|
protected readonly config!: Config
|
|
|
|
protected compiledRoutes: Collection<Route> = new Collection<Route>()
|
|
|
|
public async up(): Promise<void> {
|
|
this.app().registerFactory(new ViewEngineFactory())
|
|
const engine = <ViewEngine> this.make(ViewEngine)
|
|
this.logging.verbose('Registering @extollo view engine namespace.')
|
|
engine.registerNamespace('extollo', lib().concat('resources', 'views'))
|
|
|
|
for await ( const entry of this.path.walk() ) {
|
|
if ( !entry.endsWith('.routes.js') ) {
|
|
this.logging.debug(`Skipping routes file with invalid suffix: ${entry}`)
|
|
continue
|
|
}
|
|
|
|
this.logging.info(`Importing routes from: ${entry}`)
|
|
await import(entry)
|
|
}
|
|
|
|
this.logging.info('Compiling routes...')
|
|
this.compiledRoutes = new Collection<Route>(await Route.compile())
|
|
|
|
this.logging.info(`Compiled ${this.compiledRoutes.length} route(s).`)
|
|
this.compiledRoutes.each(route => {
|
|
this.logging.verbose(`${route}`)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Given an HTTPMethod and route path, return the Route instance that matches them,
|
|
* if one exists.
|
|
* @param method
|
|
* @param path
|
|
*/
|
|
public match(method: HTTPMethod, path: string): Route | undefined {
|
|
return this.compiledRoutes.firstWhere(route => {
|
|
return route.match(method, path)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Get the universal path to the root directory of the route definitions.
|
|
*/
|
|
public get path(): UniversalPath {
|
|
return this.app().appPath('http', 'routes')
|
|
}
|
|
|
|
/**
|
|
* Get the collection of compiled routes.
|
|
*/
|
|
public getCompiled(): Collection<Route> {
|
|
return this.compiledRoutes
|
|
}
|
|
|
|
/**
|
|
* Resolve a UniversalPath to a file served as an asset.
|
|
* @example
|
|
* ```ts
|
|
* this.getAssetPath('images', '123.jpg').toRemote // => http://localhost:8000/assets/images/123.jpg
|
|
* ```
|
|
* @param parts
|
|
*/
|
|
public getAssetPath(...parts: string[]): UniversalPath {
|
|
return this.getAssetBase().concat(...parts)
|
|
}
|
|
|
|
public getAssetBase(): UniversalPath {
|
|
return this.getAppUrl().concat(this.config.get('server.builtIns.assets.prefix', '/assets'))
|
|
}
|
|
|
|
public getVendorPath(namespace: string, ...parts: string[]): UniversalPath {
|
|
return this.getVendorBase().concat(encodeURIComponent(namespace), ...parts)
|
|
}
|
|
|
|
public getVendorBase(): UniversalPath {
|
|
return this.getAppUrl().concat(this.config.get('server.builtIns.vendor.prefix', '/vendor'))
|
|
}
|
|
|
|
public getAppUrl(): UniversalPath {
|
|
const rawHost = String(this.config.get('server.url', 'http://localhost')).toLowerCase()
|
|
const isSSL = rawHost.startsWith('https://')
|
|
const port = this.config.get('server.port', 8000)
|
|
|
|
return Pipe.wrap<string>(rawHost)
|
|
.unless(
|
|
host => host.startsWith('http://') || host.startsWith('https'),
|
|
host => `http://${host}`,
|
|
)
|
|
.when(
|
|
host => {
|
|
const hasPort = host.split(':').length > 2
|
|
const defaultRaw = !isSSL && port === 80
|
|
const defaultSSL = isSSL && port === 443
|
|
return !hasPort && !defaultRaw && !defaultSSL
|
|
},
|
|
host => {
|
|
const parts = host.split('/')
|
|
parts[2] += `:${port}`
|
|
return parts.join('/')
|
|
},
|
|
)
|
|
.tap<UniversalPath>(host => universalPath(host))
|
|
.get()
|
|
}
|
|
}
|