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.
- 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 +6 -26
- 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/bundler.ts +53 -5
- package/core/build/flux-plugins-generator.ts +315 -0
- package/core/build/index.ts +11 -7
- package/core/build/live-components-generator.ts +231 -0
- package/core/build/optimizer.ts +2 -54
- package/core/cli/index.ts +31 -13
- package/core/config/env.ts +38 -94
- package/core/config/runtime-config.ts +61 -58
- package/core/config/schema.ts +1 -0
- package/core/framework/server.ts +55 -11
- package/core/plugins/built-in/index.ts +7 -17
- package/core/plugins/built-in/static/index.ts +24 -10
- 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 +57 -14
- 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 +9 -3
- 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 +6 -12
- package/package.json +117 -114
- package/plugins/crypto-auth/README.md +238 -0
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +325 -0
- package/plugins/crypto-auth/client/components/AuthProvider.tsx +190 -0
- package/plugins/crypto-auth/client/components/LoginButton.tsx +155 -0
- package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +109 -0
- package/plugins/crypto-auth/client/components/SessionInfo.tsx +242 -0
- package/plugins/crypto-auth/client/components/index.ts +15 -0
- package/plugins/crypto-auth/client/index.ts +12 -0
- package/plugins/crypto-auth/index.ts +230 -0
- package/plugins/crypto-auth/package.json +65 -0
- package/plugins/crypto-auth/plugin.json +29 -0
- package/plugins/crypto-auth/server/AuthMiddleware.ts +237 -0
- package/plugins/crypto-auth/server/CryptoAuthService.ts +293 -0
- package/plugins/crypto-auth/server/index.ts +9 -0
- package/vite.config.ts +16 -0
- 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,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runtime Configuration System for FluxStack
|
|
3
|
-
* Uses
|
|
4
|
-
* Drop-in replacement for process.env based configuration
|
|
3
|
+
* Uses declarative configuration system
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
|
-
import { env,
|
|
8
|
-
import {
|
|
9
|
-
dynamicEnvironmentProcessor,
|
|
10
|
-
createDynamicConfig,
|
|
11
|
-
validateProductionEnv,
|
|
12
|
-
getDynamicEnvironmentInfo
|
|
13
|
-
} from './env-dynamic'
|
|
6
|
+
import { env, createNamespace } from '../utils/env'
|
|
14
7
|
import type { FluxStackConfig } from './schema'
|
|
15
8
|
import { defaultFluxStackConfig } from './schema'
|
|
9
|
+
import { loggerConfig } from '../../config/logger.config'
|
|
10
|
+
import { buildConfig } from '../../config/build.config'
|
|
11
|
+
import { appConfig } from '../../config/app.config'
|
|
16
12
|
|
|
17
13
|
/**
|
|
18
14
|
* Runtime Configuration Builder
|
|
@@ -35,11 +31,21 @@ export class RuntimeConfigBuilder {
|
|
|
35
31
|
}
|
|
36
32
|
|
|
37
33
|
/**
|
|
38
|
-
* Load from
|
|
34
|
+
* Load from environment variables
|
|
39
35
|
*/
|
|
40
36
|
private loadFromDynamicEnv(): this {
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
// Environment vars are loaded automatically by env loader
|
|
38
|
+
// Just merge common overrides here
|
|
39
|
+
const envOverrides: Partial<FluxStackConfig> = {}
|
|
40
|
+
|
|
41
|
+
if (env.has('PORT')) {
|
|
42
|
+
envOverrides.server = { ...this.config.server, port: env.PORT }
|
|
43
|
+
}
|
|
44
|
+
if (env.has('LOG_LEVEL')) {
|
|
45
|
+
envOverrides.logging = { ...this.config.logging, level: env.LOG_LEVEL }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.config = this.deepMerge(this.config, envOverrides)
|
|
43
49
|
return this
|
|
44
50
|
}
|
|
45
51
|
|
|
@@ -64,8 +70,8 @@ export class RuntimeConfigBuilder {
|
|
|
64
70
|
*/
|
|
65
71
|
build(): FluxStackConfig {
|
|
66
72
|
// Validate production environment if needed
|
|
67
|
-
if (env.
|
|
68
|
-
|
|
73
|
+
if (env.NODE_ENV === 'production') {
|
|
74
|
+
env.require(['NODE_ENV'])
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
return this.config as FluxStackConfig
|
|
@@ -140,8 +146,8 @@ export const runtimeConfig = {
|
|
|
140
146
|
*/
|
|
141
147
|
development(): FluxStackConfig {
|
|
142
148
|
return new RuntimeConfigBuilder()
|
|
143
|
-
.override('logging.level',
|
|
144
|
-
.override('logging.format',
|
|
149
|
+
.override('logging.level', loggerConfig.level)
|
|
150
|
+
.override('logging.format', 'pretty')
|
|
145
151
|
.override('build.optimization.minify', false)
|
|
146
152
|
.override('build.sourceMaps', true)
|
|
147
153
|
.override('monitoring.enabled', false)
|
|
@@ -153,11 +159,11 @@ export const runtimeConfig = {
|
|
|
153
159
|
*/
|
|
154
160
|
production(): FluxStackConfig {
|
|
155
161
|
return new RuntimeConfigBuilder()
|
|
156
|
-
.override('logging.level',
|
|
157
|
-
.override('logging.format',
|
|
162
|
+
.override('logging.level', loggerConfig.level)
|
|
163
|
+
.override('logging.format', 'json')
|
|
158
164
|
.override('build.optimization.minify', true)
|
|
159
165
|
.override('build.sourceMaps', false)
|
|
160
|
-
.override('monitoring.enabled',
|
|
166
|
+
.override('monitoring.enabled', buildConfig.monitoringEnabled)
|
|
161
167
|
.build()
|
|
162
168
|
},
|
|
163
169
|
|
|
@@ -166,7 +172,7 @@ export const runtimeConfig = {
|
|
|
166
172
|
*/
|
|
167
173
|
test(): FluxStackConfig {
|
|
168
174
|
return new RuntimeConfigBuilder()
|
|
169
|
-
.override('logging.level',
|
|
175
|
+
.override('logging.level', loggerConfig.level)
|
|
170
176
|
.override('server.port', 0) // Random port for tests
|
|
171
177
|
.override('client.port', 0)
|
|
172
178
|
.override('monitoring.enabled', false)
|
|
@@ -177,7 +183,7 @@ export const runtimeConfig = {
|
|
|
177
183
|
* Auto-detect environment and create appropriate config
|
|
178
184
|
*/
|
|
179
185
|
auto(overrides?: Partial<FluxStackConfig>): FluxStackConfig {
|
|
180
|
-
const environment = env
|
|
186
|
+
const environment = appConfig.env
|
|
181
187
|
|
|
182
188
|
let config: FluxStackConfig
|
|
183
189
|
|
|
@@ -214,27 +220,27 @@ export const envLoaders = {
|
|
|
214
220
|
/**
|
|
215
221
|
* Database environment loader
|
|
216
222
|
*/
|
|
217
|
-
database:
|
|
218
|
-
|
|
223
|
+
database: createNamespace('DATABASE_'),
|
|
224
|
+
|
|
219
225
|
/**
|
|
220
226
|
* JWT environment loader
|
|
221
227
|
*/
|
|
222
|
-
jwt:
|
|
223
|
-
|
|
228
|
+
jwt: createNamespace('JWT_'),
|
|
229
|
+
|
|
224
230
|
/**
|
|
225
231
|
* SMTP environment loader
|
|
226
232
|
*/
|
|
227
|
-
smtp:
|
|
228
|
-
|
|
233
|
+
smtp: createNamespace('SMTP_'),
|
|
234
|
+
|
|
229
235
|
/**
|
|
230
236
|
* CORS environment loader
|
|
231
237
|
*/
|
|
232
|
-
cors:
|
|
233
|
-
|
|
238
|
+
cors: createNamespace('CORS_'),
|
|
239
|
+
|
|
234
240
|
/**
|
|
235
241
|
* FluxStack specific environment loader
|
|
236
242
|
*/
|
|
237
|
-
fluxstack:
|
|
243
|
+
fluxstack: createNamespace('FLUXSTACK_')
|
|
238
244
|
}
|
|
239
245
|
|
|
240
246
|
/**
|
|
@@ -245,15 +251,12 @@ export const configHelpers = {
|
|
|
245
251
|
* Get database URL with validation
|
|
246
252
|
*/
|
|
247
253
|
getDatabaseUrl(): string | null {
|
|
248
|
-
const url = env.
|
|
249
|
-
|
|
250
|
-
if (url) {
|
|
251
|
-
|
|
252
|
-
(value) => value.includes('://'),
|
|
253
|
-
'Must be a valid URL'
|
|
254
|
-
)
|
|
254
|
+
const url = env.DATABASE_URL
|
|
255
|
+
|
|
256
|
+
if (url && !url.includes('://')) {
|
|
257
|
+
throw new Error('DATABASE_URL must be a valid URL')
|
|
255
258
|
}
|
|
256
|
-
|
|
259
|
+
|
|
257
260
|
return url || null
|
|
258
261
|
},
|
|
259
262
|
|
|
@@ -261,18 +264,18 @@ export const configHelpers = {
|
|
|
261
264
|
* Get CORS origins with proper defaults
|
|
262
265
|
*/
|
|
263
266
|
getCorsOrigins(): string[] {
|
|
264
|
-
const origins = env.
|
|
265
|
-
|
|
266
|
-
if (origins.length === 0) {
|
|
267
|
-
const environment = env.
|
|
268
|
-
|
|
267
|
+
const origins = env.CORS_ORIGINS
|
|
268
|
+
|
|
269
|
+
if (origins.length === 0 || (origins.length === 1 && origins[0] === '*')) {
|
|
270
|
+
const environment = env.NODE_ENV
|
|
271
|
+
|
|
269
272
|
if (environment === 'development') {
|
|
270
273
|
return ['http://localhost:3000', 'http://localhost:5173']
|
|
271
274
|
} else if (environment === 'production') {
|
|
272
275
|
return [] // Must be explicitly configured in production
|
|
273
276
|
}
|
|
274
277
|
}
|
|
275
|
-
|
|
278
|
+
|
|
276
279
|
return origins
|
|
277
280
|
},
|
|
278
281
|
|
|
@@ -281,15 +284,15 @@ export const configHelpers = {
|
|
|
281
284
|
*/
|
|
282
285
|
getServerConfig() {
|
|
283
286
|
return {
|
|
284
|
-
port: env.
|
|
285
|
-
host: env.
|
|
286
|
-
apiPrefix: env.
|
|
287
|
+
port: env.PORT,
|
|
288
|
+
host: env.HOST,
|
|
289
|
+
apiPrefix: env.API_PREFIX,
|
|
287
290
|
cors: {
|
|
288
291
|
origins: this.getCorsOrigins(),
|
|
289
|
-
methods: env.
|
|
290
|
-
headers: env.
|
|
291
|
-
credentials: env.
|
|
292
|
-
maxAge: env.
|
|
292
|
+
methods: env.CORS_METHODS,
|
|
293
|
+
headers: env.CORS_HEADERS,
|
|
294
|
+
credentials: env.CORS_CREDENTIALS,
|
|
295
|
+
maxAge: env.CORS_MAX_AGE
|
|
293
296
|
}
|
|
294
297
|
}
|
|
295
298
|
},
|
|
@@ -299,16 +302,16 @@ export const configHelpers = {
|
|
|
299
302
|
*/
|
|
300
303
|
getClientConfig() {
|
|
301
304
|
return {
|
|
302
|
-
port: env.
|
|
305
|
+
port: env.VITE_PORT,
|
|
303
306
|
proxy: {
|
|
304
|
-
target:
|
|
305
|
-
changeOrigin:
|
|
307
|
+
target: buildConfig.apiUrl,
|
|
308
|
+
changeOrigin: buildConfig.proxyChangeOrigin
|
|
306
309
|
},
|
|
307
310
|
build: {
|
|
308
|
-
outDir:
|
|
309
|
-
sourceMaps:
|
|
310
|
-
minify:
|
|
311
|
-
target:
|
|
311
|
+
outDir: buildConfig.clientBuildDir,
|
|
312
|
+
sourceMaps: buildConfig.clientSourceMaps,
|
|
313
|
+
minify: buildConfig.clientMinify,
|
|
314
|
+
target: buildConfig.clientTarget
|
|
312
315
|
}
|
|
313
316
|
}
|
|
314
317
|
}
|
package/core/config/schema.ts
CHANGED
package/core/framework/server.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { PluginRegistry } from "../plugins/registry"
|
|
|
5
5
|
import { PluginManager } from "../plugins/manager"
|
|
6
6
|
import { getConfigSync, getEnvironmentInfo } from "../config"
|
|
7
7
|
import { logger } from "../utils/logger"
|
|
8
|
+
import { displayStartupBanner, type StartupInfo } from "../utils/logger/startup-banner"
|
|
8
9
|
import { createErrorHandler } from "../utils/errors/handlers"
|
|
9
10
|
import { createTimer, formatBytes, isProduction, isDevelopment } from "../utils/helpers"
|
|
10
11
|
|
|
@@ -73,7 +74,7 @@ export class FluxStackFramework {
|
|
|
73
74
|
info: (message: string, meta?: any) => logger.info(message, meta),
|
|
74
75
|
warn: (message: string, meta?: any) => logger.warn(message, meta),
|
|
75
76
|
error: (message: string, meta?: any) => logger.error(message, meta),
|
|
76
|
-
child: (context: any) => (logger as any).child(context),
|
|
77
|
+
child: (context: any) => (logger as any).child ? (logger as any).child(context) : logger,
|
|
77
78
|
time: (label: string) => (logger as any).time(label),
|
|
78
79
|
timeEnd: (label: string) => (logger as any).timeEnd(label),
|
|
79
80
|
request: (method: string, path: string, status?: number, duration?: number) =>
|
|
@@ -95,10 +96,11 @@ export class FluxStackFramework {
|
|
|
95
96
|
})
|
|
96
97
|
|
|
97
98
|
this.setupCors()
|
|
99
|
+
this.setupHeadHandler()
|
|
98
100
|
this.setupHooks()
|
|
99
101
|
this.setupErrorHandling()
|
|
100
102
|
|
|
101
|
-
logger.
|
|
103
|
+
logger.debug('FluxStack framework initialized', {
|
|
102
104
|
environment: envInfo.name,
|
|
103
105
|
port: fullConfig.server.port
|
|
104
106
|
})
|
|
@@ -113,7 +115,7 @@ export class FluxStackFramework {
|
|
|
113
115
|
try {
|
|
114
116
|
await this.pluginManager.initialize()
|
|
115
117
|
const stats = this.pluginManager.getRegistry().getStats()
|
|
116
|
-
logger.
|
|
118
|
+
logger.debug('Automatic plugins loaded successfully', {
|
|
117
119
|
pluginCount: stats.totalPlugins,
|
|
118
120
|
enabledPlugins: stats.enabledPlugins,
|
|
119
121
|
disabledPlugins: stats.disabledPlugins
|
|
@@ -141,6 +143,37 @@ export class FluxStackFramework {
|
|
|
141
143
|
})
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
private setupHeadHandler() {
|
|
147
|
+
// Global HEAD handler to prevent Elysia's automatic HEAD conversion bug
|
|
148
|
+
this.app.head("*", ({ request, set }) => {
|
|
149
|
+
const url = new URL(request.url)
|
|
150
|
+
|
|
151
|
+
// Handle API routes
|
|
152
|
+
if (url.pathname.startsWith(this.context.config.server.apiPrefix)) {
|
|
153
|
+
set.status = 200
|
|
154
|
+
set.headers['Content-Type'] = 'application/json'
|
|
155
|
+
set.headers['Content-Length'] = '0'
|
|
156
|
+
return ""
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Handle static files (assume they're HTML if no extension)
|
|
160
|
+
const isStatic = url.pathname === '/' || !url.pathname.includes('.')
|
|
161
|
+
if (isStatic) {
|
|
162
|
+
set.status = 200
|
|
163
|
+
set.headers['Content-Type'] = 'text/html'
|
|
164
|
+
set.headers['Content-Length'] = '478' // approximate size of index.html
|
|
165
|
+
set.headers['Cache-Control'] = 'no-cache'
|
|
166
|
+
return ""
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Handle other file types
|
|
170
|
+
set.status = 200
|
|
171
|
+
set.headers['Content-Type'] = 'application/octet-stream'
|
|
172
|
+
set.headers['Content-Length'] = '0'
|
|
173
|
+
return ""
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
144
177
|
private setupHooks() {
|
|
145
178
|
// Setup onRequest hook and onBeforeRoute hook
|
|
146
179
|
this.app.onRequest(async ({ request, set }) => {
|
|
@@ -388,7 +421,7 @@ export class FluxStackFramework {
|
|
|
388
421
|
;(this.pluginRegistry as any).loadOrder = loadOrder
|
|
389
422
|
}
|
|
390
423
|
|
|
391
|
-
logger.
|
|
424
|
+
logger.debug(`Plugin '${plugin.name}' registered`, {
|
|
392
425
|
version: plugin.version,
|
|
393
426
|
dependencies: plugin.dependencies
|
|
394
427
|
})
|
|
@@ -446,7 +479,7 @@ export class FluxStackFramework {
|
|
|
446
479
|
}
|
|
447
480
|
|
|
448
481
|
this.isStarted = true
|
|
449
|
-
logger.
|
|
482
|
+
logger.debug('All plugins loaded successfully', {
|
|
450
483
|
pluginCount: loadOrder.length
|
|
451
484
|
})
|
|
452
485
|
|
|
@@ -503,15 +536,26 @@ export class FluxStackFramework {
|
|
|
503
536
|
const apiPrefix = this.context.config.server.apiPrefix
|
|
504
537
|
|
|
505
538
|
this.app.listen(port, () => {
|
|
506
|
-
|
|
539
|
+
const showBanner = this.context.config.server.showBanner !== false // default: true
|
|
540
|
+
const vitePluginActive = this.pluginRegistry.has('vite')
|
|
541
|
+
|
|
542
|
+
// Prepare startup info for banner or callback
|
|
543
|
+
const startupInfo: StartupInfo = {
|
|
544
|
+
port,
|
|
507
545
|
apiPrefix,
|
|
508
546
|
environment: this.context.environment,
|
|
509
|
-
pluginCount: this.pluginRegistry.getAll().length
|
|
510
|
-
|
|
547
|
+
pluginCount: this.pluginRegistry.getAll().length,
|
|
548
|
+
vitePort: this.context.config.client?.port,
|
|
549
|
+
viteEmbedded: vitePluginActive, // Vite is embedded when plugin is active
|
|
550
|
+
swaggerPath: '/swagger' // TODO: Get from swagger plugin config
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Display banner if enabled
|
|
554
|
+
if (showBanner) {
|
|
555
|
+
displayStartupBanner(startupInfo)
|
|
556
|
+
}
|
|
511
557
|
|
|
512
|
-
|
|
513
|
-
console.log(`📋 Health check: http://localhost:${port}${apiPrefix}/health`)
|
|
514
|
-
console.log()
|
|
558
|
+
// Call user callback with startup info
|
|
515
559
|
callback?.()
|
|
516
560
|
})
|
|
517
561
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Built-in Plugins for FluxStack
|
|
3
3
|
* Core plugins that provide essential functionality
|
|
4
|
+
*
|
|
5
|
+
* Note: Logger is NOT a plugin - it's core infrastructure used by plugins
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
// Import all built-in plugins
|
|
7
|
-
import { loggerPlugin } from './logger'
|
|
8
9
|
import { swaggerPlugin } from './swagger'
|
|
9
10
|
import { vitePlugin } from './vite'
|
|
10
11
|
import { staticPlugin } from './static'
|
|
11
12
|
import { monitoringPlugin } from './monitoring'
|
|
12
13
|
|
|
13
14
|
// Export individual plugins
|
|
14
|
-
export { loggerPlugin } from './logger'
|
|
15
15
|
export { swaggerPlugin } from './swagger'
|
|
16
16
|
export { vitePlugin } from './vite'
|
|
17
17
|
export { staticPlugin } from './static'
|
|
@@ -19,7 +19,6 @@ export { monitoringPlugin } from './monitoring'
|
|
|
19
19
|
|
|
20
20
|
// Export as a collection
|
|
21
21
|
export const builtInPlugins = {
|
|
22
|
-
logger: loggerPlugin,
|
|
23
22
|
swagger: swaggerPlugin,
|
|
24
23
|
vite: vitePlugin,
|
|
25
24
|
static: staticPlugin,
|
|
@@ -28,7 +27,6 @@ export const builtInPlugins = {
|
|
|
28
27
|
|
|
29
28
|
// Export as an array for easy registration
|
|
30
29
|
export const builtInPluginsList = [
|
|
31
|
-
loggerPlugin,
|
|
32
30
|
swaggerPlugin,
|
|
33
31
|
vitePlugin,
|
|
34
32
|
staticPlugin,
|
|
@@ -37,22 +35,14 @@ export const builtInPluginsList = [
|
|
|
37
35
|
|
|
38
36
|
// Plugin categories
|
|
39
37
|
export const pluginCategories = {
|
|
40
|
-
core: [
|
|
38
|
+
core: [staticPlugin],
|
|
41
39
|
development: [vitePlugin],
|
|
42
40
|
documentation: [swaggerPlugin],
|
|
43
|
-
monitoring: [
|
|
41
|
+
monitoring: [monitoringPlugin]
|
|
44
42
|
} as const
|
|
45
43
|
|
|
46
44
|
// Default plugin configuration
|
|
47
45
|
export const defaultPluginConfig = {
|
|
48
|
-
logger: {
|
|
49
|
-
logRequests: true,
|
|
50
|
-
logResponses: true,
|
|
51
|
-
logErrors: true,
|
|
52
|
-
includeHeaders: false,
|
|
53
|
-
includeBody: false,
|
|
54
|
-
slowRequestThreshold: 1000
|
|
55
|
-
},
|
|
56
46
|
swagger: {
|
|
57
47
|
enabled: true,
|
|
58
48
|
path: '/swagger',
|
|
@@ -104,15 +94,15 @@ export const defaultPluginConfig = {
|
|
|
104
94
|
* Get default plugins for a specific environment
|
|
105
95
|
*/
|
|
106
96
|
export function getDefaultPlugins(environment: 'development' | 'production' | 'test' = 'development') {
|
|
107
|
-
const basePlugins = [
|
|
108
|
-
|
|
97
|
+
const basePlugins = [staticPlugin]
|
|
98
|
+
|
|
109
99
|
switch (environment) {
|
|
110
100
|
case 'development':
|
|
111
101
|
return [...basePlugins, vitePlugin, swaggerPlugin, monitoringPlugin]
|
|
112
102
|
case 'production':
|
|
113
103
|
return [...basePlugins, monitoringPlugin]
|
|
114
104
|
case 'test':
|
|
115
|
-
return [
|
|
105
|
+
return [] // Minimal plugins for testing
|
|
116
106
|
default:
|
|
117
107
|
return basePlugins
|
|
118
108
|
}
|
|
@@ -104,8 +104,8 @@ export const staticPlugin: Plugin = {
|
|
|
104
104
|
compression: config.compression.enabled
|
|
105
105
|
})
|
|
106
106
|
|
|
107
|
-
//
|
|
108
|
-
|
|
107
|
+
// Helper function for handling both GET and HEAD requests
|
|
108
|
+
const handleStaticRequest = async ({ request, set }: { request: Request, set: any }) => {
|
|
109
109
|
const url = new URL(request.url)
|
|
110
110
|
|
|
111
111
|
// Skip API routes
|
|
@@ -123,7 +123,7 @@ export const staticPlugin: Plugin = {
|
|
|
123
123
|
// This plugin only handles static files serving in production or fallback
|
|
124
124
|
|
|
125
125
|
// Serve static files
|
|
126
|
-
return await serveStaticFile(url.pathname, config, context, set)
|
|
126
|
+
return await serveStaticFile(url.pathname, config, context, set, request.method === 'HEAD')
|
|
127
127
|
|
|
128
128
|
} catch (error) {
|
|
129
129
|
context.logger.error("Error serving static file", {
|
|
@@ -134,7 +134,11 @@ export const staticPlugin: Plugin = {
|
|
|
134
134
|
set.status = 500
|
|
135
135
|
return "Internal Server Error"
|
|
136
136
|
}
|
|
137
|
-
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Setup static file handling in Elysia - handle both GET and HEAD
|
|
140
|
+
context.app.get("/*", handleStaticRequest)
|
|
141
|
+
context.app.head("/*", handleStaticRequest)
|
|
138
142
|
},
|
|
139
143
|
|
|
140
144
|
onServerStart: async (context: PluginContext) => {
|
|
@@ -162,7 +166,8 @@ async function serveStaticFile(
|
|
|
162
166
|
pathname: string,
|
|
163
167
|
config: any,
|
|
164
168
|
context: PluginContext,
|
|
165
|
-
set: any
|
|
169
|
+
set: any,
|
|
170
|
+
isHead: boolean = false
|
|
166
171
|
): Promise<any> {
|
|
167
172
|
const isDev = context.utils.isDevelopment()
|
|
168
173
|
|
|
@@ -209,7 +214,7 @@ async function serveStaticFile(
|
|
|
209
214
|
if (config.spa.enabled && !pathname.includes('.')) {
|
|
210
215
|
const indexPath = join(process.cwd(), baseDir, config.spa.fallback)
|
|
211
216
|
if (existsSync(indexPath)) {
|
|
212
|
-
return serveFile(indexPath, config, set, context)
|
|
217
|
+
return serveFile(indexPath, config, set, context, isHead)
|
|
213
218
|
}
|
|
214
219
|
}
|
|
215
220
|
|
|
@@ -222,18 +227,18 @@ async function serveStaticFile(
|
|
|
222
227
|
if (stats.isDirectory()) {
|
|
223
228
|
const indexPath = join(filePath, config.indexFile)
|
|
224
229
|
if (existsSync(indexPath)) {
|
|
225
|
-
return serveFile(indexPath, config, set, context)
|
|
230
|
+
return serveFile(indexPath, config, set, context, isHead)
|
|
226
231
|
}
|
|
227
232
|
|
|
228
233
|
set.status = 404
|
|
229
234
|
return "Not Found"
|
|
230
235
|
}
|
|
231
236
|
|
|
232
|
-
return serveFile(filePath, config, set, context)
|
|
237
|
+
return serveFile(filePath, config, set, context, isHead)
|
|
233
238
|
}
|
|
234
239
|
|
|
235
240
|
// Serve individual file
|
|
236
|
-
function serveFile(filePath: string, config: any, set: any, context: PluginContext) {
|
|
241
|
+
function serveFile(filePath: string, config: any, set: any, context: PluginContext, isHead: boolean = false) {
|
|
237
242
|
const ext = extname(filePath)
|
|
238
243
|
const file = Bun.file(filePath)
|
|
239
244
|
|
|
@@ -258,6 +263,9 @@ function serveFile(filePath: string, config: any, set: any, context: PluginConte
|
|
|
258
263
|
const contentType = mimeTypes[ext] || 'application/octet-stream'
|
|
259
264
|
set.headers['Content-Type'] = contentType
|
|
260
265
|
|
|
266
|
+
// Set content-length for both GET and HEAD requests
|
|
267
|
+
set.headers['Content-Length'] = file.size.toString()
|
|
268
|
+
|
|
261
269
|
// Set cache headers
|
|
262
270
|
if (config.cacheControl.enabled) {
|
|
263
271
|
if (ext === '.html') {
|
|
@@ -280,9 +288,15 @@ function serveFile(filePath: string, config: any, set: any, context: PluginConte
|
|
|
280
288
|
|
|
281
289
|
context.logger.debug(`Serving static file: ${filePath}`, {
|
|
282
290
|
contentType,
|
|
283
|
-
size: file.size
|
|
291
|
+
size: file.size,
|
|
292
|
+
method: isHead ? 'HEAD' : 'GET'
|
|
284
293
|
})
|
|
285
294
|
|
|
295
|
+
// For HEAD requests, return empty body but keep all headers
|
|
296
|
+
if (isHead) {
|
|
297
|
+
return ""
|
|
298
|
+
}
|
|
299
|
+
|
|
286
300
|
return file
|
|
287
301
|
}
|
|
288
302
|
|