odac 0.9.0 → 1.0.0

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 (208) hide show
  1. package/.github/workflows/auto-pr-description.yml +0 -2
  2. package/.github/workflows/codeql.yml +46 -0
  3. package/.github/workflows/release.yml +13 -6
  4. package/.github/workflows/test-coverage.yml +10 -9
  5. package/.releaserc.js +9 -6
  6. package/CHANGELOG.md +62 -150
  7. package/CODE_OF_CONDUCT.md +1 -1
  8. package/CONTRIBUTING.md +8 -8
  9. package/LICENSE +21 -661
  10. package/README.md +12 -12
  11. package/SECURITY.md +4 -4
  12. package/bin/odac.js +101 -0
  13. package/{framework/web/candy.js → client/odac.js} +310 -44
  14. package/docs/backend/01-overview/{01-whats-in-the-candy-box.md → 01-whats-in-the-odac-box.md} +4 -2
  15. package/docs/backend/01-overview/02-super-handy-helper-functions.md +29 -1
  16. package/docs/backend/01-overview/03-development-server.md +11 -11
  17. package/docs/backend/02-structure/01-typical-project-layout.md +4 -4
  18. package/docs/backend/03-config/00-configuration-overview.md +6 -6
  19. package/docs/backend/03-config/01-database-connection.md +1 -1
  20. package/docs/backend/03-config/02-static-route-mapping-optional.md +4 -4
  21. package/docs/backend/03-config/04-environment-variables.md +20 -20
  22. package/docs/backend/03-config/05-early-hints.md +4 -4
  23. package/docs/backend/04-routing/01-basic-page-routes.md +4 -4
  24. package/docs/backend/04-routing/02-controller-less-view-routes.md +5 -5
  25. package/docs/backend/04-routing/03-api-and-data-routes.md +3 -3
  26. package/docs/backend/04-routing/04-authentication-aware-routes.md +5 -5
  27. package/docs/backend/04-routing/05-advanced-routing.md +3 -3
  28. package/docs/backend/04-routing/06-error-pages.md +17 -17
  29. package/docs/backend/04-routing/07-cron-jobs.md +13 -13
  30. package/docs/backend/04-routing/08-middleware.md +214 -0
  31. package/docs/backend/04-routing/09-websocket-auth-middleware.md +292 -0
  32. package/docs/backend/04-routing/09-websocket-examples.md +381 -0
  33. package/docs/backend/04-routing/09-websocket-quick-reference.md +211 -0
  34. package/docs/backend/04-routing/09-websocket.md +298 -0
  35. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +3 -3
  36. package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +41 -0
  37. package/docs/backend/05-controllers/03-controller-classes.md +19 -19
  38. package/docs/backend/05-forms/01-custom-forms.md +114 -114
  39. package/docs/backend/05-forms/02-automatic-database-insert.md +82 -82
  40. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +26 -26
  41. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +10 -10
  42. package/docs/backend/07-views/01-the-view-directory.md +1 -1
  43. package/docs/backend/07-views/02-rendering-a-view.md +22 -22
  44. package/docs/backend/07-views/03-template-syntax.md +52 -52
  45. package/docs/backend/07-views/03-variables.md +84 -84
  46. package/docs/backend/07-views/04-request-data.md +57 -57
  47. package/docs/backend/07-views/05-conditionals.md +78 -78
  48. package/docs/backend/07-views/06-loops.md +114 -114
  49. package/docs/backend/07-views/07-translations.md +66 -66
  50. package/docs/backend/07-views/08-backend-javascript.md +103 -103
  51. package/docs/backend/07-views/09-comments.md +71 -71
  52. package/docs/backend/08-database/01-database-connection.md +8 -8
  53. package/docs/backend/08-database/02-using-mysql.md +49 -49
  54. package/docs/backend/09-validation/01-the-validator-service.md +38 -38
  55. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +15 -15
  56. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +10 -10
  57. package/docs/backend/10-authentication/03-register.md +12 -12
  58. package/docs/backend/10-authentication/{04-candy-register-forms.md → 04-odac-register-forms.md} +141 -141
  59. package/docs/backend/10-authentication/05-session-management.md +10 -10
  60. package/docs/backend/10-authentication/{06-candy-login-forms.md → 06-odac-login-forms.md} +125 -125
  61. package/docs/backend/11-mail/01-the-mail-service.md +5 -5
  62. package/docs/backend/12-streaming/01-streaming-overview.md +96 -54
  63. package/docs/backend/13-utilities/{01-candy-var.md → 01-odac-var.md} +109 -109
  64. package/docs/frontend/01-overview/01-introduction.md +30 -30
  65. package/docs/frontend/02-ajax-navigation/01-quick-start.md +45 -45
  66. package/docs/frontend/02-ajax-navigation/02-configuration.md +14 -14
  67. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +36 -36
  68. package/docs/frontend/03-forms/01-form-handling.md +32 -32
  69. package/docs/frontend/04-api-requests/01-get-post.md +33 -33
  70. package/docs/frontend/05-streaming/01-client-streaming.md +15 -15
  71. package/docs/frontend/06-websocket/00-overview.md +76 -0
  72. package/docs/frontend/06-websocket/01-websocket-client.md +139 -0
  73. package/docs/frontend/06-websocket/02-shared-websocket.md +149 -0
  74. package/docs/index.json +49 -11
  75. package/eslint.config.mjs +6 -6
  76. package/{framework/index.js → index.js} +1 -1
  77. package/package.json +14 -39
  78. package/{framework/src → src}/Auth.js +59 -59
  79. package/{framework/src → src}/Config.js +3 -3
  80. package/{framework/src → src}/Lang.js +7 -7
  81. package/{framework/src → src}/Mail.js +5 -5
  82. package/{framework/src → src}/Mysql.js +42 -42
  83. package/src/Odac.js +112 -0
  84. package/{framework/src → src}/Request.js +38 -36
  85. package/{framework/src → src}/Route/Internal.js +116 -116
  86. package/src/Route/Middleware.js +75 -0
  87. package/src/Route.js +621 -0
  88. package/src/Server.js +22 -0
  89. package/{framework/src → src}/Stream.js +11 -3
  90. package/{framework/src → src}/Validator.js +21 -21
  91. package/{framework/src → src}/Var.js +5 -5
  92. package/{framework/src → src}/View/EarlyHints.js +1 -1
  93. package/{framework/src → src}/View/Form.js +69 -69
  94. package/{framework/src → src}/View.js +78 -81
  95. package/src/WebSocket.js +403 -0
  96. package/template/config.json +5 -0
  97. package/{web → template}/controller/page/about.js +6 -6
  98. package/{web → template}/controller/page/index.js +9 -9
  99. package/{web → template}/package.json +4 -5
  100. package/{web → template}/public/assets/css/style.css +4 -4
  101. package/{web → template}/public/assets/js/app.js +6 -6
  102. package/{web → template}/route/www.js +6 -6
  103. package/{web → template}/skeleton/main.html +1 -1
  104. package/{web → template}/view/content/about.html +5 -5
  105. package/{web → template}/view/content/home.html +12 -12
  106. package/template/view/footer/main.html +11 -0
  107. package/{web → template}/view/head/main.html +1 -1
  108. package/{web → template}/view/header/main.html +2 -2
  109. package/test/core/Candy.test.js +58 -58
  110. package/test/core/Commands.test.js +7 -7
  111. package/test/core/Config.test.js +82 -85
  112. package/test/core/Lang.test.js +2 -2
  113. package/test/core/Process.test.js +6 -6
  114. package/test/framework/Route.test.js +56 -37
  115. package/test/framework/View/EarlyHints.test.js +2 -2
  116. package/test/framework/WebSocket.test.js +100 -0
  117. package/test/framework/middleware.test.js +85 -0
  118. package/test/server/Api.test.js +31 -31
  119. package/test/server/DNS.test.js +11 -11
  120. package/test/server/Hub.test.js +497 -0
  121. package/test/server/Mail.account.test_.js +3 -3
  122. package/test/server/Mail.init.test_.js +10 -10
  123. package/test/server/Mail.test_.js +20 -20
  124. package/test/server/SSL.test_.js +54 -54
  125. package/test/server/Server.test.js +39 -39
  126. package/test/server/Service.test_.js +7 -7
  127. package/test/server/Subdomain.test.js +7 -7
  128. package/test/server/Web/Firewall.test.js +87 -87
  129. package/test/server/Web/Proxy.test.js +397 -0
  130. package/test/server/{Web.test_.js → Web.test.js} +137 -205
  131. package/test/server/__mocks__/fs.js +2 -2
  132. package/test/server/__mocks__/{globalCandy.js → globalOdac.js} +5 -5
  133. package/test/server/__mocks__/index.js +6 -6
  134. package/test/server/__mocks__/testFactories.js +1 -1
  135. package/test/server/__mocks__/testHelpers.js +7 -7
  136. package/.husky/pre-commit +0 -2
  137. package/.kiro/steering/code-style.md +0 -56
  138. package/.kiro/steering/product.md +0 -20
  139. package/.kiro/steering/structure.md +0 -77
  140. package/.kiro/steering/tech.md +0 -87
  141. package/AGENTS.md +0 -84
  142. package/bin/candy +0 -10
  143. package/bin/candypack +0 -10
  144. package/cli/index.js +0 -3
  145. package/cli/src/Cli.js +0 -348
  146. package/cli/src/Connector.js +0 -93
  147. package/cli/src/Monitor.js +0 -416
  148. package/core/Candy.js +0 -87
  149. package/core/Commands.js +0 -239
  150. package/core/Config.js +0 -1094
  151. package/core/Lang.js +0 -52
  152. package/core/Log.js +0 -43
  153. package/core/Process.js +0 -26
  154. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +0 -20
  155. package/docs/server/01-installation/01-quick-install.md +0 -19
  156. package/docs/server/01-installation/02-manual-installation-via-npm.md +0 -9
  157. package/docs/server/02-get-started/01-core-concepts.md +0 -7
  158. package/docs/server/02-get-started/02-basic-commands.md +0 -57
  159. package/docs/server/02-get-started/03-cli-reference.md +0 -276
  160. package/docs/server/02-get-started/04-cli-quick-reference.md +0 -102
  161. package/docs/server/03-service/01-start-a-new-service.md +0 -57
  162. package/docs/server/03-service/02-delete-a-service.md +0 -48
  163. package/docs/server/04-web/01-create-a-website.md +0 -36
  164. package/docs/server/04-web/02-list-websites.md +0 -9
  165. package/docs/server/04-web/03-delete-a-website.md +0 -29
  166. package/docs/server/05-subdomain/01-create-a-subdomain.md +0 -32
  167. package/docs/server/05-subdomain/02-list-subdomains.md +0 -33
  168. package/docs/server/05-subdomain/03-delete-a-subdomain.md +0 -41
  169. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +0 -34
  170. package/docs/server/07-mail/01-create-a-mail-account.md +0 -23
  171. package/docs/server/07-mail/02-delete-a-mail-account.md +0 -20
  172. package/docs/server/07-mail/03-list-mail-accounts.md +0 -20
  173. package/docs/server/07-mail/04-change-account-password.md +0 -23
  174. package/framework/src/Candy.js +0 -81
  175. package/framework/src/Route.js +0 -455
  176. package/framework/src/Server.js +0 -15
  177. package/locale/de-DE.json +0 -80
  178. package/locale/en-US.json +0 -79
  179. package/locale/es-ES.json +0 -80
  180. package/locale/fr-FR.json +0 -80
  181. package/locale/pt-BR.json +0 -80
  182. package/locale/ru-RU.json +0 -80
  183. package/locale/tr-TR.json +0 -85
  184. package/locale/zh-CN.json +0 -80
  185. package/server/index.js +0 -5
  186. package/server/src/Api.js +0 -88
  187. package/server/src/DNS.js +0 -940
  188. package/server/src/Hub.js +0 -535
  189. package/server/src/Mail.js +0 -571
  190. package/server/src/SSL.js +0 -180
  191. package/server/src/Server.js +0 -27
  192. package/server/src/Service.js +0 -248
  193. package/server/src/Subdomain.js +0 -64
  194. package/server/src/Web/Firewall.js +0 -170
  195. package/server/src/Web/Proxy.js +0 -134
  196. package/server/src/Web.js +0 -451
  197. package/server/src/mail/imap.js +0 -1091
  198. package/server/src/mail/server.js +0 -32
  199. package/server/src/mail/smtp.js +0 -786
  200. package/test/server/Client.test.js +0 -338
  201. package/test/server/__mocks__/http-proxy.js +0 -105
  202. package/watchdog/index.js +0 -3
  203. package/watchdog/src/Watchdog.js +0 -156
  204. package/web/config.json +0 -5
  205. package/web/view/footer/main.html +0 -11
  206. /package/{framework/src → src}/Env.js +0 -0
  207. /package/{framework/src → src}/Route/Cron.js +0 -0
  208. /package/{framework/src → src}/Token.js +0 -0
