odac 1.1.0 → 1.2.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 (113) hide show
  1. package/.agent/rules/coding.md +27 -0
  2. package/.agent/rules/memory.md +33 -0
  3. package/.agent/rules/project.md +30 -0
  4. package/.agent/rules/workflow.md +16 -0
  5. package/.github/workflows/release.yml +42 -1
  6. package/.github/workflows/test-coverage.yml +6 -5
  7. package/.github/workflows/test-publish.yml +36 -0
  8. package/.husky/pre-commit +10 -0
  9. package/.husky/pre-push +13 -0
  10. package/.releaserc.js +3 -3
  11. package/CHANGELOG.md +67 -0
  12. package/README.md +16 -0
  13. package/bin/odac.js +182 -40
  14. package/client/odac.js +10 -4
  15. package/docs/backend/01-overview/03-development-server.md +38 -45
  16. package/docs/backend/02-structure/01-typical-project-layout.md +59 -26
  17. package/docs/backend/03-config/00-configuration-overview.md +6 -6
  18. package/docs/backend/03-config/01-database-connection.md +2 -2
  19. package/docs/backend/03-config/02-static-route-mapping-optional.md +1 -1
  20. package/docs/backend/03-config/03-request-timeout.md +1 -1
  21. package/docs/backend/03-config/04-environment-variables.md +4 -4
  22. package/docs/backend/03-config/05-early-hints.md +2 -2
  23. package/docs/backend/04-routing/03-api-and-data-routes.md +18 -0
  24. package/docs/backend/04-routing/07-cron-jobs.md +17 -1
  25. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +48 -3
  26. package/docs/backend/05-controllers/03-controller-classes.md +40 -20
  27. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +17 -0
  28. package/docs/backend/07-views/10-styling-and-tailwind.md +93 -0
  29. package/docs/backend/08-database/01-getting-started.md +2 -2
  30. package/docs/backend/10-authentication/03-register.md +1 -1
  31. package/docs/backend/10-authentication/04-odac-register-forms.md +2 -2
  32. package/docs/backend/10-authentication/05-session-management.md +15 -1
  33. package/docs/backend/10-authentication/06-odac-login-forms.md +2 -2
  34. package/docs/backend/10-authentication/07-magic-links.md +1 -1
  35. package/docs/index.json +5 -1
  36. package/jest.config.js +1 -1
  37. package/package.json +9 -5
  38. package/src/Auth.js +58 -23
  39. package/src/Config.js +7 -7
  40. package/src/Env.js +3 -1
  41. package/src/Ipc.js +7 -0
  42. package/src/Lang.js +9 -2
  43. package/src/Odac.js +44 -35
  44. package/src/Request.js +1 -1
  45. package/src/Route/Cron.js +58 -17
  46. package/src/Route/Internal.js +1 -1
  47. package/src/Route.js +282 -99
  48. package/src/Server.js +40 -3
  49. package/src/Storage.js +4 -0
  50. package/src/Token.js +6 -4
  51. package/src/Validator.js +1 -1
  52. package/src/Var.js +22 -6
  53. package/src/View/EarlyHints.js +43 -33
  54. package/src/View/Form.js +17 -11
  55. package/src/View.js +62 -6
  56. package/template/package.json +3 -1
  57. package/template/view/content/home.html +3 -3
  58. package/template/view/head/main.html +2 -2
  59. package/test/Client.test.js +168 -0
  60. package/test/Config.test.js +112 -0
  61. package/test/Lang.test.js +92 -0
  62. package/test/Odac.test.js +86 -0
  63. package/test/{framework/middleware.test.js → Route/Middleware.test.js} +2 -2
  64. package/test/{framework/Route.test.js → Route.test.js} +1 -1
  65. package/test/{framework/View → View}/EarlyHints.test.js +1 -1
  66. package/test/{framework/WebSocket.test.js → WebSocket.test.js} +2 -2
  67. package/test/scripts/check-coverage.js +4 -4
  68. package/test/cli/Cli.test.js +0 -36
  69. package/test/core/Commands.test.js +0 -538
  70. package/test/core/Config.test.js +0 -1432
  71. package/test/core/Lang.test.js +0 -250
  72. package/test/core/Odac.test.js +0 -234
  73. package/test/core/Process.test.js +0 -156
  74. package/test/server/Api.test.js +0 -647
  75. package/test/server/DNS.test.js +0 -2050
  76. package/test/server/DNS.test.js.bak +0 -2084
  77. package/test/server/Hub.test.js +0 -497
  78. package/test/server/Log.test.js +0 -73
  79. package/test/server/Mail.account.test_.js +0 -460
  80. package/test/server/Mail.init.test_.js +0 -411
  81. package/test/server/Mail.test_.js +0 -1340
  82. package/test/server/SSL.test_.js +0 -1491
  83. package/test/server/Server.test.js +0 -765
  84. package/test/server/Service.test_.js +0 -1127
  85. package/test/server/Subdomain.test.js +0 -440
  86. package/test/server/Web/Firewall.test.js +0 -175
  87. package/test/server/Web/Proxy.test.js +0 -397
  88. package/test/server/Web.test.js +0 -1494
  89. package/test/server/__mocks__/acme-client.js +0 -17
  90. package/test/server/__mocks__/bcrypt.js +0 -50
  91. package/test/server/__mocks__/child_process.js +0 -389
  92. package/test/server/__mocks__/crypto.js +0 -432
  93. package/test/server/__mocks__/fs.js +0 -450
  94. package/test/server/__mocks__/globalOdac.js +0 -227
  95. package/test/server/__mocks__/http.js +0 -575
  96. package/test/server/__mocks__/https.js +0 -272
  97. package/test/server/__mocks__/index.js +0 -249
  98. package/test/server/__mocks__/mail/server.js +0 -100
  99. package/test/server/__mocks__/mail/smtp.js +0 -31
  100. package/test/server/__mocks__/mailparser.js +0 -81
  101. package/test/server/__mocks__/net.js +0 -369
  102. package/test/server/__mocks__/node-forge.js +0 -328
  103. package/test/server/__mocks__/os.js +0 -320
  104. package/test/server/__mocks__/path.js +0 -291
  105. package/test/server/__mocks__/selfsigned.js +0 -8
  106. package/test/server/__mocks__/server/src/mail/server.js +0 -100
  107. package/test/server/__mocks__/server/src/mail/smtp.js +0 -31
  108. package/test/server/__mocks__/smtp-server.js +0 -106
  109. package/test/server/__mocks__/sqlite3.js +0 -394
  110. package/test/server/__mocks__/testFactories.js +0 -299
  111. package/test/server/__mocks__/testHelpers.js +0 -363
  112. package/test/server/__mocks__/tls.js +0 -229
  113. /package/template/{config.json → odac.json} +0 -0
