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,16 +1,16 @@
|
|
|
1
|
-
## 🔧
|
|
1
|
+
## 🔧 Odac.Var - String Manipulation & Validation
|
|
2
2
|
|
|
3
|
-
`
|
|
3
|
+
`Odac.Var` is a powerful utility class for string manipulation, validation, encryption, and formatting. It provides a chainable, fluent interface for common string operations.
|
|
4
4
|
|
|
5
5
|
### Basic Usage
|
|
6
6
|
|
|
7
7
|
```javascript
|
|
8
8
|
// Create a Var instance
|
|
9
|
-
const result =
|
|
9
|
+
const result = Odac.Var('hello world').slug()
|
|
10
10
|
// Returns: 'hello-world'
|
|
11
11
|
|
|
12
12
|
// Chain multiple operations
|
|
13
|
-
const email =
|
|
13
|
+
const email = Odac.Var(' USER@EXAMPLE.COM ').trim().toLowerCase()
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
### String Validation
|
|
@@ -21,23 +21,23 @@ Check if a string matches specific patterns:
|
|
|
21
21
|
|
|
22
22
|
```javascript
|
|
23
23
|
// Email validation
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
Odac.Var('user@example.com').is('email') // true
|
|
25
|
+
Odac.Var('invalid-email').is('email') // false
|
|
26
26
|
|
|
27
27
|
// Numeric validation
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
Odac.Var('12345').is('numeric') // true
|
|
29
|
+
Odac.Var('abc123').is('numeric') // false
|
|
30
30
|
|
|
31
31
|
// Multiple conditions (AND logic)
|
|
32
|
-
|
|
32
|
+
Odac.Var('abc123').is('alphanumeric') // true
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
#### isAny() - Multiple Validation (OR logic)
|
|
36
36
|
|
|
37
37
|
```javascript
|
|
38
38
|
// Check if value matches ANY of the conditions
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
Odac.Var('user@example.com').isAny('email', 'domain') // true
|
|
40
|
+
Odac.Var('example.com').isAny('email', 'domain') // true
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
#### Available Validation Types
|
|
@@ -67,11 +67,11 @@ Candy.Var('example.com').isAny('email', 'domain') // true
|
|
|
67
67
|
|
|
68
68
|
```javascript
|
|
69
69
|
// Controller validation
|
|
70
|
-
module.exports = async function(
|
|
71
|
-
const email =
|
|
70
|
+
module.exports = async function(Odac) {
|
|
71
|
+
const email = Odac.Request.post('email')
|
|
72
72
|
|
|
73
|
-
if (!
|
|
74
|
-
return
|
|
73
|
+
if (!Odac.Var(email).is('email')) {
|
|
74
|
+
return Odac.return({
|
|
75
75
|
success: false,
|
|
76
76
|
message: 'Invalid email address'
|
|
77
77
|
})
|
|
@@ -87,40 +87,40 @@ module.exports = async function(Candy) {
|
|
|
87
87
|
|
|
88
88
|
```javascript
|
|
89
89
|
// Single value
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
Odac.Var('hello world').contains('world') // true
|
|
91
|
+
Odac.Var('hello world').contains('foo') // false
|
|
92
92
|
|
|
93
93
|
// Multiple values (AND logic - must contain all)
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
Odac.Var('hello world').contains('hello', 'world') // true
|
|
95
|
+
Odac.Var('hello world').contains('hello', 'foo') // false
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
#### containsAny() - Check if string contains any value
|
|
99
99
|
|
|
100
100
|
```javascript
|
|
101
101
|
// Check if contains ANY of the values (OR logic)
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
Odac.Var('hello world').containsAny('foo', 'world') // true
|
|
103
|
+
Odac.Var('hello world').containsAny('foo', 'bar') // false
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
#### isBegin() - Check if string starts with value
|
|
107
107
|
|
|
108
108
|
```javascript
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
Odac.Var('hello world').isBegin('hello') // true
|
|
110
|
+
Odac.Var('hello world').isBegin('world') // false
|
|
111
111
|
|
|
112
112
|
// Multiple options
|
|
113
|
-
|
|
113
|
+
Odac.Var('https://example.com').isBegin('http://', 'https://') // true
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
#### isEnd() - Check if string ends with value
|
|
117
117
|
|
|
118
118
|
```javascript
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
Odac.Var('hello world').isEnd('world') // true
|
|
120
|
+
Odac.Var('hello world').isEnd('hello') // false
|
|
121
121
|
|
|
122
122
|
// Multiple options
|
|
123
|
-
|
|
123
|
+
Odac.Var('image.jpg').isEnd('.jpg', '.png', '.gif') // true
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
### String Manipulation
|
|
@@ -129,47 +129,47 @@ Candy.Var('image.jpg').isEnd('.jpg', '.png', '.gif') // true
|
|
|
129
129
|
|
|
130
130
|
```javascript
|
|
131
131
|
// Simple replacement
|
|
132
|
-
|
|
132
|
+
Odac.Var('hello world').replace('world', 'universe')
|
|
133
133
|
// Returns: 'hello universe'
|
|
134
134
|
|
|
135
135
|
// Multiple replacements with object
|
|
136
|
-
|
|
136
|
+
Odac.Var('Hello {{name}}, welcome to {{site}}').replace({
|
|
137
137
|
'{{name}}': 'John',
|
|
138
|
-
'{{site}}': '
|
|
138
|
+
'{{site}}': 'Odac'
|
|
139
139
|
})
|
|
140
|
-
// Returns: 'Hello John, welcome to
|
|
140
|
+
// Returns: 'Hello John, welcome to Odac'
|
|
141
141
|
|
|
142
142
|
// Works with arrays/objects recursively
|
|
143
143
|
const data = {
|
|
144
144
|
title: 'Welcome {{name}}',
|
|
145
145
|
message: 'Hello {{name}}'
|
|
146
146
|
}
|
|
147
|
-
|
|
147
|
+
Odac.Var(data).replace({'{{name}}': 'John'})
|
|
148
148
|
// Returns: { title: 'Welcome John', message: 'Hello John' }
|
|
149
149
|
```
|
|
150
150
|
|
|
151
151
|
#### clear() - Remove specific strings
|
|
152
152
|
|
|
153
153
|
```javascript
|
|
154
|
-
|
|
154
|
+
Odac.Var('hello-world-test').clear('-')
|
|
155
155
|
// Returns: 'helloworldtest'
|
|
156
156
|
|
|
157
157
|
// Remove multiple strings
|
|
158
|
-
|
|
158
|
+
Odac.Var('a1b2c3').clear('1', '2', '3')
|
|
159
159
|
// Returns: 'abc'
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
#### slug() - Create URL-friendly slug
|
|
163
163
|
|
|
164
164
|
```javascript
|
|
165
|
-
|
|
165
|
+
Odac.Var('Hello World!').slug()
|
|
166
166
|
// Returns: 'hello-world'
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
Odac.Var('Product Name 2024').slug()
|
|
169
169
|
// Returns: 'product-name-2024'
|
|
170
170
|
|
|
171
171
|
// Custom separator
|
|
172
|
-
|
|
172
|
+
Odac.Var('Hello World').slug('_')
|
|
173
173
|
// Returns: 'hello_world'
|
|
174
174
|
```
|
|
175
175
|
|
|
@@ -177,17 +177,17 @@ Candy.Var('Hello World').slug('_')
|
|
|
177
177
|
|
|
178
178
|
```javascript
|
|
179
179
|
// ? = single character, * = rest of string
|
|
180
|
-
|
|
180
|
+
Odac.Var('1234567890').format('(???) ???-????')
|
|
181
181
|
// Returns: '(123) 456-7890'
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
Odac.Var('TR1234567890').format('?? *')
|
|
184
184
|
// Returns: 'TR 1234567890'
|
|
185
185
|
```
|
|
186
186
|
|
|
187
187
|
#### html() - Escape HTML
|
|
188
188
|
|
|
189
189
|
```javascript
|
|
190
|
-
|
|
190
|
+
Odac.Var('<script>alert("xss")</script>').html()
|
|
191
191
|
// Returns: '<script>alert("xss")</script>'
|
|
192
192
|
```
|
|
193
193
|
|
|
@@ -197,46 +197,46 @@ Candy.Var('<script>alert("xss")</script>').html()
|
|
|
197
197
|
|
|
198
198
|
```javascript
|
|
199
199
|
// Hash a password
|
|
200
|
-
const hashedPassword =
|
|
200
|
+
const hashedPassword = Odac.Var('mypassword').hash()
|
|
201
201
|
// Returns: '$2b$10$...' (BCrypt hash)
|
|
202
202
|
|
|
203
203
|
// Custom salt rounds
|
|
204
|
-
const hashedPassword =
|
|
204
|
+
const hashedPassword = Odac.Var('mypassword').hash(12)
|
|
205
205
|
```
|
|
206
206
|
|
|
207
207
|
#### hashCheck() - Verify BCrypt hash
|
|
208
208
|
|
|
209
209
|
```javascript
|
|
210
210
|
const hashedPassword = '$2b$10$...'
|
|
211
|
-
const isValid =
|
|
211
|
+
const isValid = Odac.Var(hashedPassword).hashCheck('mypassword')
|
|
212
212
|
// Returns: true or false
|
|
213
213
|
```
|
|
214
214
|
|
|
215
215
|
#### md5() - MD5 hash
|
|
216
216
|
|
|
217
217
|
```javascript
|
|
218
|
-
|
|
218
|
+
Odac.Var('hello').md5()
|
|
219
219
|
// Returns: '5d41402abc4b2a76b9719d911017c592'
|
|
220
220
|
```
|
|
221
221
|
|
|
222
222
|
#### encrypt() - AES-256 encryption
|
|
223
223
|
|
|
224
224
|
```javascript
|
|
225
|
-
// Uses key from
|
|
226
|
-
const encrypted =
|
|
225
|
+
// Uses key from Odac.Config.encrypt.key
|
|
226
|
+
const encrypted = Odac.Var('secret data').encrypt()
|
|
227
227
|
|
|
228
228
|
// Custom encryption key
|
|
229
|
-
const encrypted =
|
|
229
|
+
const encrypted = Odac.Var('secret data').encrypt('my-32-character-encryption-key')
|
|
230
230
|
```
|
|
231
231
|
|
|
232
232
|
#### decrypt() - AES-256 decryption
|
|
233
233
|
|
|
234
234
|
```javascript
|
|
235
|
-
// Uses key from
|
|
236
|
-
const decrypted =
|
|
235
|
+
// Uses key from Odac.Config.encrypt.key
|
|
236
|
+
const decrypted = Odac.Var(encryptedData).decrypt()
|
|
237
237
|
|
|
238
238
|
// Custom decryption key
|
|
239
|
-
const decrypted =
|
|
239
|
+
const decrypted = Odac.Var(encryptedData).decrypt('my-32-character-encryption-key')
|
|
240
240
|
```
|
|
241
241
|
|
|
242
242
|
### Date Formatting
|
|
@@ -246,16 +246,16 @@ const decrypted = Candy.Var(encryptedData).decrypt('my-32-character-encryption-k
|
|
|
246
246
|
```javascript
|
|
247
247
|
const timestamp = '2024-03-15 14:30:45'
|
|
248
248
|
|
|
249
|
-
|
|
249
|
+
Odac.Var(timestamp).date('Y-m-d')
|
|
250
250
|
// Returns: '2024-03-15'
|
|
251
251
|
|
|
252
|
-
|
|
252
|
+
Odac.Var(timestamp).date('d/m/Y')
|
|
253
253
|
// Returns: '15/03/2024'
|
|
254
254
|
|
|
255
|
-
|
|
255
|
+
Odac.Var(timestamp).date('H:i:s')
|
|
256
256
|
// Returns: '14:30:45'
|
|
257
257
|
|
|
258
|
-
|
|
258
|
+
Odac.Var(timestamp).date('Y-m-d H:i')
|
|
259
259
|
// Returns: '2024-03-15 14:30'
|
|
260
260
|
```
|
|
261
261
|
|
|
@@ -274,10 +274,10 @@ Candy.Var(timestamp).date('Y-m-d H:i')
|
|
|
274
274
|
|
|
275
275
|
```javascript
|
|
276
276
|
// Save content to file
|
|
277
|
-
|
|
277
|
+
Odac.Var('Hello World').save('/path/to/file.txt')
|
|
278
278
|
|
|
279
279
|
// Automatically creates directories if needed
|
|
280
|
-
|
|
280
|
+
Odac.Var(jsonData).save('/path/to/nested/dir/data.json')
|
|
281
281
|
```
|
|
282
282
|
|
|
283
283
|
### Practical Examples
|
|
@@ -285,117 +285,117 @@ Candy.Var(jsonData).save('/path/to/nested/dir/data.json')
|
|
|
285
285
|
#### User Registration with Validation
|
|
286
286
|
|
|
287
287
|
```javascript
|
|
288
|
-
module.exports = async function(
|
|
289
|
-
const email =
|
|
290
|
-
const password =
|
|
291
|
-
const username =
|
|
288
|
+
module.exports = async function(Odac) {
|
|
289
|
+
const email = Odac.Request.post('email')
|
|
290
|
+
const password = Odac.Request.post('password')
|
|
291
|
+
const username = Odac.Request.post('username')
|
|
292
292
|
|
|
293
293
|
// Validate email
|
|
294
|
-
if (!
|
|
295
|
-
return
|
|
294
|
+
if (!Odac.Var(email).is('email')) {
|
|
295
|
+
return Odac.return({
|
|
296
296
|
success: false,
|
|
297
297
|
message: 'Invalid email address'
|
|
298
298
|
})
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
// Validate username (alphanumeric only)
|
|
302
|
-
if (!
|
|
303
|
-
return
|
|
302
|
+
if (!Odac.Var(username).is('alphanumeric')) {
|
|
303
|
+
return Odac.return({
|
|
304
304
|
success: false,
|
|
305
305
|
message: 'Username must be alphanumeric'
|
|
306
306
|
})
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
// Hash password
|
|
310
|
-
const hashedPassword =
|
|
310
|
+
const hashedPassword = Odac.Var(password).hash()
|
|
311
311
|
|
|
312
312
|
// Create slug for profile URL
|
|
313
|
-
const profileSlug =
|
|
313
|
+
const profileSlug = Odac.Var(username).slug()
|
|
314
314
|
|
|
315
315
|
// Save user
|
|
316
|
-
await
|
|
316
|
+
await Odac.Mysql.table('users').insert({
|
|
317
317
|
email: email,
|
|
318
318
|
username: username,
|
|
319
319
|
password: hashedPassword,
|
|
320
320
|
slug: profileSlug
|
|
321
321
|
})
|
|
322
322
|
|
|
323
|
-
return
|
|
323
|
+
return Odac.return({success: true})
|
|
324
324
|
}
|
|
325
325
|
```
|
|
326
326
|
|
|
327
327
|
#### Login with Password Verification
|
|
328
328
|
|
|
329
329
|
```javascript
|
|
330
|
-
module.exports = async function(
|
|
331
|
-
const email =
|
|
332
|
-
const password =
|
|
330
|
+
module.exports = async function(Odac) {
|
|
331
|
+
const email = Odac.Request.post('email')
|
|
332
|
+
const password = Odac.Request.post('password')
|
|
333
333
|
|
|
334
334
|
// Find user
|
|
335
|
-
const user = await
|
|
335
|
+
const user = await Odac.Mysql.table('users')
|
|
336
336
|
.where('email', email)
|
|
337
337
|
.first()
|
|
338
338
|
|
|
339
339
|
if (!user) {
|
|
340
|
-
return
|
|
340
|
+
return Odac.return({
|
|
341
341
|
success: false,
|
|
342
342
|
message: 'User not found'
|
|
343
343
|
})
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
// Verify password
|
|
347
|
-
const isValid =
|
|
347
|
+
const isValid = Odac.Var(user.password).hashCheck(password)
|
|
348
348
|
|
|
349
349
|
if (!isValid) {
|
|
350
|
-
return
|
|
350
|
+
return Odac.return({
|
|
351
351
|
success: false,
|
|
352
352
|
message: 'Invalid password'
|
|
353
353
|
})
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
// Login successful
|
|
357
|
-
|
|
358
|
-
return
|
|
357
|
+
Odac.Auth.login(user.id)
|
|
358
|
+
return Odac.return({success: true})
|
|
359
359
|
}
|
|
360
360
|
```
|
|
361
361
|
|
|
362
362
|
#### URL Slug Generation
|
|
363
363
|
|
|
364
364
|
```javascript
|
|
365
|
-
module.exports = async function(
|
|
366
|
-
const title =
|
|
365
|
+
module.exports = async function(Odac) {
|
|
366
|
+
const title = Odac.Request.post('title')
|
|
367
367
|
|
|
368
368
|
// Create URL-friendly slug
|
|
369
|
-
const slug =
|
|
369
|
+
const slug = Odac.Var(title).slug()
|
|
370
370
|
|
|
371
371
|
// Check if slug exists
|
|
372
|
-
const exists = await
|
|
372
|
+
const exists = await Odac.Mysql.table('posts')
|
|
373
373
|
.where('slug', slug)
|
|
374
374
|
.first()
|
|
375
375
|
|
|
376
376
|
if (exists) {
|
|
377
377
|
// Add timestamp to make unique
|
|
378
378
|
const uniqueSlug = `${slug}-${Date.now()}`
|
|
379
|
-
await
|
|
379
|
+
await Odac.Mysql.table('posts').insert({
|
|
380
380
|
title: title,
|
|
381
381
|
slug: uniqueSlug
|
|
382
382
|
})
|
|
383
383
|
} else {
|
|
384
|
-
await
|
|
384
|
+
await Odac.Mysql.table('posts').insert({
|
|
385
385
|
title: title,
|
|
386
386
|
slug: slug
|
|
387
387
|
})
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
-
return
|
|
390
|
+
return Odac.return({success: true})
|
|
391
391
|
}
|
|
392
392
|
```
|
|
393
393
|
|
|
394
394
|
#### Template Variable Replacement
|
|
395
395
|
|
|
396
396
|
```javascript
|
|
397
|
-
module.exports = async function(
|
|
398
|
-
const user = await
|
|
397
|
+
module.exports = async function(Odac) {
|
|
398
|
+
const user = await Odac.Auth.user()
|
|
399
399
|
|
|
400
400
|
// Email template
|
|
401
401
|
const template = `
|
|
@@ -409,45 +409,45 @@ module.exports = async function(Candy) {
|
|
|
409
409
|
`
|
|
410
410
|
|
|
411
411
|
// Replace variables
|
|
412
|
-
const emailContent =
|
|
412
|
+
const emailContent = Odac.Var(template).replace({
|
|
413
413
|
'{{name}}': user.name,
|
|
414
414
|
'{{email}}': user.email,
|
|
415
415
|
'{{url}}': 'https://example.com/dashboard',
|
|
416
|
-
'{{site}}': '
|
|
416
|
+
'{{site}}': 'Odac'
|
|
417
417
|
})
|
|
418
418
|
|
|
419
419
|
// Send email
|
|
420
|
-
await
|
|
420
|
+
await Odac.Mail.send({
|
|
421
421
|
to: user.email,
|
|
422
422
|
subject: 'Account Verified',
|
|
423
423
|
body: emailContent
|
|
424
424
|
})
|
|
425
425
|
|
|
426
|
-
return
|
|
426
|
+
return Odac.return({success: true})
|
|
427
427
|
}
|
|
428
428
|
```
|
|
429
429
|
|
|
430
430
|
#### Phone Number Formatting
|
|
431
431
|
|
|
432
432
|
```javascript
|
|
433
|
-
module.exports = async function(
|
|
434
|
-
const phone =
|
|
433
|
+
module.exports = async function(Odac) {
|
|
434
|
+
const phone = Odac.Request.post('phone')
|
|
435
435
|
|
|
436
436
|
// Remove all non-numeric characters
|
|
437
|
-
const cleanPhone =
|
|
437
|
+
const cleanPhone = Odac.Var(phone).clear('-', ' ', '(', ')', '+')
|
|
438
438
|
|
|
439
439
|
// Validate it's numeric
|
|
440
|
-
if (!
|
|
441
|
-
return
|
|
440
|
+
if (!Odac.Var(cleanPhone).is('numeric')) {
|
|
441
|
+
return Odac.return({
|
|
442
442
|
success: false,
|
|
443
443
|
message: 'Invalid phone number'
|
|
444
444
|
})
|
|
445
445
|
}
|
|
446
446
|
|
|
447
447
|
// Format for display
|
|
448
|
-
const formattedPhone =
|
|
448
|
+
const formattedPhone = Odac.Var(cleanPhone).format('(???) ???-????')
|
|
449
449
|
|
|
450
|
-
return
|
|
450
|
+
return Odac.return({
|
|
451
451
|
success: true,
|
|
452
452
|
phone: formattedPhone
|
|
453
453
|
})
|
|
@@ -457,31 +457,31 @@ module.exports = async function(Candy) {
|
|
|
457
457
|
#### Data Encryption for Storage
|
|
458
458
|
|
|
459
459
|
```javascript
|
|
460
|
-
module.exports = async function(
|
|
461
|
-
const creditCard =
|
|
460
|
+
module.exports = async function(Odac) {
|
|
461
|
+
const creditCard = Odac.Request.post('credit_card')
|
|
462
462
|
|
|
463
463
|
// Encrypt sensitive data
|
|
464
|
-
const encryptedCard =
|
|
464
|
+
const encryptedCard = Odac.Var(creditCard).encrypt()
|
|
465
465
|
|
|
466
466
|
// Save encrypted data
|
|
467
|
-
await
|
|
468
|
-
user_id:
|
|
467
|
+
await Odac.Mysql.table('payments').insert({
|
|
468
|
+
user_id: Odac.Auth.id(),
|
|
469
469
|
card: encryptedCard
|
|
470
470
|
})
|
|
471
471
|
|
|
472
|
-
return
|
|
472
|
+
return Odac.return({success: true})
|
|
473
473
|
}
|
|
474
474
|
|
|
475
475
|
// Later, to retrieve and decrypt
|
|
476
|
-
module.exports = async function(
|
|
477
|
-
const payment = await
|
|
478
|
-
.where('user_id',
|
|
476
|
+
module.exports = async function(Odac) {
|
|
477
|
+
const payment = await Odac.Mysql.table('payments')
|
|
478
|
+
.where('user_id', Odac.Auth.id())
|
|
479
479
|
.first()
|
|
480
480
|
|
|
481
481
|
// Decrypt data
|
|
482
|
-
const creditCard =
|
|
482
|
+
const creditCard = Odac.Var(payment.card).decrypt()
|
|
483
483
|
|
|
484
|
-
return
|
|
484
|
+
return Odac.return({
|
|
485
485
|
card: creditCard
|
|
486
486
|
})
|
|
487
487
|
}
|
|
@@ -498,7 +498,7 @@ module.exports = async function(Candy) {
|
|
|
498
498
|
|
|
499
499
|
### Notes
|
|
500
500
|
|
|
501
|
-
- `
|
|
501
|
+
- `Odac.Var()` returns the processed string value, not a Var instance (except for chaining)
|
|
502
502
|
- Encryption uses AES-256-CBC with a fixed IV
|
|
503
503
|
- BCrypt hashing is one-way and cannot be decrypted
|
|
504
504
|
- Date formatting works with any valid JavaScript date string
|