create-fluxstack 1.8.1 → 1.8.3

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 (132) hide show
  1. package/.env.example +19 -0
  2. package/README.md +6 -3
  3. package/app/client/SIMPLIFICATION.md +140 -0
  4. package/app/client/frontend-only.ts +1 -1
  5. package/app/client/src/App.tsx +148 -283
  6. package/app/client/src/index.css +5 -20
  7. package/app/client/src/lib/eden-api.ts +53 -220
  8. package/app/client/src/main.tsx +2 -3
  9. package/app/server/controllers/users.controller.ts +57 -31
  10. package/app/server/index.ts +5 -2
  11. package/app/server/live/register-components.ts +18 -7
  12. package/app/server/routes/env-test.ts +53 -2
  13. package/app/server/routes/index.ts +1 -8
  14. package/app/server/routes/users.routes.ts +192 -91
  15. package/config/fluxstack.config.ts +2 -2
  16. package/config/plugins.config.ts +22 -1
  17. package/core/build/flux-plugins-generator.ts +5 -5
  18. package/core/build/live-components-generator.ts +15 -12
  19. package/core/cli/command-registry.ts +4 -14
  20. package/core/cli/commands/plugin-deps.ts +8 -8
  21. package/core/cli/generators/component.ts +3 -3
  22. package/core/cli/generators/controller.ts +4 -4
  23. package/core/cli/generators/index.ts +8 -8
  24. package/core/cli/generators/interactive.ts +4 -4
  25. package/core/cli/generators/plugin.ts +3 -3
  26. package/core/cli/generators/prompts.ts +1 -1
  27. package/core/cli/generators/route.ts +27 -11
  28. package/core/cli/generators/service.ts +5 -5
  29. package/core/cli/generators/template-engine.ts +1 -1
  30. package/core/cli/generators/types.ts +1 -1
  31. package/core/cli/index.ts +158 -193
  32. package/core/cli/plugin-discovery.ts +3 -3
  33. package/core/client/hooks/index.ts +2 -2
  34. package/core/client/hooks/state-validator.ts +1 -1
  35. package/core/client/hooks/useAuth.ts +1 -1
  36. package/core/client/hooks/useChunkedUpload.ts +1 -1
  37. package/core/client/hooks/useHybridLiveComponent.ts +1 -1
  38. package/core/client/hooks/useWebSocket.ts +1 -1
  39. package/core/config/env.ts +1 -1
  40. package/core/config/runtime-config.ts +5 -5
  41. package/core/config/schema.ts +9 -0
  42. package/core/framework/server.ts +30 -15
  43. package/core/framework/types.ts +2 -2
  44. package/core/live/ComponentRegistry.ts +1 -1
  45. package/core/plugins/built-in/live-components/commands/create-live-component.ts +1 -1
  46. package/core/plugins/built-in/live-components/index.ts +1 -1
  47. package/core/plugins/built-in/monitoring/index.ts +65 -161
  48. package/core/plugins/built-in/static/index.ts +18 -47
  49. package/core/plugins/built-in/swagger/index.ts +301 -231
  50. package/core/plugins/built-in/vite/index.ts +74 -109
  51. package/core/plugins/config.ts +2 -2
  52. package/core/plugins/dependency-manager.ts +2 -2
  53. package/core/plugins/discovery.ts +1 -1
  54. package/core/plugins/executor.ts +2 -2
  55. package/core/plugins/manager.ts +19 -4
  56. package/core/plugins/module-resolver.ts +1 -1
  57. package/core/plugins/registry.ts +3 -3
  58. package/core/plugins/types.ts +147 -5
  59. package/core/server/framework.ts +2 -2
  60. package/core/server/live/ComponentRegistry.ts +9 -26
  61. package/core/server/live/FileUploadManager.ts +1 -1
  62. package/core/server/live/auto-generated-components.ts +26 -0
  63. package/core/server/live/websocket-plugin.ts +211 -19
  64. package/core/server/middleware/errorHandling.ts +1 -1
  65. package/core/server/middleware/index.ts +4 -4
  66. package/core/server/plugins/database.ts +1 -2
  67. package/core/server/plugins/static-files-plugin.ts +259 -231
  68. package/core/server/plugins/swagger.ts +1 -1
  69. package/core/server/services/BaseService.ts +1 -1
  70. package/core/server/services/ServiceContainer.ts +1 -1
  71. package/core/server/services/index.ts +4 -4
  72. package/core/server/standalone.ts +16 -1
  73. package/core/testing/index.ts +1 -1
  74. package/core/testing/setup.ts +1 -1
  75. package/core/utils/logger/startup-banner.ts +7 -33
  76. package/core/utils/version.ts +6 -6
  77. package/create-fluxstack.ts +68 -25
  78. package/package.json +2 -2
  79. package/plugins/crypto-auth/index.ts +52 -47
  80. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  81. package/plugins/crypto-auth/server/middlewares/helpers.ts +16 -1
  82. package/vitest.config.ts +11 -2
  83. package/app/client/src/App.css +0 -883
  84. package/app/client/src/components/ErrorBoundary.tsx +0 -107
  85. package/app/client/src/components/ErrorDisplay.css +0 -365
  86. package/app/client/src/components/ErrorDisplay.tsx +0 -258
  87. package/app/client/src/components/FluxStackConfig.tsx +0 -1321
  88. package/app/client/src/components/HybridLiveCounter.tsx +0 -140
  89. package/app/client/src/components/LiveClock.tsx +0 -286
  90. package/app/client/src/components/MainLayout.tsx +0 -388
  91. package/app/client/src/components/SidebarNavigation.tsx +0 -391
  92. package/app/client/src/components/StateDemo.tsx +0 -178
  93. package/app/client/src/components/SystemMonitor.tsx +0 -1044
  94. package/app/client/src/components/UserProfile.tsx +0 -809
  95. package/app/client/src/hooks/useAuth.ts +0 -39
  96. package/app/client/src/hooks/useNotifications.ts +0 -56
  97. package/app/client/src/lib/errors.ts +0 -340
  98. package/app/client/src/lib/hooks/useErrorHandler.ts +0 -258
  99. package/app/client/src/lib/index.ts +0 -45
  100. package/app/client/src/pages/ApiDocs.tsx +0 -182
  101. package/app/client/src/pages/CryptoAuthPage.tsx +0 -394
  102. package/app/client/src/pages/Demo.tsx +0 -174
  103. package/app/client/src/pages/HybridLive.tsx +0 -263
  104. package/app/client/src/pages/Overview.tsx +0 -155
  105. package/app/client/src/store/README.md +0 -43
  106. package/app/client/src/store/index.ts +0 -16
  107. package/app/client/src/store/slices/uiSlice.ts +0 -151
  108. package/app/client/src/store/slices/userSlice.ts +0 -161
  109. package/app/client/src/test/README.md +0 -257
  110. package/app/client/src/test/setup.ts +0 -70
  111. package/app/client/src/test/types.ts +0 -12
  112. package/app/server/live/CounterComponent.ts +0 -191
  113. package/app/server/live/FluxStackConfig.ts +0 -534
  114. package/app/server/live/SidebarNavigation.ts +0 -157
  115. package/app/server/live/SystemMonitor.ts +0 -595
  116. package/app/server/live/SystemMonitorIntegration.ts +0 -151
  117. package/app/server/live/UserProfileComponent.ts +0 -141
  118. package/app/server/middleware/auth.ts +0 -136
  119. package/app/server/middleware/errorHandling.ts +0 -252
  120. package/app/server/middleware/index.ts +0 -10
  121. package/app/server/middleware/rateLimit.ts +0 -193
  122. package/app/server/middleware/requestLogging.ts +0 -215
  123. package/app/server/middleware/validation.ts +0 -270
  124. package/app/server/routes/config.ts +0 -145
  125. package/app/server/routes/crypto-auth-demo.routes.ts +0 -167
  126. package/app/server/routes/example-with-crypto-auth.routes.ts +0 -235
  127. package/app/server/routes/exemplo-posts.routes.ts +0 -161
  128. package/app/server/routes/upload.ts +0 -92
  129. package/app/server/services/NotificationService.ts +0 -302
  130. package/app/server/services/UserService.ts +0 -222
  131. package/app/server/services/index.ts +0 -46
  132. package/app/server/types/index.ts +0 -1
