aris 1.4.2 → 1.5.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +251 -0
  3. data/README.md +18 -0
  4. data/docs/ADAPTERS.md +478 -0
  5. data/docs/ARCHITECTURE.md +222 -0
  6. data/docs/CONTENT.md +967 -0
  7. data/docs/PERFORMANCE.md +492 -0
  8. data/docs/PLUGIN_DEVELOPMENT.md +688 -0
  9. data/docs/USAGE.md +4998 -0
  10. data/docs/plugins/API_KEY_AUTH.md +232 -0
  11. data/docs/plugins/BASIC_AUTH.md +582 -0
  12. data/docs/plugins/BEARER_AUTH.md +394 -0
  13. data/docs/plugins/CACHE.md +369 -0
  14. data/docs/plugins/COMPRESSION.md +216 -0
  15. data/docs/plugins/COOKIES.md +30 -0
  16. data/docs/plugins/CORS.md +283 -0
  17. data/docs/plugins/CSRF.md +751 -0
  18. data/docs/plugins/ETAG.md +308 -0
  19. data/docs/plugins/FORM_PARSER.md +193 -0
  20. data/docs/plugins/HEALTH_CHECK.md +469 -0
  21. data/docs/plugins/JSON.md +291 -0
  22. data/docs/plugins/MULTIPART.md +427 -0
  23. data/docs/plugins/RATE_LIMITER.md +368 -0
  24. data/docs/plugins/REQUEST_ID.md +369 -0
  25. data/docs/plugins/REQUEST_LOGGER.md +151 -0
  26. data/docs/plugins/SECURITY.md +193 -0
  27. data/docs/plugins/SESSION.md +98 -0
  28. data/lib/aris/adapters/rack/adapter.rb +17 -2
  29. data/lib/aris/adapters/rack/request.rb +29 -11
  30. data/lib/aris/plugins/basic_auth.rb +3 -1
  31. data/lib/aris/plugins/cookies.rb +4 -32
  32. data/lib/aris/plugins/cors.rb +8 -1
  33. data/lib/aris/plugins/csrf.rb +63 -22
  34. data/lib/aris/plugins/flash.rb +3 -1
  35. data/lib/aris/plugins/form_parser.rb +52 -31
  36. data/lib/aris/plugins/multipart.rb +22 -2
  37. data/lib/aris/plugins/request_logger.rb +8 -1
  38. data/lib/aris/plugins/security_headers.rb +8 -1
  39. data/lib/aris/plugins/session.rb +150 -99
  40. data/lib/aris/response_helpers.rb +41 -0
  41. data/lib/aris/version.rb +2 -2
  42. metadata +31 -3
