wabe 0.6.9 → 0.6.10

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 (158) hide show
  1. package/README.md +138 -32
  2. package/bucket/b.txt +1 -0
  3. package/dev/index.ts +215 -0
  4. package/dist/authentication/Session.d.ts +4 -1
  5. package/dist/authentication/interface.d.ts +16 -0
  6. package/dist/email/interface.d.ts +1 -1
  7. package/dist/graphql/resolvers.d.ts +4 -2
  8. package/dist/hooks/index.d.ts +1 -0
  9. package/dist/index.d.ts +0 -1
  10. package/dist/index.js +8713 -8867
  11. package/dist/server/index.d.ts +4 -2
  12. package/dist/utils/crypto.d.ts +7 -0
  13. package/dist/utils/helper.d.ts +4 -1
  14. package/generated/schema.graphql +16 -14
  15. package/generated/wabe.ts +4 -4
  16. package/package.json +15 -15
  17. package/src/authentication/OTP.test.ts +69 -0
  18. package/src/authentication/OTP.ts +66 -0
  19. package/src/authentication/Session.test.ts +665 -0
  20. package/src/authentication/Session.ts +529 -0
  21. package/src/authentication/defaultAuthentication.ts +214 -0
  22. package/src/authentication/index.ts +3 -0
  23. package/src/authentication/interface.ts +157 -0
  24. package/src/authentication/oauth/GitHub.test.ts +105 -0
  25. package/src/authentication/oauth/GitHub.ts +133 -0
  26. package/src/authentication/oauth/Google.test.ts +105 -0
  27. package/src/authentication/oauth/Google.ts +110 -0
  28. package/src/authentication/oauth/Oauth2Client.test.ts +225 -0
  29. package/src/authentication/oauth/Oauth2Client.ts +140 -0
  30. package/src/authentication/oauth/index.ts +2 -0
  31. package/src/authentication/oauth/utils.test.ts +35 -0
  32. package/src/authentication/oauth/utils.ts +28 -0
  33. package/src/authentication/providers/EmailOTP.test.ts +138 -0
  34. package/src/authentication/providers/EmailOTP.ts +93 -0
  35. package/src/authentication/providers/EmailPassword.test.ts +187 -0
  36. package/src/authentication/providers/EmailPassword.ts +130 -0
  37. package/src/authentication/providers/EmailPasswordSRP.test.ts +206 -0
  38. package/src/authentication/providers/EmailPasswordSRP.ts +184 -0
  39. package/src/authentication/providers/GitHub.ts +30 -0
  40. package/src/authentication/providers/Google.ts +30 -0
  41. package/src/authentication/providers/OAuth.test.ts +185 -0
  42. package/src/authentication/providers/OAuth.ts +112 -0
  43. package/src/authentication/providers/PhonePassword.test.ts +187 -0
  44. package/src/authentication/providers/PhonePassword.ts +129 -0
  45. package/src/authentication/providers/QRCodeOTP.test.ts +79 -0
  46. package/src/authentication/providers/QRCodeOTP.ts +65 -0
  47. package/src/authentication/providers/index.ts +6 -0
  48. package/src/authentication/resolvers/refreshResolver.test.ts +37 -0
  49. package/src/authentication/resolvers/refreshResolver.ts +20 -0
  50. package/src/authentication/resolvers/signInWithResolver.inte.test.ts +59 -0
  51. package/src/authentication/resolvers/signInWithResolver.test.ts +307 -0
  52. package/src/authentication/resolvers/signInWithResolver.ts +102 -0
  53. package/src/authentication/resolvers/signOutResolver.test.ts +41 -0
  54. package/src/authentication/resolvers/signOutResolver.ts +22 -0
  55. package/src/authentication/resolvers/signUpWithResolver.test.ts +186 -0
  56. package/src/authentication/resolvers/signUpWithResolver.ts +69 -0
  57. package/src/authentication/resolvers/verifyChallenge.test.ts +136 -0
  58. package/src/authentication/resolvers/verifyChallenge.ts +69 -0
  59. package/src/authentication/roles.test.ts +59 -0
  60. package/src/authentication/roles.ts +40 -0
  61. package/src/authentication/utils.test.ts +99 -0
  62. package/src/authentication/utils.ts +43 -0
  63. package/src/cache/InMemoryCache.test.ts +62 -0
  64. package/src/cache/InMemoryCache.ts +45 -0
  65. package/src/cron/index.test.ts +17 -0
  66. package/src/cron/index.ts +46 -0
  67. package/src/database/DatabaseController.test.ts +625 -0
  68. package/src/database/DatabaseController.ts +983 -0
  69. package/src/database/index.test.ts +1230 -0
  70. package/src/database/index.ts +9 -0
  71. package/src/database/interface.ts +312 -0
  72. package/src/email/DevAdapter.ts +8 -0
  73. package/src/email/EmailController.test.ts +29 -0
  74. package/src/email/EmailController.ts +13 -0
  75. package/src/email/index.ts +2 -0
  76. package/src/email/interface.ts +36 -0
  77. package/src/email/templates/sendOtpCode.ts +120 -0
  78. package/src/file/FileController.ts +28 -0
  79. package/src/file/FileDevAdapter.ts +54 -0
  80. package/src/file/hookDeleteFile.ts +27 -0
  81. package/src/file/hookReadFile.ts +70 -0
  82. package/src/file/hookUploadFile.ts +53 -0
  83. package/src/file/index.test.ts +979 -0
  84. package/src/file/index.ts +2 -0
  85. package/src/file/interface.ts +42 -0
  86. package/src/graphql/GraphQLSchema.test.ts +4399 -0
  87. package/src/graphql/GraphQLSchema.ts +928 -0
  88. package/src/graphql/index.ts +2 -0
  89. package/src/graphql/parseGraphqlSchema.ts +94 -0
  90. package/src/graphql/parser.test.ts +217 -0
  91. package/src/graphql/parser.ts +566 -0
  92. package/src/graphql/pointerAndRelationFunction.ts +200 -0
  93. package/src/graphql/resolvers.ts +467 -0
  94. package/src/graphql/tests/aggregation.test.ts +1123 -0
  95. package/src/graphql/tests/e2e.test.ts +596 -0
  96. package/src/graphql/tests/scalars.test.ts +250 -0
  97. package/src/graphql/types.ts +219 -0
  98. package/src/hooks/HookObject.test.ts +122 -0
  99. package/src/hooks/HookObject.ts +168 -0
  100. package/src/hooks/authentication.ts +76 -0
  101. package/src/hooks/createUser.test.ts +77 -0
  102. package/src/hooks/createUser.ts +10 -0
  103. package/src/hooks/defaultFields.test.ts +187 -0
  104. package/src/hooks/defaultFields.ts +40 -0
  105. package/src/hooks/deleteSession.test.ts +181 -0
  106. package/src/hooks/deleteSession.ts +20 -0
  107. package/src/hooks/hashFieldHook.test.ts +163 -0
  108. package/src/hooks/hashFieldHook.ts +97 -0
  109. package/src/hooks/index.test.ts +207 -0
  110. package/src/hooks/index.ts +430 -0
  111. package/src/hooks/permissions.test.ts +424 -0
  112. package/src/hooks/permissions.ts +113 -0
  113. package/src/hooks/protected.test.ts +551 -0
  114. package/src/hooks/protected.ts +72 -0
  115. package/src/hooks/searchableFields.test.ts +166 -0
  116. package/src/hooks/searchableFields.ts +98 -0
  117. package/src/hooks/session.test.ts +138 -0
  118. package/src/hooks/session.ts +78 -0
  119. package/src/hooks/setEmail.test.ts +216 -0
  120. package/src/hooks/setEmail.ts +35 -0
  121. package/src/hooks/setupAcl.test.ts +589 -0
  122. package/src/hooks/setupAcl.ts +29 -0
  123. package/src/index.ts +9 -0
  124. package/src/schema/Schema.test.ts +484 -0
  125. package/src/schema/Schema.ts +795 -0
  126. package/src/schema/defaultResolvers.ts +94 -0
  127. package/src/schema/index.ts +1 -0
  128. package/src/schema/resolvers/meResolver.test.ts +62 -0
  129. package/src/schema/resolvers/meResolver.ts +14 -0
  130. package/src/schema/resolvers/newFile.ts +0 -0
  131. package/src/schema/resolvers/resetPassword.test.ts +345 -0
  132. package/src/schema/resolvers/resetPassword.ts +64 -0
  133. package/src/schema/resolvers/sendEmail.test.ts +118 -0
  134. package/src/schema/resolvers/sendEmail.ts +21 -0
  135. package/src/schema/resolvers/sendOtpCode.test.ts +153 -0
  136. package/src/schema/resolvers/sendOtpCode.ts +52 -0
  137. package/src/security.test.ts +3461 -0
  138. package/src/server/defaultSessionHandler.test.ts +66 -0
  139. package/src/server/defaultSessionHandler.ts +115 -0
  140. package/src/server/generateCodegen.ts +476 -0
  141. package/src/server/index.test.ts +552 -0
  142. package/src/server/index.ts +354 -0
  143. package/src/server/interface.ts +11 -0
  144. package/src/server/routes/authHandler.ts +187 -0
  145. package/src/server/routes/index.ts +40 -0
  146. package/src/utils/crypto.test.ts +41 -0
  147. package/src/utils/crypto.ts +121 -0
  148. package/src/utils/export.ts +13 -0
  149. package/src/utils/helper.ts +195 -0
  150. package/src/utils/index.test.ts +11 -0
  151. package/src/utils/index.ts +201 -0
  152. package/src/utils/preload.ts +8 -0
  153. package/src/utils/testHelper.ts +117 -0
  154. package/tsconfig.json +32 -0
  155. package/bunfig.toml +0 -4
  156. package/dist/ai/index.d.ts +0 -1
  157. package/dist/ai/interface.d.ts +0 -9
  158. /package/dist/server/{defaultHandlers.d.ts → defaultSessionHandler.d.ts} +0 -0