@@ -1,232 +1,260 @@
1
- // đŸ”Ĩ FluxStack Static Files Plugin - Serve Public Files
2
-
3
- import { existsSync, statSync } from 'fs'
4
- import { join, extname, resolve } from 'path'
5
- import type { FluxStack, PluginContext } from '../plugins/types'
6
-
7
- export interface StaticFilesConfig {
8
- publicDir?: string // Default: 'public'
9
- uploadsDir?: string // Default: 'uploads'
10
- cacheMaxAge?: number // Default: 1 year in seconds
11
- enableUploads?: boolean // Default: true
12
- enablePublic?: boolean // Default: true
13
- publicRoute?: string // Default: '/public' (can be '/static' in dev)
14
- uploadsRoute?: string // Default: '/uploads'
15
- }
16
-
17
- export const staticFilesPlugin: Plugin = {
18
- name: 'static-files',
19
- version: '1.0.0',
20
- description: 'Serve static files and uploads with proper caching and security',
21
- author: 'FluxStack Team',
22
- priority: 'normal',
23
- category: 'core',
24
- tags: ['static', 'files', 'uploads', 'public'],
25
-
26
- setup: async (context: PluginContext) => {
27
- context.logger.debug('📁 Setting up Static Files plugin...')
28
-
29
- const config: StaticFilesConfig = {
30
- publicDir: 'public',
31
- uploadsDir: 'uploads',
32
- cacheMaxAge: 31536000, // 1 year
33
- enableUploads: true,
34
- enablePublic: true,
35
- publicRoute: '/api/static', // Use /api/static in dev to avoid Vite conflicts
36
- uploadsRoute: '/api/uploads',
37
- ...context.config.staticFiles
38
- }
39
-
40
- const projectRoot = process.cwd()
41
- const publicPath = resolve(projectRoot, config.publicDir!)
42
- const uploadsPath = resolve(projectRoot, config.uploadsDir!)
43
-
44
- // MIME types mapping
45
- const getMimeType = (extension: string): string => {
46
- const mimeTypes: Record<string, string> = {
47
- // Images
48
- '.jpg': 'image/jpeg',
49
- '.jpeg': 'image/jpeg',
50
- '.png': 'image/png',
51
- '.gif': 'image/gif',
52
- '.webp': 'image/webp',
53
- '.svg': 'image/svg+xml',
54
- '.ico': 'image/x-icon',
55
-
56
- // Documents
57
- '.pdf': 'application/pdf',
58
- '.txt': 'text/plain',
59
- '.json': 'application/json',
60
- '.xml': 'application/xml',
61
-
62
- // Web assets
63
- '.css': 'text/css',
64
- '.js': 'application/javascript',
65
- '.html': 'text/html',
66
- '.htm': 'text/html',
67
-
68
- // Fonts
69
- '.woff': 'font/woff',
70
- '.woff2': 'font/woff2',
71
- '.ttf': 'font/ttf',
72
- '.otf': 'font/otf',
73
-
74
- // Audio/Video
75
- '.mp3': 'audio/mpeg',
76
- '.mp4': 'video/mp4',
77
- '.webm': 'video/webm',
78
- '.ogg': 'audio/ogg'
79
- }
80
-
81
- return mimeTypes[extension.toLowerCase()] || 'application/octet-stream'
82
- }
83
-
84
- // Security check for path traversal
85
- const isPathSafe = (filePath: string, basePath: string): boolean => {
86
- const resolvedPath = resolve(basePath, filePath)
87
- return resolvedPath.startsWith(basePath)
88
- }
89
-
90
- // Generic file serving function
91
- const serveFile = async (filePath: string, set: any) => {
92
- try {
93
- if (!existsSync(filePath)) {
94
- set.status = 404
95
- return {
96
- error: 'File not found',
97
- path: filePath.replace(projectRoot, ''),
98
- timestamp: new Date().toISOString()
99
- }
100
- }
101
-
102
- const stats = statSync(filePath)
103
- if (!stats.isFile()) {
104
- set.status = 404
105
- return { error: 'Not a file' }
106
- }
107
-
108
- // Set appropriate headers
109
- const extension = extname(filePath).toLowerCase()
110
- const mimeType = getMimeType(extension)
111
-
112
- set.headers['content-type'] = mimeType
113
- set.headers['content-length'] = stats.size.toString()
114
- set.headers['last-modified'] = stats.mtime.toUTCString()
115
- set.headers['cache-control'] = `public, max-age=${config.cacheMaxAge}`
116
- set.headers['etag'] = `"${stats.mtime.getTime()}-${stats.size}"`
117
-
118
- // Security headers for images
119
- if (mimeType.startsWith('image/')) {
120
- set.headers['x-content-type-options'] = 'nosniff'
121
- }
122
-
123
- context.logger.debug(`📁 Serving file: ${filePath.replace(projectRoot, '')}`, {
124
- size: stats.size,
125
- mimeType,
126
- lastModified: stats.mtime
127
- })
128
-
129
- return Bun.file(filePath)
130
-
131
- } catch (error: any) {
132
- context.logger.error('❌ File serving error:', error.message)
133
- set.status = 500
134
- return { error: 'Failed to serve file' }
135
- }
136
- }
137
-
138
- // Add static file routes
139
- if (config.enablePublic) {
140
- const publicRoutePattern = `${config.publicRoute}/*`
141
- context.app.get(publicRoutePattern, ({ params, set }) => {
142
- const filePath = params['*'] || ''
143
-
144
- if (!isPathSafe(filePath, publicPath)) {
145
- set.status = 400
146
- return { error: 'Invalid file path' }
147
- }
148
-
149
- const fullPath = join(publicPath, filePath)
150
- return serveFile(fullPath, set)
151
- })
152
-
153
- context.logger.debug(`📁 Public files route enabled: ${publicRoutePattern} → ${config.publicDir}`)
154
- }
155
-
156
- if (config.enableUploads) {
157
- const uploadsRoutePattern = `${config.uploadsRoute}/*`
158
- context.app.get(uploadsRoutePattern, ({ params, set }) => {
159
- const filePath = params['*'] || ''
160
-
161
- if (!isPathSafe(filePath, uploadsPath)) {
162
- set.status = 400
163
- return { error: 'Invalid file path' }
164
- }
165
-
166
- const fullPath = join(uploadsPath, filePath)
167
- return serveFile(fullPath, set)
168
- })
169
-
170
- context.logger.debug(`📁 Uploads route enabled: ${uploadsRoutePattern} → ${config.uploadsDir}`)
171
- }
172
-
173
- // Static files info endpoint
174
- context.app.get('/api/static/info', () => {
175
- return {
176
- success: true,
177
- config: {
178
- publicDir: config.publicDir,
179
- uploadsDir: config.uploadsDir,
180
- enablePublic: config.enablePublic,
181
- enableUploads: config.enableUploads,
182
- cacheMaxAge: config.cacheMaxAge
183
- },
184
- paths: {
185
- publicPath,
186
- uploadsPath,
187
- publicUrl: config.publicRoute,
188
- uploadsUrl: config.uploadsRoute
189
- },
190
- timestamp: new Date().toISOString()
191
- }
192
- })
193
-
194
- // Create directories if they don't exist
195
- const { mkdir } = await import('fs/promises')
196
-
197
- if (config.enablePublic && !existsSync(publicPath)) {
198
- await mkdir(publicPath, { recursive: true })
199
- context.logger.debug(`📁 Created public directory: ${publicPath}`)
200
- }
201
-
202
- if (config.enableUploads && !existsSync(uploadsPath)) {
203
- await mkdir(uploadsPath, { recursive: true })
204
- await mkdir(join(uploadsPath, 'avatars'), { recursive: true })
205
- context.logger.debug(`📁 Created uploads directory: ${uploadsPath}`)
206
- }
207
-
208
- context.logger.debug('📁 Static Files plugin setup complete', {
209
- publicEnabled: config.enablePublic,
210
- uploadsEnabled: config.enableUploads,
211
- publicPath: config.enablePublic ? publicPath : 'disabled',
212
- uploadsPath: config.enableUploads ? uploadsPath : 'disabled'
213
- })
214
- },
215
-
216
- onServerStart: async (context: PluginContext) => {
217
- const config = {
218
- enablePublic: true,
219
- enableUploads: true,
220
- publicRoute: '/api/static',
221
- uploadsRoute: '/api/uploads',
222
- ...context.config.staticFiles
223
- }
224
- context.logger.debug('📁 Static Files plugin ready', {
225
- routes: [
226
- config.enablePublic ? `${config.publicRoute}/*` : null,
227
- config.enableUploads ? `${config.uploadsRoute}/*` : null,
228
- '/api/static/info'
229
- ].filter(Boolean)
230
- })
231
- }
1
+ // đŸ”Ĩ FluxStack Static Files Plugin - Serve Public Files
2
+
3
+ import { existsSync, statSync } from 'fs'
4
+ import { join, extname, resolve } from 'path'
5
+ import type { FluxStack, PluginContext, Plugin } from '../../plugins/types'
6
+ import { t } from 'elysia'
7
+
8
+ // Response schema for static files info endpoint
9
+ const StaticFilesInfoSchema = t.Object({
10
+ success: t.Boolean(),
11
+ config: t.Object({
12
+ publicDir: t.String(),
13
+ uploadsDir: t.String(),
14
+ enablePublic: t.Boolean(),
15
+ enableUploads: t.Boolean(),
16
+ cacheMaxAge: t.Number()
17
+ }),
18
+ paths: t.Object({
19
+ publicPath: t.String(),
20
+ uploadsPath: t.String(),
21
+ publicUrl: t.String(),
22
+ uploadsUrl: t.String()
23
+ }),
24
+ timestamp: t.String()
25
+ }, {
26
+ description: 'Static files configuration and paths information'
27
+ })
28
+
29
+ export interface StaticFilesConfig {
30
+ publicDir?: string // Default: 'public'
31
+ uploadsDir?: string // Default: 'uploads'
32
+ cacheMaxAge?: number // Default: 1 year in seconds
33
+ enableUploads?: boolean // Default: true
34
+ enablePublic?: boolean // Default: true
35
+ publicRoute?: string // Default: '/public' (can be '/static' in dev)
36
+ uploadsRoute?: string // Default: '/uploads'
37
+ }
38
+
39
+ export const staticFilesPlugin: Plugin = {
40
+ name: 'static-files',
41
+ description: 'Serve static files and uploads with proper caching and security',
42
+ author: 'FluxStack Team',
43
+ priority: 'normal',
44
+ category: 'core',
45
+ tags: ['static', 'files', 'uploads', 'public'],
46
+
47
+ setup: async (context: PluginContext) => {
48
+ context.logger.debug('📁 Setting up Static Files plugin...')
49
+
50
+ const config: StaticFilesConfig = {
51
+ publicDir: 'public',
52
+ uploadsDir: 'uploads',
53
+ cacheMaxAge: 31536000, // 1 year
54
+ enableUploads: true,
55
+ enablePublic: true,
56
+ publicRoute: '/api/static', // Use /api/static in dev to avoid Vite conflicts
57
+ uploadsRoute: '/api/uploads',
58
+ ...context.config.staticFiles
59
+ }
60
+
61
+ const projectRoot = process.cwd()
62
+ const publicPath = resolve(projectRoot, config.publicDir!)
63
+ const uploadsPath = resolve(projectRoot, config.uploadsDir!)
64
+
65
+ // MIME types mapping
66
+ const getMimeType = (extension: string): string => {
67
+ const mimeTypes: Record<string, string> = {
68
+ // Images
69
+ '.jpg': 'image/jpeg',
70
+ '.jpeg': 'image/jpeg',
71
+ '.png': 'image/png',
72
+ '.gif': 'image/gif',
73
+ '.webp': 'image/webp',
74
+ '.svg': 'image/svg+xml',
75
+ '.ico': 'image/x-icon',
76
+
77
+ // Documents
78
+ '.pdf': 'application/pdf',
79
+ '.txt': 'text/plain',
80
+ '.json': 'application/json',
81
+ '.xml': 'application/xml',
82
+
83
+ // Web assets
84
+ '.css': 'text/css',
85
+ '.js': 'application/javascript',
86
+ '.html': 'text/html',
87
+ '.htm': 'text/html',
88
+
89
+ // Fonts
90
+ '.woff': 'font/woff',
91
+ '.woff2': 'font/woff2',
92
+ '.ttf': 'font/ttf',
93
+ '.otf': 'font/otf',
94
+
95
+ // Audio/Video
96
+ '.mp3': 'audio/mpeg',
97
+ '.mp4': 'video/mp4',
98
+ '.webm': 'video/webm',
99
+ '.ogg': 'audio/ogg'
100
+ }
101
+
102
+ return mimeTypes[extension.toLowerCase()] || 'application/octet-stream'
103
+ }
104
+
105
+ // Security check for path traversal
106
+ const isPathSafe = (filePath: string, basePath: string): boolean => {
107
+ const resolvedPath = resolve(basePath, filePath)
108
+ return resolvedPath.startsWith(basePath)
109
+ }
110
+
111
+ // Generic file serving function
112
+ const serveFile = async (filePath: string, set: any) => {
113
+ try {
114
+ if (!existsSync(filePath)) {
115
+ set.status = 404
116
+ return {
117
+ error: 'File not found',
118
+ path: filePath.replace(projectRoot, ''),
119
+ timestamp: new Date().toISOString()
120
+ }
121
+ }
122
+
123
+ const stats = statSync(filePath)
124
+ if (!stats.isFile()) {
125
+ set.status = 404
126
+ return { error: 'Not a file' }
127
+ }
128
+
129
+ // Set appropriate headers
130
+ const extension = extname(filePath).toLowerCase()
131
+ const mimeType = getMimeType(extension)
132
+
133
+ set.headers['content-type'] = mimeType
134
+ set.headers['content-length'] = stats.size.toString()
135
+ set.headers['last-modified'] = stats.mtime.toUTCString()
136
+ set.headers['cache-control'] = `public, max-age=${config.cacheMaxAge}`
137
+ set.headers['etag'] = `"${stats.mtime.getTime()}-${stats.size}"`
138
+
139
+ // Security headers for images
140
+ if (mimeType.startsWith('image/')) {
141
+ set.headers['x-content-type-options'] = 'nosniff'
142
+ }
143
+
144
+ context.logger.debug(`📁 Serving file: ${filePath.replace(projectRoot, '')}`, {
145
+ size: stats.size,
146
+ mimeType,
147
+ lastModified: stats.mtime
148
+ })
149
+
150
+ return Bun.file(filePath)
151
+
152
+ } catch (error: any) {
153
+ context.logger.error('❌ File serving error:', error.message)
154
+ set.status = 500
155
+ return { error: 'Failed to serve file' }
156
+ }
157
+ }
158
+
159
+ // Add static file routes
160
+ if (config.enablePublic) {
161
+ const publicRoutePattern = `${config.publicRoute}/*`
162
+ context.app.get(publicRoutePattern, ({ params, set }) => {
163
+ const filePath = params['*'] || ''
164
+
165
+ if (!isPathSafe(filePath, publicPath)) {
166
+ set.status = 400
167
+ return { error: 'Invalid file path' }
168
+ }
169
+
170
+ const fullPath = join(publicPath, filePath)
171
+ return serveFile(fullPath, set)
172
+ })
173
+
174
+ context.logger.debug(`📁 Public files route enabled: ${publicRoutePattern} → ${config.publicDir}`)
175
+ }
176
+
177
+ if (config.enableUploads) {
178
+ const uploadsRoutePattern = `${config.uploadsRoute}/*`
179
+ context.app.get(uploadsRoutePattern, ({ params, set }) => {
180
+ const filePath = params['*'] || ''
181
+
182
+ if (!isPathSafe(filePath, uploadsPath)) {
183
+ set.status = 400
184
+ return { error: 'Invalid file path' }
185
+ }
186
+
187
+ const fullPath = join(uploadsPath, filePath)
188
+ return serveFile(fullPath, set)
189
+ })
190
+
191
+ context.logger.debug(`📁 Uploads route enabled: ${uploadsRoutePattern} → ${config.uploadsDir}`)
192
+ }
193
+
194
+ // Static files info endpoint
195
+ context.app.get('/api/static/info', () => {
196
+ return {
197
+ success: true,
198
+ config: {
199
+ publicDir: config.publicDir,
200
+ uploadsDir: config.uploadsDir,
201
+ enablePublic: config.enablePublic,
202
+ enableUploads: config.enableUploads,
203
+ cacheMaxAge: config.cacheMaxAge
204
+ },
205
+ paths: {
206
+ publicPath,
207
+ uploadsPath,
208
+ publicUrl: config.publicRoute,
209
+ uploadsUrl: config.uploadsRoute
210
+ },
211
+ timestamp: new Date().toISOString()
212
+ }
213
+ }, {
214
+ detail: {
215
+ summary: 'Static Files Configuration',
216
+ description: 'Returns configuration and paths for static files and uploads serving',
217
+ tags: ['Static Files', 'Configuration']
218
+ },
219
+ response: StaticFilesInfoSchema
220
+ })
221
+
222
+ // Create directories if they don't exist
223
+ const { mkdir } = await import('fs/promises')
224
+
225
+ if (config.enablePublic && !existsSync(publicPath)) {
226
+ await mkdir(publicPath, { recursive: true })
227
+ context.logger.debug(`📁 Created public directory: ${publicPath}`)
228
+ }
229
+
230
+ if (config.enableUploads && !existsSync(uploadsPath)) {
231
+ await mkdir(uploadsPath, { recursive: true })
232
+ await mkdir(join(uploadsPath, 'avatars'), { recursive: true })
233
+ context.logger.debug(`📁 Created uploads directory: ${uploadsPath}`)
234
+ }
235
+
236
+ context.logger.debug('📁 Static Files plugin setup complete', {
237
+ publicEnabled: config.enablePublic,
238
+ uploadsEnabled: config.enableUploads,
239
+ publicPath: config.enablePublic ? publicPath : 'disabled',
240
+ uploadsPath: config.enableUploads ? uploadsPath : 'disabled'
241
+ })
242
+ },
243
+
244
+ onServerStart: async (context: PluginContext) => {
245
+ const config = {
246
+ enablePublic: true,
247
+ enableUploads: true,
248
+ publicRoute: '/api/static',
249
+ uploadsRoute: '/api/uploads',
250
+ ...context.config.staticFiles
251
+ }
252
+ context.logger.debug('📁 Static Files plugin ready', {
253
+ routes: [
254
+ config.enablePublic ? `${config.publicRoute}/*` : null,
255
+ config.enableUploads ? `${config.uploadsRoute}/*` : null,
256
+ '/api/static/info'
257
+ ].filter(Boolean)
258
+ })
259
+ }
232
260
  }
@@ -1,5 +1,5 @@
1
1
  import { swagger } from '@elysiajs/swagger'
2
- import type { Plugin, PluginContext } from '../../types'
2
+ import type { Plugin, PluginContext } from '@/core/plugins/types'
3
3
 
4
4
  export const swaggerPlugin: Plugin = {
5
5
  name: 'swagger',
@@ -8,7 +8,7 @@
8
8
  * - Service container integration
9
9
  */
10
10
 
11
- import type { Logger } from '../../utils/logger/index.js'
11
+ import type { Logger } from '@/core/utils/logger/index'
12
12
 
13
13
  export interface ServiceContext {
14
14
  config: any
@@ -5,7 +5,7 @@
5
5
  * Provides service registration, resolution, and lifecycle management
6
6
  */
7
7
 
8
- import type { Logger } from '../../utils/logger/index.js'
8
+ import type { Logger } from '@/core/utils/logger/index'
9
9
 
10
10
  export interface ServiceDefinition {
11
11
  factory: (container: ServiceContainer) => any
@@ -3,7 +3,7 @@
3
3
  * FluxStack service infrastructure exports
4
4
  */
5
5
 
6
- export { BaseService } from './BaseService.js'
7
- export { ServiceContainer } from './ServiceContainer.js'
8
- export type { ServiceContext, ServiceContainer as IServiceContainer } from './BaseService.js'
9
- export type { ServiceDefinition } from './ServiceContainer.js'
6
+ export { BaseService } from './BaseService'
7
+ export { ServiceContainer } from './ServiceContainer'
8
+ export type { ServiceContext, ServiceContainer as IServiceContainer } from './BaseService'
9
+ export type { ServiceDefinition } from './ServiceContainer'
@@ -2,6 +2,21 @@
2
2
  import { FluxStackFramework } from "./index"
3
3
  import type { Plugin, PluginContext } from "../types"
4
4
 
5
+ /**
6
+ * Helper to safely parse request.url which might be relative or absolute
7
+ */
8
+ function parseRequestURL(request: Request): URL {
9
+ try {
10
+ // Try parsing as absolute URL first
11
+ return new URL(request.url)
12
+ } catch {
13
+ // If relative, use host from headers or default to localhost
14
+ const host = request.headers.get('host') || 'localhost'
15
+ const protocol = request.headers.get('x-forwarded-proto') || 'http'
16
+ return new URL(request.url, `${protocol}://${host}`)
17
+ }
18
+ }
19
+
5
20
  export const createStandaloneServer = (userConfig: any = {}) => {
6
21
  const app = new FluxStackFramework({
7
22
  server: {
@@ -29,7 +44,7 @@ export const createStandaloneServer = (userConfig: any = {}) => {
29
44
  context.app.onRequest(({ request }: { request: Request }) => {
30
45
  // Log mais limpo para backend standalone
31
46
  const timestamp = new Date().toLocaleTimeString()
32
- const path = new URL(request.url).pathname
47
+ const path = parseRequestURL(request).pathname
33
48
  console.log(`[${timestamp}] ${request.method} ${path}`)
34
49
  })
35
50
  }
@@ -7,4 +7,4 @@ export {
7
7
  setupFluxStackTests,
8
8
  createMockLogger,
9
9
  createMockServiceContext
10
- } from './setup.js'
10
+ } from './setup'
@@ -10,7 +10,7 @@ import { beforeEach, vi } from 'vitest'
10
10
  */
11
11
  export function setupFluxStackTests() {
12
12
  // Mock fetch globally
13
- global.fetch = vi.fn()
13
+ global.fetch = vi.fn() as any
14
14
 
15
15
  // Mock localStorage
16
16
  const localStorageMock = {
@@ -33,43 +33,17 @@ export function displayStartupBanner(info: StartupInfo): void {
33
33
  pluginCount = 0,
34
34
  vitePort,
35
35
  viteEmbedded = false,
36
- swaggerPath
37
36
  } = info
38
37
 
39
- console.log('\n' + chalk.cyan.bold('⚡ FluxStack') + chalk.gray(` v${FLUXSTACK_VERSION}\n`))
40
-
41
- // Server info
42
- console.log(chalk.bold('🚀 Server'))
43
- console.log(` ${chalk.gray('→')} http://localhost:${port}`)
44
- console.log(` ${chalk.gray('→')} API: http://localhost:${port}${apiPrefix}`)
45
- console.log(` ${chalk.gray('→')} Health: http://localhost:${port}${apiPrefix}/health`)
46
-
47
- // Frontend info (only if Vite is running standalone, NOT embedded)
48
- if (vitePort && !viteEmbedded) {
49
- console.log('')
50
- console.log(chalk.bold('âš›ī¸ Frontend'))
51
- console.log(` ${chalk.gray('→')} http://localhost:${vitePort}`)
52
- }
53
-
54
- // Swagger docs (if enabled)
55
- if (swaggerPath) {
56
- console.log('')
57
- console.log(chalk.bold('📋 Documentation'))
58
- console.log(` ${chalk.gray('→')} Swagger: http://localhost:${port}${swaggerPath}`)
59
- }
60
-
61
- // Environment and plugins
62
- console.log('')
63
- console.log(chalk.bold('â„šī¸ Info'))
64
- console.log(` ${chalk.gray('→')} Environment: ${chalk.green(environment)}`)
65
- console.log(` ${chalk.gray('→')} Plugins: ${chalk.yellow(pluginCount)}`)
66
-
67
- // Show Vite embedded status when applicable
68
- if (viteEmbedded && vitePort) {
69
- console.log(` ${chalk.gray('→')} Vite: ${chalk.magenta('embedded')} ${chalk.gray(`(port ${vitePort})`)}`)
38
+ // Display plugins in compact format
39
+ const plugins = (global as any).__fluxstackPlugins || []
40
+ if (plugins.length > 0) {
41
+ const pluginList = plugins.map((p: any) => `${p.name} (${p.details})`).join(', ')
42
+ console.log(`Plugins (${plugins.length}): ${pluginList}\n`)
70
43
  }
71
44
 
72
- console.log('\n' + chalk.green('✨ Ready!') + chalk.gray(' Press Ctrl+C to stop\n'))
45
+ // Simple ready message
46
+ console.log(chalk.green('Server ready!') + chalk.gray(` Environment: ${environment}${viteEmbedded ? ' | Vite: embedded' : ''}\n`))
73
47
  }
74
48
 
75
49
  /**