db structure abstraction; async collection; update/insert queries; model saving

This commit is contained in:
garrettmills
2020-07-06 09:53:03 -05:00
parent eddb4f1fbe
commit e4f5da7ac6
73 changed files with 3301 additions and 57 deletions

View File

@@ -0,0 +1,4 @@
export default class Controller {
}

69
lib/src/http/CookieJar.ts Normal file
View File

@@ -0,0 +1,69 @@
import { Injectable } from '../../../di/src/decorator/Injection.ts'
import { getCookies, setCookie, delCookie, ServerRequest } from '../external/http.ts'
import { InMemCache } from '../support/InMemCache.ts'
import { HTTPRequest } from './type/HTTPRequest.ts'
export interface Cookie {
key: string,
original_value: string,
value: any,
}
export type MaybeCookie = Cookie | undefined
// TODO cookie options (http only, expires, &c.)
@Injectable()
export class CookieJar {
protected _parsed: { [key: string]: string } = {}
protected _cache = new InMemCache()
constructor(
protected request: HTTPRequest,
) {
this._parsed = getCookies(this.request.to_native)
}
public async get(key: string): Promise<MaybeCookie> {
// Try the cache
if ( await this._cache.has(key) )
return this._cache.fetch(key)
// If the cache missed, try to parse it and load in cache
if ( key in this._parsed ) {
let value = this._parsed[key]
try {
value = JSON.parse(atob(this._parsed[key]))
} catch(e) {}
const cookie = {
key,
value,
original_value: this._parsed[key],
}
await this._cache.put(key, cookie)
return cookie
}
}
public async set(key: string, value: any): Promise<void> {
const original_value = btoa(JSON.stringify(value))
const cookie = {
key,
value,
original_value,
}
await this._cache.put(key, value)
setCookie(this.request.response, { name: key, value: original_value })
}
public async has(key: string): Promise<boolean> {
return (await this._cache.has(key)) || key in this._parsed
}
public async delete(key: string): Promise<void> {
await this._cache.drop(key)
delCookie(this.request.response, key)
}
}

View File

@@ -0,0 +1,3 @@
export class Middleware {
}

115
lib/src/http/Request.ts Normal file
View File

@@ -0,0 +1,115 @@
import { ServerRequest } from '../external/http.ts'
import { HTTPProtocol, HTTPRequest, RemoteHost } from './type/HTTPRequest.ts'
import { Response } from './Response.ts'
import { HTTPResponse } from './type/HTTPResponse.ts'
import Utility from '../service/utility/Utility.ts'
import { Injectable } from '../../../di/src/decorator/Injection.ts'
@Injectable()
export class Request implements HTTPRequest {
public readonly response: HTTPResponse
private readonly _deno_req: ServerRequest
private _body: any
private _query: { [key: string]: any } = {}
public readonly url: string
public readonly method: string
public readonly protocol: HTTPProtocol
public readonly connection: Deno.Conn
public readonly secure: boolean = false
public get headers() {
return this._deno_req.headers
}
get to_native(): ServerRequest {
return this._deno_req
}
get cookies() {
return this.response.cookies
}
constructor(
protected utility: Utility,
from: ServerRequest
) {
this._deno_req = from
this.url = this._deno_req.url
this.method = this._deno_req.method.toLowerCase()
this.protocol = {
string: this._deno_req.proto,
major: this._deno_req.protoMajor,
minor: this._deno_req.protoMinor,
}
this.connection = this._deno_req.conn
this.response = new Response(this)
}
public async prepare() {
this._body = await Deno.readAll(this._deno_req.body)
const url_params = new URLSearchParams(this.url.substr(1))
const param_obj = Object.fromEntries(url_params)
const params: any = {}
for ( const key in param_obj ) {
if ( !param_obj.hasOwnProperty(key) ) continue
if ( param_obj[key] === '' ) params[key] = true
else params[key] = this.utility.infer(param_obj[key])
}
this._query = params
}
respond(res: any) {
return this._deno_req.respond(res)
}
// public body: RequestBody = {}
// public original_body: RequestBody = {}
get remote() {
return this.connection.remoteAddr as RemoteHost
}
get body() {
return this._body
}
get query() {
return this._query
}
get hostname() {
return this.headers.get('host')?.split(':')[0]
}
get path() {
return this.url.split('?')[0]
}
get xhr() {
return this.headers.get('x-requested-with')?.toLowerCase() === 'xmlhttprequest'
}
/*
body
fresh/stale - cache
remote ips (proxy)
params
route?
signedCookies
*/
/*
accepts content type
accepts charsets
accepts encodings
accepts languages
get header
is content type
get param with default value
get input with default value
range header parser
*/
}

30
lib/src/http/Response.ts Normal file
View File

@@ -0,0 +1,30 @@
import { HTTPResponse } from './type/HTTPResponse.ts'
import { HTTPRequest } from './type/HTTPRequest.ts'
import { ServerRequest } from '../external/http.ts'
import {CookieJar} from "./CookieJar.ts";
export class Response implements HTTPResponse {
public status = 200
public headers = new Headers()
public body = ''
public readonly cookies: CookieJar
private readonly _deno_req: ServerRequest
private readonly _request: HTTPRequest
private _sent = false
get sent() {
return this._sent
}
constructor(to: HTTPRequest) {
this._deno_req = to.to_native
this._request = to
this.cookies = new CookieJar(to)
}
send() {
this._sent = true
return this._deno_req.respond(this)
}
}

View File

@@ -0,0 +1,6 @@
import { HTTPRequest } from './type/HTTPRequest.ts'
import { Request } from './Request.ts'
export default class SecureRequest extends Request implements HTTPRequest {
public readonly secure: boolean = true
}

View File

@@ -0,0 +1,30 @@
import { ServerRequest } from '../../external/http.ts'
import {HTTPResponse} from "./HTTPResponse.ts";
export interface HTTPProtocol {
string: string,
major: number,
minor: number,
}
export interface RemoteHost {
hostname: string,
port: number,
transport: string,
}
export interface HTTPRequest {
url: string
method: string
protocol: HTTPProtocol
headers: Headers
connection: Deno.Conn
response: HTTPResponse
to_native: ServerRequest
remote: RemoteHost
body: any
query: any
hostname: string | undefined
secure: boolean
}

View File

@@ -0,0 +1,12 @@
import {CookieJar} from '../CookieJar.ts'
export interface HTTPResponse {
status: number
headers: Headers
body: Uint8Array | Deno.Reader | string
trailers?: () => Promise<Headers> | Headers
sent: boolean
cookies: CookieJar,
send: () => void
}