odac 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/.editorconfig +21 -0
  2. package/.github/workflows/auto-pr-description.yml +49 -0
  3. package/.github/workflows/release.yml +32 -0
  4. package/.github/workflows/test-coverage.yml +58 -0
  5. package/.husky/pre-commit +2 -0
  6. package/.kiro/steering/code-style.md +56 -0
  7. package/.kiro/steering/product.md +20 -0
  8. package/.kiro/steering/structure.md +77 -0
  9. package/.kiro/steering/tech.md +87 -0
  10. package/.prettierrc +10 -0
  11. package/.releaserc.js +134 -0
  12. package/AGENTS.md +84 -0
  13. package/CHANGELOG.md +181 -0
  14. package/CODE_OF_CONDUCT.md +83 -0
  15. package/CONTRIBUTING.md +63 -0
  16. package/LICENSE +661 -0
  17. package/README.md +57 -0
  18. package/SECURITY.md +26 -0
  19. package/bin/candy +10 -0
  20. package/bin/candypack +10 -0
  21. package/cli/index.js +3 -0
  22. package/cli/src/Cli.js +348 -0
  23. package/cli/src/Connector.js +93 -0
  24. package/cli/src/Monitor.js +416 -0
  25. package/core/Candy.js +87 -0
  26. package/core/Commands.js +239 -0
  27. package/core/Config.js +1094 -0
  28. package/core/Lang.js +52 -0
  29. package/core/Log.js +43 -0
  30. package/core/Process.js +26 -0
  31. package/docs/backend/01-overview/01-whats-in-the-candy-box.md +9 -0
  32. package/docs/backend/01-overview/02-super-handy-helper-functions.md +9 -0
  33. package/docs/backend/01-overview/03-development-server.md +79 -0
  34. package/docs/backend/02-structure/01-typical-project-layout.md +39 -0
  35. package/docs/backend/03-config/00-configuration-overview.md +214 -0
  36. package/docs/backend/03-config/01-database-connection.md +60 -0
  37. package/docs/backend/03-config/02-static-route-mapping-optional.md +20 -0
  38. package/docs/backend/03-config/03-request-timeout.md +11 -0
  39. package/docs/backend/03-config/04-environment-variables.md +227 -0
  40. package/docs/backend/03-config/05-early-hints.md +352 -0
  41. package/docs/backend/04-routing/01-basic-page-routes.md +28 -0
  42. package/docs/backend/04-routing/02-controller-less-view-routes.md +43 -0
  43. package/docs/backend/04-routing/03-api-and-data-routes.md +20 -0
  44. package/docs/backend/04-routing/04-authentication-aware-routes.md +48 -0
  45. package/docs/backend/04-routing/05-advanced-routing.md +14 -0
  46. package/docs/backend/04-routing/06-error-pages.md +101 -0
  47. package/docs/backend/04-routing/07-cron-jobs.md +149 -0
  48. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +17 -0
  49. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +20 -0
  50. package/docs/backend/05-controllers/03-controller-classes.md +93 -0
  51. package/docs/backend/05-forms/01-custom-forms.md +395 -0
  52. package/docs/backend/05-forms/02-automatic-database-insert.md +297 -0
  53. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +96 -0
  54. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +40 -0
  55. package/docs/backend/07-views/01-the-view-directory.md +73 -0
  56. package/docs/backend/07-views/02-rendering-a-view.md +179 -0
  57. package/docs/backend/07-views/03-template-syntax.md +181 -0
  58. package/docs/backend/07-views/03-variables.md +328 -0
  59. package/docs/backend/07-views/04-request-data.md +231 -0
  60. package/docs/backend/07-views/05-conditionals.md +290 -0
  61. package/docs/backend/07-views/06-loops.md +353 -0
  62. package/docs/backend/07-views/07-translations.md +358 -0
  63. package/docs/backend/07-views/08-backend-javascript.md +398 -0
  64. package/docs/backend/07-views/09-comments.md +297 -0
  65. package/docs/backend/08-database/01-database-connection.md +99 -0
  66. package/docs/backend/08-database/02-using-mysql.md +322 -0
  67. package/docs/backend/09-validation/01-the-validator-service.md +424 -0
  68. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +53 -0
  69. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +55 -0
  70. package/docs/backend/10-authentication/03-register.md +134 -0
  71. package/docs/backend/10-authentication/04-candy-register-forms.md +676 -0
  72. package/docs/backend/10-authentication/05-session-management.md +159 -0
  73. package/docs/backend/10-authentication/06-candy-login-forms.md +596 -0
  74. package/docs/backend/11-mail/01-the-mail-service.md +42 -0
  75. package/docs/backend/12-streaming/01-streaming-overview.md +300 -0
  76. package/docs/backend/13-utilities/01-candy-var.md +504 -0
  77. package/docs/frontend/01-overview/01-introduction.md +146 -0
  78. package/docs/frontend/02-ajax-navigation/01-quick-start.md +608 -0
  79. package/docs/frontend/02-ajax-navigation/02-configuration.md +370 -0
  80. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +519 -0
  81. package/docs/frontend/03-forms/01-form-handling.md +420 -0
  82. package/docs/frontend/04-api-requests/01-get-post.md +443 -0
  83. package/docs/frontend/05-streaming/01-client-streaming.md +163 -0
  84. package/docs/index.json +452 -0
  85. package/docs/server/01-installation/01-quick-install.md +19 -0
  86. package/docs/server/01-installation/02-manual-installation-via-npm.md +9 -0
  87. package/docs/server/02-get-started/01-core-concepts.md +7 -0
  88. package/docs/server/02-get-started/02-basic-commands.md +57 -0
  89. package/docs/server/02-get-started/03-cli-reference.md +276 -0
  90. package/docs/server/02-get-started/04-cli-quick-reference.md +102 -0
  91. package/docs/server/03-service/01-start-a-new-service.md +57 -0
  92. package/docs/server/03-service/02-delete-a-service.md +48 -0
  93. package/docs/server/04-web/01-create-a-website.md +36 -0
  94. package/docs/server/04-web/02-list-websites.md +9 -0
  95. package/docs/server/04-web/03-delete-a-website.md +29 -0
  96. package/docs/server/05-subdomain/01-create-a-subdomain.md +32 -0
  97. package/docs/server/05-subdomain/02-list-subdomains.md +33 -0
  98. package/docs/server/05-subdomain/03-delete-a-subdomain.md +41 -0
  99. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +34 -0
  100. package/docs/server/07-mail/01-create-a-mail-account.md +23 -0
  101. package/docs/server/07-mail/02-delete-a-mail-account.md +20 -0
  102. package/docs/server/07-mail/03-list-mail-accounts.md +20 -0
  103. package/docs/server/07-mail/04-change-account-password.md +23 -0
  104. package/eslint.config.mjs +120 -0
  105. package/framework/index.js +4 -0
  106. package/framework/src/Auth.js +309 -0
  107. package/framework/src/Candy.js +81 -0
  108. package/framework/src/Config.js +79 -0
  109. package/framework/src/Env.js +60 -0
  110. package/framework/src/Lang.js +57 -0
  111. package/framework/src/Mail.js +83 -0
  112. package/framework/src/Mysql.js +575 -0
  113. package/framework/src/Request.js +301 -0
  114. package/framework/src/Route/Cron.js +128 -0
  115. package/framework/src/Route/Internal.js +439 -0
  116. package/framework/src/Route.js +455 -0
  117. package/framework/src/Server.js +15 -0
  118. package/framework/src/Stream.js +163 -0
  119. package/framework/src/Token.js +37 -0
  120. package/framework/src/Validator.js +271 -0
  121. package/framework/src/Var.js +211 -0
  122. package/framework/src/View/EarlyHints.js +190 -0
  123. package/framework/src/View/Form.js +600 -0
  124. package/framework/src/View.js +513 -0
  125. package/framework/web/candy.js +838 -0
  126. package/jest.config.js +22 -0
  127. package/locale/de-DE.json +80 -0
  128. package/locale/en-US.json +79 -0
  129. package/locale/es-ES.json +80 -0
  130. package/locale/fr-FR.json +80 -0
  131. package/locale/pt-BR.json +80 -0
  132. package/locale/ru-RU.json +80 -0
  133. package/locale/tr-TR.json +85 -0
  134. package/locale/zh-CN.json +80 -0
  135. package/package.json +86 -0
  136. package/server/index.js +5 -0
  137. package/server/src/Api.js +88 -0
  138. package/server/src/DNS.js +940 -0
  139. package/server/src/Hub.js +535 -0
  140. package/server/src/Mail.js +571 -0
  141. package/server/src/SSL.js +180 -0
  142. package/server/src/Server.js +27 -0
  143. package/server/src/Service.js +248 -0
  144. package/server/src/Subdomain.js +64 -0
  145. package/server/src/Web/Firewall.js +170 -0
  146. package/server/src/Web/Proxy.js +134 -0
  147. package/server/src/Web.js +451 -0
  148. package/server/src/mail/imap.js +1091 -0
  149. package/server/src/mail/server.js +32 -0
  150. package/server/src/mail/smtp.js +786 -0
  151. package/test/cli/Cli.test.js +36 -0
  152. package/test/core/Candy.test.js +234 -0
  153. package/test/core/Commands.test.js +538 -0
  154. package/test/core/Config.test.js +1435 -0
  155. package/test/core/Lang.test.js +250 -0
  156. package/test/core/Process.test.js +156 -0
  157. package/test/framework/Route.test.js +239 -0
  158. package/test/framework/View/EarlyHints.test.js +282 -0
  159. package/test/scripts/check-coverage.js +132 -0
  160. package/test/server/Api.test.js +647 -0
  161. package/test/server/Client.test.js +338 -0
  162. package/test/server/DNS.test.js +2050 -0
  163. package/test/server/DNS.test.js.bak +2084 -0
  164. package/test/server/Log.test.js +73 -0
  165. package/test/server/Mail.account.test_.js +460 -0
  166. package/test/server/Mail.init.test_.js +411 -0
  167. package/test/server/Mail.test_.js +1340 -0
  168. package/test/server/SSL.test_.js +1491 -0
  169. package/test/server/Server.test.js +765 -0
  170. package/test/server/Service.test_.js +1127 -0
  171. package/test/server/Subdomain.test.js +440 -0
  172. package/test/server/Web/Firewall.test.js +175 -0
  173. package/test/server/Web.test_.js +1562 -0
  174. package/test/server/__mocks__/acme-client.js +17 -0
  175. package/test/server/__mocks__/bcrypt.js +50 -0
  176. package/test/server/__mocks__/child_process.js +389 -0
  177. package/test/server/__mocks__/crypto.js +432 -0
  178. package/test/server/__mocks__/fs.js +450 -0
  179. package/test/server/__mocks__/globalCandy.js +227 -0
  180. package/test/server/__mocks__/http-proxy.js +105 -0
  181. package/test/server/__mocks__/http.js +575 -0
  182. package/test/server/__mocks__/https.js +272 -0
  183. package/test/server/__mocks__/index.js +249 -0
  184. package/test/server/__mocks__/mail/server.js +100 -0
  185. package/test/server/__mocks__/mail/smtp.js +31 -0
  186. package/test/server/__mocks__/mailparser.js +81 -0
  187. package/test/server/__mocks__/net.js +369 -0
  188. package/test/server/__mocks__/node-forge.js +328 -0
  189. package/test/server/__mocks__/os.js +320 -0
  190. package/test/server/__mocks__/path.js +291 -0
  191. package/test/server/__mocks__/selfsigned.js +8 -0
  192. package/test/server/__mocks__/server/src/mail/server.js +100 -0
  193. package/test/server/__mocks__/server/src/mail/smtp.js +31 -0
  194. package/test/server/__mocks__/smtp-server.js +106 -0
  195. package/test/server/__mocks__/sqlite3.js +394 -0
  196. package/test/server/__mocks__/testFactories.js +299 -0
  197. package/test/server/__mocks__/testHelpers.js +363 -0
  198. package/test/server/__mocks__/tls.js +229 -0
  199. package/watchdog/index.js +3 -0
  200. package/watchdog/src/Watchdog.js +156 -0
  201. package/web/config.json +5 -0
  202. package/web/controller/page/about.js +27 -0
  203. package/web/controller/page/index.js +34 -0
  204. package/web/package.json +18 -0
  205. package/web/public/assets/css/style.css +1835 -0
  206. package/web/public/assets/js/app.js +96 -0
  207. package/web/route/www.js +19 -0
  208. package/web/skeleton/main.html +22 -0
  209. package/web/view/content/about.html +65 -0
  210. package/web/view/content/home.html +205 -0
  211. package/web/view/footer/main.html +11 -0
  212. package/web/view/head/main.html +5 -0
  213. package/web/view/header/main.html +14 -0
