odac 1.0.0 → 1.1.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 (61) hide show
  1. package/.github/workflows/auto-pr-description.yml +3 -1
  2. package/CHANGELOG.md +127 -0
  3. package/README.md +39 -36
  4. package/bin/odac.js +1 -31
  5. package/client/odac.js +871 -994
  6. package/docs/backend/01-overview/03-development-server.md +7 -7
  7. package/docs/backend/02-structure/01-typical-project-layout.md +1 -0
  8. package/docs/backend/03-config/00-configuration-overview.md +9 -0
  9. package/docs/backend/03-config/01-database-connection.md +1 -1
  10. package/docs/backend/04-routing/02-controller-less-view-routes.md +9 -3
  11. package/docs/backend/04-routing/09-websocket.md +29 -0
  12. package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +2 -0
  13. package/docs/backend/05-controllers/03-controller-classes.md +27 -41
  14. package/docs/backend/05-forms/01-custom-forms.md +103 -95
  15. package/docs/backend/05-forms/02-automatic-database-insert.md +21 -21
  16. package/docs/backend/07-views/02-rendering-a-view.md +1 -1
  17. package/docs/backend/07-views/03-variables.md +5 -5
  18. package/docs/backend/07-views/04-request-data.md +1 -1
  19. package/docs/backend/07-views/08-backend-javascript.md +1 -1
  20. package/docs/backend/08-database/01-getting-started.md +100 -0
  21. package/docs/backend/08-database/02-basics.md +136 -0
  22. package/docs/backend/08-database/03-advanced.md +84 -0
  23. package/docs/backend/08-database/04-migrations.md +48 -0
  24. package/docs/backend/09-validation/01-the-validator-service.md +1 -0
  25. package/docs/backend/10-authentication/03-register.md +8 -1
  26. package/docs/backend/10-authentication/04-odac-register-forms.md +46 -46
  27. package/docs/backend/10-authentication/05-session-management.md +1 -1
  28. package/docs/backend/10-authentication/06-odac-login-forms.md +48 -48
  29. package/docs/backend/10-authentication/07-magic-links.md +134 -0
  30. package/docs/backend/11-mail/01-the-mail-service.md +118 -28
  31. package/docs/backend/12-streaming/01-streaming-overview.md +2 -2
  32. package/docs/backend/13-utilities/01-odac-var.md +7 -7
  33. package/docs/backend/13-utilities/02-ipc.md +73 -0
  34. package/docs/frontend/01-overview/01-introduction.md +5 -1
  35. package/docs/frontend/02-ajax-navigation/01-quick-start.md +1 -1
  36. package/docs/index.json +16 -124
  37. package/eslint.config.mjs +5 -47
  38. package/package.json +9 -4
  39. package/src/Auth.js +362 -104
  40. package/src/Config.js +7 -2
  41. package/src/Database.js +188 -0
  42. package/src/Ipc.js +330 -0
  43. package/src/Mail.js +408 -37
  44. package/src/Odac.js +65 -9
  45. package/src/Request.js +70 -48
  46. package/src/Route/Cron.js +4 -1
  47. package/src/Route/Internal.js +214 -11
  48. package/src/Route/Middleware.js +7 -2
  49. package/src/Route.js +106 -26
  50. package/src/Server.js +80 -11
  51. package/src/Storage.js +165 -0
  52. package/src/Validator.js +94 -2
  53. package/src/View/Form.js +193 -17
  54. package/src/View.js +46 -1
  55. package/src/WebSocket.js +18 -3
  56. package/template/config.json +1 -1
  57. package/template/route/www.js +12 -10
  58. package/test/core/{Candy.test.js → Odac.test.js} +2 -2
  59. package/docs/backend/08-database/01-database-connection.md +0 -99
  60. package/docs/backend/08-database/02-using-mysql.md +0 -322
  61. package/src/Mysql.js +0 -575
