@typetron/framework 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc-old.js +208 -0
- package/Cache/Cache.ts +13 -0
- package/Cache/Stores/DatabaseStore.ts +120 -0
- package/Cache/Stores/FileStore.ts +69 -0
- package/Cache/Stores/MemoryStore.ts +71 -0
- package/Cache/index.ts +5 -0
- package/Cache/types.ts +4 -0
- package/Container/ClassResolver.ts +106 -0
- package/Container/Container.ts +144 -0
- package/Container/Contracts.ts +3 -0
- package/Container/Decorators.ts +20 -0
- package/Container/Metadata.ts +16 -0
- package/Container/Resolver.ts +27 -0
- package/Container/index.ts +5 -0
- package/Database/Clauses/Where.ts +0 -0
- package/Database/Connection.ts +52 -0
- package/Database/Decorators.ts +142 -0
- package/Database/Drivers/DatabaseDriver.ts +37 -0
- package/Database/Drivers/MySQL/Alter/index.ts +55 -0
- package/Database/Drivers/MySQL/ColumnDefinition.ts +16 -0
- package/Database/Drivers/MySQL/Create.ts +47 -0
- package/Database/Drivers/MySQL/MysqlDriver.ts +123 -0
- package/Database/Drivers/MySQL/Schema.ts +82 -0
- package/Database/Drivers/MySQL/index.ts +13 -0
- package/Database/Drivers/SQL/Schema.ts +73 -0
- package/Database/Drivers/SQL/Statements/Alter.ts +22 -0
- package/Database/Drivers/SQL/Statements/BaseStatement.ts +55 -0
- package/Database/Drivers/SQL/Statements/Delete.ts +20 -0
- package/Database/Drivers/SQL/Statements/Insert.ts +24 -0
- package/Database/Drivers/SQL/Statements/Select.ts +55 -0
- package/Database/Drivers/SQL/Statements/Update.ts +33 -0
- package/Database/Drivers/SQL/index.ts +8 -0
- package/Database/Drivers/SQLite/Create.ts +45 -0
- package/Database/Drivers/SQLite/Schema.ts +54 -0
- package/Database/Drivers/SQLite/SqliteDriver.ts +144 -0
- package/Database/Drivers/SQLite/index.ts +15 -0
- package/Database/Drivers/SchemaContract.ts +11 -0
- package/Database/Drivers/Statement.ts +16 -0
- package/Database/Drivers/index.ts +4 -0
- package/Database/Entity.ts +377 -0
- package/Database/EntityNotFoundError.ts +5 -0
- package/Database/EntityProxyHandler.ts +43 -0
- package/Database/EntityQuery.ts +197 -0
- package/Database/Expression.ts +3 -0
- package/Database/Fields.ts +816 -0
- package/Database/Helpers.ts +12 -0
- package/Database/List.ts +71 -0
- package/Database/Migrations/Migration.ts +10 -0
- package/Database/Migrations/MigrationHistory.ts +20 -0
- package/Database/Migrations/Migrator.ts +122 -0
- package/Database/Migrations/index.ts +3 -0
- package/Database/ORM/BaseRelationship.ts +10 -0
- package/Database/Query.ts +374 -0
- package/Database/Seeders/Seeder.ts +3 -0
- package/Database/Seeders/SeederManager.ts +27 -0
- package/Database/Seeders/index.ts +2 -0
- package/Database/StringExpression.ts +9 -0
- package/Database/Types.ts +165 -0
- package/Database/index.ts +23 -0
- package/Encryption/index.ts +51 -0
- package/Forms/Decorators.ts +29 -0
- package/Forms/Form.ts +70 -0
- package/Forms/FormFields.ts +36 -0
- package/Forms/index.ts +3 -0
- package/Framework/App.ts +10 -0
- package/Framework/Application.ts +118 -0
- package/Framework/Auth/Auth.ts +122 -0
- package/Framework/Auth/Authenticatable.ts +9 -0
- package/Framework/Auth/User.ts +27 -0
- package/Framework/Auth/index.ts +29 -0
- package/Framework/Config/AppConfig.ts +25 -0
- package/Framework/Config/AuthConfig.ts +9 -0
- package/Framework/Config/BaseConfig.ts +9 -0
- package/Framework/Config/CacheConfig.ts +38 -0
- package/Framework/Config/DatabaseConfig.ts +21 -0
- package/Framework/Config/MailConfig.ts +30 -0
- package/Framework/Config/index.ts +6 -0
- package/Framework/Middleware/AuthMiddleware.ts +31 -0
- package/Framework/Middleware/CorsMiddleware.ts +32 -0
- package/Framework/Middleware/StaticAssetsMiddleware.ts +76 -0
- package/Framework/Middleware/index.ts +2 -0
- package/Framework/Provider.ts +9 -0
- package/Framework/Providers/CacheProvider.ts +28 -0
- package/Framework/Providers/DatabaseProvider.ts +36 -0
- package/Framework/Providers/MailProvider.ts +31 -0
- package/Framework/Providers/WebsocketsProvider.ts +120 -0
- package/Framework/Resolvers/AuthResolver.ts +23 -0
- package/Framework/Resolvers/EntityResolver.ts +41 -0
- package/Framework/Resolvers/FormResolver.ts +37 -0
- package/Framework/RootDir.ts +1 -0
- package/Framework/index.ts +8 -0
- package/Frontend/Angular/index.ts +24 -0
- package/Frontend/Websockets/index.ts +86 -0
- package/Mail/MailAddress.ts +4 -0
- package/Mail/MailTransport.ts +6 -0
- package/Mail/Mailable.ts +16 -0
- package/Mail/Mailer.ts +27 -0
- package/Mail/PendingMail.ts +68 -0
- package/Mail/SentMessage.ts +20 -0
- package/Mail/Transports/MemoryTransport.ts +13 -0
- package/Mail/Transports/SendGridTransport.ts +95 -0
- package/Mail/index.ts +3 -0
- package/Models/Model.ts +41 -0
- package/Models/index.ts +54 -0
- package/Router/BaseController.ts +7 -0
- package/Router/Decorators.ts +224 -0
- package/Router/Guard.ts +10 -0
- package/Router/Http/Contracts.ts +4 -0
- package/Router/Http/ErrorHandler.ts +45 -0
- package/Router/Http/ErrorHandlerInterface.ts +6 -0
- package/Router/Http/Errors/HttpError.ts +15 -0
- package/Router/Http/Handler.ts +183 -0
- package/Router/Http/HttpRoute.ts +110 -0
- package/Router/Http/Middleware.ts +7 -0
- package/Router/Http/Request.ts +225 -0
- package/Router/Http/Response.ts +35 -0
- package/Router/Http/index.ts +85 -0
- package/Router/Metadata.ts +34 -0
- package/Router/Middleware.ts +16 -0
- package/Router/Request.ts +65 -0
- package/Router/Route.ts +16 -0
- package/Router/RouteNotFoundError.ts +5 -0
- package/Router/Router.ts +35 -0
- package/Router/Servers/index.ts +6 -0
- package/Router/Servers/node.ts +49 -0
- package/Router/Servers/uNetworking.ts +151 -0
- package/Router/Websockets/Handler.ts +82 -0
- package/Router/Websockets/Middleware.ts +7 -0
- package/Router/Websockets/Request.ts +3 -0
- package/Router/Websockets/WebsocketRoute.ts +61 -0
- package/Router/Websockets/index.ts +57 -0
- package/Router/Websockets/types.ts +23 -0
- package/Router/index.ts +6 -0
- package/Storage/File.ts +30 -0
- package/Storage/Image.ts +19 -0
- package/Storage/InvalidImage.ts +3 -0
- package/Storage/Storage.ts +98 -0
- package/Storage/index.ts +4 -0
- package/Storage/utils.ts +18 -0
- package/Support/Array.ts +184 -0
- package/Support/Math.ts +12 -0
- package/Support/Metadata.ts +31 -0
- package/Support/String.ts +38 -0
- package/Support/index.ts +19 -0
- package/Support/utils.ts +7 -0
- package/Testing/TestCase.ts +193 -0
- package/Validation/Rule.ts +9 -0
- package/Validation/RuleInterface.ts +10 -0
- package/Validation/Rules/Email.ts +20 -0
- package/Validation/Rules/FileExtension.ts +21 -0
- package/Validation/Rules/MaxLength.ts +17 -0
- package/Validation/Rules/MinLength.ts +18 -0
- package/Validation/Rules/Optional.ts +18 -0
- package/Validation/Rules/Required.ts +29 -0
- package/Validation/Validator.ts +7 -0
- package/Validation/index.ts +12 -0
- package/benchmark-index.js +20 -0
- package/benchmark.ts +47 -0
- package/package.json +7 -3
- package/tests/Cache/DatabaseStoreTest.ts +110 -0
- package/tests/Cache/FileStoreTest.ts +93 -0
- package/tests/Cache/MemoryStoreTest.ts +92 -0
- package/tests/Container/ContainerTest.ts +336 -0
- package/tests/Database/Drivers/MySQL/AlterTest.ts +100 -0
- package/tests/Database/Drivers/MySQL/CreateTest.ts +39 -0
- package/tests/Database/Drivers/MysqlDriverTest.ts +140 -0
- package/tests/Database/Drivers/SqliteDriverTest.ts +126 -0
- package/tests/Database/Entities/Article.ts +21 -0
- package/tests/Database/Entities/Profile.ts +21 -0
- package/tests/Database/Entities/Role.ts +15 -0
- package/tests/Database/Entities/User.ts +35 -0
- package/tests/Database/EntityRelationshipsTest.ts +475 -0
- package/tests/Database/EntityTest.ts +202 -0
- package/tests/Database/Migrations/RunnerTest.ts +96 -0
- package/tests/Database/Migrations/migrations/1.createUserTable.ts +12 -0
- package/tests/Database/Migrations/migrations/2.createArticlesTable.ts +12 -0
- package/tests/Database/QueryTest.ts +573 -0
- package/tests/Database/SchemaTest.ts +32 -0
- package/tests/Database/Seeders/RunnerTest.ts +30 -0
- package/tests/Database/Seeders/seeders/RandomSeeder.ts +10 -0
- package/tests/Database/utils.ts +16 -0
- package/tests/Forms/FormsTest.ts +59 -0
- package/tests/Framework/Middleware/StaticAssetsMiddlewareTest.ts +129 -0
- package/tests/Framework/Resolvers/EntityResolverTest.ts +48 -0
- package/tests/Framework/Router/RouterTest.ts +33 -0
- package/tests/Mail/MailerTest.ts +76 -0
- package/tests/Model/ModelTest.ts +183 -0
- package/tests/Router/HttpHandlerTest.ts +72 -0
- package/tests/Router/RequestTest.ts +77 -0
- package/tests/Router/WebSocketsHandlerTest.ts +51 -0
- package/tests/Storage/StorageTest.ts +97 -0
- package/tests/Support/ArrayTest.ts +167 -0
- package/tests/Support/StringTest.ts +18 -0
- package/tests/Validation/RulesTest.ts +54 -0
- package/types.d.ts +8 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { App, HttpRequest, HttpResponse } from 'uWebSockets.js'
|
|
2
|
+
import { Http, Request, Response } from '@Typetron/Router/Http'
|
|
3
|
+
import { Buffer } from 'buffer'
|
|
4
|
+
import { IncomingHttpHeaders } from 'http'
|
|
5
|
+
import * as console from 'console'
|
|
6
|
+
import busboy, { FileInfo } from 'busboy'
|
|
7
|
+
import { File } from '@Typetron/Storage'
|
|
8
|
+
import { Readable } from 'stream'
|
|
9
|
+
import os from 'os'
|
|
10
|
+
import fs from 'fs'
|
|
11
|
+
|
|
12
|
+
async function handleRequest(serverResponse: HttpResponse, serverRequest: HttpRequest, handler: (request: Request) => Promise<Response>) {
|
|
13
|
+
serverResponse.onAborted(() => {
|
|
14
|
+
console.log('uNetworking response aborted ->', serverResponse)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const request = new Request(serverRequest.getUrl(), serverRequest.getMethod() as Http.Method, serverRequest.getQuery())
|
|
19
|
+
const headers: Record<string, string> = {}
|
|
20
|
+
|
|
21
|
+
serverRequest.forEach((name, value) => headers[name] = value)
|
|
22
|
+
request.setHeadersLoader(() => headers)
|
|
23
|
+
request.getHeader = <T extends string | string[] | undefined>(name: keyof IncomingHttpHeaders | string): T => {
|
|
24
|
+
return serverRequest.getHeader(name.toString()) as T
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (request.method.toLowerCase() !== Http.Method.GET.toLowerCase()) {
|
|
28
|
+
if (request.isMultipartRequest()) {
|
|
29
|
+
[request.body, request.files] = await readForm(serverResponse, headers)
|
|
30
|
+
} else {
|
|
31
|
+
request.body = await readJson(serverResponse, headers['content-type'])
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// const overwrittenMethod = (request.body as Record<string, string | undefined>)[Request.methodField] || ''
|
|
35
|
+
// request.method = Http.Method[overwrittenMethod.toUpperCase() as Http.Method] || request.method
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const response = await handler(request)
|
|
39
|
+
let content = response.body as string | object | number | Array<any> | Buffer
|
|
40
|
+
|
|
41
|
+
serverResponse.writeStatus(response.status.toString())
|
|
42
|
+
|
|
43
|
+
if (!(content instanceof Buffer)) {
|
|
44
|
+
if (content instanceof Object) {
|
|
45
|
+
content = JSON.stringify(content)
|
|
46
|
+
serverResponse.writeHeader('Content-Type', 'application/json')
|
|
47
|
+
}
|
|
48
|
+
content = String(content ?? '')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Object.keys(response.headers).forEach(header => {
|
|
52
|
+
const headerValue = response.headers[header]
|
|
53
|
+
if (headerValue) {
|
|
54
|
+
serverResponse.writeHeader(header, String(headerValue))
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
serverResponse.end(content)
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.log('error ->', error)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function uNetworkingServer(port: number, handler: (request: Request) => Promise<Response>) {
|
|
64
|
+
const app = App({ // or maybe SSLApp?
|
|
65
|
+
// key_file_name: 'misc/key.pem',
|
|
66
|
+
// cert_file_name: 'misc/cert.pem',
|
|
67
|
+
// passphrase: '1234'
|
|
68
|
+
})
|
|
69
|
+
.any('*', async (response, request) => {
|
|
70
|
+
await handleRequest(response, request, handler)
|
|
71
|
+
})
|
|
72
|
+
.any('/*', async (response, request) => {
|
|
73
|
+
await handleRequest(response, request, handler)
|
|
74
|
+
})
|
|
75
|
+
.listen(port, (token) => {
|
|
76
|
+
if (!token) {
|
|
77
|
+
console.log('Failed to listen to port ' + port)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Helper function for reading a posted JSON body */
|
|
83
|
+
function readJson(response: HttpResponse, contentType: string) {
|
|
84
|
+
let buffer: Buffer
|
|
85
|
+
return new Promise<object | string | undefined>((resolve, reject) => {
|
|
86
|
+
response.onData((ab, isLast) => {
|
|
87
|
+
const chunk = Buffer.from(ab)
|
|
88
|
+
if (!isLast) {
|
|
89
|
+
buffer = Buffer.concat(buffer ? [buffer, chunk] : [chunk])
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const content = (buffer ? Buffer.concat([buffer, chunk]) : chunk)
|
|
94
|
+
if (contentType === 'application/json') {
|
|
95
|
+
try {
|
|
96
|
+
const json = JSON.parse(content.toString())
|
|
97
|
+
resolve(json)
|
|
98
|
+
} catch (e) {
|
|
99
|
+
resolve({})
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
resolve(content)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function readForm(response: HttpResponse, headers: any): Promise<[string | object, Record<string, File | File[]>]> {
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
const formDataParser = busboy({headers})
|
|
112
|
+
const files: Record<string, File | File[]> = {}
|
|
113
|
+
const content: Record<string, string> = {}
|
|
114
|
+
|
|
115
|
+
formDataParser.on('file', function(fieldName: string, file: Readable, {filename, encoding, mimeType}: FileInfo) {
|
|
116
|
+
const extension = filename.split('.').pop()
|
|
117
|
+
const fileInstance = new File(`upload_${new Date().getTime().toString()}_${String.randomAlphaNum(9)}.${extension}`)
|
|
118
|
+
|
|
119
|
+
fileInstance.originalName = filename
|
|
120
|
+
fileInstance.directory = os.tmpdir()
|
|
121
|
+
fileInstance.saved = true
|
|
122
|
+
|
|
123
|
+
const stream = fs.createWriteStream(fileInstance.path)
|
|
124
|
+
stream.on('error', reject)
|
|
125
|
+
|
|
126
|
+
file.on('data', function(data) {
|
|
127
|
+
stream.write(data)
|
|
128
|
+
})
|
|
129
|
+
file.on('end', function() {
|
|
130
|
+
stream.end()
|
|
131
|
+
|
|
132
|
+
const field = files[fieldName]
|
|
133
|
+
files[fieldName] = field instanceof Array ? [...field, fileInstance] : field ? [field, fileInstance] : fileInstance
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
formDataParser.on('field', function(fieldName, value) {
|
|
137
|
+
content[fieldName] = value
|
|
138
|
+
})
|
|
139
|
+
formDataParser.on('finish', function() {
|
|
140
|
+
resolve([content, files])
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
response.onData((ab, isLast) => {
|
|
144
|
+
formDataParser.write(Buffer.from(ab.slice(0)))
|
|
145
|
+
if (isLast) {
|
|
146
|
+
formDataParser.end()
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Container, Inject } from '../../Container'
|
|
2
|
+
import { RequestHandler, Router } from '../../Router'
|
|
3
|
+
import { Response } from '../Http'
|
|
4
|
+
import { Request as BaseRequest } from '../Request'
|
|
5
|
+
import { Abstract, Constructor, Type } from '../../Support'
|
|
6
|
+
import { WebsocketRoute } from './WebsocketRoute'
|
|
7
|
+
import { Request } from '@Typetron/Router/Websockets/Request'
|
|
8
|
+
import { WebsocketMiddleware } from '@Typetron/Router/Websockets/Middleware'
|
|
9
|
+
|
|
10
|
+
export class Handler {
|
|
11
|
+
@Inject()
|
|
12
|
+
router: Router
|
|
13
|
+
|
|
14
|
+
onOpen?: WebsocketRoute
|
|
15
|
+
onClose?: WebsocketRoute
|
|
16
|
+
|
|
17
|
+
addAction(
|
|
18
|
+
name: string,
|
|
19
|
+
controller: Constructor,
|
|
20
|
+
actionName: string,
|
|
21
|
+
parametersTypes: (Type<(...args: any[]) => any> | FunctionConstructor)[] = [],
|
|
22
|
+
middleware: Abstract<WebsocketMiddleware>[] = []
|
|
23
|
+
) {
|
|
24
|
+
const action = new WebsocketRoute(
|
|
25
|
+
name,
|
|
26
|
+
controller,
|
|
27
|
+
actionName,
|
|
28
|
+
parametersTypes,
|
|
29
|
+
middleware
|
|
30
|
+
)
|
|
31
|
+
if (this.router.actions.has(name)) {
|
|
32
|
+
throw new Error(`There is already an action with the same name: '${name}'`)
|
|
33
|
+
}
|
|
34
|
+
this.router.actions.set(name, action)
|
|
35
|
+
return action
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async handle<T = unknown>(container: Container, request: Request): Promise<Response<T>> {
|
|
39
|
+
|
|
40
|
+
container.set(BaseRequest, request)
|
|
41
|
+
container.set(Request, request)
|
|
42
|
+
|
|
43
|
+
let stack: RequestHandler = async () => {
|
|
44
|
+
const route = this.router.actions.get(request.name)
|
|
45
|
+
|
|
46
|
+
if (!route) {
|
|
47
|
+
throw new Error(`Action '${request.name}' not found`)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
container.set(WebsocketRoute, route)
|
|
51
|
+
|
|
52
|
+
let routeStack: RequestHandler = async () => {
|
|
53
|
+
const content = await route.run(container, request.parameters)
|
|
54
|
+
|
|
55
|
+
if (content instanceof Response) {
|
|
56
|
+
return content
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return Response.ok(content)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
route.middleware.forEach(middlewareClass => {
|
|
63
|
+
const middleware = container.get(middlewareClass)
|
|
64
|
+
routeStack = middleware.handle.bind(middleware, request, routeStack)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return await routeStack(request)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.router.middleware.global?.forEach(middlewareClass => {
|
|
71
|
+
const middleware = container.get(middlewareClass)
|
|
72
|
+
stack = middleware.handle.bind(middleware, request, stack)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
this.router.middleware.websocket?.forEach(middlewareClass => {
|
|
76
|
+
const middleware = container.get(middlewareClass)
|
|
77
|
+
stack = middleware.handle.bind(middleware, request, stack)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
return await stack(request) as Response<T>
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Request } from '@Typetron/Router/Websockets/Request'
|
|
2
|
+
import { GlobalMiddleware, RequestHandler } from '@Typetron/Router'
|
|
3
|
+
import { Response } from '@Typetron/Router/Http' // TODO remove any Http reference from websockets
|
|
4
|
+
|
|
5
|
+
export abstract class WebsocketMiddleware extends GlobalMiddleware {
|
|
6
|
+
abstract handle(request: Request, next: RequestHandler): Promise<Response>
|
|
7
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Container } from '../../Container'
|
|
2
|
+
import { AnyFunction, Constructor, Type } from '../../Support'
|
|
3
|
+
import { ControllerMetadata, MethodMetadata } from '../Metadata'
|
|
4
|
+
import { Route } from '@Typetron/Router'
|
|
5
|
+
|
|
6
|
+
export class WebsocketRoute extends Route {
|
|
7
|
+
|
|
8
|
+
async run(container: Container, requestParameters: Record<string, unknown>): Promise<object | string> {
|
|
9
|
+
const controller = await container.get(this.controller)
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const metadata: MethodMetadata | undefined = ControllerMetadata.get(this.controller).actions[this.controllerMethod]
|
|
13
|
+
const parameters = await this.resolveParameters(
|
|
14
|
+
requestParameters,
|
|
15
|
+
this.parametersTypes,
|
|
16
|
+
metadata?.parametersOverrides ?? [],
|
|
17
|
+
container
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
for await (const guardClass of this.guards) {
|
|
21
|
+
const guard = container.get(guardClass)
|
|
22
|
+
if (!await guard.condition(...parameters)) {
|
|
23
|
+
guard.onFail()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (controller[this.controllerMethod as keyof Constructor] as AnyFunction).apply(controller, parameters)
|
|
28
|
+
} catch (unknownError) {
|
|
29
|
+
const error = unknownError as Error
|
|
30
|
+
error.stack = `Controller: ${controller.constructor.name}.${this.controllerMethod} \n at ` + error.stack
|
|
31
|
+
throw error
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private async resolveParameters(
|
|
36
|
+
requestParameters: Record<string, unknown>,
|
|
37
|
+
parametersTypes: (Type<AnyFunction> | FunctionConstructor)[],
|
|
38
|
+
overrides: AnyFunction[],
|
|
39
|
+
container: Container
|
|
40
|
+
) {
|
|
41
|
+
let parameterIndex = 0
|
|
42
|
+
// tslint:disable-next-line:no-any
|
|
43
|
+
const routeParameters: any[] = Object.values(requestParameters)
|
|
44
|
+
return parametersTypes.mapAsync(async (parameter, index) => {
|
|
45
|
+
const newValueFunction = overrides[index]
|
|
46
|
+
if (newValueFunction) {
|
|
47
|
+
return await newValueFunction.call(undefined, container)
|
|
48
|
+
}
|
|
49
|
+
if (parameter.name === 'String') {
|
|
50
|
+
return routeParameters[parameterIndex++]
|
|
51
|
+
}
|
|
52
|
+
if (parameter.name === 'Number') {
|
|
53
|
+
const value = routeParameters[parameterIndex++]
|
|
54
|
+
return value === undefined || value === 'undefined' ? undefined : Number(value)
|
|
55
|
+
}
|
|
56
|
+
return container.get(parameter, [routeParameters[parameterIndex++]])
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { WebSocket as uWebSocket } from 'uWebSockets.js'
|
|
2
|
+
import { ActionResponse, WebsocketMessageStatus } from './types'
|
|
3
|
+
import { Container } from '@Typetron/Container'
|
|
4
|
+
|
|
5
|
+
export * from './Handler'
|
|
6
|
+
export * from './types'
|
|
7
|
+
|
|
8
|
+
export class WebSocket {
|
|
9
|
+
|
|
10
|
+
public connection: uWebSocket<any> & {container: Container}
|
|
11
|
+
id?: number | string
|
|
12
|
+
|
|
13
|
+
constructor(connection: uWebSocket<any>, public container: Container) {
|
|
14
|
+
this.connection = Object.assign(connection, {container})
|
|
15
|
+
this.reset(container)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
subscribe(topic: string) {
|
|
19
|
+
this.connection.subscribe(topic)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
unsubscribe(topic: string) {
|
|
23
|
+
this.connection.unsubscribe(topic)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
reset(container?: Container) {
|
|
27
|
+
this.connection.container = container || this.connection.container.parent?.createChildContainer() || new Container()
|
|
28
|
+
this.connection.container.set(WebSocket, this)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// tslint:disable-next-line:no-any
|
|
32
|
+
publish(topic: string, action: string, body?: unknown) {
|
|
33
|
+
const sentResponse: ActionResponse<unknown> = {
|
|
34
|
+
action,
|
|
35
|
+
message: body,
|
|
36
|
+
status: WebsocketMessageStatus.OK,
|
|
37
|
+
}
|
|
38
|
+
this.connection.publish(topic, JSON.stringify(sentResponse), false, true)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// tslint:disable-next-line:no-any
|
|
42
|
+
publishAndSend(topic: string, action: string, body?: unknown) {
|
|
43
|
+
this.publish(topic, action, body)
|
|
44
|
+
this.send(action, body)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// tslint:disable-next-line:no-any
|
|
48
|
+
send(action: string, body?: unknown) {
|
|
49
|
+
const sentResponse: ActionResponse<unknown> = {
|
|
50
|
+
action,
|
|
51
|
+
message: body,
|
|
52
|
+
status: WebsocketMessageStatus.OK,
|
|
53
|
+
}
|
|
54
|
+
this.connection.send(JSON.stringify(sentResponse), false, true)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export enum WebsocketMessageStatus {
|
|
2
|
+
OK = 'OK',
|
|
3
|
+
Error = 'Error',
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface Action<T> {
|
|
7
|
+
action: string
|
|
8
|
+
message?: T
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ActionRequestMessage {
|
|
12
|
+
parameters?: (string | number)[]
|
|
13
|
+
// tslint:disable-next-line:no-any
|
|
14
|
+
body?: any
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ActionRequest = Action<ActionRequestMessage>
|
|
18
|
+
|
|
19
|
+
export interface ActionResponse<T> extends Action<T> {
|
|
20
|
+
status: WebsocketMessageStatus
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ActionErrorResponse<T extends string = string> = ActionResponse<{message: T, stack: string}>
|
package/Router/index.ts
ADDED
package/Storage/File.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as path from 'path'
|
|
2
|
+
|
|
3
|
+
export class File {
|
|
4
|
+
// content?: string | Buffer
|
|
5
|
+
originalName?: string
|
|
6
|
+
|
|
7
|
+
saved = false
|
|
8
|
+
|
|
9
|
+
constructor(public name: string, public directory?: string) {
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get extension() {
|
|
13
|
+
return this.guessExtension()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get path() {
|
|
17
|
+
return path.join(this.directory || '', this.name)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
guessExtension() {
|
|
21
|
+
const fileParts = this.name.split('.')
|
|
22
|
+
if (fileParts.length >= 1) {
|
|
23
|
+
return fileParts.pop()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
toString() {
|
|
28
|
+
return this.name
|
|
29
|
+
}
|
|
30
|
+
}
|
package/Storage/Image.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { File } from './File'
|
|
2
|
+
|
|
3
|
+
export class Image extends File {
|
|
4
|
+
// static fromBase64(base64: string): Image {
|
|
5
|
+
// const matches = base64.match(/data:image\/([a-zA-Z]*);base64,([^\\"]*)/)
|
|
6
|
+
// if (!matches) {
|
|
7
|
+
// throw new InvalidImage()
|
|
8
|
+
// }
|
|
9
|
+
// if (matches.length !== 3) {
|
|
10
|
+
// throw InvalidImage
|
|
11
|
+
// }
|
|
12
|
+
// const extension = matches[1]
|
|
13
|
+
// const data = matches[2]
|
|
14
|
+
//
|
|
15
|
+
// const image = new this('.' + extension)
|
|
16
|
+
// image.content = Buffer.from(data, 'base64')
|
|
17
|
+
// return image
|
|
18
|
+
// }
|
|
19
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as fileSystemDeprecated from 'fs'
|
|
2
|
+
import { File } from './File'
|
|
3
|
+
import * as path from 'path'
|
|
4
|
+
import * as fileSystem from 'node:fs/promises'
|
|
5
|
+
|
|
6
|
+
export class Storage {
|
|
7
|
+
files(directory: string, recursively = false): File[] {
|
|
8
|
+
return fileSystemDeprecated
|
|
9
|
+
.readdirSync(directory) // TODO make this async
|
|
10
|
+
.reduce((files, name) => {
|
|
11
|
+
const filePath = path.join(directory, name)
|
|
12
|
+
const stats = fileSystemDeprecated.statSync(filePath)
|
|
13
|
+
if (stats.isFile()) {
|
|
14
|
+
const file = new File(name)
|
|
15
|
+
file.directory = directory
|
|
16
|
+
files.push(file)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (recursively && stats.isDirectory()) {
|
|
20
|
+
files = files.concat(this.files(filePath))
|
|
21
|
+
}
|
|
22
|
+
return files
|
|
23
|
+
}, [] as File[])
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async read(filePath: string): Promise<Buffer> {
|
|
27
|
+
return fileSystem.readFile(filePath)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async exists(filePath: string): Promise<boolean> {
|
|
31
|
+
try {
|
|
32
|
+
await fileSystem.access(filePath)
|
|
33
|
+
return true
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async save(file: File, directory: string = '', name?: string | number): Promise<File> {
|
|
40
|
+
const newPath = path.join(directory, file.name)
|
|
41
|
+
if (!file.name) {
|
|
42
|
+
file.name = String(name || this.generateRandomFileName() + '.' + file.extension)
|
|
43
|
+
}
|
|
44
|
+
if (directory && !(await this.exists(directory))) {
|
|
45
|
+
await this.makeDirectory(directory)
|
|
46
|
+
}
|
|
47
|
+
return new Promise(async (resolve, reject) => {
|
|
48
|
+
fileSystemDeprecated
|
|
49
|
+
.createReadStream(file.path)
|
|
50
|
+
.on('error', reject)
|
|
51
|
+
.pipe(fileSystemDeprecated.createWriteStream(newPath))
|
|
52
|
+
.on('finish', () => resolve(file))
|
|
53
|
+
.on('error', reject)
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async put(filePath: string, content: string | Buffer, options?: any): Promise<File> {
|
|
58
|
+
const file = new File(path.basename(filePath))
|
|
59
|
+
file.directory = path.dirname(filePath)
|
|
60
|
+
|
|
61
|
+
if (file.directory && !(await this.exists(file.directory))) {
|
|
62
|
+
await this.makeDirectory(file.directory)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await fileSystem.writeFile(filePath, content, options)
|
|
66
|
+
return file
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async makeDirectory(directory: string): Promise<void> {
|
|
70
|
+
await fileSystem.mkdir(directory, {recursive: true})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async deleteDirectory(directory: string): Promise<void> {
|
|
74
|
+
if (await this.exists(directory)) {
|
|
75
|
+
await fileSystem.rm(directory, {recursive: true})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async delete(filePath: string): Promise<void> {
|
|
80
|
+
if (!(await this.exists(filePath))) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if ((await fileSystem.stat(filePath)).isDirectory()) {
|
|
85
|
+
throw new Error('Can not delete because this path leads to a directory and not a file.')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
await fileSystem.unlink(filePath)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private generateRandomFileName(): string {
|
|
92
|
+
const stringDomain =
|
|
93
|
+
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
|
|
94
|
+
return (
|
|
95
|
+
String.random(13, stringDomain) + '-' + new Date().getTime().toString()
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
}
|
package/Storage/index.ts
ADDED
package/Storage/utils.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { InvalidImage } from '@Typetron/Storage/InvalidImage'
|
|
2
|
+
|
|
3
|
+
export function fromBase64(base64: string): {extension: string, content: Buffer} {
|
|
4
|
+
const matches = base64.match(/data:image\/([a-zA-Z]*);base64,([^\\"]*)/)
|
|
5
|
+
if (!matches) {
|
|
6
|
+
throw new InvalidImage()
|
|
7
|
+
}
|
|
8
|
+
if (matches.length !== 3) {
|
|
9
|
+
throw InvalidImage
|
|
10
|
+
}
|
|
11
|
+
const extension = matches[1]
|
|
12
|
+
const data = matches[2]
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
content: Buffer.from(data, 'base64'),
|
|
16
|
+
extension: extension
|
|
17
|
+
}
|
|
18
|
+
}
|