@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.
Files changed (195) hide show
  1. package/.eslintrc-old.js +208 -0
  2. package/Cache/Cache.ts +13 -0
  3. package/Cache/Stores/DatabaseStore.ts +120 -0
  4. package/Cache/Stores/FileStore.ts +69 -0
  5. package/Cache/Stores/MemoryStore.ts +71 -0
  6. package/Cache/index.ts +5 -0
  7. package/Cache/types.ts +4 -0
  8. package/Container/ClassResolver.ts +106 -0
  9. package/Container/Container.ts +144 -0
  10. package/Container/Contracts.ts +3 -0
  11. package/Container/Decorators.ts +20 -0
  12. package/Container/Metadata.ts +16 -0
  13. package/Container/Resolver.ts +27 -0
  14. package/Container/index.ts +5 -0
  15. package/Database/Clauses/Where.ts +0 -0
  16. package/Database/Connection.ts +52 -0
  17. package/Database/Decorators.ts +142 -0
  18. package/Database/Drivers/DatabaseDriver.ts +37 -0
  19. package/Database/Drivers/MySQL/Alter/index.ts +55 -0
  20. package/Database/Drivers/MySQL/ColumnDefinition.ts +16 -0
  21. package/Database/Drivers/MySQL/Create.ts +47 -0
  22. package/Database/Drivers/MySQL/MysqlDriver.ts +123 -0
  23. package/Database/Drivers/MySQL/Schema.ts +82 -0
  24. package/Database/Drivers/MySQL/index.ts +13 -0
  25. package/Database/Drivers/SQL/Schema.ts +73 -0
  26. package/Database/Drivers/SQL/Statements/Alter.ts +22 -0
  27. package/Database/Drivers/SQL/Statements/BaseStatement.ts +55 -0
  28. package/Database/Drivers/SQL/Statements/Delete.ts +20 -0
  29. package/Database/Drivers/SQL/Statements/Insert.ts +24 -0
  30. package/Database/Drivers/SQL/Statements/Select.ts +55 -0
  31. package/Database/Drivers/SQL/Statements/Update.ts +33 -0
  32. package/Database/Drivers/SQL/index.ts +8 -0
  33. package/Database/Drivers/SQLite/Create.ts +45 -0
  34. package/Database/Drivers/SQLite/Schema.ts +54 -0
  35. package/Database/Drivers/SQLite/SqliteDriver.ts +144 -0
  36. package/Database/Drivers/SQLite/index.ts +15 -0
  37. package/Database/Drivers/SchemaContract.ts +11 -0
  38. package/Database/Drivers/Statement.ts +16 -0
  39. package/Database/Drivers/index.ts +4 -0
  40. package/Database/Entity.ts +377 -0
  41. package/Database/EntityNotFoundError.ts +5 -0
  42. package/Database/EntityProxyHandler.ts +43 -0
  43. package/Database/EntityQuery.ts +197 -0
  44. package/Database/Expression.ts +3 -0
  45. package/Database/Fields.ts +816 -0
  46. package/Database/Helpers.ts +12 -0
  47. package/Database/List.ts +71 -0
  48. package/Database/Migrations/Migration.ts +10 -0
  49. package/Database/Migrations/MigrationHistory.ts +20 -0
  50. package/Database/Migrations/Migrator.ts +122 -0
  51. package/Database/Migrations/index.ts +3 -0
  52. package/Database/ORM/BaseRelationship.ts +10 -0
  53. package/Database/Query.ts +374 -0
  54. package/Database/Seeders/Seeder.ts +3 -0
  55. package/Database/Seeders/SeederManager.ts +27 -0
  56. package/Database/Seeders/index.ts +2 -0
  57. package/Database/StringExpression.ts +9 -0
  58. package/Database/Types.ts +165 -0
  59. package/Database/index.ts +23 -0
  60. package/Encryption/index.ts +51 -0
  61. package/Forms/Decorators.ts +29 -0
  62. package/Forms/Form.ts +70 -0
  63. package/Forms/FormFields.ts +36 -0
  64. package/Forms/index.ts +3 -0
  65. package/Framework/App.ts +10 -0
  66. package/Framework/Application.ts +118 -0
  67. package/Framework/Auth/Auth.ts +122 -0
  68. package/Framework/Auth/Authenticatable.ts +9 -0
  69. package/Framework/Auth/User.ts +27 -0
  70. package/Framework/Auth/index.ts +29 -0
  71. package/Framework/Config/AppConfig.ts +25 -0
  72. package/Framework/Config/AuthConfig.ts +9 -0
  73. package/Framework/Config/BaseConfig.ts +9 -0
  74. package/Framework/Config/CacheConfig.ts +38 -0
  75. package/Framework/Config/DatabaseConfig.ts +21 -0
  76. package/Framework/Config/MailConfig.ts +30 -0
  77. package/Framework/Config/index.ts +6 -0
  78. package/Framework/Middleware/AuthMiddleware.ts +31 -0
  79. package/Framework/Middleware/CorsMiddleware.ts +32 -0
  80. package/Framework/Middleware/StaticAssetsMiddleware.ts +76 -0
  81. package/Framework/Middleware/index.ts +2 -0
  82. package/Framework/Provider.ts +9 -0
  83. package/Framework/Providers/CacheProvider.ts +28 -0
  84. package/Framework/Providers/DatabaseProvider.ts +36 -0
  85. package/Framework/Providers/MailProvider.ts +31 -0
  86. package/Framework/Providers/WebsocketsProvider.ts +120 -0
  87. package/Framework/Resolvers/AuthResolver.ts +23 -0
  88. package/Framework/Resolvers/EntityResolver.ts +41 -0
  89. package/Framework/Resolvers/FormResolver.ts +37 -0
  90. package/Framework/RootDir.ts +1 -0
  91. package/Framework/index.ts +8 -0
  92. package/Frontend/Angular/index.ts +24 -0
  93. package/Frontend/Websockets/index.ts +86 -0
  94. package/Mail/MailAddress.ts +4 -0
  95. package/Mail/MailTransport.ts +6 -0
  96. package/Mail/Mailable.ts +16 -0
  97. package/Mail/Mailer.ts +27 -0
  98. package/Mail/PendingMail.ts +68 -0
  99. package/Mail/SentMessage.ts +20 -0
  100. package/Mail/Transports/MemoryTransport.ts +13 -0
  101. package/Mail/Transports/SendGridTransport.ts +95 -0
  102. package/Mail/index.ts +3 -0
  103. package/Models/Model.ts +41 -0
  104. package/Models/index.ts +54 -0
  105. package/Router/BaseController.ts +7 -0
  106. package/Router/Decorators.ts +224 -0
  107. package/Router/Guard.ts +10 -0
  108. package/Router/Http/Contracts.ts +4 -0
  109. package/Router/Http/ErrorHandler.ts +45 -0
  110. package/Router/Http/ErrorHandlerInterface.ts +6 -0
  111. package/Router/Http/Errors/HttpError.ts +15 -0
  112. package/Router/Http/Handler.ts +183 -0
  113. package/Router/Http/HttpRoute.ts +110 -0
  114. package/Router/Http/Middleware.ts +7 -0
  115. package/Router/Http/Request.ts +225 -0
  116. package/Router/Http/Response.ts +35 -0
  117. package/Router/Http/index.ts +85 -0
  118. package/Router/Metadata.ts +34 -0
  119. package/Router/Middleware.ts +16 -0
  120. package/Router/Request.ts +65 -0
  121. package/Router/Route.ts +16 -0
  122. package/Router/RouteNotFoundError.ts +5 -0
  123. package/Router/Router.ts +35 -0
  124. package/Router/Servers/index.ts +6 -0
  125. package/Router/Servers/node.ts +49 -0
  126. package/Router/Servers/uNetworking.ts +151 -0
  127. package/Router/Websockets/Handler.ts +82 -0
  128. package/Router/Websockets/Middleware.ts +7 -0
  129. package/Router/Websockets/Request.ts +3 -0
  130. package/Router/Websockets/WebsocketRoute.ts +61 -0
  131. package/Router/Websockets/index.ts +57 -0
  132. package/Router/Websockets/types.ts +23 -0
  133. package/Router/index.ts +6 -0
  134. package/Storage/File.ts +30 -0
  135. package/Storage/Image.ts +19 -0
  136. package/Storage/InvalidImage.ts +3 -0
  137. package/Storage/Storage.ts +98 -0
  138. package/Storage/index.ts +4 -0
  139. package/Storage/utils.ts +18 -0
  140. package/Support/Array.ts +184 -0
  141. package/Support/Math.ts +12 -0
  142. package/Support/Metadata.ts +31 -0
  143. package/Support/String.ts +38 -0
  144. package/Support/index.ts +19 -0
  145. package/Support/utils.ts +7 -0
  146. package/Testing/TestCase.ts +193 -0
  147. package/Validation/Rule.ts +9 -0
  148. package/Validation/RuleInterface.ts +10 -0
  149. package/Validation/Rules/Email.ts +20 -0
  150. package/Validation/Rules/FileExtension.ts +21 -0
  151. package/Validation/Rules/MaxLength.ts +17 -0
  152. package/Validation/Rules/MinLength.ts +18 -0
  153. package/Validation/Rules/Optional.ts +18 -0
  154. package/Validation/Rules/Required.ts +29 -0
  155. package/Validation/Validator.ts +7 -0
  156. package/Validation/index.ts +12 -0
  157. package/benchmark-index.js +20 -0
  158. package/benchmark.ts +47 -0
  159. package/package.json +7 -3
  160. package/tests/Cache/DatabaseStoreTest.ts +110 -0
  161. package/tests/Cache/FileStoreTest.ts +93 -0
  162. package/tests/Cache/MemoryStoreTest.ts +92 -0
  163. package/tests/Container/ContainerTest.ts +336 -0
  164. package/tests/Database/Drivers/MySQL/AlterTest.ts +100 -0
  165. package/tests/Database/Drivers/MySQL/CreateTest.ts +39 -0
  166. package/tests/Database/Drivers/MysqlDriverTest.ts +140 -0
  167. package/tests/Database/Drivers/SqliteDriverTest.ts +126 -0
  168. package/tests/Database/Entities/Article.ts +21 -0
  169. package/tests/Database/Entities/Profile.ts +21 -0
  170. package/tests/Database/Entities/Role.ts +15 -0
  171. package/tests/Database/Entities/User.ts +35 -0
  172. package/tests/Database/EntityRelationshipsTest.ts +475 -0
  173. package/tests/Database/EntityTest.ts +202 -0
  174. package/tests/Database/Migrations/RunnerTest.ts +96 -0
  175. package/tests/Database/Migrations/migrations/1.createUserTable.ts +12 -0
  176. package/tests/Database/Migrations/migrations/2.createArticlesTable.ts +12 -0
  177. package/tests/Database/QueryTest.ts +573 -0
  178. package/tests/Database/SchemaTest.ts +32 -0
  179. package/tests/Database/Seeders/RunnerTest.ts +30 -0
  180. package/tests/Database/Seeders/seeders/RandomSeeder.ts +10 -0
  181. package/tests/Database/utils.ts +16 -0
  182. package/tests/Forms/FormsTest.ts +59 -0
  183. package/tests/Framework/Middleware/StaticAssetsMiddlewareTest.ts +129 -0
  184. package/tests/Framework/Resolvers/EntityResolverTest.ts +48 -0
  185. package/tests/Framework/Router/RouterTest.ts +33 -0
  186. package/tests/Mail/MailerTest.ts +76 -0
  187. package/tests/Model/ModelTest.ts +183 -0
  188. package/tests/Router/HttpHandlerTest.ts +72 -0
  189. package/tests/Router/RequestTest.ts +77 -0
  190. package/tests/Router/WebSocketsHandlerTest.ts +51 -0
  191. package/tests/Storage/StorageTest.ts +97 -0
  192. package/tests/Support/ArrayTest.ts +167 -0
  193. package/tests/Support/StringTest.ts +18 -0
  194. package/tests/Validation/RulesTest.ts +54 -0
  195. package/types.d.ts +8 -0
