Http Kernel!
This commit is contained in:
parent
60bb9afa29
commit
b9f2f844f3
@ -2,3 +2,5 @@ export { default as ConfigUnit } from '../../lib/src/unit/Config.ts'
|
|||||||
export { DatabaseUnit } from '../../orm/src/DatabaseUnit.ts'
|
export { DatabaseUnit } from '../../orm/src/DatabaseUnit.ts'
|
||||||
export { default as ControllerUnit } from '../../lib/src/unit/Controllers.ts'
|
export { default as ControllerUnit } from '../../lib/src/unit/Controllers.ts'
|
||||||
export { default as MiddlewareUnit } from '../../lib/src/unit/Middlewares.ts'
|
export { default as MiddlewareUnit } from '../../lib/src/unit/Middlewares.ts'
|
||||||
|
export { default as RoutesUnit } from '../../lib/src/unit/Routes.ts'
|
||||||
|
export { default as HttpKernelUnit } from '../../lib/src/unit/HttpKernel.ts'
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
export default {
|
export default {
|
||||||
port: 8080,
|
port: 8080,
|
||||||
use_ssl: false,
|
use_ssl: false,
|
||||||
|
|
||||||
|
powered_by: {
|
||||||
|
enable: true,
|
||||||
|
text: 'Daton',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
5
app/http/middleware/Test.middleware.ts
Normal file
5
app/http/middleware/Test.middleware.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import Middleware from '../../../lib/src/http/Middleware.ts'
|
||||||
|
|
||||||
|
export default class TestMiddleware extends Middleware {
|
||||||
|
|
||||||
|
}
|
11
app/units.ts
11
app/units.ts
@ -1,8 +1,17 @@
|
|||||||
import {ConfigUnit, DatabaseUnit, ControllerUnit, MiddlewareUnit} from './bundle/daton_units.ts'
|
import {
|
||||||
|
ConfigUnit,
|
||||||
|
DatabaseUnit,
|
||||||
|
ControllerUnit,
|
||||||
|
MiddlewareUnit,
|
||||||
|
RoutesUnit,
|
||||||
|
HttpKernelUnit
|
||||||
|
} from './bundle/daton_units.ts'
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
ConfigUnit,
|
ConfigUnit,
|
||||||
DatabaseUnit,
|
DatabaseUnit,
|
||||||
|
HttpKernelUnit,
|
||||||
MiddlewareUnit,
|
MiddlewareUnit,
|
||||||
ControllerUnit,
|
ControllerUnit,
|
||||||
|
RoutesUnit,
|
||||||
]
|
]
|
||||||
|
@ -205,6 +205,17 @@ class Collection<T> {
|
|||||||
return new Collection(this._items.filter(func))
|
return new Collection(this._items.filter(func))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
find<T2>(func: KeyFunction<T, T2>): number | undefined {
|
||||||
|
let found_index: number | undefined = undefined
|
||||||
|
this._items.some((item, index) => {
|
||||||
|
if ( func(item, index) ) {
|
||||||
|
found_index = index
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return found_index
|
||||||
|
}
|
||||||
|
|
||||||
when<T2>(bool: boolean, then: CollectionFunction<T, T2>): Collection<T> {
|
when<T2>(bool: boolean, then: CollectionFunction<T, T2>): Collection<T> {
|
||||||
if ( bool ) then(this)
|
if ( bool ) then(this)
|
||||||
return this
|
return this
|
||||||
|
1
lib/src/external/std.ts
vendored
1
lib/src/external/std.ts
vendored
@ -2,3 +2,4 @@ export * from 'https://deno.land/std@0.53.0/fmt/colors.ts'
|
|||||||
export { config as dotenv } from 'https://deno.land/x/dotenv/mod.ts'
|
export { config as dotenv } from 'https://deno.land/x/dotenv/mod.ts'
|
||||||
export * as path from 'https://deno.land/std@0.53.0/path/mod.ts'
|
export * as path from 'https://deno.land/std@0.53.0/path/mod.ts'
|
||||||
export * as fs from 'https://deno.land/std@0.53.0/fs/mod.ts'
|
export * as fs from 'https://deno.land/std@0.53.0/fs/mod.ts'
|
||||||
|
export { generate as uuid } from 'https://deno.land/std/uuid/v4.ts'
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
|
import AppClass from '../lifecycle/AppClass.ts'
|
||||||
|
|
||||||
export default class Controller {
|
export default class Controller extends AppClass {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { Injectable } from '../../../di/src/decorator/Injection.ts'
|
|||||||
import { getCookies, setCookie, delCookie, ServerRequest } from '../external/http.ts'
|
import { getCookies, setCookie, delCookie, ServerRequest } from '../external/http.ts'
|
||||||
import { InMemCache } from '../support/InMemCache.ts'
|
import { InMemCache } from '../support/InMemCache.ts'
|
||||||
import { HTTPRequest } from './type/HTTPRequest.ts'
|
import { HTTPRequest } from './type/HTTPRequest.ts'
|
||||||
|
import {logger} from "../service/logging/global.ts";
|
||||||
|
|
||||||
export interface Cookie {
|
export interface Cookie {
|
||||||
key: string,
|
key: string,
|
||||||
@ -23,10 +24,15 @@ export class CookieJar {
|
|||||||
this._parsed = getCookies(this.request.to_native)
|
this._parsed = getCookies(this.request.to_native)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async get_raw(key: string): Promise<string | undefined> {
|
||||||
|
return this._parsed[key]
|
||||||
|
}
|
||||||
|
|
||||||
public async get(key: string): Promise<MaybeCookie> {
|
public async get(key: string): Promise<MaybeCookie> {
|
||||||
// Try the cache
|
// Try the cache
|
||||||
if ( await this._cache.has(key) )
|
if ( await this._cache.has(key) ) {
|
||||||
return this._cache.fetch(key)
|
return JSON.parse((await this._cache.fetch(key)) || '""') as Cookie
|
||||||
|
}
|
||||||
|
|
||||||
// If the cache missed, try to parse it and load in cache
|
// If the cache missed, try to parse it and load in cache
|
||||||
if ( key in this._parsed ) {
|
if ( key in this._parsed ) {
|
||||||
@ -41,7 +47,7 @@ export class CookieJar {
|
|||||||
original_value: this._parsed[key],
|
original_value: this._parsed[key],
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._cache.put(key, cookie)
|
await this._cache.put(key, JSON.stringify(cookie))
|
||||||
return cookie
|
return cookie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,7 +60,7 @@ export class CookieJar {
|
|||||||
original_value,
|
original_value,
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._cache.put(key, value)
|
await this._cache.put(key, JSON.stringify(cookie))
|
||||||
setCookie(this.request.response, { name: key, value: original_value })
|
setCookie(this.request.response, { name: key, value: original_value })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export class Middleware {
|
import AppClass from '../lifecycle/AppClass.ts'
|
||||||
|
|
||||||
|
export default class Middleware extends AppClass {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {HTTPResponse} from './type/HTTPResponse.ts'
|
import {HTTPResponse} from './type/HTTPResponse.ts'
|
||||||
import {HTTPRequest} from './type/HTTPRequest.ts'
|
import {HTTPRequest} from './type/HTTPRequest.ts'
|
||||||
import {ServerRequest} from '../external/http.ts'
|
import {ServerRequest} from '../external/http.ts'
|
||||||
import {CookieJar} from "./CookieJar.ts";
|
import {CookieJar} from './CookieJar.ts'
|
||||||
|
|
||||||
export class Response implements HTTPResponse {
|
export class Response implements HTTPResponse {
|
||||||
public status = 200
|
public status = 200
|
||||||
|
93
lib/src/http/kernel/Kernel.ts
Normal file
93
lib/src/http/kernel/Kernel.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import Module from './Module.ts'
|
||||||
|
import Instantiable from '../../../../di/src/type/Instantiable.ts'
|
||||||
|
import AppClass from '../../lifecycle/AppClass.ts'
|
||||||
|
import {Collection} from '../../collection/Collection.ts'
|
||||||
|
import {Service} from '../../../../di/src/decorator/Service.ts'
|
||||||
|
import {Request} from '../Request.ts'
|
||||||
|
|
||||||
|
export interface ModuleRegistrationFluency {
|
||||||
|
before: (other?: Instantiable<Module>) => Kernel,
|
||||||
|
after: (other?: Instantiable<Module>) => Kernel,
|
||||||
|
first: () => Kernel,
|
||||||
|
last: () => Kernel,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KernelModuleNotFoundError extends Error {
|
||||||
|
constructor(mod_name: string) {
|
||||||
|
super(`The kernel module ${mod_name} is not registered with the kernel.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class Kernel extends AppClass {
|
||||||
|
protected preflight: Collection<Module> = new Collection<Module>()
|
||||||
|
protected postflight: Collection<Module> = new Collection<Module>()
|
||||||
|
|
||||||
|
public async handle(request: Request): Promise<Request> {
|
||||||
|
for ( const module of this.preflight.toArray() ) {
|
||||||
|
request = await module.apply(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( const module of this.postflight.toArray() ) {
|
||||||
|
request = await module.apply(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
public register(module: Instantiable<Module>): ModuleRegistrationFluency {
|
||||||
|
return {
|
||||||
|
before: (other?: Instantiable<Module>): Kernel => {
|
||||||
|
if ( !other ) {
|
||||||
|
this.preflight = this.preflight.push(this.make(module))
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
let found_index = this.preflight.find((mod: Module) => mod instanceof other)
|
||||||
|
if ( typeof found_index !== 'undefined' ) {
|
||||||
|
this.preflight = this.preflight.put(found_index, this.make(module))
|
||||||
|
} else {
|
||||||
|
found_index = this.postflight.find((mod: Module) => mod instanceof other)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( typeof found_index !== 'undefined' ) {
|
||||||
|
this.postflight = this.postflight.put(found_index, this.make(module))
|
||||||
|
} else {
|
||||||
|
throw new KernelModuleNotFoundError(other.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
after: (other?: Instantiable<Module>): Kernel => {
|
||||||
|
if ( !other ) {
|
||||||
|
this.postflight = this.postflight.push(this.make(module))
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
let found_index = this.preflight.find((mod: Module) => mod instanceof other)
|
||||||
|
if ( typeof found_index !== 'undefined' ) {
|
||||||
|
this.preflight = this.preflight.put(found_index + 1, this.make(module))
|
||||||
|
} else {
|
||||||
|
found_index = this.postflight.find((mod: Module) => mod instanceof other)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( typeof found_index !== 'undefined' ) {
|
||||||
|
this.postflight = this.postflight.put(found_index + 1, this.make(module))
|
||||||
|
} else {
|
||||||
|
console.log(this.preflight, this.postflight)
|
||||||
|
throw new KernelModuleNotFoundError(other.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
first: (): Kernel => {
|
||||||
|
this.preflight = this.preflight.put(0, this.make(module))
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
last: (): Kernel => {
|
||||||
|
this.postflight = this.postflight.push(this.make(module))
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
lib/src/http/kernel/Module.ts
Normal file
16
lib/src/http/kernel/Module.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {Request} from '../Request.ts'
|
||||||
|
import Kernel from './Kernel.ts'
|
||||||
|
|
||||||
|
export default class Module {
|
||||||
|
public async match(request: Request): Promise<boolean> {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public async apply(request: Request): Promise<Request> {
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
public static register(kernel: Kernel) {
|
||||||
|
kernel.register(this).before()
|
||||||
|
}
|
||||||
|
}
|
16
lib/src/http/kernel/module/PrepareRequest.ts
Normal file
16
lib/src/http/kernel/module/PrepareRequest.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import Module from '../Module.ts'
|
||||||
|
import Kernel from '../Kernel.ts'
|
||||||
|
import {Request} from '../../Request.ts'
|
||||||
|
|
||||||
|
export default class PrepareRequest extends Module {
|
||||||
|
|
||||||
|
public static register(kernel: Kernel) {
|
||||||
|
kernel.register(this).first()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async apply(request: Request): Promise<Request> {
|
||||||
|
await request.prepare()
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
lib/src/http/kernel/module/SetDatonHeaders.ts
Normal file
24
lib/src/http/kernel/module/SetDatonHeaders.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import Module from '../Module.ts'
|
||||||
|
import Kernel from '../Kernel.ts'
|
||||||
|
import {Request} from '../../Request.ts'
|
||||||
|
import {Injectable} from '../../../../../di/src/decorator/Injection.ts'
|
||||||
|
import Config from '../../../unit/Config.ts'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class SetDatonHeaders extends Module {
|
||||||
|
public static register(kernel: Kernel) {
|
||||||
|
kernel.register(this).after()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected readonly config: Config,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async apply(request: Request): Promise<Request> {
|
||||||
|
const text = this.config.get('server.powered_by.text', 'Daton')
|
||||||
|
request.response.headers.set('X-Powered-By', text)
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
}
|
28
lib/src/http/kernel/module/SetSessionCookie.ts
Normal file
28
lib/src/http/kernel/module/SetSessionCookie.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import Module from '../Module.ts'
|
||||||
|
import Kernel from '../Kernel.ts'
|
||||||
|
import {Request} from '../../Request.ts'
|
||||||
|
import PrepareRequest from './PrepareRequest.ts'
|
||||||
|
import Utility from '../../../service/utility/Utility.ts'
|
||||||
|
import {Injectable} from '../../../../../di/src/decorator/Injection.ts'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class SetSessionCookie extends Module {
|
||||||
|
|
||||||
|
public static register(kernel: Kernel) {
|
||||||
|
kernel.register(this).after(PrepareRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected utility: Utility,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async apply(request: Request): Promise<Request> {
|
||||||
|
if ( !(await request.cookies.has('daton.session')) ) {
|
||||||
|
await request.cookies.set('daton.session', `${this.utility.uuid()}-${this.utility.uuid()}`)
|
||||||
|
}
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
77
lib/src/http/type/RouterDefinition.ts
Normal file
77
lib/src/http/type/RouterDefinition.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import {logger} from '../../service/logging/global.ts'
|
||||||
|
|
||||||
|
export type RouteVerb = 'get' | 'post' | 'patch' | 'delete' | 'head' | 'put' | 'connect' | 'options' | 'trace'
|
||||||
|
export type RouteVerbGroup = { [key: string]: string | string[] }
|
||||||
|
|
||||||
|
export interface RouterDefinition {
|
||||||
|
prefix?: string,
|
||||||
|
middleware?: string[],
|
||||||
|
get?: RouteVerbGroup,
|
||||||
|
post?: RouteVerbGroup,
|
||||||
|
patch?: RouteVerbGroup,
|
||||||
|
delete?: RouteVerbGroup,
|
||||||
|
head?: RouteVerbGroup,
|
||||||
|
put?: RouteVerbGroup,
|
||||||
|
connect?: RouteVerbGroup,
|
||||||
|
options?: RouteVerbGroup,
|
||||||
|
trace?: RouteVerbGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRouteVerb(something: any): something is RouteVerb {
|
||||||
|
const route_verbs = ['get', 'post', 'patch', 'delete', 'head', 'put', 'connect', 'options', 'trace']
|
||||||
|
return route_verbs.includes(something)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRouteVerbGroup(something: any): something is RouteVerbGroup {
|
||||||
|
if ( !(typeof something === 'object' ) ) return false
|
||||||
|
for ( const key in something ) {
|
||||||
|
if ( !something.hasOwnProperty(key) ) continue
|
||||||
|
if ( typeof key !== 'string' ) {
|
||||||
|
logger.debug(`Route verb group key is not a string: ${key}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!(typeof something[key] === 'string')
|
||||||
|
&& !(Array.isArray(something[key]) && something[key].every((x: any) => typeof x === 'string'))
|
||||||
|
) {
|
||||||
|
logger.info(`Route verb group for key ${key} is not a string or array of strings.`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRouterDefinition(something: any): something is RouterDefinition {
|
||||||
|
if ( !(typeof something === 'object') ) {
|
||||||
|
logger.debug('Routing definition is not an object.')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for ( const key in something ) {
|
||||||
|
if (!something.hasOwnProperty(key)) continue
|
||||||
|
if ( key === 'prefix' ) {
|
||||||
|
if ( typeof something[key] !== 'string' ) {
|
||||||
|
logger.debug(`Invalid route prefix: ${something[key]}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( key === 'middleware' ) {
|
||||||
|
if ( !Array.isArray(something[key]) ) {
|
||||||
|
logger.debug('Middleware is not an array.')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
else if ( !something[key].every((x: any) => typeof x === 'string') ) {
|
||||||
|
logger.debug('Middleware array contains non-string values.')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else if ( isRouteVerb(key) ) {
|
||||||
|
if ( !isRouteVerbGroup(something[key as any]) ) {
|
||||||
|
logger.debug('Verb group value is not a valid route verb group.')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.debug(`Invalid key: ${key}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
22
lib/src/lifecycle/AppClass.ts
Normal file
22
lib/src/lifecycle/AppClass.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Instantiable from '../../../di/src/type/Instantiable.ts'
|
||||||
|
import {DependencyKey} from '../../../di/src/type/DependencyKey.ts'
|
||||||
|
import {make} from '../../../di/src/global.ts'
|
||||||
|
import Application from '../lifecycle/Application.ts'
|
||||||
|
|
||||||
|
export default class AppClass {
|
||||||
|
protected static make<T>(target: Instantiable<T>|DependencyKey, ...parameters: any[]) {
|
||||||
|
return make(target, ...parameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static get app() {
|
||||||
|
return make(Application)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected make<T>(target: Instantiable<T>|DependencyKey, ...parameters: any[]) {
|
||||||
|
return make(target, ...parameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get app() {
|
||||||
|
return make(Application)
|
||||||
|
}
|
||||||
|
}
|
@ -3,12 +3,13 @@ import { Collection } from '../collection/Collection.ts'
|
|||||||
import {container, make} from '../../../di/src/global.ts'
|
import {container, make} from '../../../di/src/global.ts'
|
||||||
import {DependencyKey} from '../../../di/src/type/DependencyKey.ts'
|
import {DependencyKey} from '../../../di/src/type/DependencyKey.ts'
|
||||||
import Instantiable, {isInstantiable} from '../../../di/src/type/Instantiable.ts'
|
import Instantiable, {isInstantiable} from '../../../di/src/type/Instantiable.ts'
|
||||||
|
import AppClass from './AppClass.ts'
|
||||||
|
|
||||||
const isLifecycleUnit = (something: any): something is (typeof LifecycleUnit) => {
|
const isLifecycleUnit = (something: any): something is (typeof LifecycleUnit) => {
|
||||||
return isInstantiable(something) && something.prototype instanceof LifecycleUnit
|
return isInstantiable(something) && something.prototype instanceof LifecycleUnit
|
||||||
}
|
}
|
||||||
|
|
||||||
export default abstract class LifecycleUnit {
|
export default abstract class LifecycleUnit extends AppClass {
|
||||||
private _status = Status.Stopped
|
private _status = Status.Stopped
|
||||||
|
|
||||||
public get status() {
|
public get status() {
|
||||||
@ -34,8 +35,4 @@ export default abstract class LifecycleUnit {
|
|||||||
}
|
}
|
||||||
return new Collection<typeof LifecycleUnit>()
|
return new Collection<typeof LifecycleUnit>()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected make<T>(target: Instantiable<T>|DependencyKey, ...parameters: any[]) {
|
|
||||||
return make(target, ...parameters)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Service } from '../../../../di/src/decorator/Service.ts'
|
import { Service } from '../../../../di/src/decorator/Service.ts'
|
||||||
import { Logging } from '../logging/Logging.ts'
|
import { Logging } from '../logging/Logging.ts'
|
||||||
|
import {uuid} from '../../external/std.ts'
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class Utility {
|
export default class Utility {
|
||||||
@ -52,4 +53,8 @@ export default class Utility {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uuid(): string {
|
||||||
|
return uuid()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,31 @@
|
|||||||
import {Service} from '../../../di/src/decorator/Service.ts'
|
import {Service} from '../../../di/src/decorator/Service.ts'
|
||||||
|
import {Canonical} from './Canonical.ts'
|
||||||
export type CanonicalResolver = (key: string) => any
|
|
||||||
|
|
||||||
export class DuplicateResolverKeyError extends Error {
|
export class DuplicateResolverKeyError extends Error {
|
||||||
constructor(key: string) {
|
constructor(key: string) {
|
||||||
super(`There is already a canonical resource with the scope ${key} registered.`)
|
super(`There is already a canonical unit with the scope ${key} registered.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class NoSuchCanonicalResolverKeyError extends Error {
|
||||||
|
constructor(key: string) {
|
||||||
|
super(`There is no such canonical unit with the scope ${key} registered.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class Canon {
|
export class Canon {
|
||||||
protected resources: { [key: string]: any } = {}
|
protected resources: { [key: string]: Canonical<any> } = {}
|
||||||
|
|
||||||
get(key: string): any {
|
resource<T>(key: string): Canonical<T> {
|
||||||
const key_parts = key.split('::')
|
if ( !this.resources[key] ) throw new NoSuchCanonicalResolverKeyError(key)
|
||||||
let desc_value = this.resources
|
return this.resources[key] as Canonical<T>
|
||||||
key_parts.forEach(part => {
|
|
||||||
if ( typeof desc_value === 'function' ) {
|
|
||||||
desc_value = desc_value(part)
|
|
||||||
} else {
|
|
||||||
desc_value = desc_value[part]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return desc_value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
register_resource(scope: string, resolver: CanonicalResolver) {
|
register_canonical(unit: Canonical<any>) {
|
||||||
if ( this.resources[scope] ) {
|
const key = unit.canonical_items
|
||||||
throw new DuplicateResolverKeyError(scope)
|
if ( this.resources[key] ) throw new DuplicateResolverKeyError(key)
|
||||||
}
|
this.resources[key] = unit
|
||||||
|
|
||||||
this.resources[scope] = resolver
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,7 @@ export class Canonical<T> extends LifecycleUnit {
|
|||||||
const def = await this._get_canonical_definition(entry.path)
|
const def = await this._get_canonical_definition(entry.path)
|
||||||
this._items[def.canonical_name] = await this.init_canonical_item(def)
|
this._items[def.canonical_name] = await this.init_canonical_item(def)
|
||||||
}
|
}
|
||||||
|
this.make(Canon).register_canonical(this)
|
||||||
this.make(Canon).register_resource(this.canonical_items, (key: string) => this.get(key))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async init_canonical_item(definition: CanonicalDefinition): Promise<T> {
|
public async init_canonical_item(definition: CanonicalDefinition): Promise<T> {
|
||||||
|
28
lib/src/unit/HttpKernel.ts
Normal file
28
lib/src/unit/HttpKernel.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import LifecycleUnit from "../lifecycle/Unit.ts";
|
||||||
|
import {Unit} from "../lifecycle/decorators.ts";
|
||||||
|
import Kernel from "../http/kernel/Kernel.ts";
|
||||||
|
import PrepareRequest from "../http/kernel/module/PrepareRequest.ts";
|
||||||
|
import SetSessionCookie from "../http/kernel/module/SetSessionCookie.ts";
|
||||||
|
import Config from "./Config.ts";
|
||||||
|
import SetDatonHeaders from "../http/kernel/module/SetDatonHeaders.ts";
|
||||||
|
|
||||||
|
@Unit()
|
||||||
|
export default class HttpKernel extends LifecycleUnit {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected readonly kernel: Kernel,
|
||||||
|
protected readonly config: Config,
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async up() {
|
||||||
|
PrepareRequest.register(this.kernel)
|
||||||
|
SetSessionCookie.register(this.kernel)
|
||||||
|
|
||||||
|
if ( this.config.get('server.powered_by.enable') ) {
|
||||||
|
SetDatonHeaders.register(this.kernel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { InstantiableCanonical } from './InstantiableCanonical.ts'
|
import { InstantiableCanonical } from './InstantiableCanonical.ts'
|
||||||
import { CanonicalDefinition } from './Canonical.ts'
|
import { CanonicalDefinition } from './Canonical.ts'
|
||||||
import { Middleware } from '../http/Middleware.ts'
|
import Middleware from '../http/Middleware.ts'
|
||||||
import { Unit } from '../lifecycle/decorators.ts'
|
import { Unit } from '../lifecycle/decorators.ts'
|
||||||
|
|
||||||
@Unit()
|
@Unit()
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import {Canonical} from './Canonical.ts'
|
import {Canonical} from './Canonical.ts'
|
||||||
|
|
||||||
export class RecursiveCanonical extends Canonical<any> {
|
export class RecursiveCanonical extends Canonical<any> {
|
||||||
public get(key: string): any | undefined {
|
public get(key: string, fallback?: any): any | undefined {
|
||||||
const parts = key.split('.')
|
const parts = key.split('.')
|
||||||
let current_value = this._items
|
let current_value = this._items
|
||||||
for ( const part of parts ) {
|
for ( const part of parts ) {
|
||||||
current_value = current_value?.[part]
|
current_value = current_value?.[part]
|
||||||
}
|
}
|
||||||
return current_value
|
return current_value ?? fallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
lib/src/unit/Routes.ts
Normal file
16
lib/src/unit/Routes.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {Canonical, CanonicalDefinition} from './Canonical.ts'
|
||||||
|
import {isRouterDefinition, RouterDefinition} from '../http/type/RouterDefinition.ts'
|
||||||
|
|
||||||
|
export default class Routes extends Canonical<RouterDefinition> {
|
||||||
|
protected base_path = './app/http/routes'
|
||||||
|
protected canonical_item = 'route_group'
|
||||||
|
protected suffix = '.routes.ts'
|
||||||
|
|
||||||
|
public async init_canonical_item(def: CanonicalDefinition): Promise<RouterDefinition> {
|
||||||
|
const item = await super.init_canonical_item(def)
|
||||||
|
if ( !isRouterDefinition(item) ) {
|
||||||
|
throw new TypeError(`Invalid routes definition: ${def.original_name}.`)
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user