odac 0.9.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/.github/workflows/auto-pr-description.yml +0 -2
  2. package/.github/workflows/codeql.yml +46 -0
  3. package/.github/workflows/release.yml +13 -6
  4. package/.github/workflows/test-coverage.yml +10 -9
  5. package/.releaserc.js +9 -6
  6. package/CHANGELOG.md +62 -150
  7. package/CODE_OF_CONDUCT.md +1 -1
  8. package/CONTRIBUTING.md +8 -8
  9. package/LICENSE +21 -661
  10. package/README.md +12 -12
  11. package/SECURITY.md +4 -4
  12. package/bin/odac.js +101 -0
  13. package/{framework/web/candy.js → client/odac.js} +310 -44
  14. package/docs/backend/01-overview/{01-whats-in-the-candy-box.md → 01-whats-in-the-odac-box.md} +4 -2
  15. package/docs/backend/01-overview/02-super-handy-helper-functions.md +29 -1
  16. package/docs/backend/01-overview/03-development-server.md +11 -11
  17. package/docs/backend/02-structure/01-typical-project-layout.md +4 -4
  18. package/docs/backend/03-config/00-configuration-overview.md +6 -6
  19. package/docs/backend/03-config/01-database-connection.md +1 -1
  20. package/docs/backend/03-config/02-static-route-mapping-optional.md +4 -4
  21. package/docs/backend/03-config/04-environment-variables.md +20 -20
  22. package/docs/backend/03-config/05-early-hints.md +4 -4
  23. package/docs/backend/04-routing/01-basic-page-routes.md +4 -4
  24. package/docs/backend/04-routing/02-controller-less-view-routes.md +5 -5
  25. package/docs/backend/04-routing/03-api-and-data-routes.md +3 -3
  26. package/docs/backend/04-routing/04-authentication-aware-routes.md +5 -5
  27. package/docs/backend/04-routing/05-advanced-routing.md +3 -3
  28. package/docs/backend/04-routing/06-error-pages.md +17 -17
  29. package/docs/backend/04-routing/07-cron-jobs.md +13 -13
  30. package/docs/backend/04-routing/08-middleware.md +214 -0
  31. package/docs/backend/04-routing/09-websocket-auth-middleware.md +292 -0
  32. package/docs/backend/04-routing/09-websocket-examples.md +381 -0
  33. package/docs/backend/04-routing/09-websocket-quick-reference.md +211 -0
  34. package/docs/backend/04-routing/09-websocket.md +298 -0
  35. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +3 -3
  36. package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +41 -0
  37. package/docs/backend/05-controllers/03-controller-classes.md +19 -19
  38. package/docs/backend/05-forms/01-custom-forms.md +114 -114
  39. package/docs/backend/05-forms/02-automatic-database-insert.md +82 -82
  40. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +26 -26
  41. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +10 -10
  42. package/docs/backend/07-views/01-the-view-directory.md +1 -1
  43. package/docs/backend/07-views/02-rendering-a-view.md +22 -22
  44. package/docs/backend/07-views/03-template-syntax.md +52 -52
  45. package/docs/backend/07-views/03-variables.md +84 -84
  46. package/docs/backend/07-views/04-request-data.md +57 -57
  47. package/docs/backend/07-views/05-conditionals.md +78 -78
  48. package/docs/backend/07-views/06-loops.md +114 -114
  49. package/docs/backend/07-views/07-translations.md +66 -66
  50. package/docs/backend/07-views/08-backend-javascript.md +103 -103
  51. package/docs/backend/07-views/09-comments.md +71 -71
  52. package/docs/backend/08-database/01-database-connection.md +8 -8
  53. package/docs/backend/08-database/02-using-mysql.md +49 -49
  54. package/docs/backend/09-validation/01-the-validator-service.md +38 -38
  55. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +15 -15
  56. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +10 -10
  57. package/docs/backend/10-authentication/03-register.md +12 -12
  58. package/docs/backend/10-authentication/{04-candy-register-forms.md → 04-odac-register-forms.md} +141 -141
  59. package/docs/backend/10-authentication/05-session-management.md +10 -10
  60. package/docs/backend/10-authentication/{06-candy-login-forms.md → 06-odac-login-forms.md} +125 -125
  61. package/docs/backend/11-mail/01-the-mail-service.md +5 -5
  62. package/docs/backend/12-streaming/01-streaming-overview.md +96 -54
  63. package/docs/backend/13-utilities/{01-candy-var.md → 01-odac-var.md} +109 -109
  64. package/docs/frontend/01-overview/01-introduction.md +30 -30
  65. package/docs/frontend/02-ajax-navigation/01-quick-start.md +45 -45
  66. package/docs/frontend/02-ajax-navigation/02-configuration.md +14 -14
  67. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +36 -36
  68. package/docs/frontend/03-forms/01-form-handling.md +32 -32
  69. package/docs/frontend/04-api-requests/01-get-post.md +33 -33
  70. package/docs/frontend/05-streaming/01-client-streaming.md +15 -15
  71. package/docs/frontend/06-websocket/00-overview.md +76 -0
  72. package/docs/frontend/06-websocket/01-websocket-client.md +139 -0
  73. package/docs/frontend/06-websocket/02-shared-websocket.md +149 -0
  74. package/docs/index.json +49 -11
  75. package/eslint.config.mjs +6 -6
  76. package/{framework/index.js → index.js} +1 -1
  77. package/package.json +14 -39
  78. package/{framework/src → src}/Auth.js +59 -59
  79. package/{framework/src → src}/Config.js +3 -3
  80. package/{framework/src → src}/Lang.js +7 -7
  81. package/{framework/src → src}/Mail.js +5 -5
  82. package/{framework/src → src}/Mysql.js +42 -42
  83. package/src/Odac.js +112 -0
  84. package/{framework/src → src}/Request.js +38 -36
  85. package/{framework/src → src}/Route/Internal.js +116 -116
  86. package/src/Route/Middleware.js +75 -0
  87. package/src/Route.js +621 -0
  88. package/src/Server.js +22 -0
  89. package/{framework/src → src}/Stream.js +11 -3
  90. package/{framework/src → src}/Validator.js +21 -21
  91. package/{framework/src → src}/Var.js +5 -5
  92. package/{framework/src → src}/View/EarlyHints.js +1 -1
  93. package/{framework/src → src}/View/Form.js +69 -69
  94. package/{framework/src → src}/View.js +78 -81
  95. package/src/WebSocket.js +403 -0
  96. package/template/config.json +5 -0
  97. package/{web → template}/controller/page/about.js +6 -6
  98. package/{web → template}/controller/page/index.js +9 -9
  99. package/{web → template}/package.json +4 -5
  100. package/{web → template}/public/assets/css/style.css +4 -4
  101. package/{web → template}/public/assets/js/app.js +6 -6
  102. package/{web → template}/route/www.js +6 -6
  103. package/{web → template}/skeleton/main.html +1 -1
  104. package/{web → template}/view/content/about.html +5 -5
  105. package/{web → template}/view/content/home.html +12 -12
  106. package/template/view/footer/main.html +11 -0
  107. package/{web → template}/view/head/main.html +1 -1
  108. package/{web → template}/view/header/main.html +2 -2
  109. package/test/core/Candy.test.js +58 -58
  110. package/test/core/Commands.test.js +7 -7
  111. package/test/core/Config.test.js +82 -85
  112. package/test/core/Lang.test.js +2 -2
  113. package/test/core/Process.test.js +6 -6
  114. package/test/framework/Route.test.js +56 -37
  115. package/test/framework/View/EarlyHints.test.js +2 -2
  116. package/test/framework/WebSocket.test.js +100 -0
  117. package/test/framework/middleware.test.js +85 -0
  118. package/test/server/Api.test.js +31 -31
  119. package/test/server/DNS.test.js +11 -11
  120. package/test/server/Hub.test.js +497 -0
  121. package/test/server/Mail.account.test_.js +3 -3
  122. package/test/server/Mail.init.test_.js +10 -10
  123. package/test/server/Mail.test_.js +20 -20
  124. package/test/server/SSL.test_.js +54 -54
  125. package/test/server/Server.test.js +39 -39
  126. package/test/server/Service.test_.js +7 -7
  127. package/test/server/Subdomain.test.js +7 -7
  128. package/test/server/Web/Firewall.test.js +87 -87
  129. package/test/server/Web/Proxy.test.js +397 -0
  130. package/test/server/{Web.test_.js → Web.test.js} +137 -205
  131. package/test/server/__mocks__/fs.js +2 -2
  132. package/test/server/__mocks__/{globalCandy.js → globalOdac.js} +5 -5
  133. package/test/server/__mocks__/index.js +6 -6
  134. package/test/server/__mocks__/testFactories.js +1 -1
  135. package/test/server/__mocks__/testHelpers.js +7 -7
  136. package/.husky/pre-commit +0 -2
  137. package/.kiro/steering/code-style.md +0 -56
  138. package/.kiro/steering/product.md +0 -20
  139. package/.kiro/steering/structure.md +0 -77
  140. package/.kiro/steering/tech.md +0 -87
  141. package/AGENTS.md +0 -84
  142. package/bin/candy +0 -10
  143. package/bin/candypack +0 -10
  144. package/cli/index.js +0 -3
  145. package/cli/src/Cli.js +0 -348
  146. package/cli/src/Connector.js +0 -93
  147. package/cli/src/Monitor.js +0 -416
  148. package/core/Candy.js +0 -87
  149. package/core/Commands.js +0 -239
  150. package/core/Config.js +0 -1094
  151. package/core/Lang.js +0 -52
  152. package/core/Log.js +0 -43
  153. package/core/Process.js +0 -26
  154. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +0 -20
  155. package/docs/server/01-installation/01-quick-install.md +0 -19
  156. package/docs/server/01-installation/02-manual-installation-via-npm.md +0 -9
  157. package/docs/server/02-get-started/01-core-concepts.md +0 -7
  158. package/docs/server/02-get-started/02-basic-commands.md +0 -57
  159. package/docs/server/02-get-started/03-cli-reference.md +0 -276
  160. package/docs/server/02-get-started/04-cli-quick-reference.md +0 -102
  161. package/docs/server/03-service/01-start-a-new-service.md +0 -57
  162. package/docs/server/03-service/02-delete-a-service.md +0 -48
  163. package/docs/server/04-web/01-create-a-website.md +0 -36
  164. package/docs/server/04-web/02-list-websites.md +0 -9
  165. package/docs/server/04-web/03-delete-a-website.md +0 -29
  166. package/docs/server/05-subdomain/01-create-a-subdomain.md +0 -32
  167. package/docs/server/05-subdomain/02-list-subdomains.md +0 -33
  168. package/docs/server/05-subdomain/03-delete-a-subdomain.md +0 -41
  169. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +0 -34
  170. package/docs/server/07-mail/01-create-a-mail-account.md +0 -23
  171. package/docs/server/07-mail/02-delete-a-mail-account.md +0 -20
  172. package/docs/server/07-mail/03-list-mail-accounts.md +0 -20
  173. package/docs/server/07-mail/04-change-account-password.md +0 -23
  174. package/framework/src/Candy.js +0 -81
  175. package/framework/src/Route.js +0 -455
  176. package/framework/src/Server.js +0 -15
  177. package/locale/de-DE.json +0 -80
  178. package/locale/en-US.json +0 -79
  179. package/locale/es-ES.json +0 -80
  180. package/locale/fr-FR.json +0 -80
  181. package/locale/pt-BR.json +0 -80
  182. package/locale/ru-RU.json +0 -80
  183. package/locale/tr-TR.json +0 -85
  184. package/locale/zh-CN.json +0 -80
  185. package/server/index.js +0 -5
  186. package/server/src/Api.js +0 -88
  187. package/server/src/DNS.js +0 -940
  188. package/server/src/Hub.js +0 -535
  189. package/server/src/Mail.js +0 -571
  190. package/server/src/SSL.js +0 -180
  191. package/server/src/Server.js +0 -27
  192. package/server/src/Service.js +0 -248
  193. package/server/src/Subdomain.js +0 -64
  194. package/server/src/Web/Firewall.js +0 -170
  195. package/server/src/Web/Proxy.js +0 -134
  196. package/server/src/Web.js +0 -451
  197. package/server/src/mail/imap.js +0 -1091
  198. package/server/src/mail/server.js +0 -32
  199. package/server/src/mail/smtp.js +0 -786
  200. package/test/server/Client.test.js +0 -338
  201. package/test/server/__mocks__/http-proxy.js +0 -105
  202. package/watchdog/index.js +0 -3
  203. package/watchdog/src/Watchdog.js +0 -156
  204. package/web/config.json +0 -5
  205. package/web/view/footer/main.html +0 -11
  206. /package/{framework/src → src}/Env.js +0 -0
  207. /package/{framework/src → src}/Route/Cron.js +0 -0
  208. /package/{framework/src → src}/Token.js +0 -0
