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.
- package/.github/workflows/auto-pr-description.yml +0 -2
- package/.github/workflows/codeql.yml +46 -0
- package/.github/workflows/release.yml +13 -6
- package/.github/workflows/test-coverage.yml +10 -9
- package/.releaserc.js +9 -6
- package/CHANGELOG.md +62 -150
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.md +8 -8
- package/LICENSE +21 -661
- package/README.md +12 -12
- package/SECURITY.md +4 -4
- package/bin/odac.js +101 -0
- package/{framework/web/candy.js → client/odac.js} +310 -44
- package/docs/backend/01-overview/{01-whats-in-the-candy-box.md → 01-whats-in-the-odac-box.md} +4 -2
- package/docs/backend/01-overview/02-super-handy-helper-functions.md +29 -1
- package/docs/backend/01-overview/03-development-server.md +11 -11
- package/docs/backend/02-structure/01-typical-project-layout.md +4 -4
- package/docs/backend/03-config/00-configuration-overview.md +6 -6
- package/docs/backend/03-config/01-database-connection.md +1 -1
- package/docs/backend/03-config/02-static-route-mapping-optional.md +4 -4
- package/docs/backend/03-config/04-environment-variables.md +20 -20
- package/docs/backend/03-config/05-early-hints.md +4 -4
- package/docs/backend/04-routing/01-basic-page-routes.md +4 -4
- package/docs/backend/04-routing/02-controller-less-view-routes.md +5 -5
- package/docs/backend/04-routing/03-api-and-data-routes.md +3 -3
- package/docs/backend/04-routing/04-authentication-aware-routes.md +5 -5
- package/docs/backend/04-routing/05-advanced-routing.md +3 -3
- package/docs/backend/04-routing/06-error-pages.md +17 -17
- package/docs/backend/04-routing/07-cron-jobs.md +13 -13
- package/docs/backend/04-routing/08-middleware.md +214 -0
- package/docs/backend/04-routing/09-websocket-auth-middleware.md +292 -0
- package/docs/backend/04-routing/09-websocket-examples.md +381 -0
- package/docs/backend/04-routing/09-websocket-quick-reference.md +211 -0
- package/docs/backend/04-routing/09-websocket.md +298 -0
- package/docs/backend/05-controllers/01-how-to-build-a-controller.md +3 -3
- package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +41 -0
- package/docs/backend/05-controllers/03-controller-classes.md +19 -19
- package/docs/backend/05-forms/01-custom-forms.md +114 -114
- package/docs/backend/05-forms/02-automatic-database-insert.md +82 -82
- package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +26 -26
- package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +10 -10
- package/docs/backend/07-views/01-the-view-directory.md +1 -1
- package/docs/backend/07-views/02-rendering-a-view.md +22 -22
- package/docs/backend/07-views/03-template-syntax.md +52 -52
- package/docs/backend/07-views/03-variables.md +84 -84
- package/docs/backend/07-views/04-request-data.md +57 -57
- package/docs/backend/07-views/05-conditionals.md +78 -78
- package/docs/backend/07-views/06-loops.md +114 -114
- package/docs/backend/07-views/07-translations.md +66 -66
- package/docs/backend/07-views/08-backend-javascript.md +103 -103
- package/docs/backend/07-views/09-comments.md +71 -71
- package/docs/backend/08-database/01-database-connection.md +8 -8
- package/docs/backend/08-database/02-using-mysql.md +49 -49
- package/docs/backend/09-validation/01-the-validator-service.md +38 -38
- package/docs/backend/10-authentication/01-user-logins-with-authjs.md +15 -15
- package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +10 -10
- package/docs/backend/10-authentication/03-register.md +12 -12
- package/docs/backend/10-authentication/{04-candy-register-forms.md → 04-odac-register-forms.md} +141 -141
- package/docs/backend/10-authentication/05-session-management.md +10 -10
- package/docs/backend/10-authentication/{06-candy-login-forms.md → 06-odac-login-forms.md} +125 -125
- package/docs/backend/11-mail/01-the-mail-service.md +5 -5
- package/docs/backend/12-streaming/01-streaming-overview.md +96 -54
- package/docs/backend/13-utilities/{01-candy-var.md → 01-odac-var.md} +109 -109
- package/docs/frontend/01-overview/01-introduction.md +30 -30
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +45 -45
- package/docs/frontend/02-ajax-navigation/02-configuration.md +14 -14
- package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +36 -36
- package/docs/frontend/03-forms/01-form-handling.md +32 -32
- package/docs/frontend/04-api-requests/01-get-post.md +33 -33
- package/docs/frontend/05-streaming/01-client-streaming.md +15 -15
- package/docs/frontend/06-websocket/00-overview.md +76 -0
- package/docs/frontend/06-websocket/01-websocket-client.md +139 -0
- package/docs/frontend/06-websocket/02-shared-websocket.md +149 -0
- package/docs/index.json +49 -11
- package/eslint.config.mjs +6 -6
- package/{framework/index.js → index.js} +1 -1
- package/package.json +14 -39
- package/{framework/src → src}/Auth.js +59 -59
- package/{framework/src → src}/Config.js +3 -3
- package/{framework/src → src}/Lang.js +7 -7
- package/{framework/src → src}/Mail.js +5 -5
- package/{framework/src → src}/Mysql.js +42 -42
- package/src/Odac.js +112 -0
- package/{framework/src → src}/Request.js +38 -36
- package/{framework/src → src}/Route/Internal.js +116 -116
- package/src/Route/Middleware.js +75 -0
- package/src/Route.js +621 -0
- package/src/Server.js +22 -0
- package/{framework/src → src}/Stream.js +11 -3
- package/{framework/src → src}/Validator.js +21 -21
- package/{framework/src → src}/Var.js +5 -5
- package/{framework/src → src}/View/EarlyHints.js +1 -1
- package/{framework/src → src}/View/Form.js +69 -69
- package/{framework/src → src}/View.js +78 -81
- package/src/WebSocket.js +403 -0
- package/template/config.json +5 -0
- package/{web → template}/controller/page/about.js +6 -6
- package/{web → template}/controller/page/index.js +9 -9
- package/{web → template}/package.json +4 -5
- package/{web → template}/public/assets/css/style.css +4 -4
- package/{web → template}/public/assets/js/app.js +6 -6
- package/{web → template}/route/www.js +6 -6
- package/{web → template}/skeleton/main.html +1 -1
- package/{web → template}/view/content/about.html +5 -5
- package/{web → template}/view/content/home.html +12 -12
- package/template/view/footer/main.html +11 -0
- package/{web → template}/view/head/main.html +1 -1
- package/{web → template}/view/header/main.html +2 -2
- package/test/core/Candy.test.js +58 -58
- package/test/core/Commands.test.js +7 -7
- package/test/core/Config.test.js +82 -85
- package/test/core/Lang.test.js +2 -2
- package/test/core/Process.test.js +6 -6
- package/test/framework/Route.test.js +56 -37
- package/test/framework/View/EarlyHints.test.js +2 -2
- package/test/framework/WebSocket.test.js +100 -0
- package/test/framework/middleware.test.js +85 -0
- package/test/server/Api.test.js +31 -31
- package/test/server/DNS.test.js +11 -11
- package/test/server/Hub.test.js +497 -0
- package/test/server/Mail.account.test_.js +3 -3
- package/test/server/Mail.init.test_.js +10 -10
- package/test/server/Mail.test_.js +20 -20
- package/test/server/SSL.test_.js +54 -54
- package/test/server/Server.test.js +39 -39
- package/test/server/Service.test_.js +7 -7
- package/test/server/Subdomain.test.js +7 -7
- package/test/server/Web/Firewall.test.js +87 -87
- package/test/server/Web/Proxy.test.js +397 -0
- package/test/server/{Web.test_.js → Web.test.js} +137 -205
- package/test/server/__mocks__/fs.js +2 -2
- package/test/server/__mocks__/{globalCandy.js → globalOdac.js} +5 -5
- package/test/server/__mocks__/index.js +6 -6
- package/test/server/__mocks__/testFactories.js +1 -1
- package/test/server/__mocks__/testHelpers.js +7 -7
- package/.husky/pre-commit +0 -2
- package/.kiro/steering/code-style.md +0 -56
- package/.kiro/steering/product.md +0 -20
- package/.kiro/steering/structure.md +0 -77
- package/.kiro/steering/tech.md +0 -87
- package/AGENTS.md +0 -84
- package/bin/candy +0 -10
- package/bin/candypack +0 -10
- package/cli/index.js +0 -3
- package/cli/src/Cli.js +0 -348
- package/cli/src/Connector.js +0 -93
- package/cli/src/Monitor.js +0 -416
- package/core/Candy.js +0 -87
- package/core/Commands.js +0 -239
- package/core/Config.js +0 -1094
- package/core/Lang.js +0 -52
- package/core/Log.js +0 -43
- package/core/Process.js +0 -26
- package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +0 -20
- package/docs/server/01-installation/01-quick-install.md +0 -19
- package/docs/server/01-installation/02-manual-installation-via-npm.md +0 -9
- package/docs/server/02-get-started/01-core-concepts.md +0 -7
- package/docs/server/02-get-started/02-basic-commands.md +0 -57
- package/docs/server/02-get-started/03-cli-reference.md +0 -276
- package/docs/server/02-get-started/04-cli-quick-reference.md +0 -102
- package/docs/server/03-service/01-start-a-new-service.md +0 -57
- package/docs/server/03-service/02-delete-a-service.md +0 -48
- package/docs/server/04-web/01-create-a-website.md +0 -36
- package/docs/server/04-web/02-list-websites.md +0 -9
- package/docs/server/04-web/03-delete-a-website.md +0 -29
- package/docs/server/05-subdomain/01-create-a-subdomain.md +0 -32
- package/docs/server/05-subdomain/02-list-subdomains.md +0 -33
- package/docs/server/05-subdomain/03-delete-a-subdomain.md +0 -41
- package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +0 -34
- package/docs/server/07-mail/01-create-a-mail-account.md +0 -23
- package/docs/server/07-mail/02-delete-a-mail-account.md +0 -20
- package/docs/server/07-mail/03-list-mail-accounts.md +0 -20
- package/docs/server/07-mail/04-change-account-password.md +0 -23
- package/framework/src/Candy.js +0 -81
- package/framework/src/Route.js +0 -455
- package/framework/src/Server.js +0 -15
- package/locale/de-DE.json +0 -80
- package/locale/en-US.json +0 -79
- package/locale/es-ES.json +0 -80
- package/locale/fr-FR.json +0 -80
- package/locale/pt-BR.json +0 -80
- package/locale/ru-RU.json +0 -80
- package/locale/tr-TR.json +0 -85
- package/locale/zh-CN.json +0 -80
- package/server/index.js +0 -5
- package/server/src/Api.js +0 -88
- package/server/src/DNS.js +0 -940
- package/server/src/Hub.js +0 -535
- package/server/src/Mail.js +0 -571
- package/server/src/SSL.js +0 -180
- package/server/src/Server.js +0 -27
- package/server/src/Service.js +0 -248
- package/server/src/Subdomain.js +0 -64
- package/server/src/Web/Firewall.js +0 -170
- package/server/src/Web/Proxy.js +0 -134
- package/server/src/Web.js +0 -451
- package/server/src/mail/imap.js +0 -1091
- package/server/src/mail/server.js +0 -32
- package/server/src/mail/smtp.js +0 -786
- package/test/server/Client.test.js +0 -338
- package/test/server/__mocks__/http-proxy.js +0 -105
- package/watchdog/index.js +0 -3
- package/watchdog/src/Watchdog.js +0 -156
- package/web/config.json +0 -5
- package/web/view/footer/main.html +0 -11
- /package/{framework/src → src}/Env.js +0 -0
- /package/{framework/src → src}/Route/Cron.js +0 -0
- /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-
|
|
129
|
-
"title": "
|
|
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-
|
|
225
|
-
"title": "Using
|
|
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-
|
|
349
|
-
"title": "
|
|
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-
|
|
357
|
-
"title": "
|
|
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-
|
|
387
|
-
"title": "
|
|
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": "
|
|
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/
|
|
10
|
+
ignores: ['server/src/Odac.js'],
|
|
11
11
|
languageOptions: {
|
|
12
12
|
globals: {
|
|
13
13
|
...globals.node,
|
|
14
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
106
|
+
odac: 'readonly'
|
|
107
107
|
},
|
|
108
108
|
sourceType: 'script'
|
|
109
109
|
},
|
package/package.json
CHANGED
|
@@ -1,63 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "odac",
|
|
3
|
-
"description": "
|
|
4
|
-
"homepage": "https://
|
|
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.
|
|
11
|
-
"license": "
|
|
10
|
+
"version": "1.0.0",
|
|
11
|
+
"license": "MIT",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=18.0.0"
|
|
14
14
|
},
|
|
15
|
-
"
|
|
15
|
+
"bin": {
|
|
16
|
+
"odac": "./bin/odac.js"
|
|
17
|
+
},
|
|
18
|
+
"main": "index.js",
|
|
16
19
|
"repository": {
|
|
17
20
|
"type": "git",
|
|
18
|
-
"url": "https://github.com/
|
|
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
|
-
"
|
|
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": "^
|
|
59
|
-
"@semantic-release/npm": "^
|
|
60
|
-
"@semantic-release/release-notes-generator": "^14.0
|
|
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": "^
|
|
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 (!
|
|
22
|
-
this.#table =
|
|
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 =
|
|
26
|
+
let sql = Odac.Mysql.table(this.#table)
|
|
27
27
|
if (!sql) {
|
|
28
|
-
console.error('
|
|
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 (
|
|
42
|
-
else if (
|
|
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
|
|
51
|
+
let check_table = await Odac.Mysql.run('SHOW TABLES LIKE ?', [this.#table])
|
|
52
52
|
if (check_table.length == 0) return false
|
|
53
|
-
let
|
|
54
|
-
let
|
|
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 (!
|
|
57
|
-
const tokenTable =
|
|
58
|
-
const primaryKey =
|
|
59
|
-
let sql_token = await
|
|
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 (!
|
|
61
|
+
if (!Odac.Var(sql_token[0].token_y).hashCheck(odac_y)) return false
|
|
62
62
|
|
|
63
|
-
const maxAge =
|
|
64
|
-
const updateAge =
|
|
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
|
|
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
|
|
74
|
+
this.#user = await Odac.Mysql.table(this.#table).where(primaryKey, sql_token[0].user).first()
|
|
75
75
|
|
|
76
76
|
if (inactiveAge > updateAge) {
|
|
77
|
-
|
|
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 (!
|
|
92
|
-
let key =
|
|
93
|
-
let token =
|
|
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
|
|
96
|
+
let check_table = await Odac.Mysql.run('SHOW TABLES LIKE ?', [token])
|
|
97
97
|
if (check_table === false) {
|
|
98
|
-
console.error('
|
|
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
|
|
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 =
|
|
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:
|
|
112
|
-
token_y:
|
|
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('
|
|
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('
|
|
122
|
-
let mysqlTable =
|
|
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('
|
|
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 =
|
|
132
|
+
const maxAge = Odac.Config.auth?.maxAge || 30 * 24 * 60 * 60 * 1000
|
|
133
133
|
const cutoffDate = new Date(Date.now() - maxAge)
|
|
134
134
|
|
|
135
|
-
|
|
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 (!
|
|
143
|
-
|
|
142
|
+
if (!Odac.Config.auth) {
|
|
143
|
+
Odac.Config.auth = {}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
this.#table =
|
|
147
|
-
const primaryKey =
|
|
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
|
|
151
|
+
const checkTable = await Odac.Mysql.run('SHOW TABLES LIKE ?', [this.#table])
|
|
152
152
|
if (checkTable === false) {
|
|
153
|
-
console.error('
|
|
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] && !
|
|
165
|
-
data[passwordField] =
|
|
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 =
|
|
170
|
+
const mysqlTable = Odac.Mysql.table(this.#table)
|
|
171
171
|
if (!mysqlTable) {
|
|
172
|
-
console.error('
|
|
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 =
|
|
183
|
+
const mysqlTable = Odac.Mysql.table(this.#table)
|
|
184
184
|
if (!mysqlTable) {
|
|
185
|
-
console.error('
|
|
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('
|
|
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('
|
|
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
|
|
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('
|
|
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 (!
|
|
233
|
-
const token =
|
|
234
|
-
const
|
|
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 (
|
|
238
|
-
const mysqlTable =
|
|
237
|
+
if (odacX && browser) {
|
|
238
|
+
const mysqlTable = Odac.Mysql.table(token)
|
|
239
239
|
if (mysqlTable) {
|
|
240
|
-
await mysqlTable.where(['token_x',
|
|
240
|
+
await mysqlTable.where(['token_x', odacX], ['browser', browser]).delete()
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
this.#request.cookie('
|
|
245
|
-
this.#request.cookie('
|
|
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
|
|
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: '
|
|
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() + '/.
|
|
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 === '
|
|
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
|
-
#
|
|
4
|
+
#odac
|
|
5
5
|
#data = {}
|
|
6
6
|
#lang
|
|
7
7
|
|
|
8
|
-
constructor(
|
|
9
|
-
this.#
|
|
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.#
|
|
46
|
-
if (this.#
|
|
47
|
-
lang = this.#
|
|
48
|
-
else lang = this.#
|
|
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 (!
|
|
41
|
-
if (!
|
|
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()}@
|
|
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'] = '
|
|
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:
|
|
70
|
+
{headers: {Authorization: Odac.Config.system.api.auth}}
|
|
71
71
|
)
|
|
72
72
|
.then(response => {
|
|
73
73
|
resolve(response.data)
|