create-fluxstack 1.1.0 → 1.4.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 (62) hide show
  1. package/app/server/backend-only.ts +5 -5
  2. package/app/server/index.ts +63 -54
  3. package/app/server/live/FluxStackConfig.ts +43 -39
  4. package/app/server/live/SystemMonitorIntegration.ts +2 -2
  5. package/app/server/live/register-components.ts +1 -1
  6. package/app/server/middleware/errorHandling.ts +6 -4
  7. package/app/server/routes/config.ts +145 -0
  8. package/app/server/routes/index.ts +5 -3
  9. package/config/app.config.ts +113 -0
  10. package/config/build.config.ts +24 -0
  11. package/config/database.config.ts +99 -0
  12. package/config/index.ts +68 -0
  13. package/config/logger.config.ts +27 -0
  14. package/config/runtime.config.ts +92 -0
  15. package/config/server.config.ts +46 -0
  16. package/config/services.config.ts +130 -0
  17. package/config/system.config.ts +105 -0
  18. package/core/build/index.ts +10 -4
  19. package/core/cli/index.ts +29 -12
  20. package/core/config/env.ts +37 -95
  21. package/core/config/runtime-config.ts +61 -58
  22. package/core/config/schema.ts +4 -0
  23. package/core/framework/server.ts +22 -10
  24. package/core/plugins/built-in/index.ts +7 -17
  25. package/core/plugins/built-in/swagger/index.ts +228 -228
  26. package/core/plugins/built-in/vite/index.ts +374 -358
  27. package/core/plugins/dependency-manager.ts +5 -5
  28. package/core/plugins/manager.ts +12 -12
  29. package/core/plugins/registry.ts +3 -3
  30. package/core/server/index.ts +0 -1
  31. package/core/server/live/ComponentRegistry.ts +34 -8
  32. package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
  33. package/core/server/live/websocket-plugin.ts +434 -434
  34. package/core/server/middleware/README.md +488 -0
  35. package/core/server/middleware/elysia-helpers.ts +227 -0
  36. package/core/server/middleware/index.ts +25 -9
  37. package/core/server/plugins/static-files-plugin.ts +231 -231
  38. package/core/utils/config-schema.ts +484 -0
  39. package/core/utils/env.ts +306 -0
  40. package/core/utils/helpers.ts +4 -4
  41. package/core/utils/logger/colors.ts +114 -0
  42. package/core/utils/logger/config.ts +35 -0
  43. package/core/utils/logger/formatter.ts +82 -0
  44. package/core/utils/logger/group-logger.ts +101 -0
  45. package/core/utils/logger/index.ts +199 -250
  46. package/core/utils/logger/stack-trace.ts +92 -0
  47. package/core/utils/logger/startup-banner.ts +92 -0
  48. package/core/utils/logger/winston-logger.ts +152 -0
  49. package/core/utils/version.ts +5 -0
  50. package/create-fluxstack.ts +1 -0
  51. package/fluxstack.config.ts +2 -2
  52. package/package.json +117 -115
  53. package/core/config/env-dynamic.ts +0 -326
  54. package/core/plugins/built-in/logger/index.ts +0 -180
  55. package/core/server/plugins/logger.ts +0 -47
  56. package/core/utils/env-runtime-v2.ts +0 -232
  57. package/core/utils/env-runtime.ts +0 -259
  58. package/core/utils/logger/formatters.ts +0 -222
  59. package/core/utils/logger/middleware.ts +0 -253
  60. package/core/utils/logger/performance.ts +0 -384
  61. package/core/utils/logger/transports.ts +0 -365
  62. package/core/utils/logger.ts +0 -106
@@ -3,14 +3,30 @@
3
3
  * FluxStack middleware infrastructure exports
4
4
  */
5
5
 
6
- export {
7
- errorHandlingMiddleware,
8
- notFoundMiddleware,
9
- createError,
10
- asyncHandler
6
+ export {
7
+ errorHandlingMiddleware,
8
+ notFoundMiddleware,
9
+ createError,
10
+ asyncHandler
11
11
  } from './errorHandling.js'
12
12
 
13
- export type {
14
- ErrorHandlingOptions,
15
- FluxStackError
16
- } from './errorHandling.js'
13
+ export type {
14
+ ErrorHandlingOptions,
15
+ FluxStackError
16
+ } from './errorHandling.js'
17
+
18
+ // Elysia Middleware Helpers
19
+ export {
20
+ createMiddleware,
21
+ createDerive,
22
+ createGuard,
23
+ createRateLimit,
24
+ composeMiddleware,
25
+ isDevelopment,
26
+ isProduction,
27
+ isTest
28
+ } from './elysia-helpers.js'
29
+
30
+ export type {
31
+ MiddlewareOptions
32
+ } from './elysia-helpers.js'
@@ -1,232 +1,232 @@
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 { Plugin, 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.info('📁 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.info(`📁 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.info(`📁 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.info(`📁 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.info(`📁 Created uploads directory: ${uploadsPath}`)
206
- }
207
-
208
- context.logger.info('📁 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.info('📁 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 { Plugin, 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
+ }
232
232
  }