@@ -0,0 +1,165 @@
1
+ import { Query } from './Query'
2
+ import { StringExpression } from './StringExpression'
3
+ import { EntityQuery } from './EntityQuery'
4
+
5
+ export enum Operators {
6
+ '=' = '=',
7
+ '<' = '<',
8
+ '<=' = '<=',
9
+ '>' = '>',
10
+ '>=' = '>=',
11
+ '!=' = '!=',
12
+ 'AND' = 'AND',
13
+ 'OR' = 'OR',
14
+ 'IS' = 'IS',
15
+ 'IN' = 'IN',
16
+ 'NOT IN' = 'NOT IN',
17
+ 'LIKE' = 'LIKE',
18
+ 'IS NOT' = 'IS NOT',
19
+ }
20
+
21
+ export enum JoinType {
22
+ INNER = 'INNER',
23
+ LEFT = 'LEFT',
24
+ RIGHT = 'RIGHT'
25
+ }
26
+
27
+ export type BooleanOperator = 'AND' | 'OR' | 'XOR';
28
+ export type Operator = keyof typeof Operators;
29
+ export type SqlValue = string | number | Date | undefined;
30
+ export type SqlValueMap = {[key: string]: SqlValue};
31
+
32
+ class JoinClause {
33
+ type: keyof typeof JoinType
34
+ table: string
35
+ first: string
36
+ operator: Operator
37
+ second: string
38
+ }
39
+
40
+ export interface BaseComponents {
41
+ table: string;
42
+ }
43
+
44
+ export interface CreateComponents {
45
+ table: string;
46
+ columns: (string | StringExpression)[];
47
+ }
48
+
49
+ export interface SelectComponents extends BaseComponents {
50
+ distinct?: boolean;
51
+ columns: (string | StringExpression)[];
52
+ aggregate?: {
53
+ function: string,
54
+ columns: string[];
55
+ };
56
+ joins: JoinClause[];
57
+ wheres: SqlClause[];
58
+ groups: string[];
59
+ orders: [string, Direction][];
60
+ having: Where[];
61
+ limit?: {from: number, count?: number};
62
+ }
63
+
64
+ export interface InsertComponents extends BaseComponents {
65
+ insert?: SqlValueMap[];
66
+ }
67
+
68
+ export interface UpdateComponents extends BaseComponents {
69
+ update?: SqlValueMap;
70
+ }
71
+
72
+ export interface Components extends BaseComponents, SelectComponents, InsertComponents, UpdateComponents, CreateComponents {
73
+ }
74
+
75
+ // export interface SelectComponents extends Components {
76
+ // distinct = false;
77
+ // select: string[] = ['*'];
78
+ // insert?: {};
79
+ // delete?: boolean;
80
+ // joins?: JoinClause[];
81
+ // wheres?: Where[];
82
+ // orderBy?: OrderBy[];
83
+ // }
84
+
85
+ export type WhereFunction = (query: Query) => void;
86
+ export type WhereCondition = {[key: string]: string | number};
87
+ // export type WhereValue = SqlValue | WhereFunction | WhereCondition;
88
+ export type WhereValue = SqlValue;
89
+
90
+ export type Direction = 'ASC' | 'DESC';
91
+
92
+ export interface SqlClause {
93
+ toSql(): string;
94
+
95
+ getValues(): SqlValue[];
96
+ }
97
+
98
+ class SqlExpression implements SqlClause {
99
+ constructor(private sql: string, private values: SqlValue[] = []) {}
100
+
101
+ toSql() {
102
+ return this.sql
103
+ }
104
+
105
+ getValues() {
106
+ return this.values
107
+ }
108
+ }
109
+
110
+ export class WhereExpression extends SqlExpression {
111
+ constructor(public column: string, public expression: SqlExpression, public boolean: BooleanOperator) {
112
+ super(`${boolean} ${column} ${expression.toSql()}`, expression.getValues())
113
+ }
114
+ }
115
+
116
+ export class Where extends WhereExpression {
117
+ constructor(column: string, operator: Operator, value: SqlValue, boolean: BooleanOperator) {
118
+ super(column, new SqlExpression(`${operator} ?`, [value]), boolean)
119
+ }
120
+ }
121
+
122
+ export class WhereNested {
123
+ }
124
+
125
+ export class WhereBetween extends WhereExpression {
126
+ constructor(column: string, values: [SqlValue, SqlValue], boolean: BooleanOperator, not = false) {
127
+ super(column, new SqlExpression(`${not ? 'NOT ' : ''}BETWEEN ? AND ?`, values), boolean)
128
+ }
129
+ }
130
+
131
+ export class WhereLike extends WhereExpression {
132
+ constructor(column: string, value: SqlValue, boolean: BooleanOperator, not = false) {
133
+ super(column, new SqlExpression(`${not ? 'NOT ' : ''}LIKE ?`, [value]), boolean)
134
+ }
135
+ }
136
+
137
+ export class WhereIn extends WhereExpression {
138
+ constructor(column: string, values: WhereValue[], boolean: BooleanOperator, not = false) {
139
+ const value = values.map(() => '?').join(', ')
140
+ if (values.length) {
141
+ super(column, new SqlExpression(`${not ? 'NOT ' : ''}IN (${value})`, values), boolean)
142
+ } else {
143
+ super('', new SqlExpression('1 = 0', values), boolean)
144
+ }
145
+ }
146
+ }
147
+
148
+ export class WhereNull extends WhereExpression {
149
+ constructor(column: string, boolean: BooleanOperator, not = false) {
150
+ super(column, new SqlExpression(`IS ${not ? 'NOT ' : ''}NULL`), boolean)
151
+ }
152
+ }
153
+
154
+ export class WhereSubSelect extends WhereExpression {
155
+ constructor(column: string, operator: Operator, public query: Query, boolean: BooleanOperator) {
156
+ super(column, new SqlExpression(`${operator} (${query.toSQL()})`, query.getBindings()), boolean)
157
+ }
158
+ }
159
+
160
+ // tslint:disable-next-line:no-any
161
+ export type RelationsTreeQuery = (query: EntityQuery<any>) => void
162
+
163
+ export interface RelationsTree {
164
+ [key: string]: RelationsTree | RelationsTreeQuery
165
+ }
@@ -0,0 +1,23 @@
1
+ import { ChildKeys, ChildObject, Constructor } from '@Typetron/Support'
2
+ import { Entity } from './Entity'
3
+ import { BaseRelationship } from './ORM/BaseRelationship'
4
+
5
+ export type EntityKeys<T extends Entity> = ChildKeys<T, Entity>
6
+ export type EntityColumns<T extends Entity> = { [P in EntityKeys<T>]: T[P] extends Function ? never : P }[ EntityKeys<T>]
7
+
8
+ export type EntityObject<T extends Entity> = ChildObject<{
9
+ [P in keyof T]: T[P] extends BaseRelationship<infer U> ? U | number : T[P]
10
+ }, Entity>
11
+ export type EntityConstructor<T extends Entity> = typeof Entity & Constructor<T>
12
+ // export type EntityConstructor<T extends Entity> = {[key in keyof typeof Entity]: (typeof Entity)[key]} & Constructor<T>;
13
+ export type DotNotationProperties<T> = string
14
+
15
+ export * from './Query'
16
+ export * from './Entity'
17
+ export * from './Decorators'
18
+ export * from './StringExpression'
19
+ export * from './Expression'
20
+ export * from './Drivers'
21
+ export * from './Fields'
22
+ export * from './Connection'
23
+
@@ -0,0 +1,51 @@
1
+ import { compare, hash } from 'bcryptjs'
2
+ import * as jwt from 'jsonwebtoken'
3
+ import { GetPublicKeyOrSecret, Secret, SignOptions, VerifyErrors, VerifyOptions } from 'jsonwebtoken'
4
+
5
+ export class Crypt {
6
+ hash(value: string, saltRounds: number): Promise<string> {
7
+ return hash(value, saltRounds)
8
+ }
9
+
10
+ compare(value: string, encrypted: string): Promise<boolean> {
11
+ return compare(value, encrypted)
12
+ }
13
+ }
14
+
15
+ export interface JWToken<T> {
16
+ sub: T
17
+ iat: number
18
+ exp: number
19
+ }
20
+
21
+ export class JWT {
22
+ sign(payload: string | Buffer | object, secretOrPrivateKey: Secret, options?: SignOptions) {
23
+ return new Promise<string>((resolve, reject) => {
24
+ jwt.sign(
25
+ payload,
26
+ secretOrPrivateKey,
27
+ options ?? {},
28
+ (error: Error | null, encoded?: string) => {
29
+ error ? reject(error) : resolve(encoded as string)
30
+ }
31
+ )
32
+ })
33
+ }
34
+
35
+ verify<T>(token: string, secretOrPrivateKey: Secret | GetPublicKeyOrSecret, options?: VerifyOptions) {
36
+ return new Promise<JWToken<T>>((resolve, reject) => {
37
+ jwt.verify(
38
+ token,
39
+ secretOrPrivateKey,
40
+ {
41
+ ...options,
42
+ complete: true
43
+ },
44
+ (error: VerifyErrors | null, decoded) => {
45
+ error ? reject(error) : resolve(decoded?.payload as any as JWToken<T>)
46
+ }
47
+ )
48
+ })
49
+ }
50
+
51
+ }
@@ -0,0 +1,29 @@
1
+ import { RuleInterface } from '../Validation'
2
+ import { Type } from '../Support'
3
+ import { FormField } from './FormFields'
4
+ import { Form } from './Form'
5
+
6
+ export const FormMetadataKey = 'form:fields'
7
+
8
+ export function Field<T extends Form>(name?: string) {
9
+ return function(target: T, property: string) {
10
+ const fields: {[key: string]: FormField} = Reflect.getMetadata(FormMetadataKey, target.constructor) || {}
11
+ const type = Reflect.getMetadata('design:type', target, property)
12
+ const field = fields[property] || new FormField(name || property, type)
13
+ field.name = name || property
14
+ fields[property] = field
15
+ Reflect.defineMetadata(FormMetadataKey, fields, target.constructor)
16
+ }
17
+ }
18
+
19
+ // tslint:disable-next-line:no-any
20
+ export function Rules(...rules: (Type<RuleInterface> | ((...args: any[]) => Type<RuleInterface>))[]) {
21
+ return function(target: Object, property: string) {
22
+ const fields: {[key: string]: FormField} = Reflect.getMetadata(FormMetadataKey, target.constructor) || {}
23
+ const type = Reflect.getMetadata('design:type', target, property)
24
+ const field = fields[property] || new FormField(property, type)
25
+ field.rules = rules
26
+ fields[property] = field
27
+ Reflect.defineMetadata(FormMetadataKey, fields, target.constructor)
28
+ }
29
+ }
package/Forms/Form.ts ADDED
@@ -0,0 +1,70 @@
1
+ import { FormMetadataKey } from './Decorators'
2
+ import { RuleValue } from '../Validation'
3
+ import { ChildKeys, ChildObject, Constructor } from '../Support'
4
+ import { FormField } from './FormFields'
5
+ import { Injectable, Scope } from '../Container'
6
+
7
+ export type FormFields<T> = ChildObject<T, Form>;
8
+
9
+ @Injectable(Scope.TRANSIENT)
10
+ export abstract class Form {
11
+
12
+ readonly errors: {[key: string]: Record<string, string>} = {}
13
+
14
+ static fields<T extends Form>(this: Constructor<T>): Record<ChildKeys<T, Form>, FormField> {
15
+ return Reflect.getMetadata(FormMetadataKey, this)
16
+ }
17
+
18
+ fields() {
19
+ return (this.constructor as (Constructor<Form> & typeof Form)).fields()
20
+ }
21
+
22
+ valid() {
23
+ const fields = Object.values(this.fields()) as FormField[]
24
+ fields.forEach(field => {
25
+ const errors = field.validate(this[field.name as keyof FormFields<this>])
26
+ if (errors) {
27
+ this.errors[field.name] = errors
28
+ }
29
+ })
30
+
31
+ return !Object.keys(this.errors).length
32
+ }
33
+
34
+ validated() {
35
+ const fields = Object.values(this.fields()) as FormField[]
36
+ return fields.filter(field => !this.errors[field.name])
37
+ .reduce((obj, field) => {
38
+ const value = this[field.name as keyof this]
39
+ if (value) {
40
+ obj[field.name] = value
41
+ }
42
+ return obj
43
+ }, <{[key: string]: RuleValue}>{})
44
+ }
45
+
46
+ value() {
47
+ const fields = Object.values(this.fields()) as FormField[]
48
+ // tslint:disable-next-line:no-any
49
+ const value: Record<string, any> = {}
50
+ fields.forEach(field => {
51
+ value[field.name] = this[field.name as keyof FormFields<this>]
52
+ })
53
+
54
+ return value
55
+ }
56
+
57
+ fill(data: Partial<FormFields<this>>) {
58
+ const fields = Object.values(this.fields()) as FormField[]
59
+ fields.forEach(field => {
60
+ if (field.name in data) {
61
+ // @ts-ignore
62
+ this[field.name] = data[field.name]
63
+ }
64
+ })
65
+ }
66
+
67
+ toJSON() {
68
+ return this.value()
69
+ }
70
+ }
@@ -0,0 +1,36 @@
1
+ import { Constructor, Type } from '../Support'
2
+ import { Optional, RuleInterface } from '../Validation'
3
+
4
+ export class FormField {
5
+ constructor(
6
+ public name: string,
7
+ // tslint:disable-next-line:no-any
8
+ public type: any,
9
+ // tslint:disable-next-line:no-any
10
+ public rules: (Type<RuleInterface> | ((...args: any[]) => Type<RuleInterface>)) [] = []
11
+ ) {
12
+ }
13
+
14
+ // tslint:disable-next-line:no-any
15
+ validate(value: any): Record<string, string> | undefined {
16
+ const errors: Record<string, string> = {}
17
+ let hasErrors = false
18
+
19
+ if (this.rules.includes(Optional) && (value === undefined || value === null)) {
20
+ return
21
+ }
22
+
23
+ this.rules.forEach(rule => {
24
+ if (!rule.prototype.passes) {
25
+ rule = (rule as Function)()
26
+ }
27
+ const instance = new (rule as Constructor<RuleInterface>)
28
+ if (!instance.passes(this.name, value)) {
29
+ hasErrors = true
30
+ errors[instance.identifier] = instance.message(this.name, value)
31
+ }
32
+ })
33
+
34
+ return hasErrors ? errors : undefined
35
+ }
36
+ }
package/Forms/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './Form'
2
+ export * from './Decorators'
3
+ export * from './FormFields'
@@ -0,0 +1,10 @@
1
+ import { Application } from './Application'
2
+ import { ServiceIdentifier } from '../Container/Contracts'
3
+
4
+ export class App {
5
+ static instance: Application
6
+
7
+ static get<T>(service: ServiceIdentifier<T> | string): T {
8
+ return App.instance.get(service)
9
+ }
10
+ }
@@ -0,0 +1,118 @@
1
+ import { Container } from '../Container'
2
+ import { Type } from '../Support'
3
+ import { App } from './App'
4
+ import { Provider } from './Provider'
5
+ import { AppConfig, AuthConfig, BaseConfig } from './Config'
6
+ import { FormResolver } from './Resolvers/FormResolver'
7
+ import { EntityResolver } from './Resolvers/EntityResolver'
8
+ import { RootDir } from './RootDir'
9
+ import { StaticAssetsMiddleware } from './Middleware/StaticAssetsMiddleware'
10
+ import { ErrorHandler, ErrorHandlerInterface, Handler as HttpHandler, } from '../Router/Http'
11
+ import { Storage } from '../Storage'
12
+ import { AuthResolver } from './Resolvers/AuthResolver'
13
+ import fileSystem from 'fs'
14
+ import path from 'path'
15
+ import { WebsocketsProvider } from './Providers/WebsocketsProvider'
16
+
17
+ export class Application extends Container {
18
+ static defaultConfigDirectory = 'config'
19
+
20
+ constructor(
21
+ public directory: string,
22
+ public configDirectory = Application.defaultConfigDirectory
23
+ ) {
24
+ super()
25
+ Application.setInstance(this)
26
+ this.set(Application, (App.instance = this))
27
+ this.set(Container, this)
28
+ this.set(RootDir, directory)
29
+ }
30
+
31
+ static async create(
32
+ directory: string,
33
+ configDirectory = Application.defaultConfigDirectory
34
+ ) {
35
+ const app = new this(
36
+ fileSystem.realpathSync.native(directory),
37
+ configDirectory
38
+ )
39
+ await app.bootstrap()
40
+ return app
41
+ }
42
+
43
+ async startServer() {
44
+ const httpHandler = this.get(HttpHandler)
45
+
46
+ const appConfig = this.get(AppConfig)
47
+
48
+ if (appConfig.websocketsPort) {
49
+ await this.registerProviders([WebsocketsProvider])
50
+ }
51
+
52
+ return httpHandler.startServer(this)
53
+ }
54
+
55
+ public async registerProviders(providers: Type<Provider>[]) {
56
+ await Promise.all(
57
+ providers.map((provider) => {
58
+ const instance = this.get(provider)
59
+ return instance.register()
60
+ })
61
+ )
62
+ }
63
+
64
+ private async loadConfig(configDirectory: string) {
65
+ const configsPath = path.join(this.directory, configDirectory)
66
+
67
+ const storage = this.get(Storage)
68
+
69
+ if (!(await storage.exists(configsPath))) {
70
+ console.warn(
71
+ `Config path '${configsPath}' does not exist. Running with default config.`
72
+ )
73
+ }
74
+
75
+ storage
76
+ .files(configsPath)
77
+ .whereIn('extension', ['ts'])
78
+ .forEach((file) => {
79
+ const configItem = require(file.path).default as BaseConfig<{}>
80
+ if (configItem && configItem.constructor) {
81
+ configItem.applyNewValues()
82
+ this.set(configItem.constructor, configItem)
83
+ }
84
+ })
85
+ }
86
+
87
+ private registerResolvers() {
88
+ this.resolvers.unshift(new FormResolver(this))
89
+ this.resolvers.unshift(new EntityResolver(this))
90
+ this.resolvers.unshift(new AuthResolver(this))
91
+ }
92
+
93
+ private async bootstrap() {
94
+ await this.loadConfig(this.configDirectory)
95
+
96
+ this.set(ErrorHandlerInterface, this.get(ErrorHandler))
97
+
98
+ const appConfig = this.get(AppConfig)
99
+ await this.checkAppSecret()
100
+
101
+ if (appConfig.staticAssets) {
102
+ appConfig.middleware.http.unshift(StaticAssetsMiddleware)
103
+ }
104
+
105
+ this.registerResolvers()
106
+
107
+ const providers = appConfig.providers || []
108
+
109
+ await this.registerProviders(providers)
110
+ }
111
+
112
+ private async checkAppSecret() {
113
+ const authConfig = this.get(AuthConfig)
114
+ if (!authConfig.signature) {
115
+ throw new Error(`APP_SECRET is not setup in your '.env' file.`)
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,122 @@
1
+ import { Inject, Injectable, Scope } from '../../Container'
2
+ import { User } from './User'
3
+ import { AuthConfig } from '../Config'
4
+ import { EntityKeys, ID } from '../../Database'
5
+ import { Crypt, JWT, JWToken } from '@Typetron/Encryption'
6
+
7
+ @Injectable(Scope.REQUEST)
8
+ export class Auth {
9
+
10
+ @Inject()
11
+ authConfig: AuthConfig
12
+
13
+ @Inject()
14
+ jwt: JWT
15
+
16
+ @Inject()
17
+ crypt: Crypt
18
+
19
+ id?: ID
20
+
21
+ /**
22
+ * This is primarily used in websocket connections so we don't need to pass the auth token for each weboscket message
23
+ */
24
+ expiresAt = new Date()
25
+
26
+ private savedUser?: User
27
+
28
+ get authenticable(): typeof User {
29
+ return this.authConfig.entity
30
+ }
31
+
32
+ // @BeforeRequest() // TODO implement the container's '@BeforeRequest' decorator to do something before each request.
33
+ // For example, load the authenticated user in memory again
34
+ // async before() {
35
+ // if (!this.id) {
36
+ // return undefined
37
+ // throw new Error('You tried to use the Auth service without authenticating the user. ' +
38
+ // 'Please use the AuthMiddleware or authenticate the user before using this route')
39
+ // }
40
+ //
41
+ // const user = await this.authenticable.find(this.id)
42
+ // if (!user) {
43
+ // throw new Error(`Authenticated user with ID ${this.id} not found `)
44
+ // }
45
+ // this.savedUser = await this.authenticable.find(this.id)
46
+ // }
47
+
48
+ async user<T extends User>(): Promise<T> {
49
+ // if (this.savedUser) { // TODO this if statement should be removed using the @BeforeRequest decorator above
50
+ // return this.savedUser as T
51
+ // }
52
+
53
+ if (!this.id) {
54
+ throw new Error('You tried to use the Auth service without authenticating the user. Please use the AuthMiddleware or authenticate the user before using this route')
55
+ }
56
+
57
+ const user = await this.authenticable.find(this.id)
58
+
59
+ if (!user) {
60
+ throw new Error(`Could not find a user with id '${this.id}'`)
61
+ }
62
+
63
+ return this.savedUser = user as T
64
+ }
65
+
66
+ async login(username: string, password: string): Promise<string> {
67
+ const user = await this.authenticable.where((new this.authenticable).getUsername() as EntityKeys<User>, username).first()
68
+ if (!user || !await this.crypt.compare(password, user.password)) {
69
+ throw new Error('Invalid credentials')
70
+ }
71
+
72
+ delete this.savedUser
73
+ return this.sign(this.id = user.id)
74
+ }
75
+
76
+ loginById(id: ID): Promise<string> {
77
+ return this.sign(id)
78
+ }
79
+
80
+ loginUser(authenticable: User): Promise<string> {
81
+ return this.loginById(authenticable[authenticable.getId()])
82
+ }
83
+
84
+ sign(id: ID, expiresAtTimestampInSeconds?: number): Promise<string> {
85
+ return this.jwt.sign(
86
+ {
87
+ sub: this.set(id, expiresAtTimestampInSeconds)
88
+ },
89
+ this.authConfig.signature,
90
+ {
91
+ expiresIn: this.authConfig.duration
92
+ }
93
+ )
94
+ }
95
+
96
+ async register<T extends User>(username: string, password: string): Promise<T> {
97
+ return await this.authenticable.create({
98
+ [(new this.authenticable).getUsername() as EntityKeys<T>]: username,
99
+ password: await this.crypt.hash(password, this.authConfig.saltRounds),
100
+ }) as T
101
+ }
102
+
103
+ async verify(token: string): Promise<JWToken<number>> {
104
+ const payload = await this.jwt.verify<number>(token, this.authConfig.signature)
105
+
106
+ this.set(payload.sub, payload.exp)
107
+
108
+ return payload
109
+ }
110
+
111
+ private set(id: ID, expiresAtTimestampInSeconds?: number): ID {
112
+ if (expiresAtTimestampInSeconds) {
113
+ this.expiresAt = new Date(expiresAtTimestampInSeconds * 1000)
114
+ } else {
115
+ this.expiresAt = new Date()
116
+ this.expiresAt.setSeconds(this.expiresAt.getSeconds() + this.authConfig.duration)
117
+ }
118
+
119
+ return this.id = id
120
+ }
121
+
122
+ }
@@ -0,0 +1,9 @@
1
+ import { Entity, EntityColumns } from '../../Database'
2
+
3
+ export interface Authenticatable<T extends Entity> {
4
+ getId(): EntityColumns<T>;
5
+
6
+ getUsername(): EntityColumns<T>;
7
+
8
+ getPassword(): EntityColumns<T>;
9
+ }
@@ -0,0 +1,27 @@
1
+ import { Column, Entity, EntityColumns, ID, PrimaryColumn } from '../../Database'
2
+ import { Authenticatable } from './Authenticatable'
3
+
4
+ export class User extends Entity implements Authenticatable<User> {
5
+
6
+ @PrimaryColumn()
7
+ id: ID
8
+
9
+ @Column()
10
+ email: string
11
+
12
+ @Column()
13
+ password: string
14
+
15
+ getId(): EntityColumns<User> {
16
+ return 'id'
17
+ }
18
+
19
+ getPassword(): EntityColumns<User> {
20
+ return 'password'
21
+ }
22
+
23
+ getUsername(): EntityColumns<User> {
24
+ return 'email'
25
+ }
26
+
27
+ }