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
package/docs/index.json CHANGED
@@ -125,8 +125,8 @@
125
125
  "title": "Backend Overview",
126
126
  "children": [
127
127
  {
128
- "file": "01-whats-in-the-candy-box.md",
129
- "title": "Candy Object"
128
+ "file": "01-whats-in-the-odac-box.md",
129
+ "title": "Odac Object"
130
130
  },
131
131
  {
132
132
  "file": "02-super-handy-helper-functions.md",
@@ -209,6 +209,26 @@
209
209
  {
210
210
  "file": "07-cron-jobs.md",
211
211
  "title": "Cron Jobs"
212
+ },
213
+ {
214
+ "file": "08-middleware.md",
215
+ "title": "Middleware"
216
+ },
217
+ {
218
+ "file": "09-websocket.md",
219
+ "title": "WebSocket"
220
+ },
221
+ {
222
+ "file": "09-websocket-examples.md",
223
+ "title": "WebSocket Examples"
224
+ },
225
+ {
226
+ "file": "09-websocket-quick-reference.md",
227
+ "title": "WebSocket Quick Reference"
228
+ },
229
+ {
230
+ "file": "09-websocket-auth-middleware.md",
231
+ "title": "WebSocket Auth & Middleware"
212
232
  }
213
233
  ]
214
234
  },
@@ -221,8 +241,8 @@
221
241
  "title": "Building Controllers"
222
242
  },
223
243
  {
224
- "file": "02-your-trusty-candy-assistant.md",
225
- "title": "Using Candy"
244
+ "file": "02-your-trusty-odac-assistant.md",
245
+ "title": "Using Odac"
226
246
  },
227
247
  {
228
248
  "file": "03-controller-classes.md",
@@ -345,16 +365,16 @@
345
365
  "title": "User Registration (Programmatic)"
346
366
  },
347
367
  {
348
- "file": "04-candy-register-forms.md",
349
- "title": "Candy Register Forms (Zero-Config)"
368
+ "file": "04-odac-register-forms.md",
369
+ "title": "Odac Register Forms (Zero-Config)"
350
370
  },
351
371
  {
352
372
  "file": "05-session-management.md",
353
373
  "title": "Session Management"
354
374
  },
355
375
  {
356
- "file": "06-candy-login-forms.md",
357
- "title": "Candy Login Forms (Zero-Config)"
376
+ "file": "06-odac-login-forms.md",
377
+ "title": "Odac Login Forms (Zero-Config)"
358
378
  }
359
379
  ]
360
380
  },
@@ -383,8 +403,8 @@
383
403
  "title": "Utilities",
384
404
  "children": [
385
405
  {
386
- "file": "01-candy-var.md",
387
- "title": "Candy.Var"
406
+ "file": "01-odac-var.md",
407
+ "title": "Odac.Var"
388
408
  }
389
409
  ]
390
410
  }
@@ -392,7 +412,7 @@
392
412
  "frontend": [
393
413
  {
394
414
  "file": "01-overview",
395
- "title": "candy.js Overview",
415
+ "title": "odac.js Overview",
396
416
  "children": [
397
417
  {
398
418
  "file": "01-introduction.md",
@@ -447,6 +467,24 @@
447
467
  "title": "Client-Side Streaming"
448
468
  }
449
469
  ]
470
+ },
471
+ {
472
+ "file": "06-websocket",
473
+ "title": "WebSocket",
474
+ "children": [
475
+ {
476
+ "file": "00-overview.md",
477
+ "title": "Overview"
478
+ },
479
+ {
480
+ "file": "01-websocket-client.md",
481
+ "title": "WebSocket Client"
482
+ },
483
+ {
484
+ "file": "02-shared-websocket.md",
485
+ "title": "Shared WebSocket (Cross-Tab)"
486
+ }
487
+ ]
450
488
  }
451
489
  ]
452
490
  }
