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
|
@@ -15,61 +15,61 @@ class Internal {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
static async register(
|
|
19
|
-
const token = await
|
|
18
|
+
static async register(Odac) {
|
|
19
|
+
const token = await Odac.request('_odac_register_token')
|
|
20
20
|
if (!token) {
|
|
21
|
-
return
|
|
21
|
+
return Odac.return({
|
|
22
22
|
result: {success: false},
|
|
23
|
-
errors: {
|
|
23
|
+
errors: {_odac_form: 'Invalid request'}
|
|
24
24
|
})
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const formData =
|
|
27
|
+
const formData = Odac.Request.session(`_register_form_${token}`)
|
|
28
28
|
|
|
29
29
|
if (!formData) {
|
|
30
|
-
return
|
|
30
|
+
return Odac.return({
|
|
31
31
|
result: {success: false},
|
|
32
|
-
errors: {
|
|
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
|
-
|
|
38
|
-
return
|
|
37
|
+
Odac.Request.session(`_register_form_${token}`, null)
|
|
38
|
+
return Odac.return({
|
|
39
39
|
result: {success: false},
|
|
40
|
-
errors: {
|
|
40
|
+
errors: {_odac_form: 'Form session expired. Please refresh the page.'}
|
|
41
41
|
})
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
if (formData.sessionId !==
|
|
45
|
-
return
|
|
44
|
+
if (formData.sessionId !== Odac.Request.session('_client')) {
|
|
45
|
+
return Odac.return({
|
|
46
46
|
result: {success: false},
|
|
47
|
-
errors: {
|
|
47
|
+
errors: {_odac_form: 'Invalid session'}
|
|
48
48
|
})
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
if (formData.userAgent !==
|
|
52
|
-
return
|
|
51
|
+
if (formData.userAgent !== Odac.Request.header('user-agent')) {
|
|
52
|
+
return Odac.return({
|
|
53
53
|
result: {success: false},
|
|
54
|
-
errors: {
|
|
54
|
+
errors: {_odac_form: 'Invalid request'}
|
|
55
55
|
})
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
if (formData.ip !==
|
|
59
|
-
return
|
|
58
|
+
if (formData.ip !== Odac.Request.ip) {
|
|
59
|
+
return Odac.return({
|
|
60
60
|
result: {success: false},
|
|
61
|
-
errors: {
|
|
61
|
+
errors: {_odac_form: 'Invalid request'}
|
|
62
62
|
})
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
const config = formData.config
|
|
66
|
-
const 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
|
|
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,
|
|
92
|
+
data[set.name] = await this.computeValue(set.compute, Odac)
|
|
93
93
|
} else if (set.callback) {
|
|
94
|
-
if (typeof
|
|
95
|
-
data[set.name] = await
|
|
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
|
|
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
|
|
111
|
+
return Odac.return({
|
|
112
112
|
result: {success: false},
|
|
113
|
-
errors: {
|
|
113
|
+
errors: {_odac_form: 'Service temporarily unavailable. Please try again later.'}
|
|
114
114
|
})
|
|
115
115
|
}
|
|
116
|
-
const errorField = registerResult.field || '
|
|
116
|
+
const errorField = registerResult.field || '_odac_form'
|
|
117
117
|
const errors = {[errorField]: registerResult.error}
|
|
118
|
-
return
|
|
118
|
+
return Odac.return({
|
|
119
119
|
result: {success: false},
|
|
120
120
|
errors: errors
|
|
121
121
|
})
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
Odac.Request.session(`_register_form_${token}`, null)
|
|
125
125
|
|
|
126
|
-
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,
|
|
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
|
|
172
|
+
return Odac.Request.ip
|
|
173
173
|
case 'user_agent':
|
|
174
|
-
return
|
|
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(
|
|
183
|
-
const token = await
|
|
182
|
+
static async login(Odac) {
|
|
183
|
+
const token = await Odac.request('_odac_login_token')
|
|
184
184
|
if (!token) {
|
|
185
|
-
return
|
|
185
|
+
return Odac.return({
|
|
186
186
|
result: {success: false},
|
|
187
|
-
errors: {
|
|
187
|
+
errors: {_odac_form: 'Invalid request'}
|
|
188
188
|
})
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
const formData =
|
|
191
|
+
const formData = Odac.Request.session(`_login_form_${token}`)
|
|
192
192
|
|
|
193
193
|
if (!formData) {
|
|
194
|
-
return
|
|
194
|
+
return Odac.return({
|
|
195
195
|
result: {success: false},
|
|
196
|
-
errors: {
|
|
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
|
-
|
|
202
|
-
return
|
|
201
|
+
Odac.Request.session(`_login_form_${token}`, null)
|
|
202
|
+
return Odac.return({
|
|
203
203
|
result: {success: false},
|
|
204
|
-
errors: {
|
|
204
|
+
errors: {_odac_form: 'Form session expired. Please refresh the page.'}
|
|
205
205
|
})
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
if (formData.sessionId !==
|
|
209
|
-
return
|
|
208
|
+
if (formData.sessionId !== Odac.Request.session('_client')) {
|
|
209
|
+
return Odac.return({
|
|
210
210
|
result: {success: false},
|
|
211
|
-
errors: {
|
|
211
|
+
errors: {_odac_form: 'Invalid session'}
|
|
212
212
|
})
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
if (formData.userAgent !==
|
|
216
|
-
return
|
|
215
|
+
if (formData.userAgent !== Odac.Request.header('user-agent')) {
|
|
216
|
+
return Odac.return({
|
|
217
217
|
result: {success: false},
|
|
218
|
-
errors: {
|
|
218
|
+
errors: {_odac_form: 'Invalid request'}
|
|
219
219
|
})
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
if (formData.ip !==
|
|
223
|
-
return
|
|
222
|
+
if (formData.ip !== Odac.Request.ip) {
|
|
223
|
+
return Odac.return({
|
|
224
224
|
result: {success: false},
|
|
225
|
-
errors: {
|
|
225
|
+
errors: {_odac_form: 'Invalid request'}
|
|
226
226
|
})
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
const config = formData.config
|
|
230
|
-
const validator =
|
|
230
|
+
const validator = Odac.validator()
|
|
231
231
|
const credentials = {}
|
|
232
232
|
|
|
233
233
|
for (const field of config.fields) {
|
|
234
|
-
const value = await
|
|
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
|
|
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
|
|
251
|
+
return Odac.return({
|
|
252
252
|
result: {success: false},
|
|
253
|
-
errors: {
|
|
253
|
+
errors: {_odac_form: 'Service temporarily unavailable. Please try again later.'}
|
|
254
254
|
})
|
|
255
255
|
}
|
|
256
|
-
const errorField = loginResult.field || '
|
|
256
|
+
const errorField = loginResult.field || '_odac_form'
|
|
257
257
|
const errors = {[errorField]: loginResult.error}
|
|
258
|
-
return
|
|
258
|
+
return Odac.return({
|
|
259
259
|
result: {success: false},
|
|
260
260
|
errors: errors
|
|
261
261
|
})
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
|
|
264
|
+
Odac.Request.session(`_login_form_${token}`, null)
|
|
265
265
|
|
|
266
|
-
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(
|
|
276
|
-
const token = await
|
|
275
|
+
static async processForm(Odac) {
|
|
276
|
+
const token = await Odac.request('_odac_form_token')
|
|
277
277
|
if (!token) return
|
|
278
278
|
|
|
279
|
-
const formData =
|
|
279
|
+
const formData = Odac.Request.session(`_custom_form_${token}`)
|
|
280
280
|
if (!formData) return
|
|
281
281
|
|
|
282
282
|
if (formData.expires < Date.now()) {
|
|
283
|
-
|
|
283
|
+
Odac.Request.session(`_custom_form_${token}`, null)
|
|
284
284
|
return
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
-
if (formData.sessionId !==
|
|
288
|
-
if (formData.userAgent !==
|
|
289
|
-
if (formData.ip !==
|
|
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 =
|
|
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
|
|
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,
|
|
320
|
+
data[set.name] = await this.computeValue(set.compute, Odac)
|
|
321
321
|
} else if (set.callback) {
|
|
322
|
-
if (typeof
|
|
323
|
-
data[set.name] = await
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
328
|
+
Odac.formData = data
|
|
329
|
+
Odac.formConfig = config
|
|
330
|
+
Odac.formValidator = validator
|
|
331
|
+
Odac.formUniqueFields = uniqueFields
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
static async customForm(
|
|
335
|
-
const token = await
|
|
334
|
+
static async customForm(Odac) {
|
|
335
|
+
const token = await Odac.request('_odac_form_token')
|
|
336
336
|
if (!token) {
|
|
337
|
-
return
|
|
337
|
+
return Odac.return({
|
|
338
338
|
result: {success: false},
|
|
339
|
-
errors: {
|
|
339
|
+
errors: {_odac_form: 'Invalid request'}
|
|
340
340
|
})
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
const formData =
|
|
343
|
+
const formData = Odac.Request.session(`_custom_form_${token}`)
|
|
344
344
|
|
|
345
345
|
if (!formData) {
|
|
346
|
-
return
|
|
346
|
+
return Odac.return({
|
|
347
347
|
result: {success: false},
|
|
348
|
-
errors: {
|
|
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
|
-
|
|
354
|
-
return
|
|
353
|
+
Odac.Request.session(`_custom_form_${token}`, null)
|
|
354
|
+
return Odac.return({
|
|
355
355
|
result: {success: false},
|
|
356
|
-
errors: {
|
|
356
|
+
errors: {_odac_form: 'Form session expired. Please refresh the page.'}
|
|
357
357
|
})
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
if (formData.sessionId !==
|
|
361
|
-
return
|
|
360
|
+
if (formData.sessionId !== Odac.Request.session('_client')) {
|
|
361
|
+
return Odac.return({
|
|
362
362
|
result: {success: false},
|
|
363
|
-
errors: {
|
|
363
|
+
errors: {_odac_form: 'Invalid session'}
|
|
364
364
|
})
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
if (formData.userAgent !==
|
|
368
|
-
return
|
|
367
|
+
if (formData.userAgent !== Odac.Request.header('user-agent')) {
|
|
368
|
+
return Odac.return({
|
|
369
369
|
result: {success: false},
|
|
370
|
-
errors: {
|
|
370
|
+
errors: {_odac_form: 'Invalid request'}
|
|
371
371
|
})
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
-
if (formData.ip !==
|
|
375
|
-
return
|
|
374
|
+
if (formData.ip !== Odac.Request.ip) {
|
|
375
|
+
return Odac.return({
|
|
376
376
|
result: {success: false},
|
|
377
|
-
errors: {
|
|
377
|
+
errors: {_odac_form: 'Invalid request'}
|
|
378
378
|
})
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
-
if (await
|
|
382
|
-
return
|
|
381
|
+
if (await Odac.formValidator.error()) {
|
|
382
|
+
return Odac.formValidator.result()
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
-
if (
|
|
385
|
+
if (Odac.formConfig.table) {
|
|
386
386
|
try {
|
|
387
|
-
const mysql =
|
|
387
|
+
const mysql = Odac.Mysql
|
|
388
388
|
|
|
389
|
-
for (const field of
|
|
390
|
-
if (
|
|
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
|
-
|
|
393
|
+
Odac.formConfig.table,
|
|
394
394
|
field.name,
|
|
395
|
-
|
|
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
|
|
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 ?', [
|
|
407
|
+
await mysql.query('INSERT INTO ?? SET ?', [Odac.formConfig.table, Odac.formData])
|
|
408
408
|
|
|
409
|
-
|
|
409
|
+
Odac.Request.session(`_custom_form_${token}`, null)
|
|
410
410
|
|
|
411
|
-
return
|
|
411
|
+
return Odac.return({
|
|
412
412
|
result: {
|
|
413
413
|
success: true,
|
|
414
|
-
message:
|
|
415
|
-
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
|
|
420
|
+
return Odac.return({
|
|
421
421
|
result: {success: false},
|
|
422
|
-
errors: {
|
|
422
|
+
errors: {_odac_form: 'Database not configured. Please check your config.json'}
|
|
423
423
|
})
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
-
return
|
|
426
|
+
return Odac.return({
|
|
427
427
|
result: {success: false},
|
|
428
|
-
errors: {
|
|
428
|
+
errors: {_odac_form: error.message || 'Database error occurred'}
|
|
429
429
|
})
|
|
430
430
|
}
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
-
|
|
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
|