@@ -15,61 +15,61 @@ class Internal {
15
15
  }
16
16
  }
17
17
 
18
- static async register(Candy) {
19
- const token = await Candy.request('_candy_register_token')
18
+ static async register(Odac) {
19
+ const token = await Odac.request('_odac_register_token')
20
20
  if (!token) {
21
- return Candy.return({
21
+ return Odac.return({
22
22
  result: {success: false},
23
- errors: {_candy_form: 'Invalid request'}
23
+ errors: {_odac_form: 'Invalid request'}
24
24
  })
25
25
  }
26
26
 
27
- const formData = Candy.Request.session(`_register_form_${token}`)
27
+ const formData = Odac.Request.session(`_register_form_${token}`)
28
28
 
29
29
  if (!formData) {
30
- return Candy.return({
30
+ return Odac.return({
31
31
  result: {success: false},
32
- errors: {_candy_form: 'Form session expired. Please refresh the page.'}
32
+ errors: {_odac_form: 'Form session expired. Please refresh the page.'}
33
33
  })
34
34
  }
35
35
 
36
36
  if (formData.expires < Date.now()) {
37
- Candy.Request.session(`_register_form_${token}`, null)
38
- return Candy.return({
37
+ Odac.Request.session(`_register_form_${token}`, null)
38
+ return Odac.return({
39
39
  result: {success: false},
40
- errors: {_candy_form: 'Form session expired. Please refresh the page.'}
40
+ errors: {_odac_form: 'Form session expired. Please refresh the page.'}
41
41
  })
42
42
  }
43
43
 
44
- if (formData.sessionId !== Candy.Request.session('_client')) {
45
- return Candy.return({
44
+ if (formData.sessionId !== Odac.Request.session('_client')) {
45
+ return Odac.return({
46
46
  result: {success: false},
47
- errors: {_candy_form: 'Invalid session'}
47
+ errors: {_odac_form: 'Invalid session'}
48
48
  })
49
49
  }
50
50
 
51
- if (formData.userAgent !== Candy.Request.header('user-agent')) {
52
- return Candy.return({
51
+ if (formData.userAgent !== Odac.Request.header('user-agent')) {
52
+ return Odac.return({
53
53
  result: {success: false},
54
- errors: {_candy_form: 'Invalid request'}
54
+ errors: {_odac_form: 'Invalid request'}
55
55
  })
56
56
  }
57
57
 
58
- if (formData.ip !== Candy.Request.ip) {
59
- return Candy.return({
58
+ if (formData.ip !== Odac.Request.ip) {
59
+ return Odac.return({
60
60
  result: {success: false},
61
- errors: {_candy_form: 'Invalid request'}
61
+ errors: {_odac_form: 'Invalid request'}
62
62
  })
63
63
  }
64
64
 
65
65
  const config = formData.config
66
- const validator = Candy.validator()
66
+ const validator = Odac.validator()
67
67
  const data = {}
68
68
 
69
69
  const uniqueFields = []
70
70
 
71
71
  for (const field of config.fields) {
72
- const value = await Candy.request(field.name)
72
+ const value = await Odac.request(field.name)
73
73
 
74
74
  for (const validation of field.validations) {
75
75
  this.#validateField(validator, field, validation, value)
@@ -89,10 +89,10 @@ class Internal {
89
89
  if (set.ifEmpty && data[set.name]) continue
90
90
  data[set.name] = set.value
91
91
  } else if (set.compute) {
92
- data[set.name] = await this.computeValue(set.compute, Candy)
92
+ data[set.name] = await this.computeValue(set.compute, Odac)
93
93
  } else if (set.callback) {
94
- if (typeof Candy.fn[set.callback] === 'function') {
95
- data[set.name] = await Candy.fn[set.callback](Candy)
94
+ if (typeof Odac.fn[set.callback] === 'function') {
95
+ data[set.name] = await Odac.fn[set.callback](Odac)
96
96
  }
97
97
  }
98
98
  }
@@ -101,29 +101,29 @@ class Internal {
101
101
  return validator.result()
102
102
  }
103
103
 
104
- const registerResult = await Candy.Auth.register(data, {
104
+ const registerResult = await Odac.Auth.register(data, {
105
105
  autoLogin: config.autologin !== false,
106
106
  uniqueFields: uniqueFields.length > 0 ? uniqueFields : ['email']
107
107
  })
108
108
 
109
109
  if (!registerResult.success) {
110
110
  if (registerResult.error === 'Database connection not configured') {
111
- return Candy.return({
111
+ return Odac.return({
112
112
  result: {success: false},
113
- errors: {_candy_form: 'Service temporarily unavailable. Please try again later.'}
113
+ errors: {_odac_form: 'Service temporarily unavailable. Please try again later.'}
114
114
  })
115
115
  }
116
- const errorField = registerResult.field || '_candy_form'
116
+ const errorField = registerResult.field || '_odac_form'
117
117
  const errors = {[errorField]: registerResult.error}
118
- return Candy.return({
118
+ return Odac.return({
119
119
  result: {success: false},
120
120
  errors: errors
121
121
  })
122
122
  }
123
123
 
124
- Candy.Request.session(`_register_form_${token}`, null)
124
+ Odac.Request.session(`_register_form_${token}`, null)
125
125
 
126
- return Candy.return({
126
+ return Odac.return({
127
127
  result: {
128
128
  success: true,
129
129
  message: 'Registration successful',
@@ -158,7 +158,7 @@ class Internal {
158
158
  return result
159
159
  }
160
160
 
161
- static async computeValue(type, Candy) {
161
+ static async computeValue(type, Odac) {
162
162
  switch (type) {
163
163
  case 'now':
164
164
  return Math.floor(Date.now() / 1000)
@@ -169,9 +169,9 @@ class Internal {
169
169
  case 'timestamp':
170
170
  return Date.now()
171
171
  case 'ip':
172
- return Candy.Request.ip
172
+ return Odac.Request.ip
173
173
  case 'user_agent':
174
- return Candy.Request.header('user-agent')
174
+ return Odac.Request.header('user-agent')
175
175
  case 'uuid':
176
176
  return require('crypto').randomUUID()
177
177
  default:
@@ -179,59 +179,59 @@ class Internal {
179
179
  }
180
180
  }
181
181
 
182
- static async login(Candy) {
183
- const token = await Candy.request('_candy_login_token')
182
+ static async login(Odac) {
183
+ const token = await Odac.request('_odac_login_token')
184
184
  if (!token) {
185
- return Candy.return({
185
+ return Odac.return({
186
186
  result: {success: false},
187
- errors: {_candy_form: 'Invalid request'}
187
+ errors: {_odac_form: 'Invalid request'}
188
188
  })
189
189
  }
190
190
 
191
- const formData = Candy.Request.session(`_login_form_${token}`)
191
+ const formData = Odac.Request.session(`_login_form_${token}`)
192
192
 
193
193
  if (!formData) {
194
- return Candy.return({
194
+ return Odac.return({
195
195
  result: {success: false},
196
- errors: {_candy_form: 'Form session expired. Please refresh the page.'}
196
+ errors: {_odac_form: 'Form session expired. Please refresh the page.'}
197
197
  })
198
198
  }
199
199
 
200
200
  if (formData.expires < Date.now()) {
201
- Candy.Request.session(`_login_form_${token}`, null)
202
- return Candy.return({
201
+ Odac.Request.session(`_login_form_${token}`, null)
202
+ return Odac.return({
203
203
  result: {success: false},
204
- errors: {_candy_form: 'Form session expired. Please refresh the page.'}
204
+ errors: {_odac_form: 'Form session expired. Please refresh the page.'}
205
205
  })
206
206
  }
207
207
 
208
- if (formData.sessionId !== Candy.Request.session('_client')) {
209
- return Candy.return({
208
+ if (formData.sessionId !== Odac.Request.session('_client')) {
209
+ return Odac.return({
210
210
  result: {success: false},
211
- errors: {_candy_form: 'Invalid session'}
211
+ errors: {_odac_form: 'Invalid session'}
212
212
  })
213
213
  }
214
214
 
215
- if (formData.userAgent !== Candy.Request.header('user-agent')) {
216
- return Candy.return({
215
+ if (formData.userAgent !== Odac.Request.header('user-agent')) {
216
+ return Odac.return({
217
217
  result: {success: false},
218
- errors: {_candy_form: 'Invalid request'}
218
+ errors: {_odac_form: 'Invalid request'}
219
219
  })
220
220
  }
221
221
 
222
- if (formData.ip !== Candy.Request.ip) {
223
- return Candy.return({
222
+ if (formData.ip !== Odac.Request.ip) {
223
+ return Odac.return({
224
224
  result: {success: false},
225
- errors: {_candy_form: 'Invalid request'}
225
+ errors: {_odac_form: 'Invalid request'}
226
226
  })
227
227
  }
228
228
 
229
229
  const config = formData.config
230
- const validator = Candy.validator()
230
+ const validator = Odac.validator()
231
231
  const credentials = {}
232
232
 
233
233
  for (const field of config.fields) {
234
- const value = await Candy.request(field.name)
234
+ const value = await Odac.request(field.name)
235
235
 
236
236
  for (const validation of field.validations) {
237
237
  this.#validateField(validator, field, validation, value)
@@ -244,26 +244,26 @@ class Internal {
244
244
  return validator.result()
245
245
  }
246
246
 
247
- const loginResult = await Candy.Auth.login(credentials)
247
+ const loginResult = await Odac.Auth.login(credentials)
248
248
 
249
249
  if (!loginResult.success) {
250
250
  if (loginResult.error === 'Database connection not configured') {
251
- return Candy.return({
251
+ return Odac.return({
252
252
  result: {success: false},
253
- errors: {_candy_form: 'Service temporarily unavailable. Please try again later.'}
253
+ errors: {_odac_form: 'Service temporarily unavailable. Please try again later.'}
254
254
  })
255
255
  }
256
- const errorField = loginResult.field || '_candy_form'
256
+ const errorField = loginResult.field || '_odac_form'
257
257
  const errors = {[errorField]: loginResult.error}
258
- return Candy.return({
258
+ return Odac.return({
259
259
  result: {success: false},
260
260
  errors: errors
261
261
  })
262
262
  }
263
263
 
264
- Candy.Request.session(`_login_form_${token}`, null)
264
+ Odac.Request.session(`_login_form_${token}`, null)
265
265
 
266
- return Candy.return({
266
+ return Odac.return({
267
267
  result: {
268
268
  success: true,
269
269
  message: 'Login successful',
@@ -272,30 +272,30 @@ class Internal {
272
272
  })
273
273
  }
274
274
 
275
- static async processForm(Candy) {
276
- const token = await Candy.request('_candy_form_token')
275
+ static async processForm(Odac) {
276
+ const token = await Odac.request('_odac_form_token')
277
277
  if (!token) return
278
278
 
279
- const formData = Candy.Request.session(`_custom_form_${token}`)
279
+ const formData = Odac.Request.session(`_custom_form_${token}`)
280
280
  if (!formData) return
281
281
 
282
282
  if (formData.expires < Date.now()) {
283
- Candy.Request.session(`_custom_form_${token}`, null)
283
+ Odac.Request.session(`_custom_form_${token}`, null)
284
284
  return
285
285
  }
286
286
 
287
- if (formData.sessionId !== Candy.Request.session('_client')) return
288
- if (formData.userAgent !== Candy.Request.header('user-agent')) return
289
- if (formData.ip !== Candy.Request.ip) return
287
+ if (formData.sessionId !== Odac.Request.session('_client')) return
288
+ if (formData.userAgent !== Odac.Request.header('user-agent')) return
289
+ if (formData.ip !== Odac.Request.ip) return
290
290
 
291
291
  const config = formData.config
292
- const validator = Candy.validator()
292
+ const validator = Odac.validator()
293
293
  const data = {}
294
294
 
295
295
  const uniqueFields = []
296
296
 
297
297
  for (const field of config.fields) {
298
- const value = await Candy.request(field.name)
298
+ const value = await Odac.request(field.name)
299
299
 
300
300
  for (const validation of field.validations) {
301
301
  this.#validateField(validator, field, validation, value)
@@ -317,120 +317,120 @@ class Internal {
317
317
  if (set.ifEmpty && data[set.name] !== undefined && data[set.name] !== null && data[set.name] !== '') continue
318
318
  data[set.name] = set.value
319
319
  } else if (set.compute) {
320
- data[set.name] = await this.computeValue(set.compute, Candy)
320
+ data[set.name] = await this.computeValue(set.compute, Odac)
321
321
  } else if (set.callback) {
322
- if (typeof Candy.fn[set.callback] === 'function') {
323
- data[set.name] = await Candy.fn[set.callback](Candy)
322
+ if (typeof Odac.fn[set.callback] === 'function') {
323
+ data[set.name] = await Odac.fn[set.callback](Odac)
324
324
  }
325
325
  }
326
326
  }
327
327
 
328
- Candy.formData = data
329
- Candy.formConfig = config
330
- Candy.formValidator = validator
331
- Candy.formUniqueFields = uniqueFields
328
+ Odac.formData = data
329
+ Odac.formConfig = config
330
+ Odac.formValidator = validator
331
+ Odac.formUniqueFields = uniqueFields
332
332
  }
333
333
 
334
- static async customForm(Candy) {
335
- const token = await Candy.request('_candy_form_token')
334
+ static async customForm(Odac) {
335
+ const token = await Odac.request('_odac_form_token')
336
336
  if (!token) {
337
- return Candy.return({
337
+ return Odac.return({
338
338
  result: {success: false},
339
- errors: {_candy_form: 'Invalid request'}
339
+ errors: {_odac_form: 'Invalid request'}
340
340
  })
341
341
  }
342
342
 
343
- const formData = Candy.Request.session(`_custom_form_${token}`)
343
+ const formData = Odac.Request.session(`_custom_form_${token}`)
344
344
 
345
345
  if (!formData) {
346
- return Candy.return({
346
+ return Odac.return({
347
347
  result: {success: false},
348
- errors: {_candy_form: 'Form session expired. Please refresh the page.'}
348
+ errors: {_odac_form: 'Form session expired. Please refresh the page.'}
349
349
  })
350
350
  }
351
351
 
352
352
  if (formData.expires < Date.now()) {
353
- Candy.Request.session(`_custom_form_${token}`, null)
354
- return Candy.return({
353
+ Odac.Request.session(`_custom_form_${token}`, null)
354
+ return Odac.return({
355
355
  result: {success: false},
356
- errors: {_candy_form: 'Form session expired. Please refresh the page.'}
356
+ errors: {_odac_form: 'Form session expired. Please refresh the page.'}
357
357
  })
358
358
  }
359
359
 
360
- if (formData.sessionId !== Candy.Request.session('_client')) {
361
- return Candy.return({
360
+ if (formData.sessionId !== Odac.Request.session('_client')) {
361
+ return Odac.return({
362
362
  result: {success: false},
363
- errors: {_candy_form: 'Invalid session'}
363
+ errors: {_odac_form: 'Invalid session'}
364
364
  })
365
365
  }
366
366
 
367
- if (formData.userAgent !== Candy.Request.header('user-agent')) {
368
- return Candy.return({
367
+ if (formData.userAgent !== Odac.Request.header('user-agent')) {
368
+ return Odac.return({
369
369
  result: {success: false},
370
- errors: {_candy_form: 'Invalid request'}
370
+ errors: {_odac_form: 'Invalid request'}
371
371
  })
372
372
  }
373
373
 
374
- if (formData.ip !== Candy.Request.ip) {
375
- return Candy.return({
374
+ if (formData.ip !== Odac.Request.ip) {
375
+ return Odac.return({
376
376
  result: {success: false},
377
- errors: {_candy_form: 'Invalid request'}
377
+ errors: {_odac_form: 'Invalid request'}
378
378
  })
379
379
  }
380
380
 
381
- if (await Candy.formValidator.error()) {
382
- return Candy.formValidator.result()
381
+ if (await Odac.formValidator.error()) {
382
+ return Odac.formValidator.result()
383
383
  }
384
384
 
385
- if (Candy.formConfig.table) {
385
+ if (Odac.formConfig.table) {
386
386
  try {
387
- const mysql = Candy.Mysql
387
+ const mysql = Odac.Mysql
388
388
 
389
- for (const field of Candy.formUniqueFields) {
390
- if (Candy.formData[field.name] == null) continue
389
+ for (const field of Odac.formUniqueFields) {
390
+ if (Odac.formData[field.name] == null) continue
391
391
 
392
392
  const existingRecord = await mysql.query(`SELECT id FROM ?? WHERE ?? = ? LIMIT 1`, [
393
- Candy.formConfig.table,
393
+ Odac.formConfig.table,
394
394
  field.name,
395
- Candy.formData[field.name]
395
+ Odac.formData[field.name]
396
396
  ])
397
397
 
398
398
  if (existingRecord && existingRecord.length > 0) {
399
399
  const errorMessage = field.message || `This ${field.name} is already registered`
400
- return Candy.return({
400
+ return Odac.return({
401
401
  result: {success: false},
402
402
  errors: {[field.name]: errorMessage}
403
403
  })
404
404
  }
405
405
  }
406
406
 
407
- await mysql.query('INSERT INTO ?? SET ?', [Candy.formConfig.table, Candy.formData])
407
+ await mysql.query('INSERT INTO ?? SET ?', [Odac.formConfig.table, Odac.formData])
408
408
 
409
- Candy.Request.session(`_custom_form_${token}`, null)
409
+ Odac.Request.session(`_custom_form_${token}`, null)
410
410
 
411
- return Candy.return({
411
+ return Odac.return({
412
412
  result: {
413
413
  success: true,
414
- message: Candy.formConfig.successMessage || 'Form submitted successfully!',
415
- redirect: Candy.formConfig.redirect
414
+ message: Odac.formConfig.successMessage || 'Form submitted successfully!',
415
+ redirect: Odac.formConfig.redirect
416
416
  }
417
417
  })
418
418
  } catch (error) {
419
419
  if (error.message === 'Database connection not configured') {
420
- return Candy.return({
420
+ return Odac.return({
421
421
  result: {success: false},
422
- errors: {_candy_form: 'Database not configured. Please check your config.json'}
422
+ errors: {_odac_form: 'Database not configured. Please check your config.json'}
423
423
  })
424
424
  }
425
425
 
426
- return Candy.return({
426
+ return Odac.return({
427
427
  result: {success: false},
428
- errors: {_candy_form: error.message || 'Database error occurred'}
428
+ errors: {_odac_form: error.message || 'Database error occurred'}
429
429
  })
430
430
  }
431
431
  }
432
432
 
433
- Candy.Request.session(`_custom_form_${token}`, null)
433
+ Odac.Request.session(`_custom_form_${token}`, null)
434
434
 
435
435
  return null
436
436
  }
@@ -0,0 +1,75 @@
1
+ class MiddlewareChain {
2
+ constructor(route, middlewares) {
3
+ this._route = route
4
+ this._middlewares = middlewares
5
+ this.auth = {
6
+ page: (path, authFile, file) => this.authPage(path, authFile, file),
7
+ post: (path, authFile, file) => this.authPost(path, authFile, file),
8
+ get: (path, authFile, file) => this.authGet(path, authFile, file),
9
+ ws: (path, handler, options) => this.authWs(path, handler, options)
10
+ }
11
+ }
12
+
13
+ use(...middlewares) {
14
+ this._middlewares.push(...middlewares.flat())
15
+ return this
16
+ }
17
+
18
+ page(path, file) {
19
+ this._route._pendingMiddlewares = [...this._middlewares]
20
+ this._route.page(path, file)
21
+ this._route._pendingMiddlewares = []
22
+ return this
23
+ }
24
+
25
+ post(path, file, options) {
26
+ this._route._pendingMiddlewares = [...this._middlewares]
27
+ this._route.post(path, file, options)
28
+ this._route._pendingMiddlewares = []
29
+ return this
30
+ }
31
+
32
+ get(path, file, options) {
33
+ this._route._pendingMiddlewares = [...this._middlewares]
34
+ this._route.get(path, file, options)
35
+ this._route._pendingMiddlewares = []
36
+ return this
37
+ }
38
+
39
+ ws(path, handler, options) {
40
+ this._route._pendingMiddlewares = [...this._middlewares]
41
+ this._route.ws(path, handler, options)
42
+ this._route._pendingMiddlewares = []
43
+ return this
44
+ }
45
+
46
+ authPage(path, authFile, file) {
47
+ this._route._pendingMiddlewares = [...this._middlewares]
48
+ this._route.authPage(path, authFile, file)
49
+ this._route._pendingMiddlewares = []
50
+ return this
51
+ }
52
+
53
+ authPost(path, authFile, file) {
54
+ this._route._pendingMiddlewares = [...this._middlewares]
55
+ this._route.authPost(path, authFile, file)
56
+ this._route._pendingMiddlewares = []
57
+ return this
58
+ }
59
+
60
+ authGet(path, authFile, file) {
61
+ this._route._pendingMiddlewares = [...this._middlewares]
62
+ this._route.authGet(path, authFile, file)
63
+ this._route._pendingMiddlewares = []
64
+ return this
65
+ }
66
+
67
+ authWs(path, handler, options) {
68
+ this._route._pendingMiddlewares = [...this._middlewares]
69
+ this._route.authWs(path, handler, options)
70
+ this._route._pendingMiddlewares = []
71
+ return this
72
+ }
73
+ }
74
+
75
+ module.exports = MiddlewareChain