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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## 💬 Comments in Views
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Odac supports two types of comments in view files: backend comments (not rendered) and regular HTML comments (rendered).
|
|
4
4
|
|
|
5
5
|
### Backend Comments (Not Rendered)
|
|
6
6
|
|
|
@@ -9,18 +9,18 @@ Backend comments are removed during template rendering and never appear in the H
|
|
|
9
9
|
#### Single-Line Backend Comments
|
|
10
10
|
|
|
11
11
|
```html
|
|
12
|
-
<!--
|
|
12
|
+
<!--odac This is a backend comment -->
|
|
13
13
|
<p>This will be rendered</p>
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
#### Multi-Line Backend Comments
|
|
17
17
|
|
|
18
18
|
```html
|
|
19
|
-
<!--
|
|
19
|
+
<!--odac
|
|
20
20
|
This is a multi-line backend comment
|
|
21
21
|
It can span multiple lines
|
|
22
22
|
None of this will appear in the output
|
|
23
|
-
|
|
23
|
+
odac-->
|
|
24
24
|
|
|
25
25
|
<div class="content">
|
|
26
26
|
<p>This will be rendered</p>
|
|
@@ -43,50 +43,50 @@ Standard HTML comments are preserved and sent to the browser:
|
|
|
43
43
|
|
|
44
44
|
**Development Notes:**
|
|
45
45
|
```html
|
|
46
|
-
<!--
|
|
47
|
-
<!--
|
|
48
|
-
<!--
|
|
46
|
+
<!--odac TODO: Add pagination here -->
|
|
47
|
+
<!--odac FIXME: This needs optimization -->
|
|
48
|
+
<!--odac NOTE: This section is for admin users only -->
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
**Sensitive Information:**
|
|
52
52
|
```html
|
|
53
|
-
<!--
|
|
53
|
+
<!--odac
|
|
54
54
|
Database query returns: id, name, email, password_hash
|
|
55
55
|
We only display: name, email
|
|
56
|
-
|
|
56
|
+
odac-->
|
|
57
57
|
|
|
58
|
-
<
|
|
59
|
-
<p><
|
|
60
|
-
</
|
|
58
|
+
<odac:for in="users" value="user">
|
|
59
|
+
<p><odac var="user.name" /> - <odac var="user.email" /></p>
|
|
60
|
+
</odac:for>
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
**Debugging Information:**
|
|
64
64
|
```html
|
|
65
|
-
<!--
|
|
66
|
-
<!--
|
|
65
|
+
<!--odac Debug: user object structure -->
|
|
66
|
+
<!--odac { id: 1, name: "John", role: "admin" } -->
|
|
67
67
|
|
|
68
|
-
<
|
|
68
|
+
<odac:if condition="user.role === 'admin'">
|
|
69
69
|
<div class="admin-panel">Admin content</div>
|
|
70
|
-
</
|
|
70
|
+
</odac:if>
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
**Temporary Code:**
|
|
74
74
|
```html
|
|
75
|
-
<!--
|
|
75
|
+
<!--odac
|
|
76
76
|
Old implementation - keeping for reference
|
|
77
77
|
<div class="old-layout">
|
|
78
|
-
<
|
|
79
|
-
<p><
|
|
80
|
-
</
|
|
78
|
+
<odac:for in="items" value="item">
|
|
79
|
+
<p><odac var="item.name" /></p>
|
|
80
|
+
</odac:for>
|
|
81
81
|
</div>
|
|
82
|
-
|
|
82
|
+
odac-->
|
|
83
83
|
|
|
84
84
|
<div class="new-layout">
|
|
85
|
-
<
|
|
85
|
+
<odac:for in="items" value="item">
|
|
86
86
|
<div class="item-card">
|
|
87
|
-
<h3><
|
|
87
|
+
<h3><odac var="item.name" /></h3>
|
|
88
88
|
</div>
|
|
89
|
-
</
|
|
89
|
+
</odac:for>
|
|
90
90
|
</div>
|
|
91
91
|
```
|
|
92
92
|
|
|
@@ -135,18 +135,18 @@ candy-->
|
|
|
135
135
|
#### Documenting Complex Logic
|
|
136
136
|
|
|
137
137
|
```html
|
|
138
|
-
<!--
|
|
138
|
+
<!--odac
|
|
139
139
|
This section displays products based on user role:
|
|
140
140
|
- Admin: sees all products including inactive
|
|
141
141
|
- Regular user: sees only active products
|
|
142
142
|
- Guest: sees only featured products
|
|
143
|
-
|
|
143
|
+
odac-->
|
|
144
144
|
|
|
145
|
-
<script:
|
|
145
|
+
<script:odac>
|
|
146
146
|
let visibleProducts;
|
|
147
147
|
|
|
148
|
-
if (
|
|
149
|
-
const user =
|
|
148
|
+
if (Odac.Auth.check()) {
|
|
149
|
+
const user = Odac.Auth.user();
|
|
150
150
|
if (user.role === 'admin') {
|
|
151
151
|
visibleProducts = products;
|
|
152
152
|
} else {
|
|
@@ -155,52 +155,52 @@ candy-->
|
|
|
155
155
|
} else {
|
|
156
156
|
visibleProducts = products.filter(p => p.featured);
|
|
157
157
|
}
|
|
158
|
-
</script:
|
|
158
|
+
</script:odac>
|
|
159
159
|
|
|
160
|
-
<
|
|
160
|
+
<odac:for in="visibleProducts" value="product">
|
|
161
161
|
<div class="product">
|
|
162
|
-
<h3><
|
|
162
|
+
<h3><odac var="product.name" /></h3>
|
|
163
163
|
</div>
|
|
164
|
-
</
|
|
164
|
+
</odac:for>
|
|
165
165
|
```
|
|
166
166
|
|
|
167
167
|
#### Marking Sections for Developers
|
|
168
168
|
|
|
169
169
|
```html
|
|
170
170
|
<div class="dashboard">
|
|
171
|
-
<!--
|
|
171
|
+
<!--odac START: User Statistics Section -->
|
|
172
172
|
<div class="stats">
|
|
173
173
|
<h2>Statistics</h2>
|
|
174
|
-
<p>Total Users: <
|
|
175
|
-
<p>Active Users: <
|
|
174
|
+
<p>Total Users: <odac var="stats.totalUsers" /></p>
|
|
175
|
+
<p>Active Users: <odac var="stats.activeUsers" /></p>
|
|
176
176
|
</div>
|
|
177
|
-
<!--
|
|
177
|
+
<!--odac END: User Statistics Section -->
|
|
178
178
|
|
|
179
|
-
<!--
|
|
179
|
+
<!--odac START: Recent Activity Section -->
|
|
180
180
|
<div class="activity">
|
|
181
181
|
<h2>Recent Activity</h2>
|
|
182
|
-
<
|
|
183
|
-
<p><
|
|
184
|
-
</
|
|
182
|
+
<odac:for in="activities" value="activity">
|
|
183
|
+
<p><odac var="activity.description" /></p>
|
|
184
|
+
</odac:for>
|
|
185
185
|
</div>
|
|
186
|
-
<!--
|
|
186
|
+
<!--odac END: Recent Activity Section -->
|
|
187
187
|
</div>
|
|
188
188
|
```
|
|
189
189
|
|
|
190
190
|
#### Explaining Template Variables
|
|
191
191
|
|
|
192
192
|
```html
|
|
193
|
-
<!--
|
|
193
|
+
<!--odac
|
|
194
194
|
Available variables from controller:
|
|
195
195
|
- user: Current user object { id, name, email, role }
|
|
196
196
|
- posts: Array of post objects
|
|
197
197
|
- categories: Array of category objects
|
|
198
198
|
- settings: Site settings object
|
|
199
|
-
|
|
199
|
+
odac-->
|
|
200
200
|
|
|
201
201
|
<div class="profile">
|
|
202
|
-
<h1><
|
|
203
|
-
<p><
|
|
202
|
+
<h1><odac var="user.name" /></h1>
|
|
203
|
+
<p><odac var="user.email" /></p>
|
|
204
204
|
</div>
|
|
205
205
|
```
|
|
206
206
|
|
|
@@ -208,31 +208,31 @@ candy-->
|
|
|
208
208
|
|
|
209
209
|
```html
|
|
210
210
|
<div class="products">
|
|
211
|
-
<
|
|
211
|
+
<odac:for in="products" value="product">
|
|
212
212
|
<div class="product-card">
|
|
213
|
-
<h3><
|
|
214
|
-
<p>$<
|
|
213
|
+
<h3><odac var="product.name" /></h3>
|
|
214
|
+
<p>$<odac var="product.price" /></p>
|
|
215
215
|
|
|
216
|
-
<!--
|
|
216
|
+
<!--odac Temporarily disabled - waiting for API
|
|
217
217
|
<div class="reviews">
|
|
218
|
-
<
|
|
218
|
+
<odac var="product.averageRating" /> stars
|
|
219
219
|
</div>
|
|
220
|
-
|
|
220
|
+
odac-->
|
|
221
221
|
</div>
|
|
222
|
-
</
|
|
222
|
+
</odac:for>
|
|
223
223
|
</div>
|
|
224
224
|
```
|
|
225
225
|
|
|
226
226
|
#### Version History
|
|
227
227
|
|
|
228
228
|
```html
|
|
229
|
-
<!--
|
|
229
|
+
<!--odac
|
|
230
230
|
Version History:
|
|
231
231
|
v1.0 - Initial implementation
|
|
232
232
|
v1.1 - Added sorting functionality
|
|
233
233
|
v1.2 - Added filtering by category
|
|
234
234
|
v2.0 - Complete redesign with new layout
|
|
235
|
-
|
|
235
|
+
odac-->
|
|
236
236
|
|
|
237
237
|
<div class="product-list">
|
|
238
238
|
<!-- Product list implementation -->
|
|
@@ -249,19 +249,19 @@ candy-->
|
|
|
249
249
|
|
|
250
250
|
**Good:**
|
|
251
251
|
```html
|
|
252
|
-
<!--
|
|
253
|
-
<
|
|
254
|
-
<div><
|
|
255
|
-
</
|
|
252
|
+
<!--odac This query is cached for 5 minutes -->
|
|
253
|
+
<odac:for in="products" value="product">
|
|
254
|
+
<div><odac var="product.name" /></div>
|
|
255
|
+
</odac:for>
|
|
256
256
|
```
|
|
257
257
|
|
|
258
258
|
**Avoid:**
|
|
259
259
|
```html
|
|
260
|
-
<!--
|
|
261
|
-
<
|
|
262
|
-
<!--
|
|
263
|
-
<div><
|
|
264
|
-
</
|
|
260
|
+
<!--odac Loop through products -->
|
|
261
|
+
<odac:for in="products" value="product">
|
|
262
|
+
<!--odac Display product name -->
|
|
263
|
+
<div><odac var="product.name" /></div>
|
|
264
|
+
</odac:for>
|
|
265
265
|
```
|
|
266
266
|
|
|
267
267
|
### Security Considerations
|
|
@@ -273,9 +273,9 @@ candy-->
|
|
|
273
273
|
<!-- Database password: secret123 -->
|
|
274
274
|
<!-- API key: abc123xyz -->
|
|
275
275
|
|
|
276
|
-
<!--
|
|
277
|
-
<!--
|
|
278
|
-
<!--
|
|
276
|
+
<!--odac GOOD: Not visible in output -->
|
|
277
|
+
<!--odac Database password: secret123 -->
|
|
278
|
+
<!--odac API key: abc123xyz -->
|
|
279
279
|
```
|
|
280
280
|
|
|
281
281
|
**Be careful with user data:**
|
|
@@ -284,14 +284,14 @@ candy-->
|
|
|
284
284
|
<!-- BAD: Exposes user data -->
|
|
285
285
|
<!-- User ID: 12345, Email: user@example.com -->
|
|
286
286
|
|
|
287
|
-
<!--
|
|
288
|
-
<!--
|
|
287
|
+
<!--odac GOOD: Hidden from output -->
|
|
288
|
+
<!--odac User ID: 12345, Email: user@example.com -->
|
|
289
289
|
```
|
|
290
290
|
|
|
291
291
|
### Comment Syntax Summary
|
|
292
292
|
|
|
293
293
|
| Type | Syntax | Rendered | Use Case |
|
|
294
294
|
|------|--------|----------|----------|
|
|
295
|
-
| Backend Single-Line | `<!--
|
|
296
|
-
| Backend Multi-Line | `<!--
|
|
295
|
+
| Backend Single-Line | `<!--odac comment -->` | No | Development notes, TODOs |
|
|
296
|
+
| Backend Multi-Line | `<!--odac ... odac-->` | No | Detailed explanations, disabled code |
|
|
297
297
|
| HTML Comment | `<!-- comment -->` | Yes | Section markers, browser hacks |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## 🔌 Database Connection
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Odac automatically connects to your MySQL database when you provide the configuration.
|
|
4
4
|
|
|
5
5
|
### Configuration
|
|
6
6
|
|
|
@@ -44,10 +44,10 @@ Access different databases:
|
|
|
44
44
|
|
|
45
45
|
```javascript
|
|
46
46
|
// Default database
|
|
47
|
-
const users = await
|
|
47
|
+
const users = await Odac.Mysql.table('users').get()
|
|
48
48
|
|
|
49
49
|
// Specific database
|
|
50
|
-
const stats = await
|
|
50
|
+
const stats = await Odac.Mysql.database('analytics').table('stats').get()
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
### Environment Variables
|
|
@@ -86,14 +86,14 @@ Available configuration options:
|
|
|
86
86
|
|
|
87
87
|
### Automatic Connection
|
|
88
88
|
|
|
89
|
-
The connection is established automatically when your application starts. You don't need to write any connection code - just use `
|
|
89
|
+
The connection is established automatically when your application starts. You don't need to write any connection code - just use `Odac.Mysql` in your controllers.
|
|
90
90
|
|
|
91
91
|
```javascript
|
|
92
|
-
module.exports = async function (
|
|
92
|
+
module.exports = async function (Odac) {
|
|
93
93
|
// Connection is already established
|
|
94
|
-
const users = await
|
|
94
|
+
const users = await Odac.Mysql.table('users').get()
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
Odac.set('users', users)
|
|
97
|
+
Odac.View.set({ skeleton: 'main', content: 'users' })
|
|
98
98
|
}
|
|
99
99
|
```
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
## 🗄️ Using MySQL
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Odac provides a powerful query builder for safe and easy database operations.
|
|
4
4
|
|
|
5
5
|
### Selecting Data
|
|
6
6
|
|
|
7
7
|
#### Get All Records
|
|
8
8
|
|
|
9
9
|
```javascript
|
|
10
|
-
const users = await
|
|
10
|
+
const users = await Odac.Mysql.table('users').get()
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
#### Get First Record
|
|
14
14
|
|
|
15
15
|
```javascript
|
|
16
|
-
const user = await
|
|
16
|
+
const user = await Odac.Mysql.table('users').where('id', 1).first()
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
#### Select Specific Columns
|
|
20
20
|
|
|
21
21
|
```javascript
|
|
22
|
-
const users = await
|
|
22
|
+
const users = await Odac.Mysql.table('users')
|
|
23
23
|
.select('id', 'name', 'email')
|
|
24
24
|
.get()
|
|
25
25
|
```
|
|
@@ -28,35 +28,35 @@ const users = await Candy.Mysql.table('users')
|
|
|
28
28
|
|
|
29
29
|
```javascript
|
|
30
30
|
// Single condition
|
|
31
|
-
const users = await
|
|
31
|
+
const users = await Odac.Mysql.table('users')
|
|
32
32
|
.where('status', 'active')
|
|
33
33
|
.get()
|
|
34
34
|
|
|
35
35
|
// Multiple conditions (AND)
|
|
36
|
-
const users = await
|
|
36
|
+
const users = await Odac.Mysql.table('users')
|
|
37
37
|
.where('status', 'active')
|
|
38
38
|
.where('verified', 1)
|
|
39
39
|
.get()
|
|
40
40
|
|
|
41
41
|
// OR condition
|
|
42
|
-
const users = await
|
|
42
|
+
const users = await Odac.Mysql.table('users')
|
|
43
43
|
.where('role', 'admin')
|
|
44
44
|
.orWhere('role', 'moderator')
|
|
45
45
|
.get()
|
|
46
46
|
|
|
47
47
|
// Comparison operators
|
|
48
|
-
const products = await
|
|
48
|
+
const products = await Odac.Mysql.table('products')
|
|
49
49
|
.where('price', '>', 100)
|
|
50
50
|
.where('stock', '<=', 10)
|
|
51
51
|
.get()
|
|
52
52
|
|
|
53
53
|
// LIKE
|
|
54
|
-
const users = await
|
|
54
|
+
const users = await Odac.Mysql.table('users')
|
|
55
55
|
.where('name', 'LIKE', '%John%')
|
|
56
56
|
.get()
|
|
57
57
|
|
|
58
58
|
// IN
|
|
59
|
-
const users = await
|
|
59
|
+
const users = await Odac.Mysql.table('users')
|
|
60
60
|
.where('role', 'IN', ['admin', 'moderator'])
|
|
61
61
|
.get()
|
|
62
62
|
```
|
|
@@ -67,7 +67,7 @@ For complex queries with nested AND/OR conditions, use arrays:
|
|
|
67
67
|
|
|
68
68
|
```javascript
|
|
69
69
|
// SQL: WHERE (status = 'active' AND verified = 1) OR (role = 'admin')
|
|
70
|
-
const users = await
|
|
70
|
+
const users = await Odac.Mysql.table('users')
|
|
71
71
|
.where([
|
|
72
72
|
['status', 'active'],
|
|
73
73
|
['verified', 1]
|
|
@@ -76,7 +76,7 @@ const users = await Candy.Mysql.table('users')
|
|
|
76
76
|
.get()
|
|
77
77
|
|
|
78
78
|
// SQL: WHERE status = 'active' AND (role = 'admin' OR role = 'moderator')
|
|
79
|
-
const users = await
|
|
79
|
+
const users = await Odac.Mysql.table('users')
|
|
80
80
|
.where('status', 'active')
|
|
81
81
|
.where([
|
|
82
82
|
['role', 'admin'],
|
|
@@ -88,7 +88,7 @@ const users = await Candy.Mysql.table('users')
|
|
|
88
88
|
// Complex nested conditions
|
|
89
89
|
// SQL: WHERE (status = 'active' AND (role = 'admin' OR role = 'moderator'))
|
|
90
90
|
// AND (verified = 1 OR email_verified = 1)
|
|
91
|
-
const users = await
|
|
91
|
+
const users = await Odac.Mysql.table('users')
|
|
92
92
|
.where([
|
|
93
93
|
['status', 'active'],
|
|
94
94
|
[
|
|
@@ -109,19 +109,19 @@ const users = await Candy.Mysql.table('users')
|
|
|
109
109
|
|
|
110
110
|
```javascript
|
|
111
111
|
// Order by
|
|
112
|
-
const users = await
|
|
112
|
+
const users = await Odac.Mysql.table('users')
|
|
113
113
|
.order('created_at', 'desc')
|
|
114
114
|
.get()
|
|
115
115
|
|
|
116
116
|
// Limit
|
|
117
|
-
const users = await
|
|
117
|
+
const users = await Odac.Mysql.table('users')
|
|
118
118
|
.limit(10)
|
|
119
119
|
.get()
|
|
120
120
|
|
|
121
121
|
// Pagination
|
|
122
122
|
const page = 2
|
|
123
123
|
const perPage = 20
|
|
124
|
-
const users = await
|
|
124
|
+
const users = await Odac.Mysql.table('users')
|
|
125
125
|
.limit((page - 1) * perPage, perPage)
|
|
126
126
|
.get()
|
|
127
127
|
```
|
|
@@ -129,7 +129,7 @@ const users = await Candy.Mysql.table('users')
|
|
|
129
129
|
### Counting Records
|
|
130
130
|
|
|
131
131
|
```javascript
|
|
132
|
-
const userCount = await
|
|
132
|
+
const userCount = await Odac.Mysql.table('users')
|
|
133
133
|
.where('status', 'active')
|
|
134
134
|
.rows()
|
|
135
135
|
```
|
|
@@ -137,7 +137,7 @@ const userCount = await Candy.Mysql.table('users')
|
|
|
137
137
|
### Joins
|
|
138
138
|
|
|
139
139
|
```javascript
|
|
140
|
-
const orders = await
|
|
140
|
+
const orders = await Odac.Mysql.table('orders')
|
|
141
141
|
.leftJoin('users', 'orders.user_id', 'users.id')
|
|
142
142
|
.select('orders.*', 'users.name', 'users.email')
|
|
143
143
|
.get()
|
|
@@ -147,7 +147,7 @@ const orders = await Candy.Mysql.table('orders')
|
|
|
147
147
|
|
|
148
148
|
```javascript
|
|
149
149
|
// Insert single record
|
|
150
|
-
const result = await
|
|
150
|
+
const result = await Odac.Mysql.table('users').insert({
|
|
151
151
|
name: 'John Doe',
|
|
152
152
|
email: 'john@example.com',
|
|
153
153
|
status: 'active'
|
|
@@ -160,7 +160,7 @@ console.log(result.affected) // Affected rows
|
|
|
160
160
|
### Updating Data
|
|
161
161
|
|
|
162
162
|
```javascript
|
|
163
|
-
const result = await
|
|
163
|
+
const result = await Odac.Mysql.table('users')
|
|
164
164
|
.where('id', 1)
|
|
165
165
|
.set({
|
|
166
166
|
name: 'Jane Doe',
|
|
@@ -173,7 +173,7 @@ console.log(result.affected) // Number of updated rows
|
|
|
173
173
|
### Deleting Data
|
|
174
174
|
|
|
175
175
|
```javascript
|
|
176
|
-
const result = await
|
|
176
|
+
const result = await Odac.Mysql.table('users')
|
|
177
177
|
.where('id', 1)
|
|
178
178
|
.delete()
|
|
179
179
|
|
|
@@ -186,12 +186,12 @@ For complex queries, use raw SQL:
|
|
|
186
186
|
|
|
187
187
|
```javascript
|
|
188
188
|
// Raw value in select
|
|
189
|
-
const users = await
|
|
190
|
-
.select('id', 'name',
|
|
189
|
+
const users = await Odac.Mysql.table('users')
|
|
190
|
+
.select('id', 'name', Odac.Mysql.raw('COUNT(*) as total'))
|
|
191
191
|
.get()
|
|
192
192
|
|
|
193
193
|
// Raw query with parameters (SAFE - recommended)
|
|
194
|
-
const result = await
|
|
194
|
+
const result = await Odac.Mysql.run('SELECT * FROM users WHERE status = ?', ['active'])
|
|
195
195
|
```
|
|
196
196
|
|
|
197
197
|
#### ⚠️ Security Warning: Mysql.raw()
|
|
@@ -200,25 +200,25 @@ const result = await Candy.Mysql.run('SELECT * FROM users WHERE status = ?', ['a
|
|
|
200
200
|
|
|
201
201
|
```javascript
|
|
202
202
|
// ❌ DANGEROUS - Never do this!
|
|
203
|
-
const userInput = await
|
|
204
|
-
const users = await
|
|
205
|
-
.where('name',
|
|
203
|
+
const userInput = await Odac.request('search')
|
|
204
|
+
const users = await Odac.Mysql.table('users')
|
|
205
|
+
.where('name', Odac.Mysql.raw(userInput)) // SQL INJECTION RISK!
|
|
206
206
|
.get()
|
|
207
207
|
|
|
208
208
|
// ✅ SAFE - Use query builder instead
|
|
209
|
-
const userInput = await
|
|
210
|
-
const users = await
|
|
209
|
+
const userInput = await Odac.request('search')
|
|
210
|
+
const users = await Odac.Mysql.table('users')
|
|
211
211
|
.where('name', 'LIKE', `%${userInput}%`) // Automatically escaped
|
|
212
212
|
.get()
|
|
213
213
|
|
|
214
214
|
// ✅ SAFE - Use with hardcoded values only
|
|
215
|
-
const users = await
|
|
216
|
-
.select('id', 'name',
|
|
215
|
+
const users = await Odac.Mysql.table('users')
|
|
216
|
+
.select('id', 'name', Odac.Mysql.raw('COUNT(*) as total')) // OK - hardcoded
|
|
217
217
|
.get()
|
|
218
218
|
|
|
219
219
|
// ✅ SAFE - Use parameterized queries for dynamic values
|
|
220
|
-
const status = await
|
|
221
|
-
const result = await
|
|
220
|
+
const status = await Odac.request('status')
|
|
221
|
+
const result = await Odac.Mysql.run(
|
|
222
222
|
'SELECT * FROM users WHERE status = ?',
|
|
223
223
|
[status] // Automatically escaped
|
|
224
224
|
)
|
|
@@ -237,8 +237,8 @@ const result = await Candy.Mysql.run(
|
|
|
237
237
|
### Group By
|
|
238
238
|
|
|
239
239
|
```javascript
|
|
240
|
-
const stats = await
|
|
241
|
-
.select('user_id',
|
|
240
|
+
const stats = await Odac.Mysql.table('orders')
|
|
241
|
+
.select('user_id', Odac.Mysql.raw('COUNT(*) as order_count'))
|
|
242
242
|
.groupBy('user_id')
|
|
243
243
|
.get()
|
|
244
244
|
```
|
|
@@ -246,13 +246,13 @@ const stats = await Candy.Mysql.table('orders')
|
|
|
246
246
|
### Complete Example
|
|
247
247
|
|
|
248
248
|
```javascript
|
|
249
|
-
module.exports = async function (
|
|
250
|
-
const page = await
|
|
249
|
+
module.exports = async function (Odac) {
|
|
250
|
+
const page = await Odac.request('page') || 1
|
|
251
251
|
const perPage = 20
|
|
252
|
-
const search = await
|
|
252
|
+
const search = await Odac.request('search')
|
|
253
253
|
|
|
254
254
|
// Build query
|
|
255
|
-
let query =
|
|
255
|
+
let query = Odac.Mysql.table('products')
|
|
256
256
|
.select('id', 'name', 'price', 'stock')
|
|
257
257
|
.where('status', 'active')
|
|
258
258
|
|
|
@@ -268,17 +268,17 @@ module.exports = async function (Candy) {
|
|
|
268
268
|
.get()
|
|
269
269
|
|
|
270
270
|
// Get total count
|
|
271
|
-
const totalCount = await
|
|
271
|
+
const totalCount = await Odac.Mysql.table('products')
|
|
272
272
|
.where('status', 'active')
|
|
273
273
|
.rows()
|
|
274
274
|
|
|
275
|
-
|
|
275
|
+
Odac.set({
|
|
276
276
|
products: products,
|
|
277
277
|
currentPage: page,
|
|
278
278
|
totalPages: Math.ceil(totalCount / perPage)
|
|
279
279
|
})
|
|
280
280
|
|
|
281
|
-
|
|
281
|
+
Odac.View.set({
|
|
282
282
|
skeleton: 'main',
|
|
283
283
|
content: 'products.list'
|
|
284
284
|
})
|
|
@@ -298,23 +298,23 @@ module.exports = async function (Candy) {
|
|
|
298
298
|
### Error Handling
|
|
299
299
|
|
|
300
300
|
```javascript
|
|
301
|
-
module.exports = async function (
|
|
301
|
+
module.exports = async function (Odac) {
|
|
302
302
|
try {
|
|
303
|
-
const user = await
|
|
304
|
-
.where('id', await
|
|
303
|
+
const user = await Odac.Mysql.table('users')
|
|
304
|
+
.where('id', await Odac.request('id'))
|
|
305
305
|
.first()
|
|
306
306
|
|
|
307
307
|
if (!user) {
|
|
308
|
-
|
|
308
|
+
Odac.set('error', 'User not found')
|
|
309
309
|
} else {
|
|
310
|
-
|
|
310
|
+
Odac.set('user', user)
|
|
311
311
|
}
|
|
312
312
|
} catch (error) {
|
|
313
313
|
console.error('Database error:', error)
|
|
314
|
-
|
|
314
|
+
Odac.set('error', 'An error occurred')
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
|
|
317
|
+
Odac.View.set({
|
|
318
318
|
skeleton: 'main',
|
|
319
319
|
content: 'user.profile'
|
|
320
320
|
})
|