@@ -0,0 +1,469 @@
1
+ # Health Check Plugin
2
+
3
+ Provides a `/health` endpoint for monitoring, load balancers, and orchestration tools (Kubernetes, ECS, etc.). Returns service health status with optional dependency checks.
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ require 'aris/plugins/health_check'
9
+ ```
10
+
11
+ ## Basic Usage
12
+
13
+ ```ruby
14
+ health = Aris::Plugins::HealthCheck.build
15
+
16
+ Aris.routes({
17
+ "api.example.com": {
18
+ use: [health], # Available at GET /health
19
+ "/users": { get: { to: UsersHandler } }
20
+ }
21
+ })
22
+
23
+ # GET /health
24
+ # => { "status": "ok", "name": "app", "checks": {}, "timestamp": "2025-10-10T14:23:45Z" }
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ | Option | Type | Default | Description |
30
+ |--------|------|---------|-------------|
31
+ | `path` | String | `'/health'` | Health check endpoint path |
32
+ | `checks` | Hash | `{}` | Health check functions (name → proc) |
33
+ | `name` | String | `'app'` | Service name |
34
+ | `version` | String | `nil` | Service version (optional) |
35
+
36
+ ## Examples
37
+
38
+ ### Simple Health Check
39
+
40
+ ```ruby
41
+ health = Aris::Plugins::HealthCheck.build
42
+
43
+ # GET /health
44
+ # Response: 200 OK
45
+ # {
46
+ # "status": "ok",
47
+ # "name": "app",
48
+ # "checks": {},
49
+ # "timestamp": "2025-10-10T14:23:45Z"
50
+ # }
51
+ ```
52
+
53
+ ### With Database Check
54
+
55
+ ```ruby
56
+ health = Aris::Plugins::HealthCheck.build(
57
+ checks: {
58
+ database: -> {
59
+ ActiveRecord::Base.connection.active?
60
+ }
61
+ }
62
+ )
63
+
64
+ # GET /health
65
+ # Response: 200 OK (if DB healthy)
66
+ # {
67
+ # "status": "ok",
68
+ # "name": "app",
69
+ # "checks": {
70
+ # "database": "ok"
71
+ # },
72
+ # "timestamp": "2025-10-10T14:23:45Z"
73
+ # }
74
+
75
+ # Response: 503 Service Unavailable (if DB down)
76
+ # {
77
+ # "status": "degraded",
78
+ # "name": "app",
79
+ # "checks": {
80
+ # "database": "fail"
81
+ # },
82
+ # "timestamp": "2025-10-10T14:23:45Z"
83
+ # }
84
+ ```
85
+
86
+ ### Multiple Dependency Checks
87
+
88
+ ```ruby
89
+ health = Aris::Plugins::HealthCheck.build(
90
+ name: 'user-api',
91
+ version: '1.2.3',
92
+ checks: {
93
+ database: -> {
94
+ ActiveRecord::Base.connection.active?
95
+ },
96
+ redis: -> {
97
+ Redis.new.ping == 'PONG'
98
+ },
99
+ s3: -> {
100
+ AWS::S3.new.list_buckets.any?
101
+ }
102
+ }
103
+ )
104
+
105
+ # All healthy: 200 OK
106
+ # Any failing: 503 Service Unavailable
107
+ ```
108
+
109
+ ### Custom Path
110
+
111
+ ```ruby
112
+ health = Aris::Plugins::HealthCheck.build(
113
+ path: '/status'
114
+ )
115
+
116
+ # Available at GET /status instead of /health
117
+ ```
118
+
119
+ ### Kubernetes Liveness Probe
120
+
121
+ ```ruby
122
+ health = Aris::Plugins::HealthCheck.build(
123
+ path: '/healthz',
124
+ name: 'my-service'
125
+ )
126
+
127
+ # In kubernetes.yaml:
128
+ # livenessProbe:
129
+ # httpGet:
130
+ # path: /healthz
131
+ # port: 3000
132
+ # initialDelaySeconds: 10
133
+ # periodSeconds: 5
134
+ ```
135
+
136
+ ### AWS ELB Health Check
137
+
138
+ ```ruby
139
+ health = Aris::Plugins::HealthCheck.build(
140
+ checks: {
141
+ database: -> { DB.ping }
142
+ }
143
+ )
144
+
145
+ # In ELB config:
146
+ # Health check path: /health
147
+ # Success codes: 200
148
+ # Unhealthy threshold: 2
149
+ ```
150
+
151
+ ## Check Function Patterns
152
+
153
+ ### Database (ActiveRecord)
154
+
155
+ ```ruby
156
+ database: -> {
157
+ ActiveRecord::Base.connection.active?
158
+ }
159
+ ```
160
+
161
+ ### Database (Sequel)
162
+
163
+ ```ruby
164
+ database: -> {
165
+ DB.test_connection
166
+ }
167
+ ```
168
+
169
+ ### Redis
170
+
171
+ ```ruby
172
+ redis: -> {
173
+ Redis.new.ping == 'PONG'
174
+ }
175
+ ```
176
+
177
+ ### External API
178
+
179
+ ```ruby
180
+ payment_api: -> {
181
+ response = HTTParty.get('https://api.stripe.com/v1/status')
182
+ response.code == 200
183
+ }
184
+ ```
185
+
186
+ ### File System
187
+
188
+ ```ruby
189
+ storage: -> {
190
+ File.writable?('/var/uploads')
191
+ }
192
+ ```
193
+
194
+ ### Memory Usage
195
+
196
+ ```ruby
197
+ memory: -> {
198
+ # Check if memory usage is below threshold
199
+ `ps -o rss= -p #{Process.pid}`.to_i < 500_000 # 500MB
200
+ }
201
+ ```
202
+
203
+ ### Custom Logic
204
+
205
+ ```ruby
206
+ workers: -> {
207
+ # Check if background workers are running
208
+ Sidekiq::ProcessSet.new.size > 0
209
+ }
210
+ ```
211
+
212
+ ## Response Format
213
+
214
+ ### Healthy Response (200 OK)
215
+
216
+ ```json
217
+ {
218
+ "status": "ok",
219
+ "name": "user-api",
220
+ "version": "1.2.3",
221
+ "checks": {
222
+ "database": "ok",
223
+ "redis": "ok"
224
+ },
225
+ "timestamp": "2025-10-10T14:23:45Z"
226
+ }
227
+ ```
228
+
229
+ ### Degraded Response (503 Service Unavailable)
230
+
231
+ ```json
232
+ {
233
+ "status": "degraded",
234
+ "name": "user-api",
235
+ "version": "1.2.3",
236
+ "checks": {
237
+ "database": "ok",
238
+ "redis": "fail"
239
+ },
240
+ "timestamp": "2025-10-10T14:23:45Z"
241
+ }
242
+ ```
243
+
244
+ ### Check Exception (503 Service Unavailable)
245
+
246
+ ```json
247
+ {
248
+ "status": "degraded",
249
+ "name": "user-api",
250
+ "checks": {
251
+ "database": "error: Connection refused"
252
+ },
253
+ "timestamp": "2025-10-10T14:23:45Z"
254
+ }
255
+ ```
256
+
257
+ ## Production Tips
258
+
259
+ ### 1. Keep Checks Fast
260
+
261
+ Health checks run on every probe (every 5-10 seconds):
262
+
263
+ ```ruby
264
+ # ❌ BAD - Slow query
265
+ database: -> { User.count > 0 }
266
+
267
+ # ✅ GOOD - Fast connection test
268
+ database: -> { ActiveRecord::Base.connection.active? }
269
+ ```
270
+
271
+ ### 2. Separate Liveness vs Readiness
272
+
273
+ **Liveness:** Is the app alive?
274
+ ```ruby
275
+ liveness = HealthCheck.build(
276
+ path: '/healthz',
277
+ checks: {} # No dependency checks
278
+ )
279
+ ```
280
+
281
+ **Readiness:** Is the app ready to serve traffic?
282
+ ```ruby
283
+ readiness = HealthCheck.build(
284
+ path: '/ready',
285
+ checks: {
286
+ database: -> { DB.ping },
287
+ redis: -> { Redis.new.ping == 'PONG' }
288
+ }
289
+ )
290
+ ```
291
+
292
+ ### 3. Timeout Checks
293
+
294
+ ```ruby
295
+ require 'timeout'
296
+
297
+ health = HealthCheck.build(
298
+ checks: {
299
+ database: -> {
300
+ Timeout.timeout(2) do # 2 second timeout
301
+ ActiveRecord::Base.connection.active?
302
+ end
303
+ rescue Timeout::Error
304
+ false
305
+ }
306
+ }
307
+ )
308
+ ```
309
+
310
+ ### 4. Log Failed Checks
311
+
312
+ ```ruby
313
+ health = HealthCheck.build(
314
+ checks: {
315
+ database: -> {
316
+ result = DB.ping
317
+ logger.error("Database health check failed") unless result
318
+ result
319
+ }
320
+ }
321
+ )
322
+ ```
323
+
324
+ ### 5. Monitoring Integration
325
+
326
+ ```ruby
327
+ # Send metrics to DataDog/StatsD
328
+ health = HealthCheck.build(
329
+ checks: {
330
+ database: -> {
331
+ start = Time.now
332
+ result = DB.ping
333
+ duration = Time.now - start
334
+
335
+ StatsD.gauge('health.database.duration', duration)
336
+ StatsD.gauge('health.database.status', result ? 1 : 0)
337
+
338
+ result
339
+ }
340
+ }
341
+ )
342
+ ```
343
+
344
+ ### 6. Security - Internal Only
345
+
346
+ Don't expose health to public:
347
+
348
+ ```ruby
349
+ # Option A: Separate domain
350
+ internal_health = HealthCheck.build
351
+
352
+ Aris.routes({
353
+ "internal.api.com": { # Internal network only
354
+ use: [internal_health],
355
+ "/admin": { get: { to: AdminHandler } }
356
+ },
357
+ "api.com": { # Public
358
+ "/users": { get: { to: UsersHandler } }
359
+ }
360
+ })
361
+
362
+ # Option B: IP whitelist
363
+ # (Use firewall/load balancer rules)
364
+ ```
365
+
366
+ ## Common Patterns
367
+
368
+ ### Kubernetes
369
+
370
+ ```yaml
371
+ # deployment.yaml
372
+ livenessProbe:
373
+ httpGet:
374
+ path: /healthz
375
+ port: 3000
376
+ initialDelaySeconds: 10
377
+ periodSeconds: 10
378
+
379
+ readinessProbe:
380
+ httpGet:
381
+ path: /ready
382
+ port: 3000
383
+ initialDelaySeconds: 5
384
+ periodSeconds: 5
385
+ ```
386
+
387
+ ```ruby
388
+ # health_checks.rb
389
+ liveness = HealthCheck.build(path: '/healthz')
390
+ readiness = HealthCheck.build(
391
+ path: '/ready',
392
+ checks: {
393
+ database: -> { DB.ping },
394
+ redis: -> { Redis.new.ping == 'PONG' }
395
+ }
396
+ )
397
+
398
+ Aris.routes({
399
+ "api.example.com": {
400
+ use: [liveness, readiness],
401
+ "/users": { get: { to: UsersHandler } }
402
+ }
403
+ })
404
+ ```
405
+
406
+ ### Docker Compose
407
+
408
+ ```yaml
409
+ # docker-compose.yml
410
+ services:
411
+ api:
412
+ healthcheck:
413
+ test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
414
+ interval: 10s
415
+ timeout: 3s
416
+ retries: 3
417
+ ```
418
+
419
+ ### AWS ECS
420
+
421
+ ```json
422
+ {
423
+ "healthCheck": {
424
+ "command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
425
+ "interval": 30,
426
+ "timeout": 5,
427
+ "retries": 3
428
+ }
429
+ }
430
+ ```
431
+
432
+ ### Uptime Monitoring (Pingdom, UptimeRobot)
433
+
434
+ ```ruby
435
+ # Simple endpoint, no dependency checks
436
+ health = HealthCheck.build
437
+
438
+ # Configure monitor:
439
+ # URL: https://api.example.com/health
440
+ # Expected: 200 OK
441
+ # Check frequency: 1 minute
442
+ ```
443
+
444
+ ## Notes
445
+
446
+ - Halts plugin pipeline (returns immediately)
447
+ - Only responds to GET requests
448
+ - Check functions should be fast (<100ms)
449
+ - Failed checks return 503 (Service Unavailable)
450
+ - Exceptions in checks are caught and reported
451
+ - Thread-safe (check functions run synchronously)
452
+
453
+ ## Troubleshooting
454
+
455
+ **Health check not responding?**
456
+ - Verify plugin is in `use:` array
457
+ - Check path matches (default: `/health`)
458
+ - Ensure GET request (POST won't work)
459
+
460
+ **Always returns 503?**
461
+ - Check which dependency is failing
462
+ - Look at `checks` field in response
463
+ - Add logging to check functions
464
+
465
+ **Slow health checks?**
466
+ - Profile check functions
467
+ - Add timeouts
468
+ - Remove expensive checks
469
+ - Consider separate liveness/readiness