create-fluxstack 1.0.13 → 1.0.15

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 (214) hide show
  1. package/.env.example +29 -29
  2. package/app/client/README.md +69 -69
  3. package/app/client/index.html +14 -13
  4. package/app/client/src/App.tsx +157 -524
  5. package/app/client/src/components/ErrorBoundary.tsx +107 -0
  6. package/app/client/src/components/ErrorDisplay.css +365 -0
  7. package/app/client/src/components/ErrorDisplay.tsx +258 -0
  8. package/app/client/src/components/FluxStackConfig.tsx +1321 -0
  9. package/app/client/src/components/HybridLiveCounter.tsx +140 -0
  10. package/app/client/src/components/LiveClock.tsx +286 -0
  11. package/app/client/src/components/MainLayout.tsx +390 -0
  12. package/app/client/src/components/SidebarNavigation.tsx +391 -0
  13. package/app/client/src/components/StateDemo.tsx +178 -0
  14. package/app/client/src/components/SystemMonitor.tsx +1038 -0
  15. package/app/client/src/components/Teste.tsx +104 -0
  16. package/app/client/src/components/UserProfile.tsx +809 -0
  17. package/app/client/src/hooks/useAuth.ts +39 -0
  18. package/app/client/src/hooks/useNotifications.ts +56 -0
  19. package/app/client/src/lib/eden-api.ts +189 -53
  20. package/app/client/src/lib/errors.ts +340 -0
  21. package/app/client/src/lib/hooks/useErrorHandler.ts +258 -0
  22. package/app/client/src/lib/index.ts +45 -0
  23. package/app/client/src/main.tsx +3 -2
  24. package/app/client/src/pages/ApiDocs.tsx +182 -0
  25. package/app/client/src/pages/Demo.tsx +174 -0
  26. package/app/client/src/pages/HybridLive.tsx +263 -0
  27. package/app/client/src/pages/Overview.tsx +155 -0
  28. package/app/client/src/store/README.md +43 -0
  29. package/app/client/src/store/index.ts +16 -0
  30. package/app/client/src/store/slices/uiSlice.ts +151 -0
  31. package/app/client/src/store/slices/userSlice.ts +161 -0
  32. package/app/client/src/test/README.md +257 -0
  33. package/app/client/src/test/setup.ts +70 -0
  34. package/app/client/src/test/types.ts +12 -0
  35. package/app/client/src/vite-env.d.ts +1 -1
  36. package/app/client/tsconfig.app.json +44 -43
  37. package/app/client/tsconfig.json +7 -7
  38. package/app/client/tsconfig.node.json +25 -25
  39. package/app/client/zustand-setup.md +65 -0
  40. package/app/server/controllers/users.controller.ts +68 -68
  41. package/app/server/index.ts +9 -1
  42. package/app/server/live/CounterComponent.ts +191 -0
  43. package/app/server/live/FluxStackConfig.ts +529 -0
  44. package/app/server/live/LiveClockComponent.ts +214 -0
  45. package/app/server/live/SidebarNavigation.ts +156 -0
  46. package/app/server/live/SystemMonitor.ts +594 -0
  47. package/app/server/live/SystemMonitorIntegration.ts +151 -0
  48. package/app/server/live/TesteComponent.ts +87 -0
  49. package/app/server/live/UserProfileComponent.ts +135 -0
  50. package/app/server/live/register-components.ts +28 -0
  51. package/app/server/middleware/auth.ts +136 -0
  52. package/app/server/middleware/errorHandling.ts +250 -0
  53. package/app/server/middleware/index.ts +10 -0
  54. package/app/server/middleware/rateLimit.ts +193 -0
  55. package/app/server/middleware/requestLogging.ts +215 -0
  56. package/app/server/middleware/validation.ts +270 -0
  57. package/app/server/routes/index.ts +14 -2
  58. package/app/server/routes/upload.ts +92 -0
  59. package/app/server/routes/users.routes.ts +2 -9
  60. package/app/server/services/NotificationService.ts +302 -0
  61. package/app/server/services/UserService.ts +222 -0
  62. package/app/server/services/index.ts +46 -0
  63. package/core/cli/commands/plugin-deps.ts +263 -0
  64. package/core/cli/generators/README.md +339 -0
  65. package/core/cli/generators/component.ts +770 -0
  66. package/core/cli/generators/controller.ts +299 -0
  67. package/core/cli/generators/index.ts +144 -0
  68. package/core/cli/generators/interactive.ts +228 -0
  69. package/core/cli/generators/prompts.ts +83 -0
  70. package/core/cli/generators/route.ts +513 -0
  71. package/core/cli/generators/service.ts +465 -0
  72. package/core/cli/generators/template-engine.ts +154 -0
  73. package/core/cli/generators/types.ts +71 -0
  74. package/core/cli/generators/utils.ts +192 -0
  75. package/core/cli/index.ts +69 -0
  76. package/core/cli/plugin-discovery.ts +16 -85
  77. package/core/client/fluxstack.ts +17 -0
  78. package/core/client/hooks/index.ts +7 -0
  79. package/core/client/hooks/state-validator.ts +130 -0
  80. package/core/client/hooks/useAuth.ts +49 -0
  81. package/core/client/hooks/useChunkedUpload.ts +258 -0
  82. package/core/client/hooks/useHybridLiveComponent.ts +967 -0
  83. package/core/client/hooks/useWebSocket.ts +373 -0
  84. package/core/client/index.ts +47 -0
  85. package/core/client/state/createStore.ts +193 -0
  86. package/core/client/state/index.ts +15 -0
  87. package/core/config/env-dynamic.ts +1 -1
  88. package/core/config/env.ts +2 -1
  89. package/core/config/runtime-config.ts +3 -3
  90. package/core/config/schema.ts +84 -49
  91. package/core/framework/server.ts +30 -0
  92. package/core/index.ts +25 -0
  93. package/core/live/ComponentRegistry.ts +399 -0
  94. package/core/live/types.ts +164 -0
  95. package/core/plugins/built-in/live-components/commands/create-live-component.ts +1201 -0
  96. package/core/plugins/built-in/live-components/index.ts +27 -0
  97. package/core/plugins/built-in/logger/index.ts +1 -1
  98. package/core/plugins/built-in/monitoring/index.ts +1 -1
  99. package/core/plugins/built-in/static/index.ts +1 -1
  100. package/core/plugins/built-in/swagger/index.ts +1 -1
  101. package/core/plugins/built-in/vite/index.ts +1 -1
  102. package/core/plugins/dependency-manager.ts +384 -0
  103. package/core/plugins/index.ts +5 -1
  104. package/core/plugins/manager.ts +7 -3
  105. package/core/plugins/registry.ts +88 -10
  106. package/core/plugins/types.ts +11 -11
  107. package/core/server/framework.ts +43 -0
  108. package/core/server/index.ts +11 -1
  109. package/core/server/live/ComponentRegistry.ts +1017 -0
  110. package/core/server/live/FileUploadManager.ts +272 -0
  111. package/core/server/live/LiveComponentPerformanceMonitor.ts +930 -0
  112. package/core/server/live/SingleConnectionManager.ts +0 -0
  113. package/core/server/live/StateSignature.ts +644 -0
  114. package/core/server/live/WebSocketConnectionManager.ts +688 -0
  115. package/core/server/live/websocket-plugin.ts +435 -0
  116. package/core/server/middleware/errorHandling.ts +141 -0
  117. package/core/server/middleware/index.ts +16 -0
  118. package/core/server/plugins/static-files-plugin.ts +232 -0
  119. package/core/server/services/BaseService.ts +95 -0
  120. package/core/server/services/ServiceContainer.ts +144 -0
  121. package/core/server/services/index.ts +9 -0
  122. package/core/templates/create-project.ts +196 -33
  123. package/core/testing/index.ts +10 -0
  124. package/core/testing/setup.ts +74 -0
  125. package/core/types/build.ts +38 -14
  126. package/core/types/types.ts +319 -0
  127. package/core/utils/env-runtime.ts +7 -0
  128. package/core/utils/errors/handlers.ts +264 -39
  129. package/core/utils/errors/index.ts +528 -18
  130. package/core/utils/errors/middleware.ts +114 -0
  131. package/core/utils/logger/formatters.ts +222 -0
  132. package/core/utils/logger/index.ts +167 -48
  133. package/core/utils/logger/middleware.ts +253 -0
  134. package/core/utils/logger/performance.ts +384 -0
  135. package/core/utils/logger/transports.ts +365 -0
  136. package/create-fluxstack.ts +296 -296
  137. package/fluxstack.config.ts +17 -1
  138. package/package-template.json +66 -66
  139. package/package.json +31 -6
  140. package/public/README.md +16 -0
  141. package/vite.config.ts +29 -14
  142. package/.claude/settings.local.json +0 -74
  143. package/.github/workflows/ci-build-tests.yml +0 -480
  144. package/.github/workflows/dependency-management.yml +0 -324
  145. package/.github/workflows/release-validation.yml +0 -355
  146. package/.kiro/specs/fluxstack-architecture-optimization/design.md +0 -700
  147. package/.kiro/specs/fluxstack-architecture-optimization/requirements.md +0 -127
  148. package/.kiro/specs/fluxstack-architecture-optimization/tasks.md +0 -330
  149. package/CLAUDE.md +0 -200
  150. package/Dockerfile +0 -58
  151. package/Dockerfile.backend +0 -52
  152. package/Dockerfile.frontend +0 -54
  153. package/README-Docker.md +0 -85
  154. package/ai-context/00-QUICK-START.md +0 -86
  155. package/ai-context/README.md +0 -88
  156. package/ai-context/development/eden-treaty-guide.md +0 -362
  157. package/ai-context/development/patterns.md +0 -382
  158. package/ai-context/development/plugins-guide.md +0 -572
  159. package/ai-context/examples/crud-complete.md +0 -626
  160. package/ai-context/project/architecture.md +0 -399
  161. package/ai-context/project/overview.md +0 -213
  162. package/ai-context/recent-changes/eden-treaty-refactor.md +0 -281
  163. package/ai-context/recent-changes/type-inference-fix.md +0 -223
  164. package/ai-context/reference/environment-vars.md +0 -384
  165. package/ai-context/reference/troubleshooting.md +0 -407
  166. package/app/client/src/components/TestPage.tsx +0 -453
  167. package/bun.lock +0 -1063
  168. package/bunfig.toml +0 -16
  169. package/core/__tests__/integration.test.ts +0 -227
  170. package/core/build/index.ts +0 -186
  171. package/core/config/__tests__/config-loader.test.ts +0 -554
  172. package/core/config/__tests__/config-merger.test.ts +0 -657
  173. package/core/config/__tests__/env-converter.test.ts +0 -372
  174. package/core/config/__tests__/env-processor.test.ts +0 -431
  175. package/core/config/__tests__/env.test.ts +0 -452
  176. package/core/config/__tests__/integration.test.ts +0 -418
  177. package/core/config/__tests__/loader.test.ts +0 -331
  178. package/core/config/__tests__/schema.test.ts +0 -129
  179. package/core/config/__tests__/validator.test.ts +0 -318
  180. package/core/framework/__tests__/server.test.ts +0 -233
  181. package/core/plugins/__tests__/built-in.test.ts.disabled +0 -366
  182. package/core/plugins/__tests__/manager.test.ts +0 -398
  183. package/core/plugins/__tests__/monitoring.test.ts +0 -401
  184. package/core/plugins/__tests__/registry.test.ts +0 -335
  185. package/core/utils/__tests__/errors.test.ts +0 -139
  186. package/core/utils/__tests__/helpers.test.ts +0 -297
  187. package/core/utils/__tests__/logger.test.ts +0 -141
  188. package/create-test-app.ts +0 -156
  189. package/docker-compose.microservices.yml +0 -75
  190. package/docker-compose.simple.yml +0 -57
  191. package/docker-compose.yml +0 -71
  192. package/eslint.config.js +0 -23
  193. package/flux-cli.ts +0 -214
  194. package/nginx-lb.conf +0 -37
  195. package/publish.sh +0 -63
  196. package/run-clean.ts +0 -26
  197. package/run-env-tests.ts +0 -313
  198. package/tailwind.config.js +0 -34
  199. package/tests/__mocks__/api.ts +0 -56
  200. package/tests/fixtures/users.ts +0 -69
  201. package/tests/integration/api/users.routes.test.ts +0 -221
  202. package/tests/setup.ts +0 -29
  203. package/tests/unit/app/client/App-simple.test.tsx +0 -56
  204. package/tests/unit/app/client/App.test.tsx.skip +0 -237
  205. package/tests/unit/app/client/eden-api.test.ts +0 -186
  206. package/tests/unit/app/client/simple.test.tsx +0 -23
  207. package/tests/unit/app/controllers/users.controller.test.ts +0 -150
  208. package/tests/unit/core/create-project.test.ts.skip +0 -95
  209. package/tests/unit/core/framework.test.ts +0 -144
  210. package/tests/unit/core/plugins/logger.test.ts.skip +0 -268
  211. package/tests/unit/core/plugins/vite.test.ts.disabled +0 -188
  212. package/tests/utils/test-helpers.ts +0 -61
  213. package/vitest.config.ts +0 -50
  214. package/workspace.json +0 -6
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Error Handling Middleware
3
+ * Provides centralized error handling for the application
4
+ */
5
+
6
+ import type { Context } from 'elysia'
7
+
8
+ export interface ErrorResponse {
9
+ error: string
10
+ message: string
11
+ code?: string
12
+ details?: any
13
+ timestamp: string
14
+ requestId?: string
15
+ }
16
+
17
+ /**
18
+ * Custom application errors
19
+ */
20
+ export class AppError extends Error {
21
+ constructor(
22
+ message: string,
23
+ public statusCode: number = 500,
24
+ public code?: string,
25
+ public details?: any
26
+ ) {
27
+ super(message)
28
+ this.name = 'AppError'
29
+ }
30
+ }
31
+
32
+ export class ValidationError extends AppError {
33
+ constructor(message: string, details?: any) {
34
+ super(message, 400, 'VALIDATION_ERROR', details)
35
+ this.name = 'ValidationError'
36
+ }
37
+ }
38
+
39
+ export class NotFoundError extends AppError {
40
+ constructor(resource: string = 'Resource') {
41
+ super(`${resource} not found`, 404, 'NOT_FOUND')
42
+ this.name = 'NotFoundError'
43
+ }
44
+ }
45
+
46
+ export class UnauthorizedError extends AppError {
47
+ constructor(message: string = 'Unauthorized') {
48
+ super(message, 401, 'UNAUTHORIZED')
49
+ this.name = 'UnauthorizedError'
50
+ }
51
+ }
52
+
53
+ export class ForbiddenError extends AppError {
54
+ constructor(message: string = 'Forbidden') {
55
+ super(message, 403, 'FORBIDDEN')
56
+ this.name = 'ForbiddenError'
57
+ }
58
+ }
59
+
60
+ export class ConflictError extends AppError {
61
+ constructor(message: string, details?: any) {
62
+ super(message, 409, 'CONFLICT', details)
63
+ this.name = 'ConflictError'
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Error handling middleware
69
+ */
70
+ export const errorHandlingMiddleware = {
71
+ name: 'error-handling',
72
+
73
+ onError: async (context: Context, error: Error): Promise<Response> => {
74
+ const requestId = context.store?.requestId || 'unknown'
75
+
76
+ // Log the error
77
+ console.error('🚨 Error occurred', {
78
+ requestId,
79
+ method: context.request.method,
80
+ path: context.path,
81
+ error: error.message,
82
+ stack: error.stack,
83
+ timestamp: new Date().toISOString()
84
+ })
85
+
86
+ // Handle different error types
87
+ if (error instanceof AppError) {
88
+ return createErrorResponse(
89
+ error.statusCode,
90
+ error.message,
91
+ error.code,
92
+ error.details,
93
+ requestId
94
+ )
95
+ }
96
+
97
+ // Handle validation errors from Elysia
98
+ if (error.name === 'ValidationError' || error.message.includes('validation')) {
99
+ return createErrorResponse(
100
+ 400,
101
+ 'Validation failed',
102
+ 'VALIDATION_ERROR',
103
+ { originalError: error.message },
104
+ requestId
105
+ )
106
+ }
107
+
108
+ // Handle syntax errors (malformed JSON, etc.)
109
+ if (error instanceof SyntaxError) {
110
+ return createErrorResponse(
111
+ 400,
112
+ 'Invalid request format',
113
+ 'SYNTAX_ERROR',
114
+ undefined,
115
+ requestId
116
+ )
117
+ }
118
+
119
+ // Handle network/timeout errors
120
+ if (error.name === 'TimeoutError' || error.message.includes('timeout')) {
121
+ return createErrorResponse(
122
+ 408,
123
+ 'Request timeout',
124
+ 'TIMEOUT_ERROR',
125
+ undefined,
126
+ requestId
127
+ )
128
+ }
129
+
130
+ // Handle database/external service errors
131
+ if (error.message.includes('ECONNREFUSED') || error.message.includes('connection')) {
132
+ return createErrorResponse(
133
+ 503,
134
+ 'Service temporarily unavailable',
135
+ 'SERVICE_UNAVAILABLE',
136
+ undefined,
137
+ requestId
138
+ )
139
+ }
140
+
141
+ // Default to internal server error
142
+ return createErrorResponse(
143
+ 500,
144
+ process.env.NODE_ENV === 'production'
145
+ ? 'Internal server error'
146
+ : error.message,
147
+ 'INTERNAL_ERROR',
148
+ process.env.NODE_ENV === 'production'
149
+ ? undefined
150
+ : { stack: error.stack },
151
+ requestId
152
+ )
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Create standardized error response
158
+ */
159
+ function createErrorResponse(
160
+ statusCode: number,
161
+ message: string,
162
+ code?: string,
163
+ details?: any,
164
+ requestId?: string
165
+ ): Response {
166
+ const errorResponse: ErrorResponse = {
167
+ error: getErrorName(statusCode),
168
+ message,
169
+ code,
170
+ details,
171
+ timestamp: new Date().toISOString(),
172
+ requestId
173
+ }
174
+
175
+ // Remove undefined fields
176
+ Object.keys(errorResponse).forEach(key => {
177
+ if (errorResponse[key as keyof ErrorResponse] === undefined) {
178
+ delete errorResponse[key as keyof ErrorResponse]
179
+ }
180
+ })
181
+
182
+ return new Response(
183
+ JSON.stringify(errorResponse),
184
+ {
185
+ status: statusCode,
186
+ headers: {
187
+ 'Content-Type': 'application/json'
188
+ }
189
+ }
190
+ )
191
+ }
192
+
193
+ /**
194
+ * Get error name from status code
195
+ */
196
+ function getErrorName(statusCode: number): string {
197
+ const errorNames: Record<number, string> = {
198
+ 400: 'Bad Request',
199
+ 401: 'Unauthorized',
200
+ 403: 'Forbidden',
201
+ 404: 'Not Found',
202
+ 405: 'Method Not Allowed',
203
+ 408: 'Request Timeout',
204
+ 409: 'Conflict',
205
+ 422: 'Unprocessable Entity',
206
+ 429: 'Too Many Requests',
207
+ 500: 'Internal Server Error',
208
+ 502: 'Bad Gateway',
209
+ 503: 'Service Unavailable',
210
+ 504: 'Gateway Timeout'
211
+ }
212
+
213
+ return errorNames[statusCode] || 'Unknown Error'
214
+ }
215
+
216
+ /**
217
+ * Async error wrapper for route handlers
218
+ */
219
+ export const asyncHandler = (fn: Function) => {
220
+ return async (context: Context) => {
221
+ try {
222
+ return await fn(context)
223
+ } catch (error) {
224
+ throw error // Let the error middleware handle it
225
+ }
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Create error response helper
231
+ */
232
+ export const createError = {
233
+ badRequest: (message: string, details?: any) =>
234
+ new ValidationError(message, details),
235
+
236
+ unauthorized: (message?: string) =>
237
+ new UnauthorizedError(message),
238
+
239
+ forbidden: (message?: string) =>
240
+ new ForbiddenError(message),
241
+
242
+ notFound: (resource?: string) =>
243
+ new NotFoundError(resource),
244
+
245
+ conflict: (message: string, details?: any) =>
246
+ new ConflictError(message, details),
247
+
248
+ internal: (message: string, details?: any) =>
249
+ new AppError(message, 500, 'INTERNAL_ERROR', details)
250
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Middleware Index
3
+ * Exports all custom middleware for the application
4
+ */
5
+
6
+ export { authMiddleware } from './auth'
7
+ export { validationMiddleware } from './validation'
8
+ export { rateLimitMiddleware } from './rateLimit'
9
+ export { requestLoggingMiddleware } from './requestLogging'
10
+ export { errorHandlingMiddleware } from './errorHandling'
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Rate Limiting Middleware
3
+ * Implements rate limiting to prevent abuse
4
+ */
5
+
6
+ import type { Context } from 'elysia'
7
+
8
+ export interface RateLimitConfig {
9
+ windowMs: number // Time window in milliseconds
10
+ maxRequests: number // Maximum requests per window
11
+ keyGenerator?: (context: Context) => string // Custom key generator
12
+ skipSuccessfulRequests?: boolean // Don't count successful requests
13
+ skipFailedRequests?: boolean // Don't count failed requests
14
+ message?: string // Custom error message
15
+ }
16
+
17
+ interface RateLimitEntry {
18
+ count: number
19
+ resetTime: number
20
+ }
21
+
22
+ /**
23
+ * In-memory rate limit store
24
+ * In production, you'd want to use Redis or another distributed store
25
+ */
26
+ class MemoryStore {
27
+ private store = new Map<string, RateLimitEntry>()
28
+
29
+ get(key: string): RateLimitEntry | undefined {
30
+ const entry = this.store.get(key)
31
+
32
+ // Clean up expired entries
33
+ if (entry && entry.resetTime < Date.now()) {
34
+ this.store.delete(key)
35
+ return undefined
36
+ }
37
+
38
+ return entry
39
+ }
40
+
41
+ set(key: string, entry: RateLimitEntry): void {
42
+ this.store.set(key, entry)
43
+ }
44
+
45
+ increment(key: string, windowMs: number): RateLimitEntry {
46
+ const now = Date.now()
47
+ const existing = this.get(key)
48
+
49
+ if (existing) {
50
+ existing.count++
51
+ return existing
52
+ } else {
53
+ const newEntry: RateLimitEntry = {
54
+ count: 1,
55
+ resetTime: now + windowMs
56
+ }
57
+ this.set(key, newEntry)
58
+ return newEntry
59
+ }
60
+ }
61
+
62
+ cleanup(): void {
63
+ const now = Date.now()
64
+ for (const [key, entry] of this.store.entries()) {
65
+ if (entry.resetTime < now) {
66
+ this.store.delete(key)
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ const store = new MemoryStore()
73
+
74
+ // Cleanup expired entries every minute
75
+ setInterval(() => {
76
+ store.cleanup()
77
+ }, 60000)
78
+
79
+ /**
80
+ * Create rate limiting middleware
81
+ */
82
+ export const rateLimitMiddleware = (config: RateLimitConfig) => ({
83
+ name: 'rate-limit',
84
+
85
+ beforeHandle: async (context: Context) => {
86
+ const key = config.keyGenerator
87
+ ? config.keyGenerator(context)
88
+ : getDefaultKey(context)
89
+
90
+ const entry = store.increment(key, config.windowMs)
91
+
92
+ // Add rate limit headers
93
+ const headers = {
94
+ 'X-RateLimit-Limit': config.maxRequests.toString(),
95
+ 'X-RateLimit-Remaining': Math.max(0, config.maxRequests - entry.count).toString(),
96
+ 'X-RateLimit-Reset': Math.ceil(entry.resetTime / 1000).toString()
97
+ }
98
+
99
+ // Check if rate limit exceeded
100
+ if (entry.count > config.maxRequests) {
101
+ return new Response(
102
+ JSON.stringify({
103
+ error: config.message || 'Too many requests',
104
+ retryAfter: Math.ceil((entry.resetTime - Date.now()) / 1000)
105
+ }),
106
+ {
107
+ status: 429,
108
+ headers: {
109
+ 'Content-Type': 'application/json',
110
+ 'Retry-After': Math.ceil((entry.resetTime - Date.now()) / 1000).toString(),
111
+ ...headers
112
+ }
113
+ }
114
+ )
115
+ }
116
+
117
+ // Add headers to successful responses
118
+ context.set.headers = { ...context.set.headers, ...headers }
119
+ }
120
+ })
121
+
122
+ /**
123
+ * Default key generator (IP-based)
124
+ */
125
+ function getDefaultKey(context: Context): string {
126
+ // Try to get real IP from various headers
127
+ const forwarded = context.headers['x-forwarded-for']
128
+ const realIp = context.headers['x-real-ip']
129
+ const cfConnectingIp = context.headers['cf-connecting-ip']
130
+
131
+ let ip = 'unknown'
132
+
133
+ if (forwarded) {
134
+ ip = forwarded.split(',')[0].trim()
135
+ } else if (realIp) {
136
+ ip = realIp
137
+ } else if (cfConnectingIp) {
138
+ ip = cfConnectingIp
139
+ }
140
+
141
+ return `rate_limit:${ip}`
142
+ }
143
+
144
+ /**
145
+ * User-based key generator
146
+ */
147
+ export const userKeyGenerator = (context: any): string => {
148
+ const userId = context.user?.id
149
+ return userId ? `rate_limit:user:${userId}` : getDefaultKey(context)
150
+ }
151
+
152
+ /**
153
+ * Endpoint-based key generator
154
+ */
155
+ export const endpointKeyGenerator = (context: Context): string => {
156
+ const ip = getDefaultKey(context)
157
+ const path = context.path
158
+ return `${ip}:${path}`
159
+ }
160
+
161
+ /**
162
+ * Common rate limit configurations
163
+ */
164
+ export const rateLimitConfigs = {
165
+ // General API rate limit
166
+ general: {
167
+ windowMs: 15 * 60 * 1000, // 15 minutes
168
+ maxRequests: 100,
169
+ message: 'Too many requests from this IP, please try again later'
170
+ },
171
+
172
+ // Strict rate limit for authentication endpoints
173
+ auth: {
174
+ windowMs: 15 * 60 * 1000, // 15 minutes
175
+ maxRequests: 5,
176
+ message: 'Too many authentication attempts, please try again later'
177
+ },
178
+
179
+ // Lenient rate limit for public endpoints
180
+ public: {
181
+ windowMs: 15 * 60 * 1000, // 15 minutes
182
+ maxRequests: 1000,
183
+ message: 'Rate limit exceeded for public API'
184
+ },
185
+
186
+ // Per-user rate limit
187
+ perUser: {
188
+ windowMs: 60 * 1000, // 1 minute
189
+ maxRequests: 60,
190
+ keyGenerator: userKeyGenerator,
191
+ message: 'Too many requests, please slow down'
192
+ }
193
+ }
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Request Logging Middleware
3
+ * Logs HTTP requests with timing and context information
4
+ */
5
+
6
+ import type { Context } from 'elysia'
7
+
8
+ export interface RequestLogConfig {
9
+ logLevel?: 'debug' | 'info' | 'warn' | 'error'
10
+ includeBody?: boolean
11
+ includeHeaders?: boolean
12
+ excludePaths?: string[]
13
+ excludeHeaders?: string[]
14
+ maxBodyLength?: number
15
+ }
16
+
17
+ /**
18
+ * Request logging middleware
19
+ */
20
+ export const requestLoggingMiddleware = (config: RequestLogConfig = {}) => ({
21
+ name: 'request-logging',
22
+
23
+ beforeHandle: async (context: Context) => {
24
+ // Skip logging for excluded paths
25
+ if (config.excludePaths?.includes(context.path)) {
26
+ return
27
+ }
28
+
29
+ const startTime = Date.now()
30
+
31
+ // Store start time for duration calculation
32
+ context.store = { ...context.store, startTime }
33
+
34
+ // Log request start
35
+ const requestId = generateRequestId()
36
+ context.store = { ...context.store, requestId }
37
+
38
+ const logData: any = {
39
+ requestId,
40
+ method: context.request.method,
41
+ path: context.path,
42
+ query: context.query,
43
+ userAgent: context.headers['user-agent'],
44
+ ip: getClientIp(context),
45
+ timestamp: new Date().toISOString()
46
+ }
47
+
48
+ // Include headers if configured
49
+ if (config.includeHeaders) {
50
+ const headers = { ...context.headers }
51
+
52
+ // Remove sensitive headers
53
+ const excludeHeaders = config.excludeHeaders || [
54
+ 'authorization',
55
+ 'cookie',
56
+ 'x-api-key'
57
+ ]
58
+
59
+ excludeHeaders.forEach(header => {
60
+ delete headers[header.toLowerCase()]
61
+ })
62
+
63
+ logData.headers = headers
64
+ }
65
+
66
+ // Include body if configured
67
+ if (config.includeBody && context.body) {
68
+ let body = context.body
69
+
70
+ // Truncate large bodies
71
+ if (config.maxBodyLength && typeof body === 'string') {
72
+ if (body.length > config.maxBodyLength) {
73
+ body = body.substring(0, config.maxBodyLength) + '...[truncated]'
74
+ }
75
+ }
76
+
77
+ logData.body = body
78
+ }
79
+
80
+ console.log('📥 Request started', logData)
81
+ },
82
+
83
+ afterHandle: async (context: Context, response: Response) => {
84
+ const startTime = context.store?.startTime
85
+ const requestId = context.store?.requestId
86
+
87
+ if (!startTime) return
88
+
89
+ const duration = Date.now() - startTime
90
+
91
+ const logData: any = {
92
+ requestId,
93
+ method: context.request.method,
94
+ path: context.path,
95
+ status: response.status,
96
+ duration: `${duration}ms`,
97
+ timestamp: new Date().toISOString()
98
+ }
99
+
100
+ // Determine log level based on status code
101
+ let logLevel = config.logLevel || 'info'
102
+
103
+ if (response.status >= 500) {
104
+ logLevel = 'error'
105
+ } else if (response.status >= 400) {
106
+ logLevel = 'warn'
107
+ }
108
+
109
+ // Add performance warning for slow requests
110
+ if (duration > 1000) {
111
+ logData.warning = 'Slow request detected'
112
+ logLevel = 'warn'
113
+ }
114
+
115
+ const logMessage = `📤 Request completed - ${context.request.method} ${context.path} ${response.status} (${duration}ms)`
116
+
117
+ switch (logLevel) {
118
+ case 'error':
119
+ console.error(logMessage, logData)
120
+ break
121
+ case 'warn':
122
+ console.warn(logMessage, logData)
123
+ break
124
+ case 'debug':
125
+ console.debug(logMessage, logData)
126
+ break
127
+ default:
128
+ console.log(logMessage, logData)
129
+ }
130
+ },
131
+
132
+ onError: async (context: Context, error: Error) => {
133
+ const startTime = context.store?.startTime
134
+ const requestId = context.store?.requestId
135
+ const duration = startTime ? Date.now() - startTime : 0
136
+
137
+ const logData = {
138
+ requestId,
139
+ method: context.request.method,
140
+ path: context.path,
141
+ error: error.message,
142
+ stack: error.stack,
143
+ duration: `${duration}ms`,
144
+ timestamp: new Date().toISOString()
145
+ }
146
+
147
+ console.error('💥 Request failed', logData)
148
+ }
149
+ })
150
+
151
+ /**
152
+ * Generate unique request ID
153
+ */
154
+ function generateRequestId(): string {
155
+ return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
156
+ }
157
+
158
+ /**
159
+ * Get client IP address
160
+ */
161
+ function getClientIp(context: Context): string {
162
+ // Try to get real IP from various headers
163
+ const forwarded = context.headers['x-forwarded-for']
164
+ const realIp = context.headers['x-real-ip']
165
+ const cfConnectingIp = context.headers['cf-connecting-ip']
166
+
167
+ if (forwarded) {
168
+ return forwarded.split(',')[0].trim()
169
+ }
170
+
171
+ if (realIp) {
172
+ return realIp
173
+ }
174
+
175
+ if (cfConnectingIp) {
176
+ return cfConnectingIp
177
+ }
178
+
179
+ return 'unknown'
180
+ }
181
+
182
+ /**
183
+ * Predefined configurations
184
+ */
185
+ export const requestLoggingConfigs = {
186
+ // Development configuration - verbose logging
187
+ development: {
188
+ logLevel: 'debug' as const,
189
+ includeBody: true,
190
+ includeHeaders: true,
191
+ maxBodyLength: 1000,
192
+ excludeHeaders: ['authorization', 'cookie']
193
+ },
194
+
195
+ // Production configuration - minimal logging
196
+ production: {
197
+ logLevel: 'info' as const,
198
+ includeBody: false,
199
+ includeHeaders: false,
200
+ excludePaths: ['/health', '/metrics']
201
+ },
202
+
203
+ // Security-focused configuration
204
+ security: {
205
+ logLevel: 'warn' as const,
206
+ includeBody: false,
207
+ includeHeaders: true,
208
+ excludeHeaders: [
209
+ 'authorization',
210
+ 'cookie',
211
+ 'x-api-key',
212
+ 'x-auth-token'
213
+ ]
214
+ }
215
+ }