Start routing; rehydratable interface; add verbose logging
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import LifecycleUnit from '../lifecycle/Unit.ts'
|
||||
import {fs, path} from '../external/std.ts'
|
||||
import {Canon} from './Canon.ts'
|
||||
import {Logging} from '../service/logging/Logging.ts'
|
||||
|
||||
export interface CanonicalDefinition {
|
||||
canonical_name: string,
|
||||
@@ -14,6 +15,10 @@ export class Canonical<T> extends LifecycleUnit {
|
||||
protected canonical_item: string = ''
|
||||
protected _items: { [key: string]: T } = {}
|
||||
|
||||
public all(): string[] {
|
||||
return Object.keys(this._items)
|
||||
}
|
||||
|
||||
public get path(): string {
|
||||
return path.resolve(this.base_path)
|
||||
}
|
||||
@@ -23,9 +28,14 @@ export class Canonical<T> extends LifecycleUnit {
|
||||
}
|
||||
|
||||
public async up() {
|
||||
const logger = this.make(Logging)
|
||||
for await ( const entry of fs.walk(this.path) ) {
|
||||
if ( !entry.isFile || !entry.path.endsWith(this.suffix) ) continue
|
||||
if ( !entry.isFile || !entry.path.endsWith(this.suffix) ) {
|
||||
if ( entry.isFile ) logger.debug(`Skipping file in canonical path with invalid suffix: ${entry.path}`)
|
||||
continue
|
||||
}
|
||||
const def = await this._get_canonical_definition(entry.path)
|
||||
logger.verbose(`Registering canonical ${this.canonical_item} "${def.canonical_name}" from ${entry.path}.`)
|
||||
this._items[def.canonical_name] = await this.init_canonical_item(def)
|
||||
}
|
||||
this.make(Canon).register_canonical(this)
|
||||
|
||||
@@ -17,6 +17,7 @@ import ModelSessionManagerFactory from '../http/session/ModelSessionManagerFacto
|
||||
import SessionInterface from '../http/session/SessionInterface.ts'
|
||||
import InjectSession from '../http/kernel/module/InjectSession.ts'
|
||||
import PersistSession from '../http/kernel/module/PersistSession.ts'
|
||||
import MountActivatedRoute from '../http/kernel/module/MountActivatedRoute.ts'
|
||||
|
||||
@Unit()
|
||||
export default class HttpKernel extends LifecycleUnit {
|
||||
@@ -32,15 +33,7 @@ export default class HttpKernel extends LifecycleUnit {
|
||||
|
||||
public async up() {
|
||||
this.determine_session_provider()
|
||||
|
||||
PrepareRequest.register(this.kernel)
|
||||
SetSessionCookie.register(this.kernel)
|
||||
InjectSession.register(this.kernel)
|
||||
PersistSession.register(this.kernel)
|
||||
|
||||
if ( this.config.get('server.powered_by.enable') ) {
|
||||
SetDatonHeaders.register(this.kernel)
|
||||
}
|
||||
this.register_modules()
|
||||
}
|
||||
|
||||
protected determine_session_provider() {
|
||||
@@ -68,4 +61,16 @@ export default class HttpKernel extends LifecycleUnit {
|
||||
this.injector.register_factory(new ModelSessionManagerFactory(ModelClass))
|
||||
}
|
||||
}
|
||||
|
||||
protected register_modules() {
|
||||
PrepareRequest.register(this.kernel)
|
||||
SetSessionCookie.register(this.kernel)
|
||||
InjectSession.register(this.kernel)
|
||||
PersistSession.register(this.kernel)
|
||||
MountActivatedRoute.register(this.kernel)
|
||||
|
||||
if ( this.config.get('server.powered_by.enable') ) {
|
||||
SetDatonHeaders.register(this.kernel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,11 @@ export default class HttpServer extends LifecycleUnit {
|
||||
|
||||
public async up() {
|
||||
this._server = serve({ port: 8000 })
|
||||
|
||||
this.logger.success(`HTTP/S server listening on port 8000!`)
|
||||
|
||||
for await ( const native_request of this._server ) {
|
||||
let req: Request = this.make(Request, native_request)
|
||||
req = await this.kernel.handle(req)
|
||||
|
||||
req.response.body = req.session.get_key()
|
||||
req.response.send()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {Canonical, CanonicalDefinition} from './Canonical.ts'
|
||||
import {isRouterDefinition, RouterDefinition} from '../http/type/RouterDefinition.ts'
|
||||
import {Unit} from '../lifecycle/decorators.ts'
|
||||
|
||||
@Unit()
|
||||
export default class Routes extends Canonical<RouterDefinition> {
|
||||
protected base_path = './app/http/routes'
|
||||
protected canonical_item = 'route_group'
|
||||
|
||||
105
lib/src/unit/Routing.ts
Normal file
105
lib/src/unit/Routing.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import LifecycleUnit from '../lifecycle/Unit.ts'
|
||||
import {Unit} from '../lifecycle/decorators.ts'
|
||||
import Routes from './Routes.ts'
|
||||
import {RouterDefinition} from '../http/type/RouterDefinition.ts'
|
||||
import {Collection} from '../collection/Collection.ts'
|
||||
import {Route} from '../http/routing/Route.ts'
|
||||
import {SimpleRoute} from "../http/routing/SimpleRoute.ts";
|
||||
import {ComplexRoute} from "../http/routing/ComplexRoute.ts";
|
||||
import ActivatedRoute from "../http/routing/ActivatedRoute.ts";
|
||||
|
||||
export type RouteHandler = () => any
|
||||
export interface RouteDefinition {
|
||||
get?: RouteHandler,
|
||||
post?: RouteHandler,
|
||||
patch?: RouteHandler,
|
||||
delete?: RouteHandler,
|
||||
head?: RouteHandler,
|
||||
put?: RouteHandler,
|
||||
connect?: RouteHandler,
|
||||
options?: RouteHandler,
|
||||
trace?: RouteHandler,
|
||||
}
|
||||
|
||||
const verbs = ['get', 'post', 'patch', 'delete', 'head', 'put', 'connect', 'options', 'trace']
|
||||
|
||||
@Unit()
|
||||
export default class Routing extends LifecycleUnit {
|
||||
protected definitions: { [key: string]: RouteDefinition } = {}
|
||||
protected instances: Collection<Route> = new Collection<Route>()
|
||||
|
||||
constructor(
|
||||
protected readonly routes: Routes,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
public async up() {
|
||||
const route_groups = this.routes.all()
|
||||
for ( const route_group_name of route_groups ) {
|
||||
const route_group: RouterDefinition | undefined = this.routes.get(route_group_name)
|
||||
if ( !route_group ) continue
|
||||
|
||||
const prefix = route_group.prefix || '/'
|
||||
for ( const verb of verbs ) {
|
||||
// @ts-ignore
|
||||
if ( route_group[verb] ) {
|
||||
// @ts-ignore
|
||||
const group = route_group[verb]
|
||||
for ( const key in group ) {
|
||||
if ( !group.hasOwnProperty(key) ) continue
|
||||
const handlers = Array.isArray(group[key]) ? group[key] : [group[key]]
|
||||
const base = this.resolve([prefix, key])
|
||||
|
||||
if ( !this.definitions[base] ) {
|
||||
this.definitions[base] = {}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
this.definitions[base][verb] = this.build_handler(handlers) // TODO want to rework this
|
||||
this.instances.push(this.build_route(base))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
public build_handler(group: string[]): () => any {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
public resolve(parts: string[]): string {
|
||||
const cleaned = parts.map(part => {
|
||||
if ( part.startsWith('/') ) part = part.substr(1)
|
||||
if ( part.endsWith('/') ) part = part.slice(0, -1)
|
||||
return part
|
||||
})
|
||||
|
||||
let joined = cleaned.join('/')
|
||||
if ( joined.startsWith('/') ) joined = joined.substr(1)
|
||||
if ( joined.endsWith('/') ) joined = joined.slice(0, -1)
|
||||
return `/${joined}`.toLowerCase()
|
||||
}
|
||||
|
||||
public build_route(base: string): Route {
|
||||
if ( !base.includes(':') && !base.includes('*') ) {
|
||||
return new SimpleRoute(base)
|
||||
} else {
|
||||
return new ComplexRoute(base)
|
||||
}
|
||||
|
||||
// TODO deep-match route
|
||||
}
|
||||
|
||||
public match(incoming: string): Route | undefined {
|
||||
return this.instances.firstWhere((route: Route) => route.match(incoming))
|
||||
}
|
||||
|
||||
public build(incoming: string): ActivatedRoute | undefined {
|
||||
const route: Route | undefined = this.match(incoming)
|
||||
if ( route ) {
|
||||
return new ActivatedRoute(incoming, route)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user