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.
- package/app/server/backend-only.ts +5 -5
- package/app/server/index.ts +63 -54
- package/app/server/live/FluxStackConfig.ts +43 -39
- package/app/server/live/SystemMonitorIntegration.ts +2 -2
- package/app/server/live/register-components.ts +1 -1
- package/app/server/middleware/errorHandling.ts +6 -4
- package/app/server/routes/config.ts +145 -0
- package/app/server/routes/index.ts +5 -3
- package/config/app.config.ts +113 -0
- package/config/build.config.ts +24 -0
- package/config/database.config.ts +99 -0
- package/config/index.ts +68 -0
- package/config/logger.config.ts +27 -0
- package/config/runtime.config.ts +92 -0
- package/config/server.config.ts +46 -0
- package/config/services.config.ts +130 -0
- package/config/system.config.ts +105 -0
- package/core/build/index.ts +10 -4
- package/core/cli/index.ts +29 -12
- package/core/config/env.ts +37 -95
- package/core/config/runtime-config.ts +61 -58
- package/core/config/schema.ts +4 -0
- package/core/framework/server.ts +22 -10
- package/core/plugins/built-in/index.ts +7 -17
- package/core/plugins/built-in/swagger/index.ts +228 -228
- package/core/plugins/built-in/vite/index.ts +374 -358
- package/core/plugins/dependency-manager.ts +5 -5
- package/core/plugins/manager.ts +12 -12
- package/core/plugins/registry.ts +3 -3
- package/core/server/index.ts +0 -1
- package/core/server/live/ComponentRegistry.ts +34 -8
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/websocket-plugin.ts +434 -434
- package/core/server/middleware/README.md +488 -0
- package/core/server/middleware/elysia-helpers.ts +227 -0
- package/core/server/middleware/index.ts +25 -9
- package/core/server/plugins/static-files-plugin.ts +231 -231
- package/core/utils/config-schema.ts +484 -0
- package/core/utils/env.ts +306 -0
- package/core/utils/helpers.ts +4 -4
- package/core/utils/logger/colors.ts +114 -0
- package/core/utils/logger/config.ts +35 -0
- package/core/utils/logger/formatter.ts +82 -0
- package/core/utils/logger/group-logger.ts +101 -0
- package/core/utils/logger/index.ts +199 -250
- package/core/utils/logger/stack-trace.ts +92 -0
- package/core/utils/logger/startup-banner.ts +92 -0
- package/core/utils/logger/winston-logger.ts +152 -0
- package/core/utils/version.ts +5 -0
- package/create-fluxstack.ts +1 -0
- package/fluxstack.config.ts +2 -2
- package/package.json +117 -115
- package/core/config/env-dynamic.ts +0 -326
- package/core/plugins/built-in/logger/index.ts +0 -180
- package/core/server/plugins/logger.ts +0 -47
- package/core/utils/env-runtime-v2.ts +0 -232
- package/core/utils/env-runtime.ts +0 -259
- package/core/utils/logger/formatters.ts +0 -222
- package/core/utils/logger/middleware.ts +0 -253
- package/core/utils/logger/performance.ts +0 -384
- package/core/utils/logger/transports.ts +0 -365
- package/core/utils/logger.ts +0 -106
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// Backend standalone entry point
|
|
2
2
|
import { startBackendOnly } from "@/core/server/standalone"
|
|
3
3
|
import { apiRoutes } from "./routes"
|
|
4
|
-
import {
|
|
4
|
+
import { serverConfig } from "@/config/server.config"
|
|
5
5
|
|
|
6
|
-
// Configuração para backend standalone
|
|
6
|
+
// Configuração para backend standalone usando config declarativo
|
|
7
7
|
const backendConfig = {
|
|
8
|
-
port:
|
|
9
|
-
apiPrefix:
|
|
8
|
+
port: serverConfig.backendPort,
|
|
9
|
+
apiPrefix: serverConfig.apiPrefix
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
console.log(`🚀 Backend standalone: ${
|
|
12
|
+
console.log(`🚀 Backend standalone: ${serverConfig.host}:${backendConfig.port}`)
|
|
13
13
|
|
|
14
14
|
// Iniciar apenas o backend
|
|
15
15
|
startBackendOnly(apiRoutes, backendConfig)
|
package/app/server/index.ts
CHANGED
|
@@ -1,52 +1,54 @@
|
|
|
1
1
|
// User application entry point
|
|
2
|
-
import { FluxStackFramework,
|
|
2
|
+
import { FluxStackFramework, vitePlugin, swaggerPlugin, staticPlugin, liveComponentsPlugin, staticFilesPlugin } from "@/core/server"
|
|
3
3
|
import { isDevelopment } from "@/core/utils/helpers"
|
|
4
|
+
import { DEBUG } from "@/core/utils/logger"
|
|
4
5
|
import { apiRoutes } from "./routes"
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import { helpers } from "@/core/utils/env"
|
|
7
|
+
import { serverConfig } from "@/config/server.config"
|
|
8
|
+
import { appConfig } from "@/config/app.config"
|
|
9
|
+
import { loggerConfig } from "@/config/logger.config"
|
|
8
10
|
import "./live/register-components"
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// Startup info moved to DEBUG level (set LOG_LEVEL=debug to see details)
|
|
13
|
+
DEBUG('🔧 Loading declarative configuration...')
|
|
14
|
+
DEBUG(`📊 Environment: ${appConfig.env}`)
|
|
15
|
+
DEBUG(`🚀 Port: ${serverConfig.port}`)
|
|
16
|
+
DEBUG(`🌐 Host: ${serverConfig.host}`)
|
|
14
17
|
|
|
15
|
-
// Criar aplicação com configuração
|
|
18
|
+
// Criar aplicação com configuração declarativa
|
|
16
19
|
const app = new FluxStackFramework({
|
|
17
20
|
server: {
|
|
18
|
-
port:
|
|
19
|
-
host:
|
|
20
|
-
apiPrefix:
|
|
21
|
+
port: serverConfig.port,
|
|
22
|
+
host: serverConfig.host,
|
|
23
|
+
apiPrefix: serverConfig.apiPrefix,
|
|
21
24
|
cors: {
|
|
22
|
-
origins:
|
|
23
|
-
methods:
|
|
24
|
-
headers:
|
|
25
|
-
credentials:
|
|
25
|
+
origins: serverConfig.corsOrigins,
|
|
26
|
+
methods: serverConfig.corsMethods,
|
|
27
|
+
headers: serverConfig.corsHeaders,
|
|
28
|
+
credentials: serverConfig.corsCredentials
|
|
26
29
|
},
|
|
27
30
|
middleware: []
|
|
28
31
|
},
|
|
29
32
|
app: {
|
|
30
|
-
name:
|
|
31
|
-
version:
|
|
33
|
+
name: serverConfig.appName,
|
|
34
|
+
version: serverConfig.appVersion
|
|
32
35
|
},
|
|
33
36
|
client: {
|
|
34
|
-
port:
|
|
37
|
+
port: serverConfig.clientPort,
|
|
35
38
|
proxy: {
|
|
36
|
-
target: helpers.getServerUrl()
|
|
39
|
+
target: helpers.getServerUrl()
|
|
37
40
|
},
|
|
38
41
|
build: {
|
|
39
|
-
sourceMaps:
|
|
40
|
-
minify:
|
|
41
|
-
target:
|
|
42
|
-
outDir:
|
|
42
|
+
sourceMaps: serverConfig.clientSourceMaps,
|
|
43
|
+
minify: false,
|
|
44
|
+
target: serverConfig.clientTarget as any,
|
|
45
|
+
outDir: serverConfig.clientOutDir
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
})
|
|
46
49
|
|
|
47
50
|
|
|
48
|
-
// Usar plugins de infraestrutura primeiro (
|
|
49
|
-
app.use(loggerPlugin)
|
|
51
|
+
// Usar plugins de infraestrutura primeiro (Logger é core, não é plugin)
|
|
50
52
|
|
|
51
53
|
// Usar plugins condicionalmente baseado no ambiente
|
|
52
54
|
if (isDevelopment()) {
|
|
@@ -60,30 +62,47 @@ app.use(staticFilesPlugin) // Add Static Files support
|
|
|
60
62
|
app.use(liveComponentsPlugin) // Add Live Components support
|
|
61
63
|
|
|
62
64
|
|
|
63
|
-
// Adicionar rota de teste para mostrar
|
|
65
|
+
// Adicionar rota de teste para mostrar config declarativo (antes das rotas)
|
|
64
66
|
app.getApp().get('/api/env-test', () => {
|
|
65
67
|
return {
|
|
66
|
-
message: '
|
|
68
|
+
message: '⚡ Declarative Config System!',
|
|
67
69
|
timestamp: new Date().toISOString(),
|
|
70
|
+
serverConfig: {
|
|
71
|
+
port: serverConfig.port,
|
|
72
|
+
host: serverConfig.host,
|
|
73
|
+
apiPrefix: serverConfig.apiPrefix,
|
|
74
|
+
appName: serverConfig.appName,
|
|
75
|
+
appVersion: serverConfig.appVersion,
|
|
76
|
+
cors: {
|
|
77
|
+
origins: serverConfig.corsOrigins,
|
|
78
|
+
methods: serverConfig.corsMethods,
|
|
79
|
+
credentials: serverConfig.corsCredentials
|
|
80
|
+
},
|
|
81
|
+
client: {
|
|
82
|
+
port: serverConfig.clientPort,
|
|
83
|
+
target: serverConfig.clientTarget,
|
|
84
|
+
sourceMaps: serverConfig.clientSourceMaps
|
|
85
|
+
},
|
|
86
|
+
features: {
|
|
87
|
+
enableSwagger: serverConfig.enableSwagger,
|
|
88
|
+
enableMetrics: serverConfig.enableMetrics,
|
|
89
|
+
enableMonitoring: serverConfig.enableMonitoring
|
|
90
|
+
}
|
|
91
|
+
},
|
|
68
92
|
environment: {
|
|
69
|
-
NODE_ENV: env
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
DEBUG: env.DEBUG, // Direto!
|
|
73
|
-
CORS_ORIGINS: env.CORS_ORIGINS, // Direto!
|
|
74
|
-
ENABLE_SWAGGER: env.ENABLE_SWAGGER, // Direto!
|
|
75
|
-
|
|
76
|
-
// Vars customizadas com casting automático
|
|
77
|
-
CUSTOM_VAR: env.get('CUSTOM_VAR', 'not-set'),
|
|
78
|
-
MAX_RETRIES: env.get('MAX_RETRIES', 3), // number
|
|
79
|
-
ENABLE_CACHE: env.get('ENABLE_CACHE', false), // boolean
|
|
80
|
-
ALLOWED_IPS: env.get('ALLOWED_IPS', []) // string[]
|
|
93
|
+
NODE_ENV: appConfig.env,
|
|
94
|
+
DEBUG: appConfig.debug,
|
|
95
|
+
LOG_LEVEL: loggerConfig.level
|
|
81
96
|
},
|
|
82
97
|
urls: {
|
|
83
|
-
server: helpers.getServerUrl(),
|
|
98
|
+
server: helpers.getServerUrl(),
|
|
99
|
+
client: helpers.getClientUrl(),
|
|
84
100
|
swagger: `${helpers.getServerUrl()}/swagger`
|
|
85
101
|
},
|
|
86
|
-
|
|
102
|
+
system: {
|
|
103
|
+
version: 'declarative-config',
|
|
104
|
+
features: ['type-safe', 'validated', 'declarative', 'runtime-reload']
|
|
105
|
+
}
|
|
87
106
|
}
|
|
88
107
|
})
|
|
89
108
|
|
|
@@ -93,18 +112,8 @@ app.routes(apiRoutes)
|
|
|
93
112
|
// Swagger por último para descobrir todas as rotas
|
|
94
113
|
app.use(swaggerPlugin)
|
|
95
114
|
|
|
96
|
-
// Iniciar servidor
|
|
97
|
-
app.listen(
|
|
98
|
-
console.log('\n✅ FluxStack com Environment Variables Simplificado!')
|
|
99
|
-
console.log(`🔗 Server: ${helpers.getServerUrl()}`)
|
|
100
|
-
console.log(`🔗 Teste dinâmico: ${helpers.getServerUrl()}/api/env-test`)
|
|
101
|
-
|
|
102
|
-
if (env.ENABLE_SWAGGER) {
|
|
103
|
-
console.log(`📋 Swagger: ${helpers.getServerUrl()}/swagger`)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
console.log('💡 Mude as env vars e reinicie para ver a diferença!')
|
|
107
|
-
})
|
|
115
|
+
// Iniciar servidor (banner displayed by framework)
|
|
116
|
+
app.listen()
|
|
108
117
|
|
|
109
118
|
|
|
110
119
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// 🔥 FluxStack Configuration Live Component
|
|
2
2
|
|
|
3
3
|
import { LiveComponent } from '@/core/types/types'
|
|
4
|
+
import { appConfig } from '@/config/app.config'
|
|
5
|
+
import { serverConfig } from '@/config/server.config'
|
|
6
|
+
import { loggerConfig } from '@/config/logger.config'
|
|
7
|
+
import { systemConfig, systemRuntimeInfo } from '@/config/system.config'
|
|
4
8
|
|
|
5
9
|
export interface FluxStackConfigState {
|
|
6
10
|
// Environment Configuration
|
|
@@ -154,10 +158,10 @@ export class FluxStackConfig extends LiveComponent<FluxStackConfigState> {
|
|
|
154
158
|
|
|
155
159
|
// Set default state with real configuration
|
|
156
160
|
this.state = {
|
|
157
|
-
environment:
|
|
158
|
-
port:
|
|
159
|
-
host:
|
|
160
|
-
apiPrefix:
|
|
161
|
+
environment: appConfig.env,
|
|
162
|
+
port: serverConfig.port,
|
|
163
|
+
host: serverConfig.host,
|
|
164
|
+
apiPrefix: serverConfig.apiPrefix,
|
|
161
165
|
|
|
162
166
|
framework: {
|
|
163
167
|
name: 'FluxStack',
|
|
@@ -195,7 +199,7 @@ export class FluxStackConfig extends LiveComponent<FluxStackConfigState> {
|
|
|
195
199
|
enabled: true,
|
|
196
200
|
dependencies: [],
|
|
197
201
|
config: {
|
|
198
|
-
level:
|
|
202
|
+
level: loggerConfig.level,
|
|
199
203
|
format: 'pretty'
|
|
200
204
|
}
|
|
201
205
|
},
|
|
@@ -244,17 +248,15 @@ export class FluxStackConfig extends LiveComponent<FluxStackConfigState> {
|
|
|
244
248
|
|
|
245
249
|
// Get runtime configuration
|
|
246
250
|
private getRuntimeConfiguration() {
|
|
247
|
-
const os = require('os')
|
|
248
|
-
|
|
249
251
|
return {
|
|
250
|
-
nodeVersion:
|
|
251
|
-
bunVersion:
|
|
252
|
-
platform:
|
|
253
|
-
architecture:
|
|
254
|
-
cpuCount:
|
|
255
|
-
totalMemory:
|
|
256
|
-
workingDirectory:
|
|
257
|
-
executablePath:
|
|
252
|
+
nodeVersion: systemRuntimeInfo.nodeVersion,
|
|
253
|
+
bunVersion: systemRuntimeInfo.bunVersion,
|
|
254
|
+
platform: systemRuntimeInfo.platform,
|
|
255
|
+
architecture: systemRuntimeInfo.architecture,
|
|
256
|
+
cpuCount: systemRuntimeInfo.cpuCount,
|
|
257
|
+
totalMemory: systemRuntimeInfo.totalMemory,
|
|
258
|
+
workingDirectory: systemRuntimeInfo.workingDirectory,
|
|
259
|
+
executablePath: systemRuntimeInfo.executablePath
|
|
258
260
|
}
|
|
259
261
|
}
|
|
260
262
|
|
|
@@ -355,17 +357,17 @@ export class FluxStackConfig extends LiveComponent<FluxStackConfigState> {
|
|
|
355
357
|
// Get Logging configuration
|
|
356
358
|
private getLoggingConfiguration() {
|
|
357
359
|
return {
|
|
358
|
-
level:
|
|
359
|
-
format: 'pretty',
|
|
360
|
+
level: loggerConfig.level as 'debug' | 'info' | 'warn' | 'error',
|
|
361
|
+
format: 'pretty' as 'json' | 'pretty' | 'compact',
|
|
360
362
|
file: {
|
|
361
|
-
enabled:
|
|
362
|
-
path: undefined,
|
|
363
|
-
maxSize:
|
|
364
|
-
maxFiles: undefined
|
|
363
|
+
enabled: loggerConfig.logToFile,
|
|
364
|
+
path: loggerConfig.logToFile ? 'logs/app.log' : undefined,
|
|
365
|
+
maxSize: loggerConfig.maxSize,
|
|
366
|
+
maxFiles: parseInt(loggerConfig.maxFiles) || undefined
|
|
365
367
|
},
|
|
366
368
|
console: {
|
|
367
369
|
enabled: true,
|
|
368
|
-
colors:
|
|
370
|
+
colors: loggerConfig.enableColors
|
|
369
371
|
}
|
|
370
372
|
}
|
|
371
373
|
}
|
|
@@ -408,16 +410,18 @@ export class FluxStackConfig extends LiveComponent<FluxStackConfigState> {
|
|
|
408
410
|
// Update specific configuration section
|
|
409
411
|
async updateConfiguration(data: { section: string; config: Record<string, any> }) {
|
|
410
412
|
const { section, config } = data
|
|
411
|
-
|
|
413
|
+
|
|
412
414
|
if (!this.state[section as keyof FluxStackConfigState]) {
|
|
413
415
|
throw new Error(`Invalid configuration section: ${section}`)
|
|
414
416
|
}
|
|
415
|
-
|
|
417
|
+
|
|
418
|
+
const currentSection = this.state[section as keyof FluxStackConfigState]
|
|
419
|
+
const updatedSection = typeof currentSection === 'object' && currentSection !== null
|
|
420
|
+
? { ...currentSection as object, ...config }
|
|
421
|
+
: config
|
|
422
|
+
|
|
416
423
|
this.setState({
|
|
417
|
-
[section]:
|
|
418
|
-
...this.state[section as keyof FluxStackConfigState],
|
|
419
|
-
...config
|
|
420
|
-
},
|
|
424
|
+
[section]: updatedSection,
|
|
421
425
|
lastUpdated: Date.now()
|
|
422
426
|
} as Partial<FluxStackConfigState>)
|
|
423
427
|
|
|
@@ -433,22 +437,22 @@ export class FluxStackConfig extends LiveComponent<FluxStackConfigState> {
|
|
|
433
437
|
// Get environment variables
|
|
434
438
|
async getEnvironmentVariables() {
|
|
435
439
|
const envVars = {
|
|
436
|
-
NODE_ENV:
|
|
437
|
-
PORT:
|
|
438
|
-
HOST:
|
|
439
|
-
LOG_LEVEL:
|
|
440
|
-
// Add other non-sensitive env vars
|
|
441
|
-
PWD:
|
|
442
|
-
PATH:
|
|
443
|
-
USER:
|
|
444
|
-
HOME:
|
|
440
|
+
NODE_ENV: appConfig.env,
|
|
441
|
+
PORT: serverConfig.port.toString(),
|
|
442
|
+
HOST: serverConfig.host,
|
|
443
|
+
LOG_LEVEL: loggerConfig.level,
|
|
444
|
+
// Add other non-sensitive env vars from system config
|
|
445
|
+
PWD: systemConfig.pwd || undefined,
|
|
446
|
+
PATH: systemConfig.path ? '***truncated***' : undefined,
|
|
447
|
+
USER: systemConfig.currentUser,
|
|
448
|
+
HOME: systemConfig.homeDirectory || undefined
|
|
445
449
|
}
|
|
446
|
-
|
|
450
|
+
|
|
447
451
|
this.emit('ENVIRONMENT_VARIABLES_REQUESTED', {
|
|
448
452
|
count: Object.keys(envVars).length,
|
|
449
453
|
timestamp: Date.now()
|
|
450
454
|
})
|
|
451
|
-
|
|
455
|
+
|
|
452
456
|
return envVars
|
|
453
457
|
}
|
|
454
458
|
|
|
@@ -87,8 +87,8 @@ export class SystemMonitorIntegration {
|
|
|
87
87
|
this.recordMessage(action, componentId, success, responseTime)
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
|
|
91
|
+
// Setup completed - logged in auto-discovery group
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// Record a connection event
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
// NOTE: If you see this in production, auto-generation worked!
|
|
6
6
|
// All Live Components should be discovered and registered automatically during build
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// Removed startup log to keep output clean - auto-generation handles everything
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Context } from 'elysia'
|
|
7
|
+
import { appConfig } from '@/config/app.config'
|
|
7
8
|
|
|
8
9
|
export interface ErrorResponse {
|
|
9
10
|
error: string
|
|
@@ -139,14 +140,15 @@ export const errorHandlingMiddleware = {
|
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
// Default to internal server error
|
|
143
|
+
const isProduction = appConfig.env === 'production'
|
|
142
144
|
return createErrorResponse(
|
|
143
145
|
500,
|
|
144
|
-
|
|
145
|
-
? 'Internal server error'
|
|
146
|
+
isProduction
|
|
147
|
+
? 'Internal server error'
|
|
146
148
|
: error.message,
|
|
147
149
|
'INTERNAL_ERROR',
|
|
148
|
-
|
|
149
|
-
? undefined
|
|
150
|
+
isProduction
|
|
151
|
+
? undefined
|
|
150
152
|
: { stack: error.stack },
|
|
151
153
|
requestId
|
|
152
154
|
)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Management Routes
|
|
3
|
+
* Allows runtime configuration reload and inspection
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Elysia, t } from 'elysia'
|
|
7
|
+
import { appRuntimeConfig } from '@/config/runtime.config'
|
|
8
|
+
|
|
9
|
+
export const configRoutes = new Elysia({ prefix: '/config' })
|
|
10
|
+
/**
|
|
11
|
+
* Get current runtime configuration
|
|
12
|
+
*/
|
|
13
|
+
.get('/', () => {
|
|
14
|
+
return {
|
|
15
|
+
success: true,
|
|
16
|
+
config: appRuntimeConfig.values,
|
|
17
|
+
timestamp: new Date().toISOString()
|
|
18
|
+
}
|
|
19
|
+
}, {
|
|
20
|
+
detail: {
|
|
21
|
+
summary: 'Get current runtime configuration',
|
|
22
|
+
tags: ['Config']
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Reload configuration from environment
|
|
28
|
+
*/
|
|
29
|
+
.post('/reload', () => {
|
|
30
|
+
try {
|
|
31
|
+
const oldConfig = { ...appRuntimeConfig.values }
|
|
32
|
+
const newConfig = appRuntimeConfig.reload()
|
|
33
|
+
|
|
34
|
+
// Find changed fields
|
|
35
|
+
const changes: Record<string, { old: any, new: any }> = {}
|
|
36
|
+
for (const key in newConfig) {
|
|
37
|
+
if (oldConfig[key] !== newConfig[key]) {
|
|
38
|
+
changes[key] = {
|
|
39
|
+
old: oldConfig[key],
|
|
40
|
+
new: newConfig[key]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
success: true,
|
|
47
|
+
message: 'Configuration reloaded successfully',
|
|
48
|
+
changes,
|
|
49
|
+
timestamp: new Date().toISOString()
|
|
50
|
+
}
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
error: error.message,
|
|
55
|
+
timestamp: new Date().toISOString()
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}, {
|
|
59
|
+
detail: {
|
|
60
|
+
summary: 'Reload configuration from environment variables',
|
|
61
|
+
description: 'Reloads configuration without restarting the server. Validates new values before applying.',
|
|
62
|
+
tags: ['Config']
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get specific config field
|
|
68
|
+
*/
|
|
69
|
+
.get('/:field', ({ params: { field } }) => {
|
|
70
|
+
const value = appRuntimeConfig.get(field as any)
|
|
71
|
+
|
|
72
|
+
if (value === undefined) {
|
|
73
|
+
return {
|
|
74
|
+
success: false,
|
|
75
|
+
error: `Field '${field}' not found`,
|
|
76
|
+
timestamp: new Date().toISOString()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
success: true,
|
|
82
|
+
field,
|
|
83
|
+
value,
|
|
84
|
+
type: typeof value,
|
|
85
|
+
timestamp: new Date().toISOString()
|
|
86
|
+
}
|
|
87
|
+
}, {
|
|
88
|
+
detail: {
|
|
89
|
+
summary: 'Get specific configuration field',
|
|
90
|
+
tags: ['Config']
|
|
91
|
+
},
|
|
92
|
+
params: t.Object({
|
|
93
|
+
field: t.String()
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if config field exists
|
|
99
|
+
*/
|
|
100
|
+
.get('/:field/exists', ({ params: { field } }) => {
|
|
101
|
+
const exists = appRuntimeConfig.has(field as any)
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
success: true,
|
|
105
|
+
field,
|
|
106
|
+
exists,
|
|
107
|
+
timestamp: new Date().toISOString()
|
|
108
|
+
}
|
|
109
|
+
}, {
|
|
110
|
+
detail: {
|
|
111
|
+
summary: 'Check if configuration field exists',
|
|
112
|
+
tags: ['Config']
|
|
113
|
+
},
|
|
114
|
+
params: t.Object({
|
|
115
|
+
field: t.String()
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Health check for config system
|
|
121
|
+
*/
|
|
122
|
+
.get('/health', () => {
|
|
123
|
+
try {
|
|
124
|
+
const config = appRuntimeConfig.values
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
success: true,
|
|
128
|
+
status: 'healthy',
|
|
129
|
+
fieldsLoaded: Object.keys(config).length,
|
|
130
|
+
timestamp: new Date().toISOString()
|
|
131
|
+
}
|
|
132
|
+
} catch (error: any) {
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
status: 'unhealthy',
|
|
136
|
+
error: error.message,
|
|
137
|
+
timestamp: new Date().toISOString()
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}, {
|
|
141
|
+
detail: {
|
|
142
|
+
summary: 'Config system health check',
|
|
143
|
+
tags: ['Config']
|
|
144
|
+
}
|
|
145
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Elysia, t } from "elysia"
|
|
2
2
|
import { usersRoutes } from "./users.routes"
|
|
3
3
|
import { uploadRoutes } from "./upload"
|
|
4
|
+
import { configRoutes } from "./config"
|
|
4
5
|
|
|
5
6
|
export const apiRoutes = new Elysia({ prefix: "/api" })
|
|
6
7
|
.get("/", () => ({ message: "🔥 Hot Reload funcionando! FluxStack API v1.4.0 ⚡" }), {
|
|
@@ -13,8 +14,8 @@ export const apiRoutes = new Elysia({ prefix: "/api" })
|
|
|
13
14
|
description: 'Returns a welcome message from the FluxStack API'
|
|
14
15
|
}
|
|
15
16
|
})
|
|
16
|
-
.get("/health", () => ({
|
|
17
|
-
status: "🚀 Hot Reload ativo!",
|
|
17
|
+
.get("/health", () => ({
|
|
18
|
+
status: "🚀 Hot Reload ativo!",
|
|
18
19
|
timestamp: new Date().toISOString(),
|
|
19
20
|
uptime: `${Math.floor(process.uptime())}s`,
|
|
20
21
|
version: "1.4.0",
|
|
@@ -34,4 +35,5 @@ export const apiRoutes = new Elysia({ prefix: "/api" })
|
|
|
34
35
|
}
|
|
35
36
|
})
|
|
36
37
|
.use(usersRoutes)
|
|
37
|
-
.use(uploadRoutes)
|
|
38
|
+
.use(uploadRoutes)
|
|
39
|
+
.use(configRoutes)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application Configuration
|
|
3
|
+
* Laravel-style declarative config with validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { defineConfig, config } from '@/core/utils/config-schema'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* App configuration schema
|
|
10
|
+
*/
|
|
11
|
+
const appConfigSchema = {
|
|
12
|
+
// App basics
|
|
13
|
+
name: config.string('APP_NAME', 'FluxStack', true),
|
|
14
|
+
|
|
15
|
+
version: {
|
|
16
|
+
type: 'string' as const,
|
|
17
|
+
env: 'APP_VERSION',
|
|
18
|
+
default: '1.0.0',
|
|
19
|
+
validate: (value: string) => /^\d+\.\d+\.\d+$/.test(value) || 'Version must be semver format (e.g., 1.0.0)'
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
description: config.string('APP_DESCRIPTION', 'A FluxStack application'),
|
|
23
|
+
|
|
24
|
+
// Environment
|
|
25
|
+
env: config.enum('NODE_ENV', ['development', 'production', 'test'] as const, 'development', true),
|
|
26
|
+
|
|
27
|
+
debug: config.boolean('DEBUG', false),
|
|
28
|
+
|
|
29
|
+
// Server
|
|
30
|
+
port: {
|
|
31
|
+
type: 'number' as const,
|
|
32
|
+
env: 'PORT',
|
|
33
|
+
default: 3000,
|
|
34
|
+
required: true,
|
|
35
|
+
validate: (value: number) => {
|
|
36
|
+
if (value < 1 || value > 65535) {
|
|
37
|
+
return 'Port must be between 1 and 65535'
|
|
38
|
+
}
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
host: config.string('HOST', 'localhost', true),
|
|
44
|
+
|
|
45
|
+
apiPrefix: {
|
|
46
|
+
type: 'string' as const,
|
|
47
|
+
env: 'API_PREFIX',
|
|
48
|
+
default: '/api',
|
|
49
|
+
validate: (value: string) => value.startsWith('/') || 'API prefix must start with /'
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
// URLs
|
|
53
|
+
url: config.string('APP_URL', undefined, false),
|
|
54
|
+
|
|
55
|
+
// Features
|
|
56
|
+
enableSwagger: config.boolean('ENABLE_SWAGGER', true),
|
|
57
|
+
enableMetrics: config.boolean('ENABLE_METRICS', false),
|
|
58
|
+
enableMonitoring: config.boolean('ENABLE_MONITORING', false),
|
|
59
|
+
|
|
60
|
+
// Client
|
|
61
|
+
clientPort: config.number('VITE_PORT', 5173),
|
|
62
|
+
|
|
63
|
+
// Logging
|
|
64
|
+
logLevel: config.enum('LOG_LEVEL', ['debug', 'info', 'warn', 'error'] as const, 'info'),
|
|
65
|
+
logFormat: config.enum('LOG_FORMAT', ['json', 'pretty'] as const, 'pretty'),
|
|
66
|
+
|
|
67
|
+
// CORS
|
|
68
|
+
corsOrigins: config.array('CORS_ORIGINS', ['*']),
|
|
69
|
+
corsMethods: config.array('CORS_METHODS', ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']),
|
|
70
|
+
corsHeaders: config.array('CORS_HEADERS', ['Content-Type', 'Authorization']),
|
|
71
|
+
corsCredentials: config.boolean('CORS_CREDENTIALS', false),
|
|
72
|
+
|
|
73
|
+
// Security
|
|
74
|
+
trustProxy: config.boolean('TRUST_PROXY', false),
|
|
75
|
+
|
|
76
|
+
sessionSecret: {
|
|
77
|
+
type: 'string' as const,
|
|
78
|
+
env: 'SESSION_SECRET',
|
|
79
|
+
default: undefined,
|
|
80
|
+
required: false,
|
|
81
|
+
validate: (value: string) => {
|
|
82
|
+
if (!value) return true // Optional
|
|
83
|
+
if (value.length < 32) {
|
|
84
|
+
return 'Session secret must be at least 32 characters'
|
|
85
|
+
}
|
|
86
|
+
return true
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} as const
|
|
90
|
+
|
|
91
|
+
export const appConfig = defineConfig(appConfigSchema)
|
|
92
|
+
|
|
93
|
+
// Export type for use in other files
|
|
94
|
+
export type AppConfig = typeof appConfig
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Type-safe environment type
|
|
98
|
+
* Use this when you need the literal type explicitly
|
|
99
|
+
*/
|
|
100
|
+
export type Environment = typeof appConfig.env
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Type-safe log level type
|
|
104
|
+
*/
|
|
105
|
+
export type LogLevel = typeof appConfig.logLevel
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Type-safe log format type
|
|
109
|
+
*/
|
|
110
|
+
export type LogFormat = typeof appConfig.logFormat
|
|
111
|
+
|
|
112
|
+
// Export default
|
|
113
|
+
export default appConfig
|