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,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'
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import type { Plugin, PluginContext, RequestContext, ResponseContext, ErrorContext } from "../../types"
|
|
2
|
-
|
|
3
|
-
export const loggerPlugin: Plugin = {
|
|
4
|
-
name: "logger",
|
|
5
|
-
version: "1.0.0",
|
|
6
|
-
description: "Enhanced logging plugin for FluxStack with request/response logging",
|
|
7
|
-
author: "FluxStack Team",
|
|
8
|
-
priority: 1000, // Logger should run first
|
|
9
|
-
category: "core",
|
|
10
|
-
tags: ["logging", "monitoring"],
|
|
11
|
-
|
|
12
|
-
configSchema: {
|
|
13
|
-
type: "object",
|
|
14
|
-
properties: {
|
|
15
|
-
logRequests: {
|
|
16
|
-
type: "boolean",
|
|
17
|
-
description: "Enable request logging"
|
|
18
|
-
},
|
|
19
|
-
logResponses: {
|
|
20
|
-
type: "boolean",
|
|
21
|
-
description: "Enable response logging"
|
|
22
|
-
},
|
|
23
|
-
logErrors: {
|
|
24
|
-
type: "boolean",
|
|
25
|
-
description: "Enable error logging"
|
|
26
|
-
},
|
|
27
|
-
includeHeaders: {
|
|
28
|
-
type: "boolean",
|
|
29
|
-
description: "Include headers in request/response logs"
|
|
30
|
-
},
|
|
31
|
-
includeBody: {
|
|
32
|
-
type: "boolean",
|
|
33
|
-
description: "Include body in request/response logs"
|
|
34
|
-
},
|
|
35
|
-
slowRequestThreshold: {
|
|
36
|
-
type: "number",
|
|
37
|
-
minimum: 0,
|
|
38
|
-
description: "Threshold in ms to log slow requests"
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
additionalProperties: false
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
defaultConfig: {
|
|
45
|
-
logRequests: process.env.ENABLE_REQUEST_LOGS === 'true',
|
|
46
|
-
logResponses: process.env.ENABLE_REQUEST_LOGS === 'true',
|
|
47
|
-
logErrors: true,
|
|
48
|
-
includeHeaders: false,
|
|
49
|
-
includeBody: false,
|
|
50
|
-
slowRequestThreshold: 1000
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
setup: async (context: PluginContext) => {
|
|
54
|
-
context.logger.info("Enhanced logger plugin initialized", {
|
|
55
|
-
environment: context.config.app?.name || 'fluxstack',
|
|
56
|
-
logLevel: context.config.logging.level,
|
|
57
|
-
format: context.config.logging.format
|
|
58
|
-
})
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
onServerStart: async (context: PluginContext) => {
|
|
62
|
-
context.logger.info("Logger plugin: Server started", {
|
|
63
|
-
port: context.config.server.port,
|
|
64
|
-
host: context.config.server.host,
|
|
65
|
-
apiPrefix: context.config.server.apiPrefix
|
|
66
|
-
})
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
onServerStop: async (context: PluginContext) => {
|
|
70
|
-
context.logger.info("Logger plugin: Server stopped")
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
onRequest: async (context: RequestContext) => {
|
|
74
|
-
const config = getPluginConfig(context)
|
|
75
|
-
|
|
76
|
-
if (!config.logRequests) return
|
|
77
|
-
|
|
78
|
-
const logData: any = {
|
|
79
|
-
method: context.method,
|
|
80
|
-
path: context.path,
|
|
81
|
-
userAgent: context.headers['user-agent'],
|
|
82
|
-
ip: context.headers['x-forwarded-for'] || context.headers['x-real-ip'] || 'unknown'
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (config.includeHeaders) {
|
|
86
|
-
logData.headers = context.headers
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (config.includeBody && context.body) {
|
|
90
|
-
logData.body = context.body
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Use a logger from context if available, otherwise create one
|
|
94
|
-
const logger = (context as any).logger || console
|
|
95
|
-
if (typeof logger.info === 'function') {
|
|
96
|
-
logger.info(`→ ${context.method} ${context.path}`, logData)
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
onResponse: async (context: ResponseContext) => {
|
|
101
|
-
const config = getPluginConfig(context)
|
|
102
|
-
|
|
103
|
-
if (!config.logResponses) return
|
|
104
|
-
|
|
105
|
-
const logData: any = {
|
|
106
|
-
method: context.method,
|
|
107
|
-
path: context.path,
|
|
108
|
-
statusCode: context.statusCode,
|
|
109
|
-
duration: context.duration,
|
|
110
|
-
size: context.size
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (config.includeHeaders) {
|
|
114
|
-
const headers: Record<string, string> = {}
|
|
115
|
-
context.response.headers.forEach((value, key) => {
|
|
116
|
-
headers[key] = value
|
|
117
|
-
})
|
|
118
|
-
logData.responseHeaders = headers
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Determine log level based on status code and duration
|
|
122
|
-
let logLevel = 'info'
|
|
123
|
-
if (context.statusCode >= 400) {
|
|
124
|
-
logLevel = 'warn'
|
|
125
|
-
}
|
|
126
|
-
if (context.statusCode >= 500) {
|
|
127
|
-
logLevel = 'error'
|
|
128
|
-
}
|
|
129
|
-
if (context.duration > config.slowRequestThreshold) {
|
|
130
|
-
logLevel = 'warn'
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const logger = (context as any).logger || console
|
|
134
|
-
const logMessage = `← ${context.method} ${context.path} ${context.statusCode} ${context.duration}ms`
|
|
135
|
-
|
|
136
|
-
if (typeof logger[logLevel] === 'function') {
|
|
137
|
-
logger[logLevel](logMessage, logData)
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
|
|
141
|
-
onError: async (context: ErrorContext) => {
|
|
142
|
-
const config = getPluginConfig(context)
|
|
143
|
-
|
|
144
|
-
if (!config.logErrors) return
|
|
145
|
-
|
|
146
|
-
// Skip logging for NOT_FOUND errors unless explicitly enabled
|
|
147
|
-
if (context.error.message === 'NOT_FOUND' && !process.env.ENABLE_NOT_FOUND_LOGS) {
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const logData: any = {
|
|
152
|
-
method: context.method,
|
|
153
|
-
path: context.path,
|
|
154
|
-
duration: context.duration,
|
|
155
|
-
error: {
|
|
156
|
-
name: context.error.name,
|
|
157
|
-
message: context.error.message,
|
|
158
|
-
stack: context.error.stack
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (config.includeHeaders) {
|
|
163
|
-
logData.headers = context.headers
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const logger = (context as any).logger || console
|
|
167
|
-
if (typeof logger.error === 'function') {
|
|
168
|
-
logger.error(`✗ ${context.method} ${context.path} - ${context.error.message}`, logData)
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Helper function to get plugin config from context
|
|
174
|
-
function getPluginConfig(_context: any) {
|
|
175
|
-
// In a real implementation, this would get the config from the plugin context
|
|
176
|
-
// For now, return default config
|
|
177
|
-
return loggerPlugin.defaultConfig || {}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export default loggerPlugin
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { Plugin, PluginContext } from "../../types"
|
|
2
|
-
import { log } from "../../utils/logger"
|
|
3
|
-
|
|
4
|
-
export const loggerPlugin: Plugin = {
|
|
5
|
-
name: "logger",
|
|
6
|
-
setup: (context: PluginContext) => {
|
|
7
|
-
const logLevel = process.env.LOG_LEVEL || context.config.logging?.level || 'info'
|
|
8
|
-
const isDev = process.env.NODE_ENV === 'development'
|
|
9
|
-
|
|
10
|
-
log.plugin("logger", "Logger plugin initialized", {
|
|
11
|
-
logLevel,
|
|
12
|
-
environment: process.env.NODE_ENV || 'development'
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
// Only enable verbose request logging if explicitly requested
|
|
16
|
-
if (process.env.ENABLE_REQUEST_LOGS === 'true') {
|
|
17
|
-
// Setup logging hooks on the Elysia app
|
|
18
|
-
context.app.onRequest(({ request }: { request: Request }) => {
|
|
19
|
-
const startTime = Date.now()
|
|
20
|
-
const path = new URL(request.url).pathname
|
|
21
|
-
|
|
22
|
-
// Store start time for duration calculation
|
|
23
|
-
;(request as any).__startTime = startTime
|
|
24
|
-
|
|
25
|
-
log.request(request.method, path)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
context.app.onResponse(({ request, set }: { request: Request, set: any }) => {
|
|
29
|
-
const duration = Date.now() - ((request as any).__startTime || Date.now())
|
|
30
|
-
const path = new URL(request.url).pathname
|
|
31
|
-
|
|
32
|
-
log.request(request.method, path, set.status || 200, duration)
|
|
33
|
-
})
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Always log errors
|
|
37
|
-
context.app.onError(({ error, request }: { error: Error, request: Request }) => {
|
|
38
|
-
const duration = Date.now() - ((request as any).__startTime || Date.now())
|
|
39
|
-
const path = new URL(request.url).pathname
|
|
40
|
-
|
|
41
|
-
log.error(`${request.method} ${path} - ${error.message}`, {
|
|
42
|
-
duration,
|
|
43
|
-
stack: error.stack
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
}
|