package/client/odac.js CHANGED
@@ -526,7 +526,10 @@ if (typeof window !== 'undefined') {
526
526
  let errorEl = formElement.querySelector(`[odac-form-error="${name}"]`)
527
527
  if (errorEl) {
528
528
  errorEl.innerHTML = this.textToHtml(message)
529
- errorEl.style.cssText = 'display:block;color:#dc3545;font-size:0.875rem;margin-top:0.25rem'
529
+ errorEl.style.display = 'block'
530
+ if (!errorEl.style.color) errorEl.style.color = '#dc3545'
531
+ if (!errorEl.style.fontSize) errorEl.style.fontSize = '0.875rem'
532
+ if (!errorEl.style.marginTop) errorEl.style.marginTop = '0.25rem'
530
533
  } else {
531
534
  const inputEl = formElement.querySelector(`*[name="${name}"]`)
532
535
  if (inputEl) {
@@ -545,7 +548,10 @@ if (typeof window !== 'undefined') {
545
548
  const errorEl = document.createElement('div')
546
549
  errorEl.setAttribute('odac-form-error', name)
547
550
  errorEl.innerHTML = this.textToHtml(message)
548
- errorEl.style.cssText = 'display:block;color:#dc3545;background-color:#f8d7da;border:1px solid #f5c2c7;border-radius:0.375rem;padding:0.75rem 1rem;margin-bottom:1rem;font-size:0.875rem'
551
+ errorEl.style.display = 'block'
552
+ errorEl.style.color = '#dc3545'
553
+ errorEl.style.fontSize = '0.875rem'
554
+ errorEl.style.marginBottom = '1rem'
549
555
  formElement.insertBefore(errorEl, formElement.firstChild)
550
556
  }
551
557
  }
@@ -956,8 +962,8 @@ if (typeof window !== 'undefined') {
956
962
  socket = new OdacWebSocket(wsUrl, protocols, options)
957
963
  socket.on('open', () => broadcast('open'))
958
964
  socket.on('message', data => broadcast('message', data))
959
- socket.on('close', e => broadcast('close', e))
960
- socket.on('error', e => broadcast('error', e))
965
+ socket.on('close', e => broadcast('close', {code: e?.code, reason: e?.reason, wasClean: e?.wasClean}))
966
+ socket.on('error', e => broadcast('error', {message: e?.message || 'WebSocket error'}))
961
967
  } else if (socket.connected) {
962
968
  // If already connected, notify the new port immediately
963
969
  port.postMessage({type: 'open'})
@@ -1,79 +1,72 @@
1
- ## 🚀 Development Server
1
+ ## 🚀 CLI Commands & Deployment
2
2
 
3
- ODAC provides a built-in development server that allows you to test your website locally without running the full ODAC server infrastructure.
3
+ ODAC comes with a powerful CLI to manage your project's lifecycle, from development to production.
4
4
 
5
- ### Quick Start
5
+ ### Development Mode (`dev`)
6
6
 
7
- Navigate to your website directory and run one of these commands:
7
+ Start the development server with **hot-reloading** and **automatic Tailwind CSS compilation**:
8
8
 
9
9
  ```bash
10
10
  # Using npm script
11
- npm start
11
+ npm run dev
12
12
 
13
13
  # Using ODAC directly
14
- odac framework run
14
+ npx odac dev
15
15
  ```
16
16
 
17
- Both commands will start a local development server on port `1071` by default, and your website will be accessible at `http://127.0.0.1:1071`.
17
+ **What it does:**
18
+ - Starts the Node.js server (default port `1071`).
19
+ - **Zero-Config Tailwind:** Automatically watches and compiles your classes.
20
+ - **Watch Mode:** Recompiles CSS instantly when you change files.
18
21
 
19
- ### Custom Port
22
+ ### Production Build (`build`)
20
23
 
21
- You can specify a custom port by adding it as an argument:
24
+ Prepare your application for production deployment. This command compiles and modifies your assets for optimal performance.
22
25
 
23
26
  ```bash
24
- # Using npm script with custom port
25
- npm start 8080
27
+ # Using npm script
28
+ npm run build
26
29
 
27
- # Using ODAC directly with custom port
28
- odac framework run 8080
30
+ # Using ODAC directly
31
+ npx odac build
29
32
  ```
30
33
 
31
- This will start the server on your specified port (e.g., `http://127.0.0.1:8080`).
32
-
33
- ### Development vs Production
34
+ **What it does:**
35
+ - **Compiles CSS:** Generates the final `public/assets/css/app.css`.
36
+ - **Minification:** Compresses the CSS to reduce file size.
37
+ - **One-off Run:** Runs once and exits. Does not start a server.
34
38
 
35
- The development server (`npm start`) is designed for:
39
+ ### Production Server (`start`)
36
40
 
37
- - **Local testing** and development only
38
- - **Quick iteration** without server setup
39
- - **Debugging** your application logic
40
- - **Testing on localhost** (127.0.0.1)
41
-
42
- **Important**: The development server does NOT provide DNS, SSL, or other production services.
43
-
44
- For production deployment with full ODAC server features, create your website using:
41
+ Start the application in **production mode**. This is the command you should run on your server or hosting platform.
45
42
 
46
43
  ```bash
47
- odac web create
48
- ```
44
+ # Using npm script
45
+ npm start
49
46
 
50
- This registers your website with the ODAC server and provides:
47
+ # Using ODAC directly
48
+ npx odac start
49
+ ```
51
50
 
52
- - **Automatic SSL** certificate management
53
- - **DNS handling** for your domain
54
- - **Multi-domain hosting** capabilities
55
- - **Process monitoring** and auto-restart
56
- - **Production optimizations** and security
51
+ **What it does:**
52
+ - Sets `NODE_ENV=production`.
53
+ - Starts the Node.js server.
54
+ - **No Overhead:** Does not run Tailwind watchers or dev tools. Simplicity and performance focused.
57
55
 
58
56
  ### Package.json Scripts
59
57
 
60
- When you create a new website, ODAC automatically generates a `package.json` with these useful scripts:
58
+ When you create a new ODAC project, your `package.json` comes pre-configured with these scripts:
61
59
 
62
60
  ```json
63
61
  {
64
62
  "scripts": {
65
- "start": "odac framework run",
66
- "test": "echo \"Error: no test specified\" && exit 1"
63
+ "dev": "odac dev",
64
+ "build": "odac build",
65
+ "start": "odac start"
67
66
  }
68
67
  }
69
68
  ```
70
69
 
71
- - `npm start` - Starts the development server for local testing
72
- - `npm test` - Placeholder for your test suite
73
-
74
- ### Tips
75
-
76
- - The development server automatically detects changes in your code
77
- - Use `Ctrl+C` to stop the development server
78
- - The server will show helpful error messages in the console
79
- - All ODAC framework features are available in development mode
70
+ - `npm run dev` - Your daily development command.
71
+ - `npm run build` - Run this before deploying (e.g., in CI/CD).
72
+ - `npm start` - The command that runs your live website.
@@ -2,37 +2,70 @@
2
2
 
3
3
  Let's take a look at a typical project layout:
4
4
 
5
- - `project_root/`
6
- - `config.json`: This is where you'll keep your app's secrets and settings, like database passwords or API keys.
7
- - `index.js`: The starting pistol for your web application! This file kicks off the Odac framework.
8
- - `package.json`: Contains project metadata and npm scripts for development. Automatically generated when creating a new website.
9
- - `public/`: All files placed in this folder are directly accessible from the outside. This is the perfect place for your images, stylesheets, and client-side JavaScript.
10
- - `route/`: This folder holds all your route definitions. The filename of the route file corresponds to the subdomain it serves.
11
- - `www.js`: Used for routes on your main domain (e.g., `www.example.com` or `example.com`).
12
- - `api.js`: Used for routes on a subdomain (e.g., `api.example.com`). When a request comes in for `api.example.com`, this is the route file that gets used. You can create files for any subdomain!
13
- - `class/`: A dedicated place for your business logic classes and reusable services (e.g., `User.js`, `Mailer.js`).
14
- - `controller/`: This is where the magic happens! Controllers contain the main logic for your application.
15
- - `page/`: We suggest putting controllers that show HTML pages in here.
16
- - `api/`: If you're building an API, it's a great idea to keep those controllers separate in their own folder.
17
- - `view/`: For all your HTML template files. This is what your users will see.
18
-
19
- ### Development Commands
20
-
21
- Your `package.json` includes helpful npm scripts for development:
5
+ ```
6
+ project/
7
+ ├── class/ # Business logic classes (request-scoped)
8
+ ├── Payment.js
9
+ └── User.js
10
+ ├── controller/ # HTTP request handlers
11
+ ├── api/ # API endpoint controllers
12
+ │ ├── auth.js
13
+ │ └── users.js
14
+ └── page/ # HTML page controllers
15
+ │ ├── about.js
16
+ │ └── home.js
17
+ ├── middleware/ # Route middleware functions
18
+ │ └── auth.js
19
+ ├── public/ # Static assets (directly accessible)
20
+ │ └── assets/
21
+ │ ├── css/
22
+ │ │ └── app.css # Auto-generated by Tailwind
23
+ │ ├── img/
24
+ │ └── js/
25
+ ├── route/ # Route definitions (subdomain-based)
26
+ │ ├── api.js # Routes for api.example.com
27
+ │ └── www.js # Routes for www.example.com
28
+ ├── storage/ # App storage (logs, cache, uploads)
29
+ │ └── .cache/
30
+ ├── view/ # HTML templates
31
+ │ ├── content/
32
+ │ ├── css/ # (Optional) Custom Tailwind entry points
33
+ │ ├── footer/
34
+ │ ├── header/
35
+ │ └── skeleton/
36
+ ├── .env # Environment variables (secrets, API keys)
37
+ ├── odac.json # App configuration
38
+ └── package.json # Project metadata & npm scripts
39
+ ```
22
40
 
23
- ```bash
24
- # Start development server (default port 1071)
25
- npm start
41
+ ### 📁 Directory Breakdown
26
42
 
27
- # Start development server on custom port
28
- npm start 8080
29
- ```
43
+ | Directory | Purpose |
44
+ |-----------|---------|
45
+ | `class/` | Request-scoped service classes for business logic |
46
+ | `controller/` | HTTP handlers that process requests and return responses |
47
+ | `middleware/` | Functions that run before controllers (auth, logging, etc.) |
48
+ | `route/` | Route definitions, one file per subdomain |
49
+ | `view/` | HTML templates and partials |
50
+ | `public/` | Static files served directly to browsers |
51
+ | `storage/` | Runtime data (cache, logs, file uploads) |
30
52
 
31
- You can also use Odac commands directly:
53
+ ### CLI Commands
54
+
55
+ Your `package.json` includes scripts to manage your project lifecycle:
32
56
 
33
57
  ```bash
34
- # Development server (local testing only)
35
- odac framework run [port]
58
+ # STAGE 1: DEVELOPMENT
59
+ # Starts dev server with hot-reload & automatic Tailwind CSS
60
+ npm run dev
61
+
62
+ # STAGE 2: BUILD
63
+ # Compiles and optimizes assets (CSS) for production
64
+ npm run build
65
+
66
+ # STAGE 3: PRODUCTION
67
+ # Starts the optimized server
68
+ npm start
36
69
  ```
37
70
 
38
71
  **Note**: For production websites with DNS and SSL, use `odac web create` to register with Odac server.
@@ -1,10 +1,10 @@
1
1
  ## ⚙️ Configuration Overview
2
2
 
3
- Odac uses a simple and flexible configuration system based on `config.json` and optional `.env` files. You can choose the approach that best fits your needs.
3
+ Odac uses a simple and flexible configuration system based on `odac.json` and optional `.env` files. You can choose the approach that best fits your needs.
4
4
 
5
5
  ### Configuration Files
6
6
 
7
- #### config.json (Required)
7
+ #### odac.json (Required)
8
8
  The main configuration file located in your website's root directory. This file contains all your application settings in JSON format.
9
9
 
10
10
  ```json
@@ -46,7 +46,7 @@ Perfect for development or non-sensitive settings:
46
46
  ```
47
47
 
48
48
  #### 2. Environment Variables (Secure)
49
- Use `${VARIABLE}` syntax in `config.json` to reference `.env` values:
49
+ Use `${VARIABLE}` syntax in `odac.json` to reference `.env` values:
50
50
 
51
51
  ```json
52
52
  {
@@ -101,7 +101,7 @@ const nodeEnv = process.env.NODE_ENV
101
101
  ### Best Practices
102
102
 
103
103
  **Development:**
104
- - Use direct values in `config.json` for quick setup
104
+ - Use direct values in `odac.json` for quick setup
105
105
  - Keep development credentials simple
106
106
 
107
107
  **Production:**
@@ -111,7 +111,7 @@ const nodeEnv = process.env.NODE_ENV
111
111
  - Copy `.env.example` to `.env` and fill in production values
112
112
 
113
113
  **Version Control:**
114
- - Commit `config.json` with `${VARIABLE}` placeholders
114
+ - Commit `odac.json` with `${VARIABLE}` placeholders
115
115
  - Commit `.env.example` with dummy values
116
116
  - Never commit `.env` with real credentials
117
117
 
@@ -176,7 +176,7 @@ See individual documentation sections for detailed configuration options.
176
176
 
177
177
  ### Example Setup
178
178
 
179
- **config.json** (committed to git):
179
+ **odac.json** (committed to git):
180
180
  ```json
181
181
  {
182
182
  "request": {
@@ -1,6 +1,6 @@
1
1
  ## 🔌 Database Connection
2
2
 
3
- When you add a `mysql` object to your `config.json`, the system will automatically connect to your MySQL database. No separate connection setup is needed in your code.
3
+ When you add a `mysql` object to your `odac.json`, the system will automatically connect to your MySQL database. No separate connection setup is needed in your code.
4
4
 
5
5
  ### Basic Configuration
6
6
 
@@ -21,7 +21,7 @@ Once this is configured, you can directly use `Odac.DB` commands to run queries.
21
21
 
22
22
  For better security, especially in production, you can use environment variables for sensitive information:
23
23
 
24
- **config.json:**
24
+ **odac.json:**
25
25
  ```json
26
26
  {
27
27
  "mysql": {
@@ -1,6 +1,6 @@
1
1
  ## 🗺️ Static Route Mapping (Optional)
2
2
 
3
- Normally, all publicly served files should be in your `public` folder. However, if you need to expose a specific file from somewhere else on your server, you can use the optional `route` object in your `config.json`. This creates a direct mapping from a URL path to that file.
3
+ Normally, all publicly served files should be in your `public` folder. However, if you need to expose a specific file from somewhere else on your server, you can use the optional `route` object in your `odac.json`. This creates a direct mapping from a URL path to that file.
4
4
 
5
5
  ```json
6
6
  "route": {
@@ -1,6 +1,6 @@
1
1
  ## ⏱️ Request Timeout
2
2
 
3
- You can configure the global timeout for incoming requests by adding a `request` object to your `config.json`.
3
+ You can configure the global timeout for incoming requests by adding a `request` object to your `odac.json`.
4
4
 
5
5
  ```json
6
6
  "request": {
@@ -4,7 +4,7 @@ Odac supports environment variables through `.env` files, making it easy to mana
4
4
 
5
5
  ### Creating a .env File
6
6
 
7
- Create a `.env` file in your website's root directory (same location as `config.json`):
7
+ Create a `.env` file in your website's root directory (same location as `odac.json`):
8
8
 
9
9
  ```bash
10
10
  # .env
@@ -37,7 +37,7 @@ FEATURE_BETA=true
37
37
  MAINTENANCE_MODE=false
38
38
  ```
39
39
 
40
- ### Using in config.json
40
+ ### Using in odac.json
41
41
 
42
42
  Reference environment variables using `${VARIABLE_NAME}` syntax:
43
43
 
@@ -91,7 +91,7 @@ module.exports = function() {
91
91
  }
92
92
  ```
93
93
 
94
- #### 3. From Odac.Config (if defined in config.json)
94
+ #### 3. From Odac.Config (if defined in odac.json)
95
95
 
96
96
  ```javascript
97
97
  module.exports = function() {
@@ -222,6 +222,6 @@ MAIL_FROM='noreply@example.com'
222
222
 
223
223
  - Environment variables are loaded when the application starts
224
224
  - Changes to `.env` require restarting the application
225
- - The `.env` file is **optional** - you can use direct values in `config.json` if preferred
225
+ - The `.env` file is **optional** - you can use direct values in `odac.json` if preferred
226
226
  - Variables defined in `.env` are available throughout your entire application
227
227
  - If a variable is not found, `Odac.env()` returns the default value or `undefined`
@@ -54,7 +54,7 @@ Same flow - hints are sent from the manifest immediately.
54
54
 
55
55
  ## Configuration (Optional)
56
56
 
57
- While Early Hints works automatically, you can customize it in `config.json`:
57
+ While Early Hints works automatically, you can customize it in `odac.json`:
58
58
 
59
59
  ```json
60
60
  {
@@ -329,7 +329,7 @@ Reduce `maxResources` in config:
329
329
 
330
330
  ## Disabling Early Hints
331
331
 
332
- Removing the `earlyHints` configuration section from your `config.json` is equivalent to using the default settings, which has the feature enabled. To truly disable Early Hints, you must explicitly set `enabled: false` in your configuration:
332
+ Removing the `earlyHints` configuration section from your `odac.json` is equivalent to using the default settings, which has the feature enabled. To truly disable Early Hints, you must explicitly set `enabled: false` in your configuration:
333
333
 
334
334
  ```json
335
335
  {
@@ -1,5 +1,22 @@
1
1
  ## 📦 API and Data Routes
2
2
 
3
+
4
+ #### Class-Based Route Definition (Recommended)
5
+
6
+ You can route requests to specific methods within a Controller Class using the `ClassName@methodName` syntax. This allows you to group related logic in a single file clearly.
7
+
8
+ ```javascript
9
+ // Calls the 'index' method of the class exported in controller/User.js
10
+ Odac.Route.get('/users', 'User@index');
11
+
12
+ // Calls the 'store' method of the class exported in controller/User.js
13
+ Odac.Route.post('/users', 'User@store');
14
+
15
+ // You can also use dot notation for controllers in subdirectories
16
+ // controller/Admin/Dashboard.js -> Admin.Dashboard
17
+ Odac.Route.get('/admin', 'Admin.Dashboard@index');
18
+ ```
19
+
3
20
  #### `get(path, controller, options)`
4
21
  Defines a route that responds to `GET` requests. This is ideal for API endpoints that return data (like JSON).
5
22
 
@@ -18,3 +35,4 @@ Defines a route that responds to `POST` requests, typically used for form submis
18
35
  // A form that posts data to /login
19
36
  Odac.Route.post('/login', 'auth.login');
20
37
  ```
38
+
@@ -79,6 +79,9 @@ Odac.Route.cron('task')
79
79
  // Day (1-31)
80
80
  .day(15) // On the 15th of the month
81
81
 
82
+ // At Specific Time (HH:MM)
83
+ .at('14:30') // At 14:30 (Shorthand for .hour(14).minute(30))
84
+
82
85
  // Week day (0-6, 0=Sunday)
83
86
  .weekDay(1) // On Monday
84
87
 
@@ -114,6 +117,18 @@ Odac.Route.cron('periodic')
114
117
 
115
118
  // Every N years
116
119
  .everyYear(1) // Every year
120
+
121
+ ## Raw Cron Expression
122
+ You can use standard UNIX cron expressions (Minute Hour Day Month WeekDay) using the `.raw()` method.
123
+ Supported formats for each field: `*`, `*/n` (interval), `n` (exact match).
124
+
125
+ ```javascript
126
+ // Run every 15 minutes
127
+ Odac.Route.cron('quarter-task').raw('*\/15 * * * *')
128
+
129
+ // Run at 14:30 on Mondays
130
+ Odac.Route.cron('weekly-meeting').raw('30 14 * * 1')
131
+ ```
117
132
  ```
118
133
 
119
134
  ## Combination Usage
@@ -146,4 +161,5 @@ Odac.Route.cron('monthly-cleanup')
146
161
  - If the same job is defined multiple times, the last definition takes precedence
147
162
  - Controller files are re-required on each execution
148
163
  - Inline functions are stored in memory and executed directly
149
- - If a job fails, it stops but the system continues
164
+ - If a job fails, it stops but the system continues
165
+ - **CRITICAL:** Missing conditions act as wildcards (`*`). If you specify `.hour(14)` but omit `.minute()`, the task will run **every minute** between 14:00 and 14:59. Always specify smaller units (minute) to pin execution to a single point in time. Use `.at('14:00')` to safely set both hour and minute.
@@ -1,8 +1,52 @@
1
1
  ## 🏗️ How to Build a Controller
2
2
 
3
- A controller is just a JavaScript module that exports a function. This function automatically gets the magical `Odac` context object we talked about in the overview.
3
+ A controller is just a JavaScript module that exports a function or a Class. This function automatically gets the magical `Odac` context object we talked about in the overview.
4
4
 
5
- #### A Simple "Hello World" Controller
5
+ #### Class-Based Controllers (Professional & Recommended)
6
+
7
+ For professional applications, we **strongly recommend** using Class-Based Controllers. This approach allows you to group related actions (like all User-related or Product-related logic) into a single file in the `controller/` directory, keeping your project organized.
8
+
9
+ **Example: `controller/User.js`**
10
+
11
+ ```javascript
12
+ class User {
13
+ // Access this via 'User@index'
14
+ index(Odac) {
15
+ return Odac.View.make('user.list', {
16
+ title: 'User List'
17
+ })
18
+ }
19
+
20
+ // Access this via 'User@show'
21
+ show(Odac) {
22
+ const id = Odac.Request.input('id')
23
+ // Fetch user logic...
24
+ return Odac.return({ id: id, name: 'John Doe' })
25
+ }
26
+
27
+ // Access this via 'User@store'
28
+ store(Odac) {
29
+ // Save user logic...
30
+ return Odac.return({ success: true })
31
+ }
32
+ }
33
+
34
+ module.exports = User
35
+ ```
36
+
37
+ When using this structure, you define your routes using the `ControllerName@MethodName` syntax:
38
+
39
+ ```javascript
40
+ Odac.Route.get('/users', 'User@index')
41
+ Odac.Route.get('/users/{id}', 'User@show')
42
+ Odac.Route.post('/users', 'User@store')
43
+ ```
44
+
45
+ The framework automatically instantiates your Controller class and calls the specified method with the `Odac` instance passed as an argument.
46
+
47
+ #### specific Function Controllers (Basic)
48
+
49
+ For very simple or single-purpose routes, you can export a single function.
6
50
 
7
51
  Check out this basic example from `controller/page/index.js`:
8
52
 
@@ -14,4 +58,5 @@ module.exports = function (Odac) {
14
58
  }
15
59
  ```
16
60
 
17
- This little guy is responsible for handling the homepage route (`/`). When it runs, it just sends a simple string back to the user's browser.
61
+ This simple function is responsible for handling the homepage route (`/`). When it runs, it just sends a simple string back to the user's browser.
62
+
@@ -4,9 +4,28 @@ You can organize your business logic into reusable classes. This is especially u
4
4
 
5
5
  We recommend placing these files in the `class/` directory.
6
6
 
7
+ ### ❓ Service Class vs. Controller
8
+
9
+ It is important not to confuse **Service Classes** with **Class-Based Controllers**.
10
+
11
+ - **Controllers** (located in `controller/`):
12
+ - Handle HTTP requests (Input -> Process -> Response).
13
+ - Can be defined as Classes for better organization.
14
+ - Are mapped to specific Routes (e.g., via `Route.get()`).
15
+
16
+ - **Service Classes** (located in `class/`):
17
+ - Contain reusable business logic (e.g., `User.calculateReputation()`, `Mail.sendWelcome()`).
18
+ - Are **not** directly mapped to routes.
19
+ - Can be used by *multiple* controllers or other services.
20
+ - Are **Request Scoped**: They are instantiated fresh for every request and attached to the request's `Odac` instance. They correspond to the life-cycle of the request.
21
+
22
+ **Rule of Thumb:** If it talks to the browser/API client, it's a **Controller**. If it processes data behind the scenes, it's a **Service Class**.
23
+
7
24
  #### Creating a Service Class
8
25
 
9
- Any class file placed in the `class/` directory will be automatically loaded and available on the `Odac` object. The class receives the global `Odac` request instance in its constructor, giving you access to all request-scoped services:
26
+ Any class file placed in the `class/` directory will be automatically detected. When a request comes in, Odac creates a **new instance** of your class and passes the current request's `Odac` object to the constructor.
27
+
28
+ This means `this.Odac` inside your class gives you access to the specific request, response, authentication state, and database for *that specific user request*.
10
29
 
11
30
  ```javascript
12
31
  // class/User.js
@@ -15,6 +34,7 @@ class User {
15
34
  this.Odac = Odac
16
35
  }
17
36
 
37
+
18
38
  async getProfile() {
19
39
  const user = await this.Odac.Auth.user()
20
40
  return {
@@ -58,22 +78,22 @@ If your class name conflicts with a built-in Odac service (like `Mail`, `DB`, `A
58
78
  Example: `class/Mail.js` (conflicts with core Mail) -> `Odac.App.Mail`
59
79
 
60
80
  #### Accessing Services in Controllers
61
-
62
- Service classes are automatically instantiated for each request and attached to the `Odac` object using the file name. You can access them from any controller:
63
-
64
- ```javascript
65
- // controller/get/profile.js
66
- module.exports = async function (Odac) {
67
- // Access your User class via Odac.User
68
- const profile = await Odac.User.getProfile()
69
-
70
- return Odac.return(profile)
71
- }
72
- ```
73
-
74
- #### Benefits of Service Classes
75
-
76
- - **Organization**: Group related business logic together in `class/`
77
- - **Reusability**: Share logic between different controllers and routes
78
- - **Context**: The `Odac` object is injected, providing access to `Auth`, `DB`, `Request`, etc.
79
- - **Separation of Concerns**: Keep your Controllers lightweight by moving heavy logic to Services.
81
+
82
+ Since Service classes are attached to the `Odac` instance for each request, you can access them directly by their file name.
83
+
84
+ ```javascript
85
+ // controller/get/profile.js
86
+ module.exports = async function (Odac) {
87
+ // Odac.User is a fresh instance of the User class dedicated to this request
88
+ const profile = await Odac.User.getProfile()
89
+
90
+ return Odac.return(profile)
91
+ }
92
+ ```
93
+
94
+ #### Benefits of Service Classes
95
+
96
+ - **Organization**: Group related business logic together in `class/`
97
+ - **Reusability**: Share logic between different controllers and routes
98
+ - **Context Awareness**: The `Odac` request object is injected automatically, so your services know about the current user and request.
99
+ - **Separation of Concerns**: Keep your Controllers lightweight by moving heavy logic to Services.
@@ -94,3 +94,20 @@ module.exports = async function (Odac) {
94
94
  })
95
95
  }
96
96
  ```
97
+
98
+ ### Session Data
99
+
100
+ You can store data in the current user's session using `Odac.session()`. This data persists across requests.
101
+
102
+ ```javascript
103
+ module.exports = async function (Odac) {
104
+ // Set a session value
105
+ Odac.session('cart_id', 12345)
106
+
107
+ // Get a session value
108
+ const cartId = Odac.session('cart_id')
109
+
110
+ // Remove a session value
111
+ Odac.session('cart_id', null)
112
+ }
113
+ ```