@@ -0,0 +1,395 @@
1
+ # Custom Forms
2
+
3
+ CandyPack provides an automatic form system with built-in validation, CSRF protection, and seamless client-side integration. The `<candy:form>` tag allows you to create forms with minimal code while maintaining full control.
4
+
5
+ ## Basic Usage
6
+
7
+ ```html
8
+ <candy:form action="/contact/submit" method="POST">
9
+ <candy:field name="email" type="email" label="Email">
10
+ <candy:validate rule="required|email" message="Valid email required"/>
11
+ </candy:field>
12
+
13
+ <candy:submit text="Send" loading="Sending..."/>
14
+ </candy:form>
15
+ ```
16
+
17
+ ## Form Attributes
18
+
19
+ ### `<candy:form>`
20
+
21
+ - `action` - Form submission URL (optional if using `table`)
22
+ - `method` - HTTP method (default: POST)
23
+ - `table` - Database table name for automatic insert (optional)
24
+ - `redirect` - Redirect URL after success (optional)
25
+ - `success` - Success message (optional)
26
+ - `class` - Additional CSS classes
27
+ - `id` - Form ID attribute
28
+
29
+ ```html
30
+ <!-- With custom controller -->
31
+ <candy:form action="/api/save" method="POST" class="my-form" id="contact-form">
32
+ <!-- fields here -->
33
+ </candy:form>
34
+
35
+ <!-- With automatic DB insert -->
36
+ <candy:form table="waitlist" redirect="/" success="Thank you for joining!">
37
+ <!-- fields here -->
38
+ </candy:form>
39
+ ```
40
+
41
+ ## Field Types
42
+
43
+ ### `<candy:field>`
44
+
45
+ Supports all standard HTML input types:
46
+
47
+ ```html
48
+ <!-- Text input with multiple validations -->
49
+ <candy:field name="username" type="text" label="Username" placeholder="Enter username">
50
+ <candy:validate rule="required" message="Username is required"/>
51
+ <candy:validate rule="minlen:3" message="Username must be at least 3 characters"/>
52
+ <candy:validate rule="maxlen:20" message="Username cannot exceed 20 characters"/>
53
+ <candy:validate rule="alphanumeric" message="Username can only contain letters and numbers"/>
54
+ </candy:field>
55
+
56
+ <!-- Email input -->
57
+ <candy:field name="email" type="email" label="Email Address" placeholder="your@email.com">
58
+ <candy:validate rule="required" message="Email address is required"/>
59
+ <candy:validate rule="email" message="Please enter a valid email address"/>
60
+ <candy:validate rule="maxlen:100" message="Email is too long"/>
61
+ </candy:field>
62
+
63
+ <!-- Password input with strong validation -->
64
+ <candy:field name="password" type="password" label="Password">
65
+ <candy:validate rule="required" message="Password is required"/>
66
+ <candy:validate rule="minlen:8" message="Password must be at least 8 characters long"/>
67
+ <candy:validate rule="maxlen:50" message="Password is too long"/>
68
+ </candy:field>
69
+
70
+ <!-- Textarea with character limits -->
71
+ <candy:field name="message" type="textarea" label="Your Message" placeholder="Tell us what you think...">
72
+ <candy:validate rule="required" message="Please enter your message"/>
73
+ <candy:validate rule="minlen:10" message="Message must be at least 10 characters"/>
74
+ <candy:validate rule="maxlen:500" message="Message cannot exceed 500 characters"/>
75
+ </candy:field>
76
+
77
+ <!-- Checkbox for terms acceptance -->
78
+ <candy:field name="agree" type="checkbox" label="I agree to the Terms of Service and Privacy Policy">
79
+ <candy:validate rule="accepted" message="You must accept the terms to continue"/>
80
+ </candy:field>
81
+
82
+ <!-- Number input with range -->
83
+ <candy:field name="age" type="number" label="Your Age">
84
+ <candy:validate rule="required" message="Age is required"/>
85
+ <candy:validate rule="min:18" message="You must be at least 18 years old"/>
86
+ <candy:validate rule="max:120" message="Please enter a valid age"/>
87
+ </candy:field>
88
+
89
+ <!-- Phone number -->
90
+ <candy:field name="phone" type="text" label="Phone Number" placeholder="+1 (555) 123-4567">
91
+ <candy:validate rule="required" message="Phone number is required"/>
92
+ <candy:validate rule="minlen:10" message="Phone number must be at least 10 digits"/>
93
+ </candy:field>
94
+
95
+ <!-- URL input -->
96
+ <candy:field name="website" type="url" label="Website" placeholder="https://example.com">
97
+ <candy:validate rule="url" message="Please enter a valid URL"/>
98
+ </candy:field>
99
+
100
+ <!-- Name with alpha validation -->
101
+ <candy:field name="full_name" type="text" label="Full Name" placeholder="John Doe">
102
+ <candy:validate rule="required" message="Full name is required"/>
103
+ <candy:validate rule="minlen:2" message="Name must be at least 2 characters"/>
104
+ <candy:validate rule="maxlen:50" message="Name is too long"/>
105
+ </candy:field>
106
+ ```
107
+
108
+ ### Field Attributes
109
+
110
+ - `name` - Field name (required)
111
+ - `type` - Input type (default: text)
112
+ - `label` - Field label
113
+ - `placeholder` - Placeholder text
114
+ - `class` - CSS classes
115
+ - `id` - Field ID
116
+
117
+ ## Validation Rules
118
+
119
+ ### `<candy:validate>`
120
+
121
+ Add validation rules to fields:
122
+
123
+ ```html
124
+ <candy:field name="username" type="text">
125
+ <candy:validate rule="required|minlen:3|maxlen:20" message="Username must be 3-20 characters"/>
126
+ </candy:field>
127
+ ```
128
+
129
+ ### Available Rules
130
+
131
+ - `required` - Field is required
132
+ - `email` - Must be valid email
133
+ - `url` - Must be valid URL
134
+ - `minlen:n` - Minimum length
135
+ - `maxlen:n` - Maximum length
136
+ - `min:n` - Minimum value (numbers)
137
+ - `max:n` - Maximum value (numbers)
138
+ - `numeric` - Only numbers
139
+ - `alpha` - Only letters
140
+ - `alphanumeric` - Letters and numbers only
141
+ - `accepted` - Checkbox must be checked
142
+
143
+ ### Multiple Rules
144
+
145
+ Combine rules with `|`:
146
+
147
+ ```html
148
+ <candy:validate rule="required|email|maxlen:100" message="Invalid email"/>
149
+ ```
150
+
151
+ ### Unique Validation
152
+
153
+ For automatic DB insert, use `unique` to check if value already exists:
154
+
155
+ ```html
156
+ <candy:validate rule="required|email|unique" message="This email is already registered"/>
157
+ ```
158
+
159
+ ## Auto-Set Values
160
+
161
+ ### `<candy:set>`
162
+
163
+ Automatically set field values without user input:
164
+
165
+ ```html
166
+ <candy:set name="created_at" compute="now"/>
167
+ <candy:set name="ip" compute="ip"/>
168
+ <candy:set name="user_agent" compute="user_agent"/>
169
+ <candy:set name="status" value="pending"/>
170
+ ```
171
+
172
+ ### Available Compute Types
173
+
174
+ - `now` - Current Unix timestamp (seconds)
175
+ - `date` - Current date (YYYY-MM-DD)
176
+ - `datetime` - Current ISO datetime
177
+ - `timestamp` - Current timestamp (milliseconds)
178
+ - `ip` - User's IP address
179
+ - `user_agent` - User's browser user agent
180
+ - `uuid` - Generate UUID v4
181
+
182
+ ### Set Attributes
183
+
184
+ - `name` - Field name (required)
185
+ - `value` - Static value
186
+ - `compute` - Computed value type
187
+ - `callback` - Custom function name
188
+ - `if-empty` - Only set if field is empty
189
+
190
+ ## Submit Button
191
+
192
+ ### `<candy:submit>`
193
+
194
+ ```html
195
+ <!-- Simple -->
196
+ <candy:submit text="Submit"/>
197
+
198
+ <!-- With loading state -->
199
+ <candy:submit text="Send Message" loading="Sending..."/>
200
+
201
+ <!-- With styling -->
202
+ <candy:submit text="Save" loading="Saving..." class="btn btn-primary" id="save-btn"/>
203
+ ```
204
+
205
+ ## Controller Handler
206
+
207
+ Handle form submission in your controller:
208
+
209
+ ```javascript
210
+ module.exports = {
211
+ submit: Candy => {
212
+ // Access validated form data
213
+ const data = Candy.formData
214
+
215
+ // data contains all field values
216
+ console.log(data.email, data.message)
217
+
218
+ // Process the data (save to database, send email, etc.)
219
+
220
+ // Return success response
221
+ return Candy.return({
222
+ result: {
223
+ success: true,
224
+ message: 'Form submitted successfully!',
225
+ redirect: '/thank-you' // Optional redirect
226
+ }
227
+ })
228
+ }
229
+ }
230
+ ```
231
+
232
+ ### Error Handling
233
+
234
+ Return validation errors:
235
+
236
+ ```javascript
237
+ module.exports = {
238
+ submit: Candy => {
239
+ const data = Candy.formData
240
+
241
+ // Custom validation
242
+ if (data.email.includes('spam')) {
243
+ return Candy.return({
244
+ result: {success: false},
245
+ errors: {
246
+ email: 'This email is not allowed'
247
+ }
248
+ })
249
+ }
250
+
251
+ return Candy.return({
252
+ result: {success: true, message: 'Success!'}
253
+ })
254
+ }
255
+ }
256
+ ```
257
+
258
+ ## Automatic Database Insert
259
+
260
+ Forms can automatically insert data into database without writing a controller:
261
+
262
+ ### View (view/content/waitlist.html)
263
+
264
+ ```html
265
+ <candy:form table="waitlist" redirect="/" success="Thank you for joining!">
266
+ <candy:field name="email" type="email" label="Email">
267
+ <candy:validate rule="required|email|unique" message="Valid email required"/>
268
+ </candy:field>
269
+
270
+ <candy:field name="name" type="text" label="Name">
271
+ <candy:validate rule="required|minlen:2" message="Name required"/>
272
+ </candy:field>
273
+
274
+ <candy:set name="created_at" compute="now"/>
275
+ <candy:set name="ip" compute="ip"/>
276
+
277
+ <candy:submit text="Join Waitlist" loading="Joining..."/>
278
+ </candy:form>
279
+ ```
280
+
281
+ ### Database Table
282
+
283
+ ```sql
284
+ CREATE TABLE `waitlist` (
285
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
286
+ `email` VARCHAR(255) NOT NULL UNIQUE,
287
+ `name` VARCHAR(255) NOT NULL,
288
+ `created_at` INT UNSIGNED NOT NULL,
289
+ `ip` VARCHAR(45) NULL,
290
+ INDEX `idx_email` (`email`)
291
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
292
+ ```
293
+
294
+ ### Route (route/www.js)
295
+
296
+ ```javascript
297
+ Candy.Route.page('/waitlist', 'waitlist')
298
+ ```
299
+
300
+ That's it! No controller needed. The form will:
301
+ - Validate all fields
302
+ - Check email uniqueness
303
+ - Insert data into `waitlist` table
304
+ - Show success message
305
+ - Redirect to home page
306
+
307
+ ## Complete Example with Custom Controller
308
+
309
+ ### View (view/content/contact.html)
310
+
311
+ ```html
312
+ <div class="contact-page">
313
+ <h1>Contact Us</h1>
314
+
315
+ <candy:form action="/contact/submit" method="POST" class="contact-form">
316
+ <candy:field name="name" type="text" label="Your Name" placeholder="Enter your name">
317
+ <candy:validate rule="required|minlen:3" message="Name must be at least 3 characters"/>
318
+ </candy:field>
319
+
320
+ <candy:field name="email" type="email" label="Email" placeholder="your@email.com">
321
+ <candy:validate rule="required|email" message="Please enter a valid email"/>
322
+ </candy:field>
323
+
324
+ <candy:field name="subject" type="text" label="Subject" placeholder="What is this about?">
325
+ <candy:validate rule="required|minlen:5" message="Subject must be at least 5 characters"/>
326
+ </candy:field>
327
+
328
+ <candy:field name="message" type="textarea" label="Message" placeholder="Your message...">
329
+ <candy:validate rule="required|minlen:10" message="Message must be at least 10 characters"/>
330
+ </candy:field>
331
+
332
+ <candy:submit text="Send Message" loading="Sending..." class="btn btn-primary"/>
333
+ </candy:form>
334
+ </div>
335
+ ```
336
+
337
+ ### Controller (controller/contact.js)
338
+
339
+ ```javascript
340
+ module.exports = {
341
+ index: Candy => {
342
+ Candy.View.skeleton('default')
343
+ Candy.View.set({content: 'contact'})
344
+ Candy.View.print()
345
+ },
346
+
347
+ submit: Candy => {
348
+ const data = Candy.formData
349
+
350
+ // Save to database
351
+ // await Candy.Mysql.query('INSERT INTO contacts SET ?', data)
352
+
353
+ // Send email notification
354
+ // await Candy.Mail().to('admin@example.com').subject('New Contact').send(data.message)
355
+
356
+ return Candy.return({
357
+ result: {
358
+ success: true,
359
+ message: 'Thank you! We will get back to you soon.',
360
+ redirect: '/'
361
+ }
362
+ })
363
+ }
364
+ }
365
+ ```
366
+
367
+ ### Route (route/www.js)
368
+
369
+ ```javascript
370
+ Candy.Route.page('/contact', 'contact')
371
+ Candy.Route.post('/contact/submit', 'contact.submit')
372
+ ```
373
+
374
+ ## Features
375
+
376
+ - **Automatic CSRF Protection** - Built-in token validation
377
+ - **Client-Side Validation** - HTML5 validation with custom messages
378
+ - **Server-Side Validation** - Automatic validation before controller execution
379
+ - **Session Security** - Form tokens tied to user session, IP, and user agent
380
+ - **Loading States** - Automatic button state management
381
+ - **Error Display** - Automatic error message rendering
382
+ - **Success Messages** - Built-in success message handling
383
+ - **Redirect Support** - Optional redirect after successful submission
384
+
385
+ ## Security
386
+
387
+ Forms automatically include:
388
+
389
+ - CSRF token validation
390
+ - Session verification
391
+ - IP address validation
392
+ - User agent verification
393
+ - Token expiration (30 minutes)
394
+
395
+ All validation happens before your controller is executed, ensuring only valid, secure data reaches your code.
@@ -0,0 +1,297 @@
1
+ # Automatic Database Insert
2
+
3
+ Forms can automatically insert data into your database without writing any controller code. This is perfect for simple use cases like waitlists, newsletter signups, contact forms, and feedback collection.
4
+
5
+ ## Basic Usage
6
+
7
+ ```html
8
+ <candy:form table="waitlist">
9
+ <candy:field name="email" type="email" label="Email">
10
+ <candy:validate rule="required|email|unique"/>
11
+ </candy:field>
12
+
13
+ <candy:submit text="Join"/>
14
+ </candy:form>
15
+ ```
16
+
17
+ That's it! The form will automatically:
18
+ 1. Validate the email
19
+ 2. Check if it's unique in the database
20
+ 3. Insert the record into `waitlist` table
21
+ 4. Show success message
22
+
23
+ ## Complete Example
24
+
25
+ ### 1. Create Database Table
26
+
27
+ ```sql
28
+ CREATE TABLE `waitlist` (
29
+ `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
30
+ `email` VARCHAR(255) NOT NULL UNIQUE,
31
+ `name` VARCHAR(255) NOT NULL,
32
+ `created_at` INT UNSIGNED NOT NULL,
33
+ `ip` VARCHAR(45) NULL,
34
+ `user_agent` TEXT NULL,
35
+ INDEX `idx_email` (`email`),
36
+ INDEX `idx_created_at` (`created_at`)
37
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
38
+ ```
39
+
40
+ ### 2. Create View
41
+
42
+ **view/content/waitlist.html**
43
+ ```html
44
+ <div class="waitlist-page">
45
+ <h1>Join Our Waitlist</h1>
46
+
47
+ <candy:form table="waitlist" redirect="/" success="Thank you for joining!">
48
+ <candy:field name="email" type="email" label="Email" placeholder="your@email.com">
49
+ <candy:validate rule="required|email|unique" message="Please enter a valid email"/>
50
+ </candy:field>
51
+
52
+ <candy:field name="name" type="text" label="Name" placeholder="Your name">
53
+ <candy:validate rule="required|minlen:2" message="Name is required"/>
54
+ </candy:field>
55
+
56
+ <candy:set name="created_at" compute="now"/>
57
+ <candy:set name="ip" compute="ip"/>
58
+ <candy:set name="user_agent" compute="user_agent"/>
59
+
60
+ <candy:submit text="Join Waitlist" loading="Joining..." class="btn btn-primary"/>
61
+ </candy:form>
62
+ </div>
63
+ ```
64
+
65
+ ### 3. Create Controller
66
+
67
+ **controller/waitlist.js**
68
+ ```javascript
69
+ module.exports = Candy => {
70
+ Candy.View.skeleton('default')
71
+ Candy.View.set({content: 'waitlist'})
72
+ Candy.View.print()
73
+ }
74
+ ```
75
+
76
+ ### 4. Add Route
77
+
78
+ **route/www.js**
79
+ ```javascript
80
+ Candy.Route.page('/waitlist', 'waitlist')
81
+ ```
82
+
83
+ Done! No form submission handler needed.
84
+
85
+ ## Form Attributes
86
+
87
+ ### `table` (required)
88
+ Database table name where data will be inserted.
89
+
90
+ ```html
91
+ <candy:form table="newsletter_subscribers">
92
+ ```
93
+
94
+ ### `redirect` (optional)
95
+ URL to redirect after successful submission.
96
+
97
+ ```html
98
+ <candy:form table="waitlist" redirect="/thank-you">
99
+ ```
100
+
101
+ ### `success` (optional)
102
+ Custom success message to display.
103
+
104
+ ```html
105
+ <candy:form table="waitlist" success="Welcome! We'll notify you soon.">
106
+ ```
107
+
108
+ ## Unique Validation
109
+
110
+ Use `unique` rule to prevent duplicate entries:
111
+
112
+ ```html
113
+ <candy:field name="email" type="email">
114
+ <candy:validate rule="required|email|unique" message="This email is already registered"/>
115
+ </candy:field>
116
+ ```
117
+
118
+ The system will:
119
+ 1. Check if the value exists in the table
120
+ 2. Return error if duplicate found
121
+ 3. Show the custom error message
122
+
123
+ ## Auto-Set Values
124
+
125
+ Use `<candy:set>` to automatically populate fields:
126
+
127
+ ```html
128
+ <candy:set name="created_at" compute="now"/>
129
+ <candy:set name="ip" compute="ip"/>
130
+ <candy:set name="user_agent" compute="user_agent"/>
131
+ <candy:set name="status" value="pending"/>
132
+ ```
133
+
134
+ ### Compute Types
135
+
136
+ - `now` - Unix timestamp in seconds
137
+ - `date` - Current date (YYYY-MM-DD)
138
+ - `datetime` - ISO datetime string
139
+ - `timestamp` - Timestamp in milliseconds
140
+ - `ip` - User's IP address
141
+ - `user_agent` - Browser user agent
142
+ - `uuid` - Generate UUID v4
143
+
144
+ ### Static Values
145
+
146
+ ```html
147
+ <candy:set name="status" value="pending"/>
148
+ <candy:set name="source" value="website"/>
149
+ <candy:set name="plan" value="free"/>
150
+ ```
151
+
152
+ ### Conditional Set
153
+
154
+ Only set if field is empty:
155
+
156
+ ```html
157
+ <candy:set name="country" value="US" if-empty/>
158
+ ```
159
+
160
+ ## Use Cases
161
+
162
+ ### Newsletter Signup
163
+
164
+ ```html
165
+ <candy:form table="newsletter" success="Thanks for subscribing!">
166
+ <candy:field name="email" type="email">
167
+ <candy:validate rule="required|email|unique"/>
168
+ </candy:field>
169
+
170
+ <candy:set name="subscribed_at" compute="now"/>
171
+ <candy:set name="status" value="active"/>
172
+
173
+ <candy:submit text="Subscribe"/>
174
+ </candy:form>
175
+ ```
176
+
177
+ ### Feedback Form
178
+
179
+ ```html
180
+ <candy:form table="feedback" redirect="/" success="Thank you for your feedback!">
181
+ <candy:field name="rating" type="number" label="Rating (1-5)">
182
+ <candy:validate rule="required|min:1|max:5"/>
183
+ </candy:field>
184
+
185
+ <candy:field name="comment" type="textarea" label="Comment">
186
+ <candy:validate rule="required|minlen:10"/>
187
+ </candy:field>
188
+
189
+ <candy:set name="created_at" compute="now"/>
190
+ <candy:set name="ip" compute="ip"/>
191
+
192
+ <candy:submit text="Submit Feedback"/>
193
+ </candy:form>
194
+ ```
195
+
196
+ ### Beta Access Request
197
+
198
+ ```html
199
+ <candy:form table="beta_requests" success="You're on the list!">
200
+ <candy:field name="email" type="email">
201
+ <candy:validate rule="required|email|unique"/>
202
+ </candy:field>
203
+
204
+ <candy:field name="company" type="text">
205
+ <candy:validate rule="required"/>
206
+ </candy:field>
207
+
208
+ <candy:field name="use_case" type="textarea">
209
+ <candy:validate rule="required|minlen:20"/>
210
+ </candy:field>
211
+
212
+ <candy:set name="requested_at" compute="now"/>
213
+ <candy:set name="status" value="pending"/>
214
+
215
+ <candy:submit text="Request Access"/>
216
+ </candy:form>
217
+ ```
218
+
219
+ ## Error Handling
220
+
221
+ The system automatically handles:
222
+
223
+ - **Validation errors** - Shows field-specific error messages
224
+ - **Duplicate entries** - Shows unique constraint errors
225
+ - **Database errors** - Shows generic error message
226
+ - **Missing database** - Shows configuration error
227
+
228
+ All errors are displayed inline next to the relevant field.
229
+
230
+ ## Security
231
+
232
+ Automatic DB insert includes all security features:
233
+
234
+ - CSRF token validation
235
+ - Session verification
236
+ - IP address validation
237
+ - User agent verification
238
+ - SQL injection prevention (parameterized queries)
239
+ - Token expiration (30 minutes)
240
+
241
+ ## When to Use
242
+
243
+ **Use automatic DB insert when:**
244
+ - Simple data collection (waitlist, newsletter, feedback)
245
+ - No complex business logic needed
246
+ - Direct database insert is sufficient
247
+ - You want rapid development
248
+
249
+ **Use custom controller when:**
250
+ - Need to send emails
251
+ - Complex validation logic
252
+ - Multiple database operations
253
+ - External API calls
254
+ - Custom response handling
255
+
256
+ ## Combining with Custom Logic
257
+
258
+ You can add custom logic by specifying a custom `action` attribute. When you do this, the form data is validated and prepared, but the automatic DB insert is skipped. Instead, your controller receives the validated data via `Candy.formData`:
259
+
260
+ ```javascript
261
+ // In your view:
262
+ // <candy:form action="/contact/submit" table="contacts">
263
+
264
+ // In your controller:
265
+ Candy.Route.post('/contact/submit', async Candy => {
266
+ // Candy.formData contains validated form data
267
+ // Candy.formConfig contains form configuration
268
+
269
+ // Perform custom logic (send email, call API, etc.)
270
+ await sendEmail(Candy.formData.email, 'Thank you!')
271
+
272
+ // Manually insert to database if needed
273
+ await Candy.Mysql.query('INSERT INTO contacts SET ?', Candy.formData)
274
+
275
+ return Candy.return({
276
+ result: {success: true, message: 'Message sent!'}
277
+ })
278
+ })
279
+ const data = Candy.formData
280
+
281
+ // Send welcome email
282
+ Candy.Mail()
283
+ .to(data.email)
284
+ .subject('Welcome!')
285
+ .send('Thanks for joining!')
286
+
287
+ // Return custom response
288
+ return Candy.return({
289
+ result: {
290
+ success: true,
291
+ message: 'Check your email!'
292
+ }
293
+ })
294
+ })
295
+ ```
296
+
297
+ But for most cases, automatic insert is all you need!