package/eslint.config.mjs CHANGED
@@ -7,11 +7,11 @@ import prettierConfig from 'eslint-config-prettier'
7
7
  export default defineConfig([
8
8
  {
9
9
  files: ['core/**/*.js', 'watchdog/**/*.js', 'server/**/*.js', 'cli/**/*.js'],
10
- ignores: ['server/src/Candy.js'],
10
+ ignores: ['server/src/Odac.js'],
11
11
  languageOptions: {
12
12
  globals: {
13
13
  ...globals.node,
14
- Candy: 'readonly',
14
+ Odac: 'readonly',
15
15
  __: 'readonly'
16
16
  },
17
17
  sourceType: 'script'
@@ -27,7 +27,7 @@ export default defineConfig([
27
27
  }
28
28
  },
29
29
  {
30
- files: ['server/src/Candy.js'],
30
+ files: ['server/src/Odac.js'],
31
31
  languageOptions: {
32
32
  globals: {
33
33
  ...globals.node,
@@ -52,7 +52,7 @@ export default defineConfig([
52
52
  languageOptions: {
53
53
  globals: {
54
54
  ...globals.node,
55
- Candy: 'readonly',
55
+ Odac: 'readonly',
56
56
  __dir: 'readonly'
57
57
  },
58
58
  sourceType: 'script'
@@ -84,7 +84,7 @@ export default defineConfig([
84
84
  languageOptions: {
85
85
  globals: {
86
86
  ...globals.node,
87
- Candy: 'readonly'
87
+ Odac: 'readonly'
88
88
  },
89
89
  sourceType: 'script'
90
90
  },
@@ -103,7 +103,7 @@ export default defineConfig([
103
103
  languageOptions: {
104
104
  globals: {
105
105
  ...globals.browser,
106
- Candy: 'readonly'
106
+ odac: 'readonly'
107
107
  },
108
108
  sourceType: 'script'
109
109
  },
@@ -1,4 +1,4 @@
1
1
  'use strict'
2
2
 
3
3
  global.__dir = process.cwd()
4
- require('./src/Candy.js').init()
4
+ require('./src/Odac.js').init()
package/package.json CHANGED
@@ -1,63 +1,38 @@
1
1
  {
2
2
  "name": "odac",
3
- "description": "🍭 Next-Gen Server & Framework: Web, DNS, Mail, SSL & Monitoring in one CLI.",
4
- "homepage": "https://candypack.dev",
3
+ "description": " Next-Gen Server & Framework: Web, DNS, Mail, SSL & Monitoring in one CLI.",
4
+ "homepage": "https://odac.run",
5
5
  "author": {
6
6
  "name": "emre.red",
7
7
  "email": "mail@emre.red",
8
8
  "url": "https://emre.red"
9
9
  },
10
- "version": "0.9.0",
11
- "license": "AGPL-3.0",
10
+ "version": "1.0.0",
11
+ "license": "MIT",
12
12
  "engines": {
13
13
  "node": ">=18.0.0"
14
14
  },
15
- "main": "framework/index.js",
15
+ "bin": {
16
+ "odac": "./bin/odac.js"
17
+ },
18
+ "main": "index.js",
16
19
  "repository": {
17
20
  "type": "git",
18
- "url": "https://github.com/CandyPack/CandyPack.git"
19
- },
20
- "directories": {
21
- "bin": "./bin",
22
- "cli": "./cli",
23
- "core": "./core",
24
- "docs": "./docs",
25
- "framework": "./framework",
26
- "locale": "./locale",
27
- "server": "./server",
28
- "test": "./test",
29
- "watchdog": "./watchdog",
30
- "web": "./web"
31
- },
32
- "bin": {
33
- "candy": "bin/candy",
34
- "candypack": "bin/candypack"
21
+ "url": "https://github.com/odac-run/framework.git"
35
22
  },
36
23
  "dependencies": {
37
- "acme-client": "^5.1.0",
38
24
  "axios": "^1.7.9",
39
25
  "bcrypt": "^5.1.1",
40
- "dkim-signer": "^0.2.2",
41
- "find-process": "^2.0.0",
42
- "http-proxy": "^1.18.1",
43
- "mailparser": "^3.7.1",
44
- "mysql2": "^3.6.5",
45
- "native-dns": "^0.7.0",
46
- "node-forge": "^1.3.1",
47
- "selfsigned": "^2.4.1",
48
- "smtp-server": "^3.13.4",
49
- "sqlite3": "^5.1.7",
50
- "ssh2": "^1.15.0",
51
- "ws": "^8.18.0"
26
+ "mysql2": "^3.6.5"
52
27
  },
53
28
  "devDependencies": {
54
29
  "@eslint/js": "^9.33.0",
55
30
  "@semantic-release/changelog": "^6.0.3",
56
31
  "@semantic-release/commit-analyzer": "^13.0.1",
57
32
  "@semantic-release/git": "^10.0.1",
58
- "@semantic-release/github": "^11.0.4",
59
- "@semantic-release/npm": "^12.0.2",
60
- "@semantic-release/release-notes-generator": "^14.0.3",
33
+ "@semantic-release/github": "^12.0.2",
34
+ "@semantic-release/npm": "^13.1.3",
35
+ "@semantic-release/release-notes-generator": "^14.1.0",
61
36
  "conventional-changelog-conventionalcommits": "^9.1.0",
62
37
  "eslint": "^9.33.0",
63
38
  "eslint-config-prettier": "^10.1.8",
@@ -67,7 +42,7 @@
67
42
  "jest": "^30.0.5",
68
43
  "lint-staged": "^16.1.5",
69
44
  "prettier": "3.6.2",
70
- "semantic-release": "^24.2.7"
45
+ "semantic-release": "^25.0.2"
71
46
  },
72
47
  "scripts": {
73
48
  "lint": "eslint .",
@@ -18,14 +18,14 @@ class Auth {
18
18
  }
19
19
 
20
20
  async check(where) {
21
- if (!Candy.Config.auth) Candy.Config.auth = {}
22
- this.#table = Candy.Config.auth.table || 'users'
21
+ if (!Odac.Config.auth) Odac.Config.auth = {}
22
+ this.#table = Odac.Config.auth.table || 'users'
23
23
  if (!this.#table) return false
24
24
  if (where) {
25
25
  if (!this.#validateInput(where)) return false
26
- let sql = Candy.Mysql.table(this.#table)
26
+ let sql = Odac.Mysql.table(this.#table)
27
27
  if (!sql) {
28
- console.error('CandyPack Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
28
+ console.error('Odac Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
29
29
  return false
30
30
  }
31
31
  for (let key in where) sql = sql.orWhere(key, where[key] instanceof Promise ? await where[key] : where[key])
@@ -38,8 +38,8 @@ class Auth {
38
38
  if (where[key] instanceof Promise) where[key] = await where[key]
39
39
  if (!user[key]) equal = false
40
40
  if (user[key] === where[key]) equal = equal && true
41
- else if (Candy.Var(user[key]).is('bcrypt')) equal = equal && Candy.Var(user[key]).hashCheck(where[key])
42
- else if (Candy.Var(user[key]).is('md5')) equal = equal && Candy.Var(where[key]).md5() === user[key]
41
+ else if (Odac.Var(user[key]).is('bcrypt')) equal = equal && Odac.Var(user[key]).hashCheck(where[key])
42
+ else if (Odac.Var(user[key]).is('md5')) equal = equal && Odac.Var(where[key]).md5() === user[key]
43
43
  }
44
44
  if (equal) break
45
45
  }
@@ -48,33 +48,33 @@ class Auth {
48
48
  } else if (this.#user) {
49
49
  return true
50
50
  } else {
51
- let check_table = await Candy.Mysql.run('SHOW TABLES LIKE ?', [this.#table])
51
+ let check_table = await Odac.Mysql.run('SHOW TABLES LIKE ?', [this.#table])
52
52
  if (check_table.length == 0) return false
53
- let candy_x = this.#request.cookie('candy_x')
54
- let candy_y = this.#request.cookie('candy_y')
53
+ let odac_x = this.#request.cookie('odac_x')
54
+ let odac_y = this.#request.cookie('odac_y')
55
55
  let browser = this.#request.header('user-agent')
56
- if (!candy_x || !candy_y || !browser) return false
57
- const tokenTable = Candy.Config.auth.token || 'candy_auth'
58
- const primaryKey = Candy.Config.auth.key || 'id'
59
- let sql_token = await Candy.Mysql.table(tokenTable).where(['token_x', candy_x], ['browser', browser]).get()
56
+ if (!odac_x || !odac_y || !browser) return false
57
+ const tokenTable = Odac.Config.auth.token || 'odac_auth'
58
+ const primaryKey = Odac.Config.auth.key || 'id'
59
+ let sql_token = await Odac.Mysql.table(tokenTable).where(['token_x', odac_x], ['browser', browser]).get()
60
60
  if (sql_token.length !== 1) return false
61
- if (!Candy.Var(sql_token[0].token_y).hashCheck(candy_y)) return false
61
+ if (!Odac.Var(sql_token[0].token_y).hashCheck(odac_y)) return false
62
62
 
63
- const maxAge = Candy.Config.auth?.maxAge || 30 * 24 * 60 * 60 * 1000
64
- const updateAge = Candy.Config.auth?.updateAge || 24 * 60 * 60 * 1000
63
+ const maxAge = Odac.Config.auth?.maxAge || 30 * 24 * 60 * 60 * 1000
64
+ const updateAge = Odac.Config.auth?.updateAge || 24 * 60 * 60 * 1000
65
65
  const now = Date.now()
66
66
  const lastActive = new Date(sql_token[0].active).getTime()
67
67
  const inactiveAge = now - lastActive
68
68
 
69
69
  if (inactiveAge > maxAge) {
70
- await Candy.Mysql.table(tokenTable).where('id', sql_token[0].id).delete()
70
+ await Odac.Mysql.table(tokenTable).where('id', sql_token[0].id).delete()
71
71
  return false
72
72
  }
73
73
 
74
- this.#user = await Candy.Mysql.table(this.#table).where(primaryKey, sql_token[0].user).first()
74
+ this.#user = await Odac.Mysql.table(this.#table).where(primaryKey, sql_token[0].user).first()
75
75
 
76
76
  if (inactiveAge > updateAge) {
77
- Candy.Mysql.table(tokenTable)
77
+ Odac.Mysql.table(tokenTable)
78
78
  .where('id', sql_token[0].id)
79
79
  .set({active: new Date()})
80
80
  .catch(() => {})
@@ -88,40 +88,40 @@ class Auth {
88
88
  this.#user = null
89
89
  let user = await this.check(where)
90
90
  if (!user) return false
91
- if (!Candy.Config.auth) Candy.Config.auth = {}
92
- let key = Candy.Config.auth.key || 'id'
93
- let token = Candy.Config.auth.token || 'candy_auth'
91
+ if (!Odac.Config.auth) Odac.Config.auth = {}
92
+ let key = Odac.Config.auth.key || 'id'
93
+ let token = Odac.Config.auth.token || 'odac_auth'
94
94
  const mysql = require('mysql2')
95
95
  const safeTokenTable = mysql.escapeId(token)
96
- let check_table = await Candy.Mysql.run('SHOW TABLES LIKE ?', [token])
96
+ let check_table = await Odac.Mysql.run('SHOW TABLES LIKE ?', [token])
97
97
  if (check_table === false) {
98
- console.error('CandyPack Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
98
+ console.error('Odac Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
99
99
  return false
100
100
  }
101
101
  if (check_table.length == 0)
102
- await Candy.Mysql.run(
102
+ await Odac.Mysql.run(
103
103
  `CREATE TABLE ${safeTokenTable} (id INT NOT NULL AUTO_INCREMENT, user INT NOT NULL, token_x VARCHAR(255) NOT NULL, token_y VARCHAR(255) NOT NULL, browser VARCHAR(255) NOT NULL, ip VARCHAR(255) NOT NULL, \`date\` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \`active\` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id))`
104
104
  )
105
105
 
106
106
  this.#cleanupExpiredTokens(token)
107
107
 
108
- let token_y = Candy.Var(Math.random().toString() + Date.now().toString() + this.#request.id + this.#request.ip).md5()
108
+ let token_y = Odac.Var(Math.random().toString() + Date.now().toString() + this.#request.id + this.#request.ip).md5()
109
109
  let cookie = {
110
110
  user: user[key],
111
- token_x: Candy.Var(Math.random().toString() + Date.now().toString()).md5(),
112
- token_y: Candy.Var(token_y).hash(),
111
+ token_x: Odac.Var(Math.random().toString() + Date.now().toString()).md5(),
112
+ token_y: Odac.Var(token_y).hash(),
113
113
  browser: this.#request.header('user-agent'),
114
114
  ip: this.#request.ip
115
115
  }
116
- this.#request.cookie('candy_x', cookie.token_x, {
116
+ this.#request.cookie('odac_x', cookie.token_x, {
117
117
  httpOnly: true,
118
118
  secure: true,
119
119
  sameSite: 'Strict'
120
120
  })
121
- this.#request.cookie('candy_y', token_y, {httpOnly: true, secure: true, sameSite: 'Strict'})
122
- let mysqlTable = Candy.Mysql.table(token)
121
+ this.#request.cookie('odac_y', token_y, {httpOnly: true, secure: true, sameSite: 'Strict'})
122
+ let mysqlTable = Odac.Mysql.table(token)
123
123
  if (!mysqlTable) {
124
- console.error('CandyPack Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
124
+ console.error('Odac Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
125
125
  return false
126
126
  }
127
127
  let sql = await mysqlTable.insert(cookie)
@@ -129,28 +129,28 @@ class Auth {
129
129
  }
130
130
 
131
131
  async #cleanupExpiredTokens(tokenTable) {
132
- const maxAge = Candy.Config.auth?.maxAge || 30 * 24 * 60 * 60 * 1000
132
+ const maxAge = Odac.Config.auth?.maxAge || 30 * 24 * 60 * 60 * 1000
133
133
  const cutoffDate = new Date(Date.now() - maxAge)
134
134
 
135
- Candy.Mysql.table(tokenTable)
135
+ Odac.Mysql.table(tokenTable)
136
136
  .where('active', '<', cutoffDate)
137
137
  .delete()
138
138
  .catch(() => {})
139
139
  }
140
140
 
141
141
  async register(data, options = {}) {
142
- if (!Candy.Config.auth) {
143
- Candy.Config.auth = {}
142
+ if (!Odac.Config.auth) {
143
+ Odac.Config.auth = {}
144
144
  }
145
145
 
146
- this.#table = Candy.Config.auth.table || 'users'
147
- const primaryKey = Candy.Config.auth.key || 'id'
146
+ this.#table = Odac.Config.auth.table || 'users'
147
+ const primaryKey = Odac.Config.auth.key || 'id'
148
148
  const passwordField = options.passwordField || 'password'
149
149
  const uniqueFields = options.uniqueFields || ['email']
150
150
 
151
- const checkTable = await Candy.Mysql.run('SHOW TABLES LIKE ?', [this.#table])
151
+ const checkTable = await Odac.Mysql.run('SHOW TABLES LIKE ?', [this.#table])
152
152
  if (checkTable === false) {
153
- console.error('CandyPack Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
153
+ console.error('Odac Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
154
154
  return {success: false, error: 'Database connection not configured'}
155
155
  }
156
156
  if (checkTable.length === 0) {
@@ -161,15 +161,15 @@ class Auth {
161
161
  return {success: false, error: 'Invalid data provided'}
162
162
  }
163
163
 
164
- if (data[passwordField] && !Candy.Var(data[passwordField]).is('bcrypt')) {
165
- data[passwordField] = Candy.Var(data[passwordField]).hash()
164
+ if (data[passwordField] && !Odac.Var(data[passwordField]).is('bcrypt')) {
165
+ data[passwordField] = Odac.Var(data[passwordField]).hash()
166
166
  }
167
167
 
168
168
  for (const field of uniqueFields) {
169
169
  if (data[field]) {
170
- const mysqlTable = Candy.Mysql.table(this.#table)
170
+ const mysqlTable = Odac.Mysql.table(this.#table)
171
171
  if (!mysqlTable) {
172
- console.error('CandyPack Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
172
+ console.error('Odac Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
173
173
  return {success: false, error: 'Database connection not configured'}
174
174
  }
175
175
  const existing = await mysqlTable.where(field, data[field]).first()
@@ -180,26 +180,26 @@ class Auth {
180
180
  }
181
181
 
182
182
  try {
183
- const mysqlTable = Candy.Mysql.table(this.#table)
183
+ const mysqlTable = Odac.Mysql.table(this.#table)
184
184
  if (!mysqlTable) {
185
- console.error('CandyPack Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
185
+ console.error('Odac Auth Error: MySQL connection not configured. Please add database configuration to your config.json')
186
186
  return {success: false, error: 'Database connection not configured'}
187
187
  }
188
188
  const insertResult = await mysqlTable.insert(data)
189
189
  if (insertResult === false) {
190
- console.error('CandyPack Auth Error: Failed to insert user into database - query failed')
190
+ console.error('Odac Auth Error: Failed to insert user into database - query failed')
191
191
  console.error('Data attempted to insert:', {...data, [passwordField]: '[REDACTED]'})
192
192
  return {success: false, error: 'Failed to create user'}
193
193
  }
194
194
  if (!insertResult.affected || insertResult.affected === 0) {
195
- console.error('CandyPack Auth Error: Insert query succeeded but no rows were affected')
195
+ console.error('Odac Auth Error: Insert query succeeded but no rows were affected')
196
196
  console.error('Insert result:', insertResult)
197
197
  console.error('Data attempted to insert:', {...data, [passwordField]: '[REDACTED]'})
198
198
  return {success: false, error: 'Failed to create user'}
199
199
  }
200
200
 
201
201
  const userId = insertResult.id
202
- const newUser = await Candy.Mysql.table(this.#table).where(primaryKey, userId).first()
202
+ const newUser = await Odac.Mysql.table(this.#table).where(primaryKey, userId).first()
203
203
 
204
204
  if (!newUser) {
205
205
  return {success: false, error: 'User created but could not be retrieved'}
@@ -219,7 +219,7 @@ class Auth {
219
219
 
220
220
  return {success: true, user: newUser}
221
221
  } catch (error) {
222
- console.error('CandyPack Auth Error: Registration failed with exception')
222
+ console.error('Odac Auth Error: Registration failed with exception')
223
223
  console.error('Error:', error.message)
224
224
  console.error('Stack:', error.stack)
225
225
  return {success: false, error: error.message || 'Registration failed'}
@@ -229,20 +229,20 @@ class Auth {
229
229
  async logout() {
230
230
  if (!this.#user) return false
231
231
 
232
- if (!Candy.Config.auth) Candy.Config.auth = {}
233
- const token = Candy.Config.auth.token || 'user_tokens'
234
- const candyX = this.#request.cookie('candy_x')
232
+ if (!Odac.Config.auth) Odac.Config.auth = {}
233
+ const token = Odac.Config.auth.token || 'user_tokens'
234
+ const odacX = this.#request.cookie('odac_x')
235
235
  const browser = this.#request.header('user-agent')
236
236
 
237
- if (candyX && browser) {
238
- const mysqlTable = Candy.Mysql.table(token)
237
+ if (odacX && browser) {
238
+ const mysqlTable = Odac.Mysql.table(token)
239
239
  if (mysqlTable) {
240
- await mysqlTable.where(['token_x', candyX], ['browser', browser]).delete()
240
+ await mysqlTable.where(['token_x', odacX], ['browser', browser]).delete()
241
241
  }
242
242
  }
243
243
 
244
- this.#request.cookie('candy_x', '', {maxAge: -1})
245
- this.#request.cookie('candy_y', '', {maxAge: -1})
244
+ this.#request.cookie('odac_x', '', {maxAge: -1})
245
+ this.#request.cookie('odac_y', '', {maxAge: -1})
246
246
 
247
247
  this.#user = null
248
248
  return true
@@ -296,7 +296,7 @@ class Auth {
296
296
  const safeTableName = mysql.escapeId(tableName)
297
297
  const sql = `CREATE TABLE ${safeTableName} (${columns.join(', ')}) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`
298
298
 
299
- await Candy.Mysql.run(sql)
299
+ await Odac.Mysql.run(sql)
300
300
  }
301
301
 
302
302
  user(col) {
@@ -11,7 +11,7 @@ module.exports = {
11
11
  timeout: 10000
12
12
  },
13
13
  encrypt: {
14
- key: 'candy'
14
+ key: 'odac'
15
15
  },
16
16
  earlyHints: {
17
17
  enabled: true,
@@ -21,7 +21,7 @@ module.exports = {
21
21
 
22
22
  init: function () {
23
23
  try {
24
- this.system = JSON.parse(fs.readFileSync(os.homedir() + '/.candypack/config.json'))
24
+ this.system = JSON.parse(fs.readFileSync(os.homedir() + '/.odac/config.json'))
25
25
  } catch {
26
26
  this.system = {}
27
27
  }
@@ -43,7 +43,7 @@ module.exports = {
43
43
  if (typeof obj === 'string') {
44
44
  return obj.replace(/\$\{(\w+)\}/g, (_, key) => {
45
45
  // Special variables
46
- if (key === 'candy') {
46
+ if (key === 'odac') {
47
47
  return __dirname.replace(/\/framework\/src$/, '')
48
48
  }
49
49
  // Environment variables
@@ -1,12 +1,12 @@
1
1
  const fs = require('fs')
2
2
 
3
3
  class Lang {
4
- #candy
4
+ #odac
5
5
  #data = {}
6
6
  #lang
7
7
 
8
- constructor(Candy) {
9
- this.#candy = Candy
8
+ constructor(Odac) {
9
+ this.#odac = Odac
10
10
  this.set()
11
11
  }
12
12
 
@@ -42,10 +42,10 @@ class Lang {
42
42
  }
43
43
 
44
44
  set(lang) {
45
- if (!lang || lang.length !== 2 || !this.#candy.Var(lang).is('alpha')) {
46
- if (this.#candy.Request.header('ACCEPT-LANGUAGE') && this.#candy.Request.header('ACCEPT-LANGUAGE').length > 1)
47
- lang = this.#candy.Request.header('ACCEPT-LANGUAGE').substr(0, 2)
48
- else lang = this.#candy.Config.lang?.default || 'en'
45
+ if (!lang || lang.length !== 2 || !this.#odac.Var(lang).is('alpha')) {
46
+ if (this.#odac.Request.header('ACCEPT-LANGUAGE') && this.#odac.Request.header('ACCEPT-LANGUAGE').length > 1)
47
+ lang = this.#odac.Request.header('ACCEPT-LANGUAGE').substr(0, 2)
48
+ else lang = this.#odac.Config.lang?.default || 'en'
49
49
  }
50
50
  this.#lang = lang
51
51
  if (fs.existsSync(__dir + '/storage/language/' + lang + '.json'))
@@ -37,17 +37,17 @@ class Mail {
37
37
  return new Promise(resolve => {
38
38
  if (!fs.existsSync(__dir + '/view/mail/' + this.#template + '.html')) return console.log('Template not found') && false
39
39
  if (!this.#from || !this.#subject || !this.#to) return console.log('From, Subject and To fields are required') && false
40
- if (!Candy.Var(this.#from.email).is('email')) return console.log('From field is not a valid e-mail address') && false
41
- if (!Candy.Var(this.#to.value[0].address).is('email')) return console.log('To field is not a valid e-mail address') && false
40
+ if (!Odac.Var(this.#from.email).is('email')) return console.log('From field is not a valid e-mail address') && false
41
+ if (!Odac.Var(this.#to.value[0].address).is('email')) return console.log('To field is not a valid e-mail address') && false
42
42
  if (!this.#header['From']) this.#header['From'] = `${this.#from.name} <${this.#from.email}>`
43
43
  if (!this.#header['To']) this.#header['To'] = this.#to
44
44
  if (!this.#header['Subject']) this.#header['Subject'] = this.#subject
45
- if (!this.#header['Message-ID']) this.#header['Message-ID'] = `<${crypto.randomBytes(16).toString('hex')}-${Date.now()}@candypack>`
45
+ if (!this.#header['Message-ID']) this.#header['Message-ID'] = `<${crypto.randomBytes(16).toString('hex')}-${Date.now()}@odac>`
46
46
  if (!this.#header['Content-Transfer-Encoding']) this.#header['Content-Transfer-Encoding'] = 'quoted-printable'
47
47
  if (!this.#header['Date']) this.#header['Date'] = new Date().toUTCString()
48
48
  if (!this.#header['Content-Type'])
49
49
  this.#header['Content-Type'] = 'multipart/alternative; boundary="----=' + crypto.randomBytes(32).toString('hex') + '"'
50
- if (!this.#header['X-Mailer']) this.#header['X-Mailer'] = 'CandyPack'
50
+ if (!this.#header['X-Mailer']) this.#header['X-Mailer'] = 'Odac'
51
51
  if (!this.#header['MIME-Version']) this.#header['MIME-Version'] = '1.0'
52
52
  let content = fs.readFileSync(__dir + '/view/mail/' + this.#template + '.html').toString()
53
53
  for (const iterator of Object.keys(data)) content = content.replace(new RegExp(`{${iterator}}`, 'g'), data[iterator])
@@ -67,7 +67,7 @@ class Mail {
67
67
  }
68
68
  ]
69
69
  },
70
- {headers: {Authorization: Candy.Config.system.api.auth}}
70
+ {headers: {Authorization: Odac.Config.system.api.auth}}
71
71
  )
72
72
  .then(response => {
73
73
  resolve(response.data)