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.
- package/.github/workflows/auto-pr-description.yml +3 -1
- package/CHANGELOG.md +127 -0
- package/README.md +39 -36
- package/bin/odac.js +1 -31
- package/client/odac.js +871 -994
- package/docs/backend/01-overview/03-development-server.md +7 -7
- package/docs/backend/02-structure/01-typical-project-layout.md +1 -0
- package/docs/backend/03-config/00-configuration-overview.md +9 -0
- package/docs/backend/03-config/01-database-connection.md +1 -1
- package/docs/backend/04-routing/02-controller-less-view-routes.md +9 -3
- package/docs/backend/04-routing/09-websocket.md +29 -0
- package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +2 -0
- package/docs/backend/05-controllers/03-controller-classes.md +27 -41
- package/docs/backend/05-forms/01-custom-forms.md +103 -95
- package/docs/backend/05-forms/02-automatic-database-insert.md +21 -21
- package/docs/backend/07-views/02-rendering-a-view.md +1 -1
- package/docs/backend/07-views/03-variables.md +5 -5
- package/docs/backend/07-views/04-request-data.md +1 -1
- package/docs/backend/07-views/08-backend-javascript.md +1 -1
- package/docs/backend/08-database/01-getting-started.md +100 -0
- package/docs/backend/08-database/02-basics.md +136 -0
- package/docs/backend/08-database/03-advanced.md +84 -0
- package/docs/backend/08-database/04-migrations.md +48 -0
- package/docs/backend/09-validation/01-the-validator-service.md +1 -0
- package/docs/backend/10-authentication/03-register.md +8 -1
- package/docs/backend/10-authentication/04-odac-register-forms.md +46 -46
- package/docs/backend/10-authentication/05-session-management.md +1 -1
- package/docs/backend/10-authentication/06-odac-login-forms.md +48 -48
- package/docs/backend/10-authentication/07-magic-links.md +134 -0
- package/docs/backend/11-mail/01-the-mail-service.md +118 -28
- package/docs/backend/12-streaming/01-streaming-overview.md +2 -2
- package/docs/backend/13-utilities/01-odac-var.md +7 -7
- package/docs/backend/13-utilities/02-ipc.md +73 -0
- package/docs/frontend/01-overview/01-introduction.md +5 -1
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +1 -1
- package/docs/index.json +16 -124
- package/eslint.config.mjs +5 -47
- package/package.json +9 -4
- package/src/Auth.js +362 -104
- package/src/Config.js +7 -2
- package/src/Database.js +188 -0
- package/src/Ipc.js +330 -0
- package/src/Mail.js +408 -37
- package/src/Odac.js +65 -9
- package/src/Request.js +70 -48
- package/src/Route/Cron.js +4 -1
- package/src/Route/Internal.js +214 -11
- package/src/Route/Middleware.js +7 -2
- package/src/Route.js +106 -26
- package/src/Server.js +80 -11
- package/src/Storage.js +165 -0
- package/src/Validator.js +94 -2
- package/src/View/Form.js +193 -17
- package/src/View.js +46 -1
- package/src/WebSocket.js +18 -3
- package/template/config.json +1 -1
- package/template/route/www.js +12 -10
- package/test/core/{Candy.test.js → Odac.test.js} +2 -2
- package/docs/backend/08-database/01-database-connection.md +0 -99
- package/docs/backend/08-database/02-using-mysql.md +0 -322
- package/src/Mysql.js +0 -575
|
@@ -1,42 +1,132 @@
|
|
|
1
1
|
## ✉️ The `Mail` Service
|
|
2
2
|
|
|
3
|
-
The `Odac.Mail` service
|
|
3
|
+
The `Odac.Mail` service provides a fluent, chainable interface to send emails. **It is a zero-config service** designed to work exclusively with the **ODAC Core** server. If you are running Odac, the mail service works out of the box without any additional setup.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### How to Send an Email
|
|
6
6
|
|
|
7
|
-
`Odac.Mail.
|
|
7
|
+
The `Odac.Mail` class uses a builder pattern. You start by instantiating it with a template name, set various properties, and finally call `send()`.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
* `subject`: The subject line for your email.
|
|
11
|
-
* `htmlBody`: The main content of your email. You can even use HTML tags to make it look fancy!
|
|
9
|
+
#### Syntax
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
```javascript
|
|
12
|
+
await Odac.Mail('template_name')
|
|
13
|
+
.from('sender@example.com', 'Sender Name')
|
|
14
|
+
.to('recipient@example.com')
|
|
15
|
+
.subject('Your Subject Here')
|
|
16
|
+
.send({
|
|
17
|
+
variable1: 'value1',
|
|
18
|
+
variable2: 'value2'
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
* **Template**: The constructor takes the name of the HTML template file located in `view/mail/`.
|
|
23
|
+
* **Data**: The object passed to `send()` contains key-value pairs that replace `{key}` placeholders in your HTML template.
|
|
14
24
|
|
|
15
|
-
#### Example:
|
|
25
|
+
#### Example: Contact Form Controller
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
Here is a complete example of how to use the Mail service inside a controller:
|
|
18
28
|
|
|
19
29
|
```javascript
|
|
20
30
|
module.exports = async function (Odac) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
const { name, email, message } = Odac.Request.post;
|
|
32
|
+
|
|
33
|
+
// 1. Validate Input
|
|
34
|
+
if (!name || !email || !message) {
|
|
35
|
+
return { error: 'Please fill in all fields.' };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// 2. Prepare and Send Email
|
|
40
|
+
const result = await Odac.Mail('contact_form_notification')
|
|
41
|
+
.from('system@myapp.com', 'My App System')
|
|
42
|
+
.to('admin@myapp.com')
|
|
43
|
+
.subject('New Contact Form Submission')
|
|
44
|
+
.send({
|
|
45
|
+
user_name: name,
|
|
46
|
+
user_email: email,
|
|
47
|
+
user_message: message,
|
|
48
|
+
timestamp: new Date().toISOString()
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 3. Check Result
|
|
52
|
+
if (result) {
|
|
53
|
+
return { success: true, message: 'Thank you! We received your message.' };
|
|
54
|
+
} else {
|
|
55
|
+
return { error: 'Failed to send email.' };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error(e);
|
|
60
|
+
return { error: 'An unexpected error occurred.' };
|
|
61
|
+
}
|
|
39
62
|
}
|
|
40
63
|
```
|
|
41
64
|
|
|
42
|
-
|
|
65
|
+
### Template File
|
|
66
|
+
|
|
67
|
+
Create your HTML template in `view/mail/contact_form_notification.html`:
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<h1>New Contact Message</h1>
|
|
71
|
+
<p><strong>From:</strong> {user_name} ({user_email})</p>
|
|
72
|
+
<p><strong>Time:</strong> {timestamp}</p>
|
|
73
|
+
<hr>
|
|
74
|
+
<p>{user_message}</p>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Sending Plain Text or Raw HTML (No Template)
|
|
78
|
+
|
|
79
|
+
You can send emails **without creating a template file** by using the `.text()` or `.html()` methods directly. This is useful for simple notifications or dynamic content.
|
|
80
|
+
|
|
81
|
+
**Note:** If you provide both a template name AND manual content, the template will take precedence.
|
|
82
|
+
|
|
83
|
+
#### 1. Plain Text Email
|
|
84
|
+
|
|
85
|
+
Use the `.text()` method to send a simple, text-only email. The `Content-Type` will automatically be set to `text/plain`.
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
await Odac.Mail() // No template name required
|
|
89
|
+
.to('recipient@example.com')
|
|
90
|
+
.subject('System Alert')
|
|
91
|
+
.text('Server load is critical. Please check immediately.')
|
|
92
|
+
.send();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> **Note:** Plain text does not support HTML tags like links (`<a href>`). If you write a URL (e.g., `https://odac.run`), most email clients will automatically make it clickable.
|
|
96
|
+
|
|
97
|
+
#### 2. Raw HTML Email
|
|
98
|
+
|
|
99
|
+
Use the `.html()` method to send an HTML email without a file.
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
await Odac.Mail()
|
|
103
|
+
.to('user@example.com')
|
|
104
|
+
.subject('Welcome')
|
|
105
|
+
.html('<h1>Welcome!</h1><p>Please <a href="https://example.com">click here</a>.</p>')
|
|
106
|
+
.send();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If you use `.html()`, the system will automatically generate a plain-text version of your email by stripping the tags, just like it does for templates.
|
|
110
|
+
|
|
111
|
+
### Advanced Usage
|
|
112
|
+
|
|
113
|
+
#### Custom Headers
|
|
114
|
+
|
|
115
|
+
You can inject custom headers into the email using the `header()` method:
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
.header({
|
|
119
|
+
'X-Custom-Header': 'CustomValue',
|
|
120
|
+
'Reply-To': 'support@example.com'
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### Chainable Methods
|
|
125
|
+
|
|
126
|
+
* `from(email, name)`: Sets the sender.
|
|
127
|
+
* `to(email)`: Sets the recipient.
|
|
128
|
+
* `subject(text)`: Sets the subject line.
|
|
129
|
+
* `text(content)`: Sets the plain text body (used if no template is provided).
|
|
130
|
+
* `html(content)`: Sets the HTML body (used if no template is provided).
|
|
131
|
+
* `header(object)`: Merges custom headers.
|
|
132
|
+
* `send(data)`: Compiles the template with `data`, connects to the Odac Core, and sends the email payload. Returns a `Promise`.
|
|
@@ -91,7 +91,7 @@ module.exports = async (Odac) => {
|
|
|
91
91
|
// controller/users/get/index.js
|
|
92
92
|
module.exports = async (Odac) => {
|
|
93
93
|
Odac.stream(async function* () {
|
|
94
|
-
const users = await Odac.
|
|
94
|
+
const users = await Odac.DB.users.get()
|
|
95
95
|
|
|
96
96
|
for (const user of users) {
|
|
97
97
|
yield user
|
|
@@ -194,7 +194,7 @@ module.exports = async (Odac) => {
|
|
|
194
194
|
let hasMore = true
|
|
195
195
|
|
|
196
196
|
while (hasMore) {
|
|
197
|
-
const posts = await Odac.
|
|
197
|
+
const posts = await Odac.DB.posts
|
|
198
198
|
.limit(10)
|
|
199
199
|
.offset((page - 1) * 10)
|
|
200
200
|
.get()
|
|
@@ -313,7 +313,7 @@ module.exports = async function(Odac) {
|
|
|
313
313
|
const profileSlug = Odac.Var(username).slug()
|
|
314
314
|
|
|
315
315
|
// Save user
|
|
316
|
-
await Odac.
|
|
316
|
+
await Odac.DB.users.insert({
|
|
317
317
|
email: email,
|
|
318
318
|
username: username,
|
|
319
319
|
password: hashedPassword,
|
|
@@ -332,7 +332,7 @@ module.exports = async function(Odac) {
|
|
|
332
332
|
const password = Odac.Request.post('password')
|
|
333
333
|
|
|
334
334
|
// Find user
|
|
335
|
-
const user = await Odac.
|
|
335
|
+
const user = await Odac.DB.users
|
|
336
336
|
.where('email', email)
|
|
337
337
|
.first()
|
|
338
338
|
|
|
@@ -369,19 +369,19 @@ module.exports = async function(Odac) {
|
|
|
369
369
|
const slug = Odac.Var(title).slug()
|
|
370
370
|
|
|
371
371
|
// Check if slug exists
|
|
372
|
-
const exists = await Odac.
|
|
372
|
+
const exists = await Odac.DB.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 Odac.
|
|
379
|
+
await Odac.DB.posts.insert({
|
|
380
380
|
title: title,
|
|
381
381
|
slug: uniqueSlug
|
|
382
382
|
})
|
|
383
383
|
} else {
|
|
384
|
-
await Odac.
|
|
384
|
+
await Odac.DB.posts.insert({
|
|
385
385
|
title: title,
|
|
386
386
|
slug: slug
|
|
387
387
|
})
|
|
@@ -464,7 +464,7 @@ module.exports = async function(Odac) {
|
|
|
464
464
|
const encryptedCard = Odac.Var(creditCard).encrypt()
|
|
465
465
|
|
|
466
466
|
// Save encrypted data
|
|
467
|
-
await Odac.
|
|
467
|
+
await Odac.DB.payments.insert({
|
|
468
468
|
user_id: Odac.Auth.id(),
|
|
469
469
|
card: encryptedCard
|
|
470
470
|
})
|
|
@@ -474,7 +474,7 @@ module.exports = async function(Odac) {
|
|
|
474
474
|
|
|
475
475
|
// Later, to retrieve and decrypt
|
|
476
476
|
module.exports = async function(Odac) {
|
|
477
|
-
const payment = await Odac.
|
|
477
|
+
const payment = await Odac.DB.payments
|
|
478
478
|
.where('user_id', Odac.Auth.id())
|
|
479
479
|
.first()
|
|
480
480
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# IPC (Inter-Process Communication)
|
|
2
|
+
|
|
3
|
+
Odac provides a built-in IPC system that allows your application workers to communicate with each other, share data, and sync states. It abstracts the underlying driver, allowing you to switch between in-memory (cluster) and Redis-based communication with a simple configuration change.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
To configure the IPC system, update your project configuration. The default driver is `memory`.
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
// config.js
|
|
11
|
+
module.exports = {
|
|
12
|
+
// ...
|
|
13
|
+
database: {
|
|
14
|
+
// ... other dbs
|
|
15
|
+
redis: {
|
|
16
|
+
default: {
|
|
17
|
+
// node-redis connection options
|
|
18
|
+
// e.g., url: 'redis://user:pass@host:port'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
ipc: {
|
|
23
|
+
driver: 'memory', // Options: 'memory' or 'redis'
|
|
24
|
+
redis: 'default' // The name of the redis connection to use (if driver is 'redis')
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Drivers
|
|
30
|
+
|
|
31
|
+
- **memory**: Uses Node.js `cluster` IPC. Ideal for single-server deployments. Data is stored in the Main/Primary process RAM and shared across workers via `process.send`.
|
|
32
|
+
- **redis**: Uses a Redis server. Ideal for multi-server/horizontal scaling deployments. Requires a configured Redis connection.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
You can access the IPC module via `Odac.Ipc`.
|
|
37
|
+
|
|
38
|
+
### Data Storage (Key-Value)
|
|
39
|
+
|
|
40
|
+
You can set, get, and delete values. These values are shared across all workers.
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
// Set a value (with optional TTL in seconds)
|
|
44
|
+
await Odac.Ipc.set('maintenance_mode', true);
|
|
45
|
+
await Odac.Ipc.set('temp_cache', { foo: 'bar' }, 60);
|
|
46
|
+
|
|
47
|
+
// Get a value
|
|
48
|
+
const isMaintenance = await Odac.Ipc.get('maintenance_mode');
|
|
49
|
+
|
|
50
|
+
// Delete a value
|
|
51
|
+
await Odac.Ipc.del('maintenance_mode');
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> [!NOTE]
|
|
55
|
+
> `get`, `set`, and `del` are asynchronous and return Promises.
|
|
56
|
+
|
|
57
|
+
### Pub/Sub (Messaging)
|
|
58
|
+
|
|
59
|
+
You can publish messages to channels and subscribe to them in other workers.
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
// Subscribe to a channel
|
|
63
|
+
// Best practice: Subscribe in your app initialization or a Service Provider
|
|
64
|
+
await Odac.Ipc.subscribe('chat:global', (message) => {
|
|
65
|
+
console.log('New chat message:', message);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Publish a message
|
|
69
|
+
await Odac.Ipc.publish('chat:global', { user: 'Emre', text: 'Hello World' });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> [!TIP]
|
|
73
|
+
> When using `memory` driver, the subscription listener is registered in the current worker. When a message is published, it goes to the Main process and is then broadcasted to all subscribed workers.
|
|
@@ -131,7 +131,11 @@ odac.get('/api/users', function(data) {
|
|
|
131
131
|
## Other Utility Functions
|
|
132
132
|
|
|
133
133
|
- **`Odac.client()`**: Returns a unique client identifier from a cookie.
|
|
134
|
-
- **`Odac.data()`**: Returns data from the
|
|
134
|
+
- **`Odac.data(key)`**: Returns shared data passed from the backend via `Odac.share`. You can get the full data object or a specific key:
|
|
135
|
+
```javascript
|
|
136
|
+
let allData = odac.data();
|
|
137
|
+
let user = odac.data('user'); // Returns null if not exists
|
|
138
|
+
```
|
|
135
139
|
- **`Odac.page()`**: Returns the identifier of the current page. This is the controller name (e.g., `'user'`) or view name (e.g., `'dashboard'`) set by the backend. Use this to conditionally run code for specific pages.
|
|
136
140
|
- **`Odac.storage()`**: A wrapper for `localStorage`.
|
|
137
141
|
```javascript
|
|
@@ -347,7 +347,7 @@ Odac.action({
|
|
|
347
347
|
**Page Identifier Rules:**
|
|
348
348
|
- **With controller**: Uses controller filename (e.g., `user.js` → `'user'`)
|
|
349
349
|
- **With view object**: Uses `content` or `all` value (e.g., `{content: 'dashboard'}` → `'dashboard'`)
|
|
350
|
-
- Accessible via `Odac.page()`
|
|
350
|
+
- Accessible via `Odac.page()`
|
|
351
351
|
|
|
352
352
|
## Server Variables
|
|
353
353
|
|
package/docs/index.json
CHANGED
|
@@ -1,124 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"server": [
|
|
3
|
-
{
|
|
4
|
-
"file": "01-installation",
|
|
5
|
-
"title": "Installation",
|
|
6
|
-
"children": [
|
|
7
|
-
{
|
|
8
|
-
"file": "01-quick-install.md",
|
|
9
|
-
"title": "Quick Install"
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
"file": "02-manual-installation-via-npm.md",
|
|
13
|
-
"title": "Manual Installation (via NPM)"
|
|
14
|
-
}
|
|
15
|
-
]
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"file": "02-get-started",
|
|
19
|
-
"title": "Getting Started",
|
|
20
|
-
"children": [
|
|
21
|
-
{
|
|
22
|
-
"file": "01-core-concepts.md",
|
|
23
|
-
"title": "Core Concepts"
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"file": "02-basic-commands.md",
|
|
27
|
-
"title": "Basic Commands"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"file": "03-cli-reference.md",
|
|
31
|
-
"title": "CLI Reference"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"file": "04-cli-quick-reference.md",
|
|
35
|
-
"title": "CLI Quick Reference"
|
|
36
|
-
}
|
|
37
|
-
]
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"file": "03-service",
|
|
41
|
-
"title": "Running a Service",
|
|
42
|
-
"children": [
|
|
43
|
-
{
|
|
44
|
-
"file": "01-start-a-new-service.md",
|
|
45
|
-
"title": "Start a New Service"
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"file": "02-delete-a-service.md",
|
|
49
|
-
"title": "Delete a Service"
|
|
50
|
-
}
|
|
51
|
-
]
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"file": "04-web",
|
|
55
|
-
"title": "Managing Websites",
|
|
56
|
-
"children": [
|
|
57
|
-
{
|
|
58
|
-
"file": "01-create-a-website.md",
|
|
59
|
-
"title": "Create a Website"
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"file": "02-list-websites.md",
|
|
63
|
-
"title": "List Websites"
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"file": "03-delete-a-website.md",
|
|
67
|
-
"title": "Delete a Website"
|
|
68
|
-
}
|
|
69
|
-
]
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
"file": "05-subdomain",
|
|
73
|
-
"title": "Managing Subdomains",
|
|
74
|
-
"children": [
|
|
75
|
-
{
|
|
76
|
-
"file": "01-create-a-subdomain.md",
|
|
77
|
-
"title": "Create a Subdomain"
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"file": "02-list-subdomains.md",
|
|
81
|
-
"title": "List Subdomains"
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"file": "03-delete-a-subdomain.md",
|
|
85
|
-
"title": "Delete a Subdomain"
|
|
86
|
-
}
|
|
87
|
-
]
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
"file": "06-ssl",
|
|
91
|
-
"title": "Managing SSL",
|
|
92
|
-
"children": [
|
|
93
|
-
{
|
|
94
|
-
"file": "01-renew-an-ssl-certificate.md",
|
|
95
|
-
"title": "Renew an SSL Certificate"
|
|
96
|
-
}
|
|
97
|
-
]
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
"file": "07-mail",
|
|
101
|
-
"title": "Managing Mail",
|
|
102
|
-
"children": [
|
|
103
|
-
{
|
|
104
|
-
"file": "01-create-a-mail-account.md",
|
|
105
|
-
"title": "Create a Mail Account"
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
"file": "02-delete-a-mail-account.md",
|
|
109
|
-
"title": "Delete a Mail Account"
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"file": "03-list-mail-accounts.md",
|
|
113
|
-
"title": "List Mail Accounts"
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
"file": "04-change-account-password.md",
|
|
117
|
-
"title": "Change Account Password"
|
|
118
|
-
}
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
],
|
|
122
2
|
"backend": [
|
|
123
3
|
{
|
|
124
4
|
"file": "01-overview",
|
|
@@ -329,12 +209,20 @@
|
|
|
329
209
|
"title": "Database",
|
|
330
210
|
"children": [
|
|
331
211
|
{
|
|
332
|
-
"file": "01-
|
|
333
|
-
"title": "
|
|
212
|
+
"file": "01-getting-started.md",
|
|
213
|
+
"title": "Getting Started"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"file": "02-basics.md",
|
|
217
|
+
"title": "Query Basics"
|
|
334
218
|
},
|
|
335
219
|
{
|
|
336
|
-
"file": "
|
|
337
|
-
"title": "
|
|
220
|
+
"file": "03-advanced.md",
|
|
221
|
+
"title": "Advanced Queries"
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"file": "04-migrations.md",
|
|
225
|
+
"title": "Code-First Migrations"
|
|
338
226
|
}
|
|
339
227
|
]
|
|
340
228
|
},
|
|
@@ -375,6 +263,10 @@
|
|
|
375
263
|
{
|
|
376
264
|
"file": "06-odac-login-forms.md",
|
|
377
265
|
"title": "Odac Login Forms (Zero-Config)"
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"file": "07-magic-links.md",
|
|
269
|
+
"title": "Magic Links"
|
|
378
270
|
}
|
|
379
271
|
]
|
|
380
272
|
},
|
package/eslint.config.mjs
CHANGED
|
@@ -6,49 +6,7 @@ import prettierConfig from 'eslint-config-prettier'
|
|
|
6
6
|
|
|
7
7
|
export default defineConfig([
|
|
8
8
|
{
|
|
9
|
-
files: ['
|
|
10
|
-
ignores: ['server/src/Odac.js'],
|
|
11
|
-
languageOptions: {
|
|
12
|
-
globals: {
|
|
13
|
-
...globals.node,
|
|
14
|
-
Odac: 'readonly',
|
|
15
|
-
__: 'readonly'
|
|
16
|
-
},
|
|
17
|
-
sourceType: 'script'
|
|
18
|
-
},
|
|
19
|
-
plugins: {
|
|
20
|
-
js,
|
|
21
|
-
prettier: prettierPlugin
|
|
22
|
-
},
|
|
23
|
-
rules: {
|
|
24
|
-
...js.configs.recommended.rules,
|
|
25
|
-
...prettierConfig.rules,
|
|
26
|
-
'prettier/prettier': 'error'
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
files: ['server/src/Odac.js'],
|
|
31
|
-
languageOptions: {
|
|
32
|
-
globals: {
|
|
33
|
-
...globals.node,
|
|
34
|
-
log: 'readonly',
|
|
35
|
-
__: 'readonly'
|
|
36
|
-
},
|
|
37
|
-
sourceType: 'script'
|
|
38
|
-
},
|
|
39
|
-
plugins: {
|
|
40
|
-
js,
|
|
41
|
-
prettier: prettierPlugin
|
|
42
|
-
},
|
|
43
|
-
rules: {
|
|
44
|
-
...js.configs.recommended.rules,
|
|
45
|
-
...prettierConfig.rules,
|
|
46
|
-
'prettier/prettier': 'error'
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
files: ['framework/**/*.js'],
|
|
51
|
-
ignores: ['framework/web/**/*.js'],
|
|
9
|
+
files: ['src/**/*.js'],
|
|
52
10
|
languageOptions: {
|
|
53
11
|
globals: {
|
|
54
12
|
...globals.node,
|
|
@@ -68,7 +26,7 @@ export default defineConfig([
|
|
|
68
26
|
}
|
|
69
27
|
},
|
|
70
28
|
{
|
|
71
|
-
files: ['
|
|
29
|
+
files: ['client/**/*.js'],
|
|
72
30
|
languageOptions: {
|
|
73
31
|
globals: {...globals.browser},
|
|
74
32
|
sourceType: 'module'
|
|
@@ -79,8 +37,8 @@ export default defineConfig([
|
|
|
79
37
|
}
|
|
80
38
|
},
|
|
81
39
|
{
|
|
82
|
-
files: ['
|
|
83
|
-
ignores: ['
|
|
40
|
+
files: ['template/**/*.js'],
|
|
41
|
+
ignores: ['template/public/**/*.js'],
|
|
84
42
|
languageOptions: {
|
|
85
43
|
globals: {
|
|
86
44
|
...globals.node,
|
|
@@ -99,7 +57,7 @@ export default defineConfig([
|
|
|
99
57
|
}
|
|
100
58
|
},
|
|
101
59
|
{
|
|
102
|
-
files: ['
|
|
60
|
+
files: ['template/public/**/*.js'],
|
|
103
61
|
languageOptions: {
|
|
104
62
|
globals: {
|
|
105
63
|
...globals.browser,
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"email": "mail@emre.red",
|
|
8
8
|
"url": "https://emre.red"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.1.0",
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=18.0.0"
|
|
@@ -18,12 +18,16 @@
|
|
|
18
18
|
"main": "index.js",
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
21
|
-
"url": "https://github.com/odac-run/
|
|
21
|
+
"url": "https://github.com/odac-run/odac.js.git"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"axios": "^1.7.9",
|
|
25
25
|
"bcrypt": "^5.1.1",
|
|
26
|
-
"
|
|
26
|
+
"knex": "^3.1.0",
|
|
27
|
+
"lmdb": "^3.4.4",
|
|
28
|
+
"mysql2": "^3.16.0",
|
|
29
|
+
"pg": "^8.16.3",
|
|
30
|
+
"redis": "^5.10.0"
|
|
27
31
|
},
|
|
28
32
|
"devDependencies": {
|
|
29
33
|
"@eslint/js": "^9.33.0",
|
|
@@ -42,7 +46,8 @@
|
|
|
42
46
|
"jest": "^30.0.5",
|
|
43
47
|
"lint-staged": "^16.1.5",
|
|
44
48
|
"prettier": "3.6.2",
|
|
45
|
-
"semantic-release": "^25.0.2"
|
|
49
|
+
"semantic-release": "^25.0.2",
|
|
50
|
+
"sqlite3": "^5.1.7"
|
|
46
51
|
},
|
|
47
52
|
"scripts": {
|
|
48
53
|
"lint": "eslint .",
|