@@ -0,0 +1,214 @@
1
+ ## 🔗 Middleware
2
+
3
+ Middleware allows you to run code before your controllers execute. Perfect for authentication, logging, rate limiting, and more.
4
+
5
+ ### Creating Middleware
6
+
7
+ Create middleware files in the `middleware/` directory:
8
+
9
+ ```javascript
10
+ // middleware/auth.js
11
+ module.exports = async (Odac) => {
12
+ if (!await Odac.Auth.check()) {
13
+ return Odac.direct('/login') // Redirect to login
14
+ }
15
+ // No return = continue to next middleware or controller
16
+ }
17
+ ```
18
+
19
+ **Middleware Rules:**
20
+ - Return `false` → Stop execution (403 Forbidden)
21
+ - Return `Odac.abort(code)` → Stop with custom error code
22
+ - Return `Odac.direct(url)` → Stop and redirect
23
+ - Return nothing or `true` → Continue to next middleware/controller
24
+
25
+ ### Using Middleware
26
+
27
+ #### Single Route
28
+ ```javascript
29
+ Odac.Route
30
+ .use('logger')
31
+ .page('/contact', 'contact')
32
+ ```
33
+
34
+ #### Multiple Routes
35
+ ```javascript
36
+ Odac.Route
37
+ .use('cors')
38
+ .page('/api/users', 'api.users')
39
+ .page('/api/posts', 'api.posts')
40
+ .get('/api/stats', 'api.stats')
41
+ ```
42
+
43
+ #### Multiple Middlewares
44
+ ```javascript
45
+ Odac.Route
46
+ .use('cors', 'rateLimit')
47
+ .post('/api/upload', 'api.upload')
48
+ ```
49
+
50
+ #### With Auth Routes
51
+
52
+ `Odac.Route.auth` already requires authentication. You can add additional middleware on top:
53
+
54
+ ```javascript
55
+ // Admin-only routes (requires login + admin role)
56
+ Odac.Route.auth
57
+ .use('admin')
58
+ .page('/admin/dashboard', 'admin.dashboard')
59
+ .page('/admin/users', 'admin.users')
60
+ .post('/admin/settings', 'admin.settings')
61
+ ```
62
+
63
+ ```javascript
64
+ // Premium user routes (requires login + premium subscription)
65
+ Odac.Route.auth
66
+ .use('premium')
67
+ .page('/premium/content', 'premium.content')
68
+ .page('/premium/downloads', 'premium.downloads')
69
+ ```
70
+
71
+ ```javascript
72
+ // Multiple middlewares with auth
73
+ Odac.Route.auth
74
+ .use('verified', 'rateLimit')
75
+ .post('/api/sensitive', 'api.sensitive')
76
+ ```
77
+
78
+ ### Middleware Examples
79
+
80
+ #### Authentication
81
+ ```javascript
82
+ // middleware/auth.js
83
+ module.exports = async (Odac) => {
84
+ if (!await Odac.Auth.check()) {
85
+ return Odac.direct('/login')
86
+ }
87
+ }
88
+ ```
89
+
90
+ #### Admin Check
91
+ ```javascript
92
+ // middleware/admin.js
93
+ module.exports = async (Odac) => {
94
+ const user = await Odac.Auth.user()
95
+ if (!user || user.role !== 'admin') {
96
+ return false // 403 Forbidden
97
+ }
98
+ }
99
+ ```
100
+
101
+ #### CORS Headers
102
+ ```javascript
103
+ // middleware/cors.js
104
+ module.exports = async (Odac) => {
105
+ Odac.Request.header('Access-Control-Allow-Origin', '*')
106
+ Odac.Request.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
107
+ }
108
+ ```
109
+
110
+ #### Rate Limiting
111
+ ```javascript
112
+ // middleware/rateLimit.js
113
+ const requests = new Map()
114
+ let lastCleanup = Date.now()
115
+
116
+ module.exports = async (Odac) => {
117
+ const ip = Odac.Request.ip
118
+ const now = Date.now()
119
+ const limit = 100
120
+ const window = 60000
121
+
122
+ if (now - lastCleanup > window) {
123
+ for (const [key, times] of requests.entries()) {
124
+ const recent = times.filter(time => now - time < window)
125
+ if (recent.length === 0) {
126
+ requests.delete(key)
127
+ } else {
128
+ requests.set(key, recent)
129
+ }
130
+ }
131
+ lastCleanup = now
132
+ }
133
+
134
+ if (!requests.has(ip)) {
135
+ requests.set(ip, [])
136
+ }
137
+
138
+ const userRequests = requests.get(ip).filter(time => now - time < window)
139
+
140
+ if (userRequests.length >= limit) {
141
+ return Odac.abort(429, 'Too many requests')
142
+ }
143
+
144
+ userRequests.push(now)
145
+ requests.set(ip, userRequests)
146
+ }
147
+ ```
148
+
149
+ > **Note:** This example uses in-memory storage for simplicity. For production environments with multiple server instances, consider using Redis or Memcached for distributed rate limiting.
150
+
151
+ #### Premium Check with View
152
+ ```javascript
153
+ // middleware/premium.js
154
+ module.exports = async (Odac) => {
155
+ const user = await Odac.Auth.user()
156
+
157
+ if (!user.isPremium) {
158
+ return Odac.View.render('premium/upgrade', {
159
+ user: user,
160
+ currentPage: Odac.Request.url
161
+ })
162
+ }
163
+ }
164
+ ```
165
+
166
+ #### Logging
167
+ ```javascript
168
+ // middleware/logger.js
169
+ module.exports = async (Odac) => {
170
+ console.log(`${Odac.Request.method} ${Odac.Request.url}`)
171
+ }
172
+ ```
173
+
174
+ #### Inline Middleware
175
+ ```javascript
176
+ Odac.Route
177
+ .use(async (Odac) => {
178
+ console.log('Custom middleware')
179
+ })
180
+ .page('/special', 'special')
181
+ ```
182
+
183
+ ### Complete Example
184
+
185
+ ```javascript
186
+ // route/www.js
187
+
188
+ // Public routes
189
+ Odac.Route.page('/', 'index')
190
+ Odac.Route.page('/about', 'about')
191
+
192
+ // API routes with CORS
193
+ Odac.Route
194
+ .use('cors', 'rateLimit')
195
+ .get('/api/public', 'api.public')
196
+ .post('/api/contact', 'api.contact')
197
+
198
+ // User routes (requires login)
199
+ Odac.Route.auth
200
+ .page('/profile', 'profile')
201
+ .page('/settings', 'settings')
202
+ .post('/api/update', 'update')
203
+
204
+ // Admin routes (requires login + admin role)
205
+ Odac.Route.auth
206
+ .use('admin')
207
+ .page('/admin', 'admin.index')
208
+ .page('/admin/users', 'admin.users')
209
+ .post('/admin/delete', 'admin.delete')
210
+ ```
211
+
212
+ ### Hot Reloading
213
+
214
+ Middleware files are automatically reloaded when changed (every 5 seconds), just like controllers and routes. No server restart needed during development.
@@ -0,0 +1,292 @@
1
+ # WebSocket Authentication & Middleware
2
+
3
+ Advanced authentication and middleware patterns for WebSocket routes.
4
+
5
+ ## Authentication Methods
6
+
7
+ ### 1. Using auth.ws() (Recommended)
8
+
9
+ The simplest way to require authentication:
10
+
11
+ ```javascript
12
+ Odac.Route.auth.ws('/secure', async Odac => {
13
+ const user = await Odac.Auth.user()
14
+
15
+ Odac.ws.send({
16
+ type: 'authenticated',
17
+ user: user.name
18
+ })
19
+ })
20
+ ```
21
+
22
+ **Behavior:**
23
+ - Automatically checks authentication before handler runs
24
+ - Closes connection with code `4001` if not authenticated
25
+ - No need for manual auth checks
26
+
27
+ ### 2. Manual Authentication Check
28
+
29
+ For custom authentication logic:
30
+
31
+ ```javascript
32
+ Odac.Route.ws('/custom-auth', async Odac => {
33
+ const token = Odac.Request.header('Authorization')
34
+
35
+ if (!token) {
36
+ Odac.ws.close(4001, 'Missing token')
37
+ return
38
+ }
39
+
40
+ const user = await validateCustomToken(token)
41
+
42
+ if (!user) {
43
+ Odac.ws.close(4001, 'Invalid token')
44
+ return
45
+ }
46
+
47
+ Odac.ws.data.user = user
48
+ Odac.ws.send({type: 'authenticated'})
49
+ })
50
+ ```
51
+
52
+ ## Middleware Support
53
+
54
+ ### Basic Middleware
55
+
56
+ ```javascript
57
+ Odac.Route.use('rate-limit').ws('/chat', Odac => {
58
+ Odac.ws.send({type: 'connected'})
59
+ })
60
+ ```
61
+
62
+ ### Multiple Middleware
63
+
64
+ Middleware runs in order:
65
+
66
+ ```javascript
67
+ Odac.Route.use('auth', 'rate-limit', 'log').ws('/chat', Odac => {
68
+ Odac.ws.send({type: 'ready'})
69
+ })
70
+ ```
71
+
72
+ ### Creating WebSocket Middleware
73
+
74
+ **middleware/websocket-rate-limit.js:**
75
+
76
+ ```javascript
77
+ const connections = new Map()
78
+ const RATE_LIMIT = 5
79
+ const WINDOW = 60000
80
+
81
+ module.exports = async Odac => {
82
+ const ip = Odac.Request.ip
83
+ const now = Date.now()
84
+
85
+ if (!connections.has(ip)) {
86
+ connections.set(ip, [])
87
+ }
88
+
89
+ const userConnections = connections.get(ip)
90
+ const recentConnections = userConnections.filter(time => now - time < WINDOW)
91
+
92
+ if (recentConnections.length >= RATE_LIMIT) {
93
+ return false
94
+ }
95
+
96
+ recentConnections.push(now)
97
+ connections.set(ip, recentConnections)
98
+
99
+ return true
100
+ }
101
+ ```
102
+
103
+ **Usage:**
104
+
105
+ ```javascript
106
+ Odac.Route.use('websocket-rate-limit').ws('/chat', Odac => {
107
+ Odac.ws.send({type: 'connected'})
108
+ })
109
+ ```
110
+
111
+ ### Middleware Return Values
112
+
113
+ | Return Value | Behavior |
114
+ |--------------|----------|
115
+ | `true` or `undefined` | Continue to next middleware/handler |
116
+ | `false` | Close connection with code `4003` (Forbidden) |
117
+ | Any other value | Close connection with code `4000` |
118
+
119
+ ### Advanced Middleware Example
120
+
121
+ **middleware/websocket-auth.js:**
122
+
123
+ ```javascript
124
+ module.exports = async Odac => {
125
+ const token = Odac.Request.header('X-WS-Token')
126
+
127
+ if (!token) {
128
+ console.log('WebSocket connection rejected: No token')
129
+ return false
130
+ }
131
+
132
+ try {
133
+ const user = await verifyToken(token)
134
+
135
+ if (!user) {
136
+ console.log('WebSocket connection rejected: Invalid token')
137
+ return false
138
+ }
139
+
140
+ Odac.Auth.setUser(user)
141
+ return true
142
+
143
+ } catch (error) {
144
+ console.error('WebSocket auth error:', error)
145
+ return false
146
+ }
147
+ }
148
+ ```
149
+
150
+ ## Combining Auth and Middleware
151
+
152
+ You can combine `auth.ws()` with middleware:
153
+
154
+ ```javascript
155
+ // This won't work - auth.ws() doesn't support chaining
156
+ // Odac.Route.use('rate-limit').auth.ws('/chat', handler)
157
+
158
+ // Instead, use middleware that includes auth check
159
+ Odac.Route.use('websocket-auth', 'rate-limit').ws('/chat', Odac => {
160
+ const user = Odac.Auth.user()
161
+ Odac.ws.send({user: user.name})
162
+ })
163
+
164
+ // Or use auth.ws() without middleware
165
+ Odac.Route.auth.ws('/chat', Odac => {
166
+ const user = Odac.Auth.user()
167
+ Odac.ws.send({user: user.name})
168
+ })
169
+ ```
170
+
171
+ ## Real-World Examples
172
+
173
+ ### Rate-Limited Chat
174
+
175
+ ```javascript
176
+ // middleware/chat-rate-limit.js
177
+ const userMessages = new Map()
178
+
179
+ module.exports = async Odac => {
180
+ const user = await Odac.Auth.user()
181
+ if (!user) return false
182
+
183
+ const userId = user.id
184
+ const now = Date.now()
185
+
186
+ if (!userMessages.has(userId)) {
187
+ userMessages.set(userId, [])
188
+ }
189
+
190
+ const messages = userMessages.get(userId)
191
+ const recentMessages = messages.filter(time => now - time < 10000)
192
+
193
+ if (recentMessages.length >= 10) {
194
+ return false
195
+ }
196
+
197
+ recentMessages.push(now)
198
+ userMessages.set(userId, recentMessages)
199
+
200
+ return true
201
+ }
202
+
203
+ // route/websocket.js
204
+ Odac.Route.use('chat-rate-limit').auth.ws('/chat', async Odac => {
205
+ const user = await Odac.Auth.user()
206
+ Odac.ws.join('general')
207
+
208
+ Odac.ws.on('message', data => {
209
+ Odac.ws.to('general').send({
210
+ user: user.name,
211
+ text: data.text
212
+ })
213
+ })
214
+ })
215
+ ```
216
+
217
+ ### Admin-Only WebSocket
218
+
219
+ ```javascript
220
+ // middleware/admin-only.js
221
+ module.exports = async Odac => {
222
+ const user = await Odac.Auth.user()
223
+
224
+ if (!user || !user.isAdmin) {
225
+ return false
226
+ }
227
+
228
+ return true
229
+ }
230
+
231
+ // route/websocket.js
232
+ Odac.Route.use('admin-only').ws('/admin-dashboard', Odac => {
233
+ const sendStats = async () => {
234
+ const stats = await getSystemStats()
235
+ Odac.ws.send({type: 'stats', data: stats})
236
+ }
237
+
238
+ sendStats()
239
+ const interval = setInterval(sendStats, 5000)
240
+
241
+ Odac.ws.on('close', () => clearInterval(interval))
242
+ })
243
+ ```
244
+
245
+ ### IP Whitelist
246
+
247
+ ```javascript
248
+ // middleware/ip-whitelist.js
249
+ const ALLOWED_IPS = ['127.0.0.1', '192.168.1.100']
250
+
251
+ module.exports = async Odac => {
252
+ const ip = Odac.Request.ip
253
+
254
+ if (!ALLOWED_IPS.includes(ip)) {
255
+ console.log(`WebSocket connection rejected from ${ip}`)
256
+ return false
257
+ }
258
+
259
+ return true
260
+ }
261
+
262
+ // route/websocket.js
263
+ Odac.Route.use('ip-whitelist').ws('/internal', Odac => {
264
+ Odac.ws.send({type: 'internal-access-granted'})
265
+ })
266
+ ```
267
+
268
+ ## Error Handling
269
+
270
+ Middleware errors should be handled gracefully:
271
+
272
+ ```javascript
273
+ module.exports = async Odac => {
274
+ try {
275
+ const result = await someAsyncOperation()
276
+ return result.isValid
277
+ } catch (error) {
278
+ console.error('Middleware error:', error)
279
+ return false
280
+ }
281
+ }
282
+ ```
283
+
284
+ ## Best Practices
285
+
286
+ 1. **Always return a value** from middleware (true/false/undefined)
287
+ 2. **Log rejections** for debugging
288
+ 3. **Clean up resources** in middleware if needed
289
+ 4. **Use auth.ws()** for simple authentication
290
+ 5. **Use middleware** for complex logic (rate limiting, logging, etc.)
291
+ 6. **Combine middleware** for layered security
292
+ 7. **Test middleware** independently before using in routes