create-fluxstack 1.0.22 → 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 (82) 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 +6 -26
  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/bundler.ts +53 -5
  19. package/core/build/flux-plugins-generator.ts +315 -0
  20. package/core/build/index.ts +11 -7
  21. package/core/build/live-components-generator.ts +231 -0
  22. package/core/build/optimizer.ts +2 -54
  23. package/core/cli/index.ts +31 -13
  24. package/core/config/env.ts +38 -94
  25. package/core/config/runtime-config.ts +61 -58
  26. package/core/config/schema.ts +1 -0
  27. package/core/framework/server.ts +55 -11
  28. package/core/plugins/built-in/index.ts +7 -17
  29. package/core/plugins/built-in/static/index.ts +24 -10
  30. package/core/plugins/built-in/swagger/index.ts +228 -228
  31. package/core/plugins/built-in/vite/index.ts +374 -358
  32. package/core/plugins/dependency-manager.ts +5 -5
  33. package/core/plugins/manager.ts +57 -14
  34. package/core/plugins/registry.ts +3 -3
  35. package/core/server/index.ts +0 -1
  36. package/core/server/live/ComponentRegistry.ts +34 -8
  37. package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
  38. package/core/server/live/websocket-plugin.ts +434 -434
  39. package/core/server/middleware/README.md +488 -0
  40. package/core/server/middleware/elysia-helpers.ts +227 -0
  41. package/core/server/middleware/index.ts +25 -9
  42. package/core/server/plugins/static-files-plugin.ts +231 -231
  43. package/core/utils/config-schema.ts +484 -0
  44. package/core/utils/env.ts +306 -0
  45. package/core/utils/helpers.ts +9 -3
  46. package/core/utils/logger/colors.ts +114 -0
  47. package/core/utils/logger/config.ts +35 -0
  48. package/core/utils/logger/formatter.ts +82 -0
  49. package/core/utils/logger/group-logger.ts +101 -0
  50. package/core/utils/logger/index.ts +199 -250
  51. package/core/utils/logger/stack-trace.ts +92 -0
  52. package/core/utils/logger/startup-banner.ts +92 -0
  53. package/core/utils/logger/winston-logger.ts +152 -0
  54. package/core/utils/version.ts +5 -0
  55. package/create-fluxstack.ts +1 -0
  56. package/fluxstack.config.ts +6 -12
  57. package/package.json +117 -114
  58. package/plugins/crypto-auth/README.md +238 -0
  59. package/plugins/crypto-auth/client/CryptoAuthClient.ts +325 -0
  60. package/plugins/crypto-auth/client/components/AuthProvider.tsx +190 -0
  61. package/plugins/crypto-auth/client/components/LoginButton.tsx +155 -0
  62. package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +109 -0
  63. package/plugins/crypto-auth/client/components/SessionInfo.tsx +242 -0
  64. package/plugins/crypto-auth/client/components/index.ts +15 -0
  65. package/plugins/crypto-auth/client/index.ts +12 -0
  66. package/plugins/crypto-auth/index.ts +230 -0
  67. package/plugins/crypto-auth/package.json +65 -0
  68. package/plugins/crypto-auth/plugin.json +29 -0
  69. package/plugins/crypto-auth/server/AuthMiddleware.ts +237 -0
  70. package/plugins/crypto-auth/server/CryptoAuthService.ts +293 -0
  71. package/plugins/crypto-auth/server/index.ts +9 -0
  72. package/vite.config.ts +16 -0
  73. package/core/config/env-dynamic.ts +0 -326
  74. package/core/plugins/built-in/logger/index.ts +0 -180
  75. package/core/server/plugins/logger.ts +0 -47
  76. package/core/utils/env-runtime-v2.ts +0 -232
  77. package/core/utils/env-runtime.ts +0 -259
  78. package/core/utils/logger/formatters.ts +0 -222
  79. package/core/utils/logger/middleware.ts +0 -253
  80. package/core/utils/logger/performance.ts +0 -384
  81. package/core/utils/logger/transports.ts +0 -365
  82. package/core/utils/logger.ts +0 -106
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Serviço de Autenticação Criptográfica
3
+ * Implementa autenticação baseada em Ed25519
4
+ */
5
+
6
+ import { ed25519 } from '@noble/curves/ed25519'
7
+ import { sha256 } from '@noble/hashes/sha256'
8
+ import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
9
+ export interface Logger {
10
+ debug(message: string, meta?: any): void
11
+ info(message: string, meta?: any): void
12
+ warn(message: string, meta?: any): void
13
+ error(message: string, meta?: any): void
14
+ }
15
+
16
+ export interface SessionData {
17
+ sessionId: string
18
+ publicKey: string
19
+ createdAt: Date
20
+ lastUsed: Date
21
+ isAdmin: boolean
22
+ permissions: string[]
23
+ }
24
+
25
+ export interface AuthResult {
26
+ success: boolean
27
+ sessionId?: string
28
+ error?: string
29
+ user?: {
30
+ sessionId: string
31
+ isAdmin: boolean
32
+ isSuperAdmin: boolean
33
+ permissions: string[]
34
+ }
35
+ }
36
+
37
+ export interface CryptoAuthConfig {
38
+ sessionTimeout: number
39
+ maxTimeDrift: number
40
+ adminKeys: string[]
41
+ logger?: Logger
42
+ }
43
+
44
+ export class CryptoAuthService {
45
+ private sessions: Map<string, SessionData> = new Map()
46
+ private config: CryptoAuthConfig
47
+ private logger?: Logger
48
+
49
+ constructor(config: CryptoAuthConfig) {
50
+ this.config = config
51
+ this.logger = config.logger
52
+
53
+ // Limpar sessões expiradas a cada 5 minutos
54
+ setInterval(() => {
55
+ this.cleanupExpiredSessions()
56
+ }, 5 * 60 * 1000)
57
+ }
58
+
59
+ /**
60
+ * Inicializar uma nova sessão
61
+ */
62
+ async initializeSession(data: { publicKey?: string }): Promise<AuthResult> {
63
+ try {
64
+ let publicKey: string
65
+
66
+ if (data.publicKey) {
67
+ // Validar chave pública fornecida
68
+ if (!this.isValidPublicKey(data.publicKey)) {
69
+ return {
70
+ success: false,
71
+ error: "Chave pública inválida"
72
+ }
73
+ }
74
+ publicKey = data.publicKey
75
+ } else {
76
+ // Gerar novo par de chaves
77
+ const privateKey = ed25519.utils.randomPrivateKey()
78
+ publicKey = bytesToHex(ed25519.getPublicKey(privateKey))
79
+ }
80
+
81
+ const sessionId = publicKey
82
+ const isAdmin = this.config.adminKeys.includes(publicKey)
83
+
84
+ const sessionData: SessionData = {
85
+ sessionId,
86
+ publicKey,
87
+ createdAt: new Date(),
88
+ lastUsed: new Date(),
89
+ isAdmin,
90
+ permissions: isAdmin ? ['admin', 'read', 'write'] : ['read']
91
+ }
92
+
93
+ this.sessions.set(sessionId, sessionData)
94
+
95
+ this.logger?.info("Nova sessão inicializada", {
96
+ sessionId: sessionId.substring(0, 8) + "...",
97
+ isAdmin,
98
+ permissions: sessionData.permissions
99
+ })
100
+
101
+ return {
102
+ success: true,
103
+ sessionId,
104
+ user: {
105
+ sessionId,
106
+ isAdmin,
107
+ isSuperAdmin: isAdmin,
108
+ permissions: sessionData.permissions
109
+ }
110
+ }
111
+ } catch (error) {
112
+ this.logger?.error("Erro ao inicializar sessão", { error })
113
+ return {
114
+ success: false,
115
+ error: "Erro interno ao inicializar sessão"
116
+ }
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Validar uma sessão com assinatura
122
+ */
123
+ async validateSession(data: {
124
+ sessionId: string
125
+ timestamp: number
126
+ nonce: string
127
+ signature: string
128
+ message?: string
129
+ }): Promise<AuthResult> {
130
+ try {
131
+ const { sessionId, timestamp, nonce, signature, message = "" } = data
132
+
133
+ // Verificar se a sessão existe
134
+ const session = this.sessions.get(sessionId)
135
+ if (!session) {
136
+ return {
137
+ success: false,
138
+ error: "Sessão não encontrada"
139
+ }
140
+ }
141
+
142
+ // Verificar se a sessão não expirou
143
+ const now = Date.now()
144
+ const sessionAge = now - session.lastUsed.getTime()
145
+ if (sessionAge > this.config.sessionTimeout) {
146
+ this.sessions.delete(sessionId)
147
+ return {
148
+ success: false,
149
+ error: "Sessão expirada"
150
+ }
151
+ }
152
+
153
+ // Verificar drift de tempo
154
+ const timeDrift = Math.abs(now - timestamp)
155
+ if (timeDrift > this.config.maxTimeDrift) {
156
+ return {
157
+ success: false,
158
+ error: "Timestamp inválido"
159
+ }
160
+ }
161
+
162
+ // Construir mensagem para verificação
163
+ const messageToVerify = `${sessionId}:${timestamp}:${nonce}:${message}`
164
+ const messageHash = sha256(new TextEncoder().encode(messageToVerify))
165
+
166
+ // Verificar assinatura
167
+ const publicKeyBytes = hexToBytes(session.publicKey)
168
+ const signatureBytes = hexToBytes(signature)
169
+
170
+ const isValidSignature = ed25519.verify(signatureBytes, messageHash, publicKeyBytes)
171
+
172
+ if (!isValidSignature) {
173
+ return {
174
+ success: false,
175
+ error: "Assinatura inválida"
176
+ }
177
+ }
178
+
179
+ // Atualizar último uso da sessão
180
+ session.lastUsed = new Date()
181
+
182
+ return {
183
+ success: true,
184
+ sessionId,
185
+ user: {
186
+ sessionId,
187
+ isAdmin: session.isAdmin,
188
+ isSuperAdmin: session.isAdmin,
189
+ permissions: session.permissions
190
+ }
191
+ }
192
+ } catch (error) {
193
+ this.logger?.error("Erro ao validar sessão", { error })
194
+ return {
195
+ success: false,
196
+ error: "Erro interno ao validar sessão"
197
+ }
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Obter informações da sessão
203
+ */
204
+ async getSessionInfo(sessionId: string): Promise<SessionData | null> {
205
+ const session = this.sessions.get(sessionId)
206
+ if (!session) {
207
+ return null
208
+ }
209
+
210
+ // Verificar se não expirou
211
+ const now = Date.now()
212
+ const sessionAge = now - session.lastUsed.getTime()
213
+ if (sessionAge > this.config.sessionTimeout) {
214
+ this.sessions.delete(sessionId)
215
+ return null
216
+ }
217
+
218
+ return { ...session }
219
+ }
220
+
221
+ /**
222
+ * Destruir uma sessão
223
+ */
224
+ async destroySession(sessionId: string): Promise<void> {
225
+ this.sessions.delete(sessionId)
226
+ this.logger?.info("Sessão destruída", {
227
+ sessionId: sessionId.substring(0, 8) + "..."
228
+ })
229
+ }
230
+
231
+ /**
232
+ * Verificar se uma chave pública é válida
233
+ */
234
+ private isValidPublicKey(publicKey: string): boolean {
235
+ try {
236
+ if (publicKey.length !== 64) {
237
+ return false
238
+ }
239
+
240
+ const bytes = hexToBytes(publicKey)
241
+ return bytes.length === 32
242
+ } catch {
243
+ return false
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Limpar sessões expiradas
249
+ */
250
+ private cleanupExpiredSessions(): void {
251
+ const now = Date.now()
252
+ let cleanedCount = 0
253
+
254
+ for (const [sessionId, session] of this.sessions.entries()) {
255
+ const sessionAge = now - session.lastUsed.getTime()
256
+ if (sessionAge > this.config.sessionTimeout) {
257
+ this.sessions.delete(sessionId)
258
+ cleanedCount++
259
+ }
260
+ }
261
+
262
+ if (cleanedCount > 0) {
263
+ this.logger?.debug(`Limpeza de sessões: ${cleanedCount} sessões expiradas removidas`)
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Obter estatísticas das sessões
269
+ */
270
+ getStats() {
271
+ const now = Date.now()
272
+ let activeSessions = 0
273
+ let adminSessions = 0
274
+
275
+ for (const session of this.sessions.values()) {
276
+ const sessionAge = now - session.lastUsed.getTime()
277
+ if (sessionAge <= this.config.sessionTimeout) {
278
+ activeSessions++
279
+ if (session.isAdmin) {
280
+ adminSessions++
281
+ }
282
+ }
283
+ }
284
+
285
+ return {
286
+ totalSessions: this.sessions.size,
287
+ activeSessions,
288
+ adminSessions,
289
+ sessionTimeout: this.config.sessionTimeout,
290
+ adminKeys: this.config.adminKeys.length
291
+ }
292
+ }
293
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Exportações do servidor de autenticação
3
+ */
4
+
5
+ export { CryptoAuthService } from './CryptoAuthService'
6
+ export type { SessionData, AuthResult, CryptoAuthConfig } from './CryptoAuthService'
7
+
8
+ export { AuthMiddleware } from './AuthMiddleware'
9
+ export type { AuthMiddlewareConfig, AuthMiddlewareResult } from './AuthMiddleware'
package/vite.config.ts CHANGED
@@ -23,6 +23,22 @@ export default defineConfig({
23
23
  },
24
24
  })
25
25
  ],
26
+ define: {
27
+ __DEFINES__: JSON.stringify({}),
28
+ global: 'globalThis',
29
+ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
30
+ __HMR_CONFIG_NAME__: JSON.stringify('vite.config.ts'),
31
+ __BASE__: JSON.stringify('/'),
32
+ __SERVER_HOST__: JSON.stringify('localhost'),
33
+ __HMR_PROTOCOL__: JSON.stringify('ws'),
34
+ __HMR_PORT__: JSON.stringify(5173),
35
+ __HMR_HOSTNAME__: JSON.stringify('localhost'),
36
+ __HMR_BASE__: JSON.stringify('/'),
37
+ __HMR_DIRECT_TARGET__: JSON.stringify('localhost:5173'),
38
+ __HMR_ENABLE_OVERLAY__: JSON.stringify(true),
39
+ __HMR_TIMEOUT__: JSON.stringify(30000),
40
+ __WS_TOKEN__: JSON.stringify(''),
41
+ },
26
42
  root: 'app/client',
27
43
  server: {
28
44
  port: 5173,
@@ -1,326 +0,0 @@
1
- /**
2
- * Dynamic Environment Configuration Adapter for FluxStack
3
- * Integrates runtime env loader with existing configuration system
4
- * Solves Bun build issue by using dynamic environment access
5
- */
6
-
7
- import { env, runtimeEnv, envValidation } from '../utils/env-runtime'
8
- import type { FluxStackConfig, LogLevel, BuildTarget, LogFormat } from './schema'
9
-
10
- /**
11
- * Enhanced Environment Processor that uses dynamic env access
12
- * Replaces the original EnvironmentProcessor from env.ts
13
- */
14
- export class DynamicEnvironmentProcessor {
15
- private precedenceMap: Map<string, any> = new Map()
16
-
17
- /**
18
- * Process environment variables using dynamic runtime access
19
- * This prevents Bun from fixing env values during build
20
- */
21
- processEnvironmentVariables(): Partial<FluxStackConfig> {
22
- const config: any = {}
23
-
24
- // App configuration
25
- this.setConfigValue(config, 'app.name',
26
- env.get('FLUXSTACK_APP_NAME') || env.get('APP_NAME'), 'string')
27
- this.setConfigValue(config, 'app.version',
28
- env.get('FLUXSTACK_APP_VERSION') || env.get('APP_VERSION'), 'string')
29
- this.setConfigValue(config, 'app.description',
30
- env.get('FLUXSTACK_APP_DESCRIPTION') || env.get('APP_DESCRIPTION'), 'string')
31
-
32
- // Server configuration
33
- this.setConfigValue(config, 'server.port',
34
- env.get('PORT') || env.get('FLUXSTACK_PORT'), 'number')
35
- this.setConfigValue(config, 'server.host',
36
- env.get('HOST') || env.get('FLUXSTACK_HOST'), 'string')
37
- this.setConfigValue(config, 'server.apiPrefix',
38
- env.get('FLUXSTACK_API_PREFIX') || env.get('API_PREFIX'), 'string')
39
-
40
- // CORS configuration
41
- this.setConfigValue(config, 'server.cors.origins',
42
- env.get('CORS_ORIGINS') || env.get('FLUXSTACK_CORS_ORIGINS'), 'array')
43
- this.setConfigValue(config, 'server.cors.methods',
44
- env.get('CORS_METHODS') || env.get('FLUXSTACK_CORS_METHODS'), 'array')
45
- this.setConfigValue(config, 'server.cors.headers',
46
- env.get('CORS_HEADERS') || env.get('FLUXSTACK_CORS_HEADERS'), 'array')
47
- this.setConfigValue(config, 'server.cors.credentials',
48
- env.get('CORS_CREDENTIALS') || env.get('FLUXSTACK_CORS_CREDENTIALS'), 'boolean')
49
- this.setConfigValue(config, 'server.cors.maxAge',
50
- env.get('CORS_MAX_AGE') || env.get('FLUXSTACK_CORS_MAX_AGE'), 'number')
51
-
52
- // Client configuration
53
- this.setConfigValue(config, 'client.port',
54
- env.get('VITE_PORT') || env.get('CLIENT_PORT') || env.get('FLUXSTACK_CLIENT_PORT'), 'number')
55
- this.setConfigValue(config, 'client.proxy.target',
56
- env.get('VITE_API_URL') || env.get('API_URL') || env.get('FLUXSTACK_PROXY_TARGET'), 'string')
57
- this.setConfigValue(config, 'client.build.sourceMaps',
58
- env.get('FLUXSTACK_CLIENT_SOURCEMAPS'), 'boolean')
59
- this.setConfigValue(config, 'client.build.minify',
60
- env.get('FLUXSTACK_CLIENT_MINIFY'), 'boolean')
61
-
62
- // Build configuration
63
- this.setConfigValue(config, 'build.target',
64
- env.get('BUILD_TARGET') || env.get('FLUXSTACK_BUILD_TARGET'), 'buildTarget')
65
- this.setConfigValue(config, 'build.outDir',
66
- env.get('BUILD_OUTDIR') || env.get('FLUXSTACK_BUILD_OUTDIR'), 'string')
67
- this.setConfigValue(config, 'build.sourceMaps',
68
- env.get('BUILD_SOURCEMAPS') || env.get('FLUXSTACK_BUILD_SOURCEMAPS'), 'boolean')
69
- this.setConfigValue(config, 'build.clean',
70
- env.get('BUILD_CLEAN') || env.get('FLUXSTACK_BUILD_CLEAN'), 'boolean')
71
-
72
- // Build optimization
73
- this.setConfigValue(config, 'build.optimization.minify',
74
- env.get('BUILD_MINIFY') || env.get('FLUXSTACK_BUILD_MINIFY'), 'boolean')
75
- this.setConfigValue(config, 'build.optimization.treeshake',
76
- env.get('BUILD_TREESHAKE') || env.get('FLUXSTACK_BUILD_TREESHAKE'), 'boolean')
77
- this.setConfigValue(config, 'build.optimization.compress',
78
- env.get('BUILD_COMPRESS') || env.get('FLUXSTACK_BUILD_COMPRESS'), 'boolean')
79
- this.setConfigValue(config, 'build.optimization.splitChunks',
80
- env.get('BUILD_SPLIT_CHUNKS') || env.get('FLUXSTACK_BUILD_SPLIT_CHUNKS'), 'boolean')
81
- this.setConfigValue(config, 'build.optimization.bundleAnalyzer',
82
- env.get('BUILD_ANALYZER') || env.get('FLUXSTACK_BUILD_ANALYZER'), 'boolean')
83
-
84
- // Logging configuration
85
- this.setConfigValue(config, 'logging.level',
86
- env.get('LOG_LEVEL') || env.get('FLUXSTACK_LOG_LEVEL'), 'logLevel')
87
- this.setConfigValue(config, 'logging.format',
88
- env.get('LOG_FORMAT') || env.get('FLUXSTACK_LOG_FORMAT'), 'logFormat')
89
-
90
- // Monitoring configuration
91
- this.setConfigValue(config, 'monitoring.enabled',
92
- env.get('MONITORING_ENABLED') || env.get('FLUXSTACK_MONITORING_ENABLED'), 'boolean')
93
- this.setConfigValue(config, 'monitoring.metrics.enabled',
94
- env.get('METRICS_ENABLED') || env.get('FLUXSTACK_METRICS_ENABLED'), 'boolean')
95
- this.setConfigValue(config, 'monitoring.metrics.collectInterval',
96
- env.get('METRICS_INTERVAL') || env.get('FLUXSTACK_METRICS_INTERVAL'), 'number')
97
- this.setConfigValue(config, 'monitoring.profiling.enabled',
98
- env.get('PROFILING_ENABLED') || env.get('FLUXSTACK_PROFILING_ENABLED'), 'boolean')
99
- this.setConfigValue(config, 'monitoring.profiling.sampleRate',
100
- env.get('PROFILING_SAMPLE_RATE') || env.get('FLUXSTACK_PROFILING_SAMPLE_RATE'), 'number')
101
-
102
- // Database configuration
103
- this.setConfigValue(config, 'database.url', env.get('DATABASE_URL'), 'string')
104
- this.setConfigValue(config, 'database.host', env.get('DATABASE_HOST'), 'string')
105
- this.setConfigValue(config, 'database.port', env.get('DATABASE_PORT'), 'number')
106
- this.setConfigValue(config, 'database.database', env.get('DATABASE_NAME'), 'string')
107
- this.setConfigValue(config, 'database.user', env.get('DATABASE_USER'), 'string')
108
- this.setConfigValue(config, 'database.password', env.get('DATABASE_PASSWORD'), 'string')
109
- this.setConfigValue(config, 'database.ssl', env.get('DATABASE_SSL'), 'boolean')
110
- this.setConfigValue(config, 'database.poolSize', env.get('DATABASE_POOL_SIZE'), 'number')
111
-
112
- // Auth configuration
113
- this.setConfigValue(config, 'auth.secret', env.get('JWT_SECRET'), 'string')
114
- this.setConfigValue(config, 'auth.expiresIn', env.get('JWT_EXPIRES_IN'), 'string')
115
- this.setConfigValue(config, 'auth.algorithm', env.get('JWT_ALGORITHM'), 'string')
116
- this.setConfigValue(config, 'auth.issuer', env.get('JWT_ISSUER'), 'string')
117
-
118
- // Email configuration
119
- this.setConfigValue(config, 'email.host', env.get('SMTP_HOST'), 'string')
120
- this.setConfigValue(config, 'email.port', env.get('SMTP_PORT'), 'number')
121
- this.setConfigValue(config, 'email.user', env.get('SMTP_USER'), 'string')
122
- this.setConfigValue(config, 'email.password', env.get('SMTP_PASSWORD'), 'string')
123
- this.setConfigValue(config, 'email.secure', env.get('SMTP_SECURE'), 'boolean')
124
- this.setConfigValue(config, 'email.from', env.get('SMTP_FROM'), 'string')
125
-
126
- // Storage configuration
127
- this.setConfigValue(config, 'storage.uploadPath', env.get('UPLOAD_PATH'), 'string')
128
- this.setConfigValue(config, 'storage.maxFileSize', env.get('MAX_FILE_SIZE'), 'number')
129
- this.setConfigValue(config, 'storage.provider', env.get('STORAGE_PROVIDER'), 'string')
130
-
131
- // Plugin configuration
132
- this.setConfigValue(config, 'plugins.enabled',
133
- env.get('FLUXSTACK_PLUGINS_ENABLED'), 'array')
134
- this.setConfigValue(config, 'plugins.disabled',
135
- env.get('FLUXSTACK_PLUGINS_DISABLED'), 'array')
136
-
137
- return this.cleanEmptyObjects(config)
138
- }
139
-
140
- private setConfigValue(
141
- config: any,
142
- path: string,
143
- value: string | undefined,
144
- type: string
145
- ): void {
146
- if (value === undefined || value === '') return
147
-
148
- const convertedValue = this.convertValue(value, type)
149
- if (convertedValue !== undefined) {
150
- this.setNestedProperty(config, path, convertedValue)
151
-
152
- // Track precedence
153
- this.precedenceMap.set(path, {
154
- source: 'environment',
155
- path,
156
- value: convertedValue,
157
- priority: 3
158
- })
159
- }
160
- }
161
-
162
- private convertValue(value: string, type: string): any {
163
- switch (type) {
164
- case 'string':
165
- return value
166
- case 'number':
167
- const num = parseInt(value, 10)
168
- return isNaN(num) ? undefined : num
169
- case 'boolean':
170
- return ['true', '1', 'yes', 'on'].includes(value.toLowerCase())
171
- case 'array':
172
- return value.split(',').map(v => v.trim()).filter(Boolean)
173
- case 'logLevel':
174
- const level = value.toLowerCase() as LogLevel
175
- return ['debug', 'info', 'warn', 'error'].includes(level) ? level : 'info'
176
- case 'buildTarget':
177
- const target = value.toLowerCase() as BuildTarget
178
- return ['bun', 'node', 'docker'].includes(target) ? target : 'bun'
179
- case 'logFormat':
180
- const format = value.toLowerCase() as LogFormat
181
- return ['json', 'pretty'].includes(format) ? format : 'pretty'
182
- case 'object':
183
- try {
184
- return JSON.parse(value)
185
- } catch {
186
- return {}
187
- }
188
- default:
189
- return value
190
- }
191
- }
192
-
193
- private setNestedProperty(obj: any, path: string, value: any): void {
194
- const keys = path.split('.')
195
- let current = obj
196
-
197
- for (let i = 0; i < keys.length - 1; i++) {
198
- const key = keys[i]
199
- if (!(key in current) || typeof current[key] !== 'object') {
200
- current[key] = {}
201
- }
202
- current = current[key]
203
- }
204
-
205
- current[keys[keys.length - 1]] = value
206
- }
207
-
208
- private cleanEmptyObjects(obj: any): any {
209
- if (typeof obj !== 'object' || obj === null) return obj
210
-
211
- const cleaned: any = {}
212
-
213
- for (const [key, value] of Object.entries(obj)) {
214
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
215
- const cleanedValue = this.cleanEmptyObjects(value)
216
- if (Object.keys(cleanedValue).length > 0) {
217
- cleaned[key] = cleanedValue
218
- }
219
- } else if (value !== undefined && value !== null) {
220
- cleaned[key] = value
221
- }
222
- }
223
-
224
- return cleaned
225
- }
226
-
227
- getPrecedenceInfo(): Map<string, any> {
228
- return new Map(this.precedenceMap)
229
- }
230
-
231
- clearPrecedence(): void {
232
- this.precedenceMap.clear()
233
- }
234
- }
235
-
236
- /**
237
- * Enhanced environment info with dynamic access
238
- */
239
- export function getDynamicEnvironmentInfo() {
240
- const nodeEnv = env.get('NODE_ENV', 'development') as 'development' | 'production' | 'test'
241
-
242
- return {
243
- name: nodeEnv,
244
- isDevelopment: nodeEnv === 'development',
245
- isProduction: nodeEnv === 'production',
246
- isTest: nodeEnv === 'test',
247
- nodeEnv
248
- }
249
- }
250
-
251
- /**
252
- * Runtime configuration loader that uses dynamic env access
253
- */
254
- export function loadConfigFromDynamicEnv(): Partial<FluxStackConfig> {
255
- const processor = new DynamicEnvironmentProcessor()
256
- return processor.processEnvironmentVariables()
257
- }
258
-
259
- /**
260
- * Utility functions for backward compatibility
261
- */
262
- export function isDevelopment(): boolean {
263
- return getDynamicEnvironmentInfo().isDevelopment
264
- }
265
-
266
- export function isProduction(): boolean {
267
- return getDynamicEnvironmentInfo().isProduction
268
- }
269
-
270
- export function isTest(): boolean {
271
- return getDynamicEnvironmentInfo().isTest
272
- }
273
-
274
- /**
275
- * Validate critical environment variables for production
276
- */
277
- export function validateProductionEnv(): void {
278
- if (isProduction()) {
279
- const requiredVars = ['NODE_ENV']
280
- const missingVars = requiredVars.filter(key => !env.has(key))
281
-
282
- if (missingVars.length > 0) {
283
- throw new Error(`Missing required production environment variables: ${missingVars.join(', ')}`)
284
- }
285
-
286
- // Validate LOG_LEVEL for production
287
- const logLevel = env.get('LOG_LEVEL')
288
- if (logLevel === 'debug') {
289
- console.warn('⚠️ Production environment should not use debug logging')
290
- }
291
- }
292
- }
293
-
294
- /**
295
- * Create environment-aware configuration
296
- */
297
- export function createDynamicConfig(): Partial<FluxStackConfig> {
298
- const envInfo = getDynamicEnvironmentInfo()
299
- const envConfig = loadConfigFromDynamicEnv()
300
-
301
- // Add environment-specific defaults
302
- const config: any = { ...envConfig }
303
-
304
- // Ensure proper defaults based on environment
305
- if (envInfo.isDevelopment) {
306
- config.logging = {
307
- level: env.get('LOG_LEVEL', 'debug'),
308
- format: env.get('LOG_FORMAT', 'pretty'),
309
- ...config.logging
310
- }
311
- } else if (envInfo.isProduction) {
312
- config.logging = {
313
- level: env.get('LOG_LEVEL', 'warn'),
314
- format: env.get('LOG_FORMAT', 'json'),
315
- ...config.logging
316
- }
317
- }
318
-
319
- return config
320
- }
321
-
322
- // Export singleton instance
323
- export const dynamicEnvironmentProcessor = new DynamicEnvironmentProcessor()
324
-
325
- // Export runtime environment access
326
- export { env, runtimeEnv, envValidation } from '../utils/env-runtime'