@@ -1,322 +0,0 @@
1
- ## 🗄️ Using MySQL
2
-
3
- Odac provides a powerful query builder for safe and easy database operations.
4
-
5
- ### Selecting Data
6
-
7
- #### Get All Records
8
-
9
- ```javascript
10
- const users = await Odac.Mysql.table('users').get()
11
- ```
12
-
13
- #### Get First Record
14
-
15
- ```javascript
16
- const user = await Odac.Mysql.table('users').where('id', 1).first()
17
- ```
18
-
19
- #### Select Specific Columns
20
-
21
- ```javascript
22
- const users = await Odac.Mysql.table('users')
23
- .select('id', 'name', 'email')
24
- .get()
25
- ```
26
-
27
- ### Where Conditions
28
-
29
- ```javascript
30
- // Single condition
31
- const users = await Odac.Mysql.table('users')
32
- .where('status', 'active')
33
- .get()
34
-
35
- // Multiple conditions (AND)
36
- const users = await Odac.Mysql.table('users')
37
- .where('status', 'active')
38
- .where('verified', 1)
39
- .get()
40
-
41
- // OR condition
42
- const users = await Odac.Mysql.table('users')
43
- .where('role', 'admin')
44
- .orWhere('role', 'moderator')
45
- .get()
46
-
47
- // Comparison operators
48
- const products = await Odac.Mysql.table('products')
49
- .where('price', '>', 100)
50
- .where('stock', '<=', 10)
51
- .get()
52
-
53
- // LIKE
54
- const users = await Odac.Mysql.table('users')
55
- .where('name', 'LIKE', '%John%')
56
- .get()
57
-
58
- // IN
59
- const users = await Odac.Mysql.table('users')
60
- .where('role', 'IN', ['admin', 'moderator'])
61
- .get()
62
- ```
63
-
64
- ### Complex Where Conditions
65
-
66
- For complex queries with nested AND/OR conditions, use arrays:
67
-
68
- ```javascript
69
- // SQL: WHERE (status = 'active' AND verified = 1) OR (role = 'admin')
70
- const users = await Odac.Mysql.table('users')
71
- .where([
72
- ['status', 'active'],
73
- ['verified', 1]
74
- ])
75
- .orWhere(['role', 'admin'])
76
- .get()
77
-
78
- // SQL: WHERE status = 'active' AND (role = 'admin' OR role = 'moderator')
79
- const users = await Odac.Mysql.table('users')
80
- .where('status', 'active')
81
- .where([
82
- ['role', 'admin'],
83
- 'OR',
84
- ['role', 'moderator']
85
- ])
86
- .get()
87
-
88
- // Complex nested conditions
89
- // SQL: WHERE (status = 'active' AND (role = 'admin' OR role = 'moderator'))
90
- // AND (verified = 1 OR email_verified = 1)
91
- const users = await Odac.Mysql.table('users')
92
- .where([
93
- ['status', 'active'],
94
- [
95
- ['role', 'admin'],
96
- 'OR',
97
- ['role', 'moderator']
98
- ]
99
- ])
100
- .where([
101
- ['verified', 1],
102
- 'OR',
103
- ['email_verified', 1]
104
- ])
105
- .get()
106
- ```
107
-
108
- ### Ordering and Limiting
109
-
110
- ```javascript
111
- // Order by
112
- const users = await Odac.Mysql.table('users')
113
- .order('created_at', 'desc')
114
- .get()
115
-
116
- // Limit
117
- const users = await Odac.Mysql.table('users')
118
- .limit(10)
119
- .get()
120
-
121
- // Pagination
122
- const page = 2
123
- const perPage = 20
124
- const users = await Odac.Mysql.table('users')
125
- .limit((page - 1) * perPage, perPage)
126
- .get()
127
- ```
128
-
129
- ### Counting Records
130
-
131
- ```javascript
132
- const userCount = await Odac.Mysql.table('users')
133
- .where('status', 'active')
134
- .rows()
135
- ```
136
-
137
- ### Joins
138
-
139
- ```javascript
140
- const orders = await Odac.Mysql.table('orders')
141
- .leftJoin('users', 'orders.user_id', 'users.id')
142
- .select('orders.*', 'users.name', 'users.email')
143
- .get()
144
- ```
145
-
146
- ### Inserting Data
147
-
148
- ```javascript
149
- // Insert single record
150
- const result = await Odac.Mysql.table('users').insert({
151
- name: 'John Doe',
152
- email: 'john@example.com',
153
- status: 'active'
154
- })
155
-
156
- console.log(result.id) // Inserted ID
157
- console.log(result.affected) // Affected rows
158
- ```
159
-
160
- ### Updating Data
161
-
162
- ```javascript
163
- const result = await Odac.Mysql.table('users')
164
- .where('id', 1)
165
- .set({
166
- name: 'Jane Doe',
167
- email: 'jane@example.com'
168
- })
169
-
170
- console.log(result.affected) // Number of updated rows
171
- ```
172
-
173
- ### Deleting Data
174
-
175
- ```javascript
176
- const result = await Odac.Mysql.table('users')
177
- .where('id', 1)
178
- .delete()
179
-
180
- console.log(result.affected) // Number of deleted rows
181
- ```
182
-
183
- ### Raw Queries
184
-
185
- For complex queries, use raw SQL:
186
-
187
- ```javascript
188
- // Raw value in select
189
- const users = await Odac.Mysql.table('users')
190
- .select('id', 'name', Odac.Mysql.raw('COUNT(*) as total'))
191
- .get()
192
-
193
- // Raw query with parameters (SAFE - recommended)
194
- const result = await Odac.Mysql.run('SELECT * FROM users WHERE status = ?', ['active'])
195
- ```
196
-
197
- #### ⚠️ Security Warning: Mysql.raw()
198
-
199
- **CRITICAL**: `Mysql.raw()` bypasses ALL SQL injection protection. Only use with hardcoded, trusted values.
200
-
201
- ```javascript
202
- // ❌ DANGEROUS - Never do this!
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
- .get()
207
-
208
- // ✅ SAFE - Use query builder instead
209
- const userInput = await Odac.request('search')
210
- const users = await Odac.Mysql.table('users')
211
- .where('name', 'LIKE', `%${userInput}%`) // Automatically escaped
212
- .get()
213
-
214
- // ✅ SAFE - Use with hardcoded values only
215
- const users = await Odac.Mysql.table('users')
216
- .select('id', 'name', Odac.Mysql.raw('COUNT(*) as total')) // OK - hardcoded
217
- .get()
218
-
219
- // ✅ SAFE - Use parameterized queries for dynamic values
220
- const status = await Odac.request('status')
221
- const result = await Odac.Mysql.run(
222
- 'SELECT * FROM users WHERE status = ?',
223
- [status] // Automatically escaped
224
- )
225
- ```
226
-
227
- **When to use raw()**:
228
- - Aggregate functions: `COUNT(*)`, `SUM(price)`, `AVG(rating)`
229
- - Database functions: `NOW()`, `CONCAT()`, `DATE_FORMAT()`
230
- - Complex expressions that can't be built with query builder
231
-
232
- **Never use raw() with**:
233
- - User input from forms, URLs, or cookies
234
- - Data from external APIs
235
- - Any untrusted source
236
-
237
- ### Group By
238
-
239
- ```javascript
240
- const stats = await Odac.Mysql.table('orders')
241
- .select('user_id', Odac.Mysql.raw('COUNT(*) as order_count'))
242
- .groupBy('user_id')
243
- .get()
244
- ```
245
-
246
- ### Complete Example
247
-
248
- ```javascript
249
- module.exports = async function (Odac) {
250
- const page = await Odac.request('page') || 1
251
- const perPage = 20
252
- const search = await Odac.request('search')
253
-
254
- // Build query
255
- let query = Odac.Mysql.table('products')
256
- .select('id', 'name', 'price', 'stock')
257
- .where('status', 'active')
258
-
259
- // Add search filter
260
- if (search) {
261
- query = query.where('name', 'LIKE', `%${search}%`)
262
- }
263
-
264
- // Get products
265
- const products = await query
266
- .order('created_at', 'desc')
267
- .limit((page - 1) * perPage, perPage)
268
- .get()
269
-
270
- // Get total count
271
- const totalCount = await Odac.Mysql.table('products')
272
- .where('status', 'active')
273
- .rows()
274
-
275
- Odac.set({
276
- products: products,
277
- currentPage: page,
278
- totalPages: Math.ceil(totalCount / perPage)
279
- })
280
-
281
- Odac.View.set({
282
- skeleton: 'main',
283
- content: 'products.list'
284
- })
285
- }
286
- ```
287
-
288
- ### Best Practices
289
-
290
- 1. **Always use the query builder** - It protects against SQL injection
291
- 2. **Never use Mysql.raw() with user input** - Only use with hardcoded values
292
- 3. **Use parameterized queries** - When using `Mysql.run()`, always pass parameters as array
293
- 4. **Use async/await** - All database methods are asynchronous
294
- 5. **Handle errors** - Wrap database calls in try/catch blocks
295
- 6. **Limit results** - Use `.limit()` to prevent loading too much data
296
- 7. **Select only needed columns** - Use `.select()` instead of selecting all columns
297
-
298
- ### Error Handling
299
-
300
- ```javascript
301
- module.exports = async function (Odac) {
302
- try {
303
- const user = await Odac.Mysql.table('users')
304
- .where('id', await Odac.request('id'))
305
- .first()
306
-
307
- if (!user) {
308
- Odac.set('error', 'User not found')
309
- } else {
310
- Odac.set('user', user)
311
- }
312
- } catch (error) {
313
- console.error('Database error:', error)
314
- Odac.set('error', 'An error occurred')
315
- }
316
-
317
- Odac.View.set({
318
- skeleton: 'main',
319
- content: 'user.profile'
320
- })
321
- }
322
- ```