@@ -0,0 +1,9 @@
1
+ import type { WabeTypes } from '../server'
2
+ import type { DatabaseAdapter } from './interface'
3
+
4
+ export interface DatabaseConfig<T extends WabeTypes> {
5
+ adapter: DatabaseAdapter<T>
6
+ }
7
+
8
+ export * from './DatabaseController'
9
+ export * from './interface'
@@ -0,0 +1,312 @@
1
+ import type { WabeContext } from '../server/interface'
2
+ import type { WabeTypes } from '../server'
3
+ import type { SchemaInterface } from '../schema'
4
+
5
+ type IsScalar<T> = T extends string | number | boolean | Date ? true : false
6
+
7
+ type IsArray<T> = T extends Array<any> ? true : false
8
+
9
+ type IsObject<T, K extends WabeTypes> = T extends object
10
+ ? T extends K['types'][keyof K['types']]
11
+ ? false
12
+ : true
13
+ : false
14
+
15
+ type ExtractType<
16
+ T extends WabeTypes,
17
+ ClassName extends keyof T['types'],
18
+ FieldName extends keyof T['types'][ClassName],
19
+ > = T['types'][ClassName][FieldName]
20
+
21
+ type ExtractWhereType<
22
+ T extends WabeTypes,
23
+ ClassName extends keyof T['where'],
24
+ FieldName extends keyof T['where'][ClassName],
25
+ > = T['where'][ClassName][FieldName]
26
+
27
+ type WhereScalar<T> = {
28
+ equalTo?: T
29
+ notEqualTo?: T
30
+ greaterThan?: T
31
+ lessThan?: T
32
+ greaterThanOrEqualTo?: T
33
+ lessThanOrEqualTo?: T
34
+ in?: T[]
35
+ notIn?: T[]
36
+ contains?: T
37
+ notContains?: T
38
+ }
39
+
40
+ type WhereObject<T> = {
41
+ [P in keyof T]: IsScalar<T[P]> extends false
42
+ ? WhereObject<Partial<T[P]>>
43
+ : WhereScalar<T[P]>
44
+ }
45
+
46
+ type WhereAggregation<T extends WabeTypes, K = keyof T['where']> = {
47
+ [P in keyof T['where'][K]]: IsScalar<ExtractWhereType<T, K, P>> extends false
48
+ ? WhereObject<Partial<ExtractWhereType<T, K, P>>>
49
+ : WhereScalar<ExtractWhereType<T, K, P>>
50
+ }
51
+
52
+ type WhereConditional<T extends WabeTypes, K = keyof T['where']> = {
53
+ OR?: Array<WhereType<T, K>>
54
+ AND?: Array<WhereType<T, K>>
55
+ }
56
+
57
+ export type WhereType<T extends WabeTypes, K = keyof T['where']> = Partial<
58
+ WhereAggregation<T, K>
59
+ > &
60
+ WhereConditional<T, K>
61
+
62
+ type SelectObject<T, K extends WabeTypes, Depth extends number = 3> = {
63
+ [P in keyof T]: IsScalar<T[P]> extends true
64
+ ? boolean
65
+ : IsArray<T[P]> extends true
66
+ ? T[P] extends Array<infer Item>
67
+ ?
68
+ | (Depth extends 0
69
+ ? boolean
70
+ : SelectObject<Partial<Item>, K, Decrement<Depth>>)
71
+ | boolean
72
+ : boolean
73
+ : IsObject<[P], K> extends true
74
+ ?
75
+ | (Depth extends 0
76
+ ? boolean
77
+ : SelectObject<Partial<T[P]>, K, Decrement<Depth>>)
78
+ | boolean
79
+ : boolean
80
+ }
81
+
82
+ type Decrement<N extends number> = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10][N]
83
+
84
+ export type SelectType<
85
+ T extends WabeTypes,
86
+ K extends keyof T['types'],
87
+ U extends keyof T['types'][K],
88
+ Depth extends number = 3,
89
+ > = Partial<{
90
+ [P in U]: IsScalar<ExtractType<T, K, P>> extends true
91
+ ? boolean
92
+ : IsArray<ExtractType<T, K, P>> extends true
93
+ ? ExtractType<T, K, P> extends Array<infer Item>
94
+ ?
95
+ | (Depth extends 0
96
+ ? boolean
97
+ : SelectObject<Partial<Item>, T, Decrement<Depth>>)
98
+ | boolean
99
+ : boolean
100
+ : ExtractType<T, K, P> extends object
101
+ ?
102
+ | (Depth extends 0
103
+ ? boolean
104
+ : SelectObject<
105
+ Partial<ExtractType<T, K, P>>,
106
+ T,
107
+ Decrement<Depth>
108
+ >)
109
+ | boolean
110
+ : boolean
111
+ }>
112
+
113
+ export type OrderType<
114
+ T extends WabeTypes,
115
+ K extends keyof T['types'],
116
+ U extends keyof T['types'][K],
117
+ > = Record<U, 'ASC' | 'DESC'>
118
+
119
+ export type OutputType<
120
+ T extends WabeTypes,
121
+ K extends keyof T['types'],
122
+ U extends keyof T['types'][K],
123
+ > = (Pick<T['types'][K], U> & { id: string }) | null
124
+
125
+ export interface AdapterOptions {
126
+ databaseUrl: string
127
+ databaseName: string
128
+ }
129
+
130
+ export type MutationData<
131
+ T extends WabeTypes,
132
+ K extends keyof T['types'],
133
+ U extends keyof T['types'][K],
134
+ > = Record<U, any>
135
+
136
+ export interface CountOptions<T extends WabeTypes, K extends keyof T['types']> {
137
+ className: K
138
+ where?: WhereType<T, K>
139
+ context: WabeContext<T>
140
+ }
141
+
142
+ export interface GetObjectOptions<
143
+ T extends WabeTypes,
144
+ K extends keyof T['types'],
145
+ U extends keyof T['types'][K],
146
+ > {
147
+ className: K
148
+ id: string
149
+ where?: WhereType<T, K>
150
+ context: WabeContext<T>
151
+ _skipHooks?: boolean
152
+ select?: SelectType<T, K, U>
153
+ }
154
+
155
+ export interface GetObjectsOptions<
156
+ T extends WabeTypes,
157
+ K extends keyof T['types'],
158
+ U extends keyof T['types'][K],
159
+ W extends keyof T['types'][K],
160
+ > {
161
+ className: K
162
+ where?: WhereType<T, K>
163
+ order?: OrderType<T, K, U>
164
+ offset?: number
165
+ first?: number
166
+ context: WabeContext<T>
167
+ _skipHooks?: boolean
168
+ select?: SelectType<T, K, W>
169
+ }
170
+
171
+ export interface CreateObjectOptions<
172
+ T extends WabeTypes,
173
+ K extends keyof T['types'],
174
+ U extends keyof T['types'][K],
175
+ W extends keyof T['types'][K],
176
+ > {
177
+ className: K
178
+ data: MutationData<T, K, U>
179
+ context: WabeContext<T>
180
+ select?: SelectType<T, K, W>
181
+ }
182
+ export interface CreateObjectsOptions<
183
+ T extends WabeTypes,
184
+ K extends keyof T['types'],
185
+ U extends keyof T['types'][K],
186
+ W extends keyof T['types'][K],
187
+ X extends keyof T['types'][K],
188
+ > {
189
+ className: K
190
+ data: Array<MutationData<T, K, U>>
191
+ offset?: number
192
+ first?: number
193
+ order?: OrderType<T, U, X>
194
+ context: WabeContext<T>
195
+ select?: SelectType<T, K, W>
196
+ }
197
+
198
+ export interface UpdateObjectOptions<
199
+ T extends WabeTypes,
200
+ K extends keyof T['types'],
201
+ U extends keyof T['types'][K],
202
+ W extends keyof T['types'][K],
203
+ > {
204
+ className: K
205
+ id: string
206
+ where?: WhereType<T, K>
207
+ data: MutationData<T, K, U>
208
+ context: WabeContext<T>
209
+ _skipHooks?: boolean
210
+ select?: SelectType<T, K, W>
211
+ }
212
+
213
+ export interface UpdateObjectsOptions<
214
+ T extends WabeTypes,
215
+ K extends keyof T['types'],
216
+ U extends keyof T['types'][K],
217
+ W extends keyof T['types'][K],
218
+ X extends keyof T['types'][K],
219
+ > {
220
+ className: K
221
+ where: WhereType<T, K>
222
+ order?: OrderType<T, K, X>
223
+ data: MutationData<T, K, U>
224
+ offset?: number
225
+ first?: number
226
+ context: WabeContext<T>
227
+ _skipHooks?: boolean
228
+ select?: SelectType<T, K, W>
229
+ }
230
+
231
+ export interface DeleteObjectOptions<
232
+ T extends WabeTypes,
233
+ K extends keyof T['types'],
234
+ U extends keyof T['types'][K],
235
+ > {
236
+ className: K
237
+ id: string
238
+ where?: WhereType<T, K>
239
+ context: WabeContext<T>
240
+ select?: SelectType<T, K, U>
241
+ }
242
+
243
+ export interface DeleteObjectsOptions<
244
+ T extends WabeTypes,
245
+ K extends keyof T['types'],
246
+ U extends keyof T['types'][K],
247
+ W extends keyof T['types'][K],
248
+ > {
249
+ className: K
250
+ where: WhereType<T, K>
251
+ order?: OrderType<T, K, U>
252
+ offset?: number
253
+ first?: number
254
+ context: WabeContext<T>
255
+ select?: SelectType<T, K, W>
256
+ }
257
+
258
+ export interface DatabaseAdapter<T extends WabeTypes> {
259
+ close(): Promise<void>
260
+
261
+ createClassIfNotExist(
262
+ className: string,
263
+ schema: SchemaInterface<T>,
264
+ ): Promise<any> | any
265
+
266
+ initializeDatabase(schema: SchemaInterface<T>): Promise<void>
267
+ clearDatabase(): Promise<void>
268
+
269
+ count<K extends keyof T['types']>(params: CountOptions<T, K>): Promise<number>
270
+
271
+ getObject<K extends keyof T['types'], U extends keyof T['types'][K]>(
272
+ params: GetObjectOptions<T, K, U>,
273
+ ): Promise<OutputType<T, K, U>>
274
+ getObjects<
275
+ K extends keyof T['types'],
276
+ U extends keyof T['types'][K],
277
+ W extends keyof T['types'][K],
278
+ >(params: GetObjectsOptions<T, K, U, W>): Promise<OutputType<T, K, W>[]>
279
+
280
+ createObject<
281
+ K extends keyof T['types'],
282
+ U extends keyof T['types'][K],
283
+ W extends keyof T['types'][K],
284
+ >(params: CreateObjectOptions<T, K, U, W>): Promise<{ id: string }>
285
+ createObjects<
286
+ K extends keyof T['types'],
287
+ U extends keyof T['types'][K],
288
+ W extends keyof T['types'][K],
289
+ X extends keyof T['types'][K],
290
+ >(params: CreateObjectsOptions<T, K, U, W, X>): Promise<Array<{ id: string }>>
291
+
292
+ updateObject<
293
+ K extends keyof T['types'],
294
+ U extends keyof T['types'][K],
295
+ W extends keyof T['types'][K],
296
+ >(params: UpdateObjectOptions<T, K, U, W>): Promise<{ id: string }>
297
+ updateObjects<
298
+ K extends keyof T['types'],
299
+ U extends keyof T['types'][K],
300
+ W extends keyof T['types'][K],
301
+ X extends keyof T['types'][K],
302
+ >(params: UpdateObjectsOptions<T, K, U, W, X>): Promise<Array<{ id: string }>>
303
+
304
+ deleteObject<K extends keyof T['types'], U extends keyof T['types'][K]>(
305
+ params: DeleteObjectOptions<T, K, U>,
306
+ ): Promise<void>
307
+ deleteObjects<
308
+ K extends keyof T['types'],
309
+ U extends keyof T['types'][K],
310
+ W extends keyof T['types'][K],
311
+ >(params: DeleteObjectsOptions<T, K, U, W>): Promise<void>
312
+ }
@@ -0,0 +1,8 @@
1
+ import type { EmailAdapter } from './interface'
2
+
3
+ export class EmailDevAdapter implements EmailAdapter {
4
+ // biome-ignore lint/suspicious/useAwait: false
5
+ async send() {
6
+ return '123456'
7
+ }
8
+ }
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it, mock } from 'bun:test'
2
+ import { EmailController } from './EmailController'
3
+
4
+ describe('EmailController', () => {
5
+ it('should send email using correct adapter', async () => {
6
+ const mockSend = mock(() => {})
7
+ const dummyAdapter = {
8
+ send: mockSend,
9
+ }
10
+
11
+ // @ts-expect-error
12
+ const controller = new EmailController(dummyAdapter)
13
+
14
+ await controller.send({
15
+ from: 'from',
16
+ to: ['to'],
17
+ subject: 'subject',
18
+ text: 'text',
19
+ })
20
+
21
+ expect(mockSend).toHaveBeenCalledTimes(1)
22
+ expect(mockSend).toHaveBeenCalledWith({
23
+ from: 'from',
24
+ to: ['to'],
25
+ subject: 'subject',
26
+ text: 'text',
27
+ })
28
+ })
29
+ })
@@ -0,0 +1,13 @@
1
+ import type { EmailAdapter, EmailSendOptions } from './interface'
2
+
3
+ export class EmailController implements EmailAdapter {
4
+ public adapter: EmailAdapter
5
+
6
+ constructor(adapter: EmailAdapter) {
7
+ this.adapter = adapter
8
+ }
9
+
10
+ send(options: EmailSendOptions) {
11
+ return this.adapter.send(options)
12
+ }
13
+ }
@@ -0,0 +1,2 @@
1
+ export * from './interface'
2
+ export * from './DevAdapter'
@@ -0,0 +1,36 @@
1
+ export type HtmlTemplates = {
2
+ sendOTPCode: {
3
+ fn: (options: { otp: string }) => string | Promise<string>
4
+ subject: string
5
+ }
6
+ }
7
+
8
+ export interface EmailSendOptions {
9
+ from: string
10
+ to: Array<string>
11
+ subject: string
12
+ node?: any
13
+ html?: string
14
+ text?: string
15
+ }
16
+
17
+ export interface EmailAdapter {
18
+ /**
19
+ * Send an email using the provided adapter
20
+ * @param options Mail options (expeditor, recipient, subject ...)
21
+ * @return The id of the email sended, throw an error if something wrong
22
+ */
23
+ send(options: EmailSendOptions): Promise<string>
24
+ }
25
+
26
+ /**
27
+ * Configuration for the email in Wabe
28
+ * @property adapter The adapter to use to send emails
29
+ * @property mainEmail The email to use as sender for emails sent by Wabe
30
+ * @property templates The html templates to use for a specific email. If not provided, Wabe will use the default templates
31
+ */
32
+ export interface EmailConfig {
33
+ adapter: EmailAdapter
34
+ mainEmail?: string
35
+ htmlTemplates?: HtmlTemplates
36
+ }
@@ -0,0 +1,120 @@
1
+ export const sendOtpCodeTemplate = (otp: string) => `
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <meta charset="UTF-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
9
+ <title>Password Reset</title>
10
+ <style>
11
+ body {
12
+ font-family: 'Arial', sans-serif;
13
+ margin: 0;
14
+ padding: 0;
15
+ background-color: #f4f4f4;
16
+ }
17
+
18
+ .email-container {
19
+ max-width: 600px;
20
+ margin: 0 auto;
21
+ background-color: #ffffff;
22
+ border-radius: 8px;
23
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
24
+ overflow: hidden;
25
+ }
26
+
27
+ .header {
28
+ background-color: #4CAF50;
29
+ padding: 20px;
30
+ text-align: center;
31
+ color: white;
32
+ }
33
+
34
+ .header h1 {
35
+ margin: 0;
36
+ font-size: 24px;
37
+ }
38
+
39
+ .content {
40
+ padding: 20px;
41
+ font-size: 16px;
42
+ line-height: 1.6;
43
+ color: #333333;
44
+ }
45
+
46
+ .content p {
47
+ margin-bottom: 20px;
48
+ }
49
+
50
+ .otp-code {
51
+ display: inline-block;
52
+ padding: 10px 20px;
53
+ background-color: #4CAF50;
54
+ color: white;
55
+ font-size: 18px;
56
+ font-weight: bold;
57
+ border-radius: 5px;
58
+ letter-spacing: 2px;
59
+ text-align: center;
60
+ }
61
+
62
+ .footer {
63
+ background-color: #f4f4f4;
64
+ text-align: center;
65
+ padding: 10px;
66
+ font-size: 14px;
67
+ color: #888888;
68
+ }
69
+
70
+ .footer p {
71
+ margin: 0;
72
+ }
73
+
74
+ /* Responsive Design */
75
+ @media screen and (max-width: 600px) {
76
+ .email-container {
77
+ width: 100%;
78
+ }
79
+
80
+ .header h1 {
81
+ font-size: 20px;
82
+ }
83
+
84
+ .content {
85
+ padding: 15px;
86
+ }
87
+
88
+ .otp-code {
89
+ font-size: 16px;
90
+ padding: 8px 16px;
91
+ }
92
+ }
93
+ </style>
94
+ </head>
95
+
96
+ <body>
97
+ <div class="email-container">
98
+ <!-- Email Header -->
99
+ <div class="header">
100
+ <h1>Confirmation code</h1>
101
+ </div>
102
+
103
+ <!-- Email Content -->
104
+ <div class="content">
105
+ <p>Hello,</p>
106
+ <p>Here is your confirmation code. Please use the OTP code (valid for 5 minutes) below to proceed the action:</p>
107
+ <p class="otp-code">${otp}</p>
108
+ <p>If you did not request this code, please ignore this email.</p>
109
+ <p>Thank you</p>
110
+ </div>
111
+
112
+ <!-- Email Footer -->
113
+ <div class="footer">
114
+ <p>Powered by Wabe</p>
115
+ </div>
116
+ </div>
117
+ </body>
118
+
119
+ </html>
120
+ `
@@ -0,0 +1,28 @@
1
+ import type { Wabe } from '../server'
2
+ import type { DevWabeTypes } from '../utils/helper'
3
+ import type { FileAdapter, ReadFileOptions } from './interface'
4
+
5
+ export class FileController implements FileAdapter {
6
+ public adapter: FileAdapter
7
+ private wabe: Wabe<DevWabeTypes>
8
+
9
+ constructor(adapter: FileAdapter, wabe: Wabe<any>) {
10
+ this.adapter = adapter
11
+ this.wabe = wabe
12
+ }
13
+
14
+ uploadFile(file: File) {
15
+ return this.adapter.uploadFile(file)
16
+ }
17
+
18
+ readFile(fileName: string, options?: ReadFileOptions) {
19
+ return this.adapter.readFile(fileName, {
20
+ ...options,
21
+ port: this.wabe.config.port,
22
+ })
23
+ }
24
+
25
+ deleteFile(fileName: string) {
26
+ return this.adapter.deleteFile(fileName)
27
+ }
28
+ }
@@ -0,0 +1,54 @@
1
+ import { writeFile, mkdir, rm, access, constants } from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import type { FileAdapter, ReadFileOptions } from '.'
4
+
5
+ export class FileDevAdapter implements FileAdapter {
6
+ private basePath = 'bucket'
7
+ private rootPath = process.cwd()
8
+
9
+ async uploadFile(file: File): Promise<void> {
10
+ const fullPath = path.join(this.rootPath, this.basePath)
11
+
12
+ await mkdir(fullPath, { recursive: true })
13
+
14
+ const fileType = file.type
15
+
16
+ let fileContent: Buffer
17
+
18
+ if (fileType.startsWith('text') || fileType.includes('json')) {
19
+ const textContent = await file.text()
20
+ fileContent = Buffer.from(textContent, 'utf-8')
21
+ } else {
22
+ const arrayBuffer = await file.arrayBuffer()
23
+ fileContent = Buffer.from(arrayBuffer)
24
+ }
25
+
26
+ await writeFile(path.join(fullPath, file.name), fileContent)
27
+ }
28
+
29
+ async readFile(
30
+ fileName: string,
31
+ options?: ReadFileOptions,
32
+ ): Promise<string | null> {
33
+ const filePath = path.join(this.rootPath, this.basePath, fileName)
34
+
35
+ try {
36
+ await access(filePath, constants.F_OK)
37
+ return `http://127.0.0.1:${options?.port || 3001}/${this.basePath}/${fileName}`
38
+ } catch {
39
+ return null
40
+ }
41
+ }
42
+
43
+ async deleteFile(fileName: string): Promise<void> {
44
+ const filePath = path.join(this.rootPath, this.basePath, fileName)
45
+
46
+ try {
47
+ await access(filePath, constants.F_OK)
48
+
49
+ await rm(filePath)
50
+ } catch {
51
+ // Do nothing
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,27 @@
1
+ import type { HookObject } from '../hooks/HookObject'
2
+
3
+ const deleteFile = async (hookObject: HookObject<any, any>) => {
4
+ const schema = hookObject.context.wabe.config.schema?.classes?.find(
5
+ (currentClass) => currentClass.name === hookObject.className,
6
+ )
7
+
8
+ if (!schema) return
9
+
10
+ await Promise.all(
11
+ Object.entries(schema.fields)
12
+ .filter(([_, value]) => value.type === 'File')
13
+ .map(([fieldName]) => {
14
+ const fileName = hookObject.originalObject?.[fieldName]?.name as string
15
+
16
+ if (!fileName) return Promise.resolve()
17
+
18
+ if (!hookObject.context.wabe.controllers.file)
19
+ throw new Error('No file adapter found')
20
+
21
+ return hookObject.context.wabe.controllers.file?.deleteFile(fileName)
22
+ }),
23
+ )
24
+ }
25
+
26
+ export const defaultAfterDeleteFile = (hookObject: HookObject<any, any>) =>
27
+ deleteFile(hookObject)