create-fluxstack 1.10.1 → 1.12.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/.dockerignore +1 -2
- package/Dockerfile +8 -8
- package/LLMD/INDEX.md +64 -0
- package/LLMD/MAINTENANCE.md +197 -0
- package/LLMD/MIGRATION.md +156 -0
- package/LLMD/config/.gitkeep +1 -0
- package/LLMD/config/declarative-system.md +268 -0
- package/LLMD/config/environment-vars.md +327 -0
- package/LLMD/config/runtime-reload.md +401 -0
- package/LLMD/core/.gitkeep +1 -0
- package/LLMD/core/build-system.md +599 -0
- package/LLMD/core/framework-lifecycle.md +229 -0
- package/LLMD/core/plugin-system.md +451 -0
- package/LLMD/patterns/.gitkeep +1 -0
- package/LLMD/patterns/anti-patterns.md +297 -0
- package/LLMD/patterns/project-structure.md +264 -0
- package/LLMD/patterns/type-safety.md +440 -0
- package/LLMD/reference/.gitkeep +1 -0
- package/LLMD/reference/cli-commands.md +250 -0
- package/LLMD/reference/plugin-hooks.md +357 -0
- package/LLMD/reference/routing.md +39 -0
- package/LLMD/reference/troubleshooting.md +364 -0
- package/LLMD/resources/.gitkeep +1 -0
- package/LLMD/resources/controllers.md +465 -0
- package/LLMD/resources/live-components.md +703 -0
- package/LLMD/resources/live-rooms.md +482 -0
- package/LLMD/resources/live-upload.md +130 -0
- package/LLMD/resources/plugins-external.md +617 -0
- package/LLMD/resources/routes-eden.md +254 -0
- package/README.md +37 -17
- package/app/client/index.html +0 -1
- package/app/client/src/App.tsx +107 -150
- package/app/client/src/components/AppLayout.tsx +68 -0
- package/app/client/src/components/BackButton.tsx +13 -0
- package/app/client/src/components/DemoPage.tsx +20 -0
- package/app/client/src/components/LiveUploadWidget.tsx +204 -0
- package/app/client/src/lib/eden-api.ts +85 -60
- package/app/client/src/live/ChatDemo.tsx +107 -0
- package/app/client/src/live/CounterDemo.tsx +206 -0
- package/app/client/src/live/FormDemo.tsx +119 -0
- package/app/client/src/live/RoomChatDemo.tsx +242 -0
- package/app/client/src/live/UploadDemo.tsx +21 -0
- package/app/client/src/main.tsx +4 -1
- package/app/client/src/pages/ApiTestPage.tsx +108 -0
- package/app/client/src/pages/HomePage.tsx +76 -0
- package/app/server/app.ts +1 -4
- package/app/server/controllers/users.controller.ts +36 -44
- package/app/server/index.ts +25 -35
- package/app/server/live/LiveChat.ts +77 -0
- package/app/server/live/LiveCounter.ts +67 -0
- package/app/server/live/LiveForm.ts +63 -0
- package/app/server/live/LiveLocalCounter.ts +32 -0
- package/app/server/live/LiveRoomChat.ts +285 -0
- package/app/server/live/LiveUpload.ts +81 -0
- package/app/server/routes/index.ts +3 -1
- package/app/server/routes/room.routes.ts +117 -0
- package/app/server/routes/users.routes.ts +35 -27
- package/app/shared/types/index.ts +14 -2
- package/config/app.config.ts +2 -62
- package/config/client.config.ts +2 -95
- package/config/database.config.ts +2 -99
- package/config/fluxstack.config.ts +25 -45
- package/config/index.ts +57 -38
- package/config/monitoring.config.ts +2 -114
- package/config/plugins.config.ts +2 -80
- package/config/server.config.ts +2 -68
- package/config/services.config.ts +2 -130
- package/config/system/app.config.ts +29 -0
- package/config/system/build.config.ts +49 -0
- package/config/system/client.config.ts +68 -0
- package/config/system/database.config.ts +17 -0
- package/config/system/fluxstack.config.ts +114 -0
- package/config/{logger.config.ts → system/logger.config.ts} +3 -1
- package/config/system/monitoring.config.ts +114 -0
- package/config/system/plugins.config.ts +84 -0
- package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
- package/config/system/server.config.ts +68 -0
- package/config/system/services.config.ts +46 -0
- package/config/{system.config.ts → system/system.config.ts} +1 -1
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +39 -27
- package/core/build/live-components-generator.ts +3 -3
- package/core/build/optimizer.ts +235 -235
- package/core/cli/command-registry.ts +6 -4
- package/core/cli/commands/build.ts +79 -0
- package/core/cli/commands/create.ts +54 -0
- package/core/cli/commands/dev.ts +101 -0
- package/core/cli/commands/help.ts +34 -0
- package/core/cli/commands/index.ts +34 -0
- package/core/cli/commands/make-plugin.ts +90 -0
- package/core/cli/commands/plugin-add.ts +197 -0
- package/core/cli/commands/plugin-deps.ts +2 -2
- package/core/cli/commands/plugin-list.ts +208 -0
- package/core/cli/commands/plugin-remove.ts +170 -0
- package/core/cli/generators/component.ts +769 -769
- package/core/cli/generators/controller.ts +1 -1
- package/core/cli/generators/index.ts +146 -146
- package/core/cli/generators/interactive.ts +227 -227
- package/core/cli/generators/plugin.ts +2 -2
- package/core/cli/generators/prompts.ts +82 -82
- package/core/cli/generators/route.ts +6 -6
- package/core/cli/generators/service.ts +2 -2
- package/core/cli/generators/template-engine.ts +4 -3
- package/core/cli/generators/types.ts +2 -2
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +115 -686
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/LiveComponentsProvider.tsx +60 -8
- package/core/client/api/eden.ts +183 -0
- package/core/client/api/index.ts +11 -0
- package/core/client/components/Live.tsx +104 -0
- package/core/client/fluxstack.ts +1 -9
- package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/hooks/useChunkedUpload.ts +85 -35
- package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
- package/core/client/hooks/useLiveComponent.ts +800 -0
- package/core/client/hooks/useLiveUpload.ts +71 -0
- package/core/client/hooks/useRoom.ts +409 -0
- package/core/client/hooks/useRoomProxy.ts +382 -0
- package/core/client/index.ts +17 -68
- package/core/client/standalone-entry.ts +8 -0
- package/core/client/standalone.ts +74 -53
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +70 -291
- package/core/config/schema.ts +42 -723
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +47 -40
- package/core/framework/types.ts +2 -2
- package/core/index.ts +23 -4
- package/core/live/ComponentRegistry.ts +3 -3
- package/core/live/types.ts +77 -0
- package/core/plugins/built-in/index.ts +134 -134
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +111 -47
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +68 -265
- package/core/plugins/built-in/vite/index.ts +85 -185
- package/core/plugins/built-in/vite/vite-dev.ts +10 -16
- package/core/plugins/config.ts +9 -7
- package/core/plugins/dependency-manager.ts +31 -1
- package/core/plugins/discovery.ts +19 -7
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +203 -203
- package/core/plugins/manager.ts +27 -39
- package/core/plugins/module-resolver.ts +19 -8
- package/core/plugins/registry.ts +255 -19
- package/core/plugins/types.ts +20 -53
- package/core/server/framework.ts +66 -43
- package/core/server/index.ts +15 -15
- package/core/server/live/ComponentRegistry.ts +78 -71
- package/core/server/live/FileUploadManager.ts +23 -10
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/LiveRoomManager.ts +261 -0
- package/core/server/live/RoomEventBus.ts +234 -0
- package/core/server/live/RoomStateManager.ts +172 -0
- package/core/server/live/StateSignature.ts +643 -643
- package/core/server/live/WebSocketConnectionManager.ts +30 -19
- package/core/server/live/auto-generated-components.ts +21 -9
- package/core/server/live/index.ts +14 -0
- package/core/server/live/websocket-plugin.ts +214 -67
- package/core/server/middleware/elysia-helpers.ts +7 -2
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +180 -180
- package/core/server/plugins/static-files-plugin.ts +69 -69
- package/core/server/plugins/swagger.ts +1 -1
- package/core/server/rooms/RoomBroadcaster.ts +357 -0
- package/core/server/rooms/RoomSystem.ts +463 -0
- package/core/server/rooms/index.ts +13 -0
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +12 -12
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/build.ts +219 -219
- package/core/types/config.ts +56 -26
- package/core/types/index.ts +4 -4
- package/core/types/plugin.ts +107 -107
- package/core/types/types.ts +353 -14
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +2 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +36 -1
- package/core/utils/errors/index.ts +49 -5
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +6 -16
- package/core/utils/index.ts +17 -17
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +13 -9
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +6 -1
- package/core/utils/logger/stack-trace.ts +3 -1
- package/core/utils/logger/startup-banner.ts +82 -82
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +66 -66
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +8 -7
- package/package.json +12 -13
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
- package/plugins/crypto-auth/client/components/index.ts +11 -11
- package/plugins/crypto-auth/client/index.ts +11 -11
- package/plugins/crypto-auth/config/index.ts +1 -1
- package/plugins/crypto-auth/index.ts +4 -4
- package/plugins/crypto-auth/package.json +65 -65
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/index.ts +21 -21
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/tsconfig.api-strict.json +16 -0
- package/tsconfig.json +48 -52
- package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
- package/types/global.d.ts +29 -29
- package/types/vitest.d.ts +8 -8
- package/vite.config.ts +38 -62
- package/vitest.config.live.ts +10 -9
- package/vitest.config.ts +29 -17
- package/app/client/README.md +0 -69
- package/app/client/SIMPLIFICATION.md +0 -140
- package/app/client/frontend-only.ts +0 -12
- package/app/client/src/live/FileUploadExample.tsx +0 -359
- package/app/client/src/live/MinimalLiveClock.tsx +0 -47
- package/app/client/src/live/QuickUploadTest.tsx +0 -193
- package/app/client/tsconfig.app.json +0 -45
- package/app/client/tsconfig.json +0 -7
- package/app/client/zustand-setup.md +0 -65
- package/app/server/backend-only.ts +0 -18
- package/app/server/live/LiveClockComponent.ts +0 -215
- package/app/server/live/LiveFileUploadComponent.ts +0 -77
- package/app/server/routes/env-test.ts +0 -110
- package/core/client/hooks/index.ts +0 -7
- package/core/client/hooks/useHybridLiveComponent.ts +0 -685
- package/core/client/hooks/useTypedLiveComponent.ts +0 -133
- package/core/client/hooks/useWebSocket.ts +0 -361
- package/core/config/env.ts +0 -546
- package/core/config/loader.ts +0 -522
- package/core/config/runtime-config.ts +0 -327
- package/core/config/validator.ts +0 -540
- package/core/server/backend-entry.ts +0 -51
- package/core/server/standalone.ts +0 -106
- package/core/utils/regenerate-files.ts +0 -69
- package/fluxstack.config.ts +0 -354
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
* Provides performance monitoring, metrics collection, and system monitoring
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { FluxStack, PluginContext, RequestContext, ResponseContext, ErrorContext } from "
|
|
7
|
-
import { MetricsCollector } from "
|
|
8
|
-
import { appConfig } from '
|
|
9
|
-
import { monitoringConfig } from '@/config/monitoring.config'
|
|
6
|
+
import type { FluxStack, PluginContext, RequestContext, ResponseContext, ErrorContext } from "@core/plugins/types"
|
|
7
|
+
import { MetricsCollector } from "@core/utils/monitoring"
|
|
8
|
+
import { appConfig, monitoringConfig } from '@config'
|
|
10
9
|
import * as os from 'os'
|
|
11
10
|
import * as fs from 'fs'
|
|
12
11
|
import * as path from 'path'
|
|
@@ -95,6 +94,71 @@ const DEFAULTS = {
|
|
|
95
94
|
alerts: [] as AlertThreshold[]
|
|
96
95
|
}
|
|
97
96
|
|
|
97
|
+
type MonitoringOptions = typeof DEFAULTS
|
|
98
|
+
|
|
99
|
+
function mergeMonitoringOptions(base: MonitoringOptions, overrides: Partial<MonitoringOptions>): MonitoringOptions {
|
|
100
|
+
const result: MonitoringOptions = { ...base }
|
|
101
|
+
|
|
102
|
+
for (const key of Object.keys(overrides) as (keyof MonitoringOptions)[]) {
|
|
103
|
+
const value = overrides[key]
|
|
104
|
+
if (value === undefined) continue
|
|
105
|
+
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
;(result as any)[key] = value
|
|
108
|
+
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
109
|
+
;(result as any)[key] = { ...(base as any)[key], ...value }
|
|
110
|
+
} else {
|
|
111
|
+
;(result as any)[key] = value
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return result
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function resolveMonitoringOptions(source: any): MonitoringOptions {
|
|
119
|
+
if (source?.monitoringConfig) return source.monitoringConfig
|
|
120
|
+
if (source?.context?.monitoringConfig) return source.context.monitoringConfig
|
|
121
|
+
return DEFAULTS
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function normalizeRuntimeMonitoringConfig(runtime: any): Partial<MonitoringOptions> {
|
|
125
|
+
if (!runtime) return {}
|
|
126
|
+
const overrides: Partial<MonitoringOptions> = {}
|
|
127
|
+
|
|
128
|
+
if (typeof runtime.enabled === 'boolean') overrides.enabled = runtime.enabled
|
|
129
|
+
if (runtime.metrics) {
|
|
130
|
+
overrides.httpMetrics = runtime.metrics.httpMetrics
|
|
131
|
+
overrides.systemMetrics = runtime.metrics.systemMetrics
|
|
132
|
+
overrides.customMetrics = runtime.metrics.customMetrics
|
|
133
|
+
overrides.collectInterval = runtime.metrics.collectInterval
|
|
134
|
+
overrides.retentionPeriod = runtime.metrics.retentionPeriod
|
|
135
|
+
}
|
|
136
|
+
if (Array.isArray(runtime.exporters)) {
|
|
137
|
+
overrides.exporters = runtime.exporters as MetricsExporter[]
|
|
138
|
+
}
|
|
139
|
+
if (runtime.thresholds) {
|
|
140
|
+
overrides.thresholds = { ...DEFAULTS.thresholds, ...runtime.thresholds }
|
|
141
|
+
}
|
|
142
|
+
if (Array.isArray(runtime.alerts)) {
|
|
143
|
+
overrides.alerts = runtime.alerts
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return overrides
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const monitoringPluginConfigSchema = {
|
|
150
|
+
type: 'object' as const,
|
|
151
|
+
properties: {
|
|
152
|
+
enabled: { type: 'boolean' },
|
|
153
|
+
httpMetrics: { type: 'boolean' },
|
|
154
|
+
systemMetrics: { type: 'boolean' },
|
|
155
|
+
customMetrics: { type: 'boolean' },
|
|
156
|
+
collectInterval: { type: 'number' },
|
|
157
|
+
retentionPeriod: { type: 'number' },
|
|
158
|
+
exporters: { type: 'array' }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
98
162
|
export const monitoringPlugin: Plugin = {
|
|
99
163
|
name: "monitoring",
|
|
100
164
|
version: "1.0.0",
|
|
@@ -104,64 +168,64 @@ export const monitoringPlugin: Plugin = {
|
|
|
104
168
|
category: "monitoring",
|
|
105
169
|
tags: ["monitoring", "metrics", "performance", "observability"],
|
|
106
170
|
dependencies: [],
|
|
171
|
+
configSchema: monitoringPluginConfigSchema,
|
|
172
|
+
defaultConfig: DEFAULTS,
|
|
107
173
|
|
|
108
174
|
setup: async (context: PluginContext) => {
|
|
109
|
-
|
|
175
|
+
const runtimeOverrides = normalizeRuntimeMonitoringConfig((context as any).config?.monitoring)
|
|
176
|
+
const pluginOverrides = (context as any).config?.plugins?.config?.monitoring as Partial<MonitoringOptions> | undefined
|
|
177
|
+
const resolvedConfig = mergeMonitoringOptions(
|
|
178
|
+
mergeMonitoringOptions(DEFAULTS, runtimeOverrides),
|
|
179
|
+
pluginOverrides || {}
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if (!resolvedConfig.enabled) {
|
|
110
183
|
context.logger.info('Monitoring plugin disabled by configuration')
|
|
111
184
|
return
|
|
112
185
|
}
|
|
113
186
|
|
|
114
187
|
context.logger.info('Initializing monitoring plugin', {
|
|
115
|
-
httpMetrics:
|
|
116
|
-
systemMetrics:
|
|
117
|
-
customMetrics:
|
|
118
|
-
exporters:
|
|
119
|
-
alerts:
|
|
188
|
+
httpMetrics: resolvedConfig.httpMetrics,
|
|
189
|
+
systemMetrics: resolvedConfig.systemMetrics,
|
|
190
|
+
customMetrics: resolvedConfig.customMetrics,
|
|
191
|
+
exporters: resolvedConfig.exporters.length,
|
|
192
|
+
alerts: resolvedConfig.alerts.length
|
|
120
193
|
})
|
|
121
194
|
|
|
122
|
-
// Initialize enhanced metrics registry
|
|
123
195
|
const metricsRegistry: MetricsRegistry = {
|
|
124
196
|
counters: new Map(),
|
|
125
197
|
gauges: new Map(),
|
|
126
198
|
histograms: new Map()
|
|
127
199
|
}
|
|
128
200
|
|
|
129
|
-
// Initialize metrics collector
|
|
130
201
|
const metricsCollector = new MetricsCollector()
|
|
131
202
|
|
|
132
|
-
// Store registry and collector in context for access by other hooks
|
|
133
203
|
;(context as any).metricsRegistry = metricsRegistry
|
|
134
204
|
;(context as any).metricsCollector = metricsCollector
|
|
205
|
+
;(context as any).monitoringConfig = resolvedConfig
|
|
135
206
|
|
|
136
|
-
|
|
137
|
-
if (DEFAULTS.httpMetrics) {
|
|
207
|
+
if (resolvedConfig.httpMetrics) {
|
|
138
208
|
initializeHttpMetrics(metricsRegistry, metricsCollector)
|
|
139
209
|
}
|
|
140
210
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
startSystemMetricsCollection(context, metricsCollector)
|
|
211
|
+
if (resolvedConfig.systemMetrics) {
|
|
212
|
+
startSystemMetricsCollection(context, metricsCollector, resolvedConfig)
|
|
144
213
|
}
|
|
145
214
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// Start metrics exporters
|
|
150
|
-
startMetricsExporters(context, metricsRegistry, metricsCollector)
|
|
151
|
-
|
|
152
|
-
// Setup metrics cleanup
|
|
153
|
-
setupMetricsCleanup(context, metricsRegistry)
|
|
215
|
+
setupMetricsEndpoint(context, metricsRegistry, metricsCollector, resolvedConfig)
|
|
216
|
+
startMetricsExporters(context, metricsRegistry, metricsCollector, resolvedConfig)
|
|
217
|
+
setupMetricsCleanup(context, metricsRegistry, resolvedConfig)
|
|
154
218
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
setupAlertMonitoring(context, metricsRegistry)
|
|
219
|
+
if (resolvedConfig.alerts.length > 0) {
|
|
220
|
+
setupAlertMonitoring(context, metricsRegistry, resolvedConfig.alerts)
|
|
158
221
|
}
|
|
159
222
|
|
|
160
223
|
context.logger.info('Monitoring plugin initialized successfully')
|
|
161
224
|
},
|
|
162
225
|
|
|
163
226
|
onServerStart: async (context: PluginContext) => {
|
|
164
|
-
|
|
227
|
+
const options = resolveMonitoringOptions(context)
|
|
228
|
+
if (options.enabled) {
|
|
165
229
|
context.logger.info('Monitoring plugin: Server monitoring started', {
|
|
166
230
|
pid: process.pid,
|
|
167
231
|
nodeVersion: process.version,
|
|
@@ -172,14 +236,15 @@ export const monitoringPlugin: Plugin = {
|
|
|
172
236
|
const metricsRegistry = (context as any).metricsRegistry as MetricsRegistry
|
|
173
237
|
if (metricsRegistry) {
|
|
174
238
|
recordCounter(metricsRegistry, 'server_starts_total', 1, {
|
|
175
|
-
version: appConfig.version
|
|
239
|
+
version: appConfig.version ?? '1.0.0'
|
|
176
240
|
})
|
|
177
241
|
}
|
|
178
242
|
}
|
|
179
243
|
},
|
|
180
244
|
|
|
181
245
|
onServerStop: async (context: PluginContext) => {
|
|
182
|
-
|
|
246
|
+
const options = resolveMonitoringOptions(context)
|
|
247
|
+
if (options.enabled) {
|
|
183
248
|
context.logger.info('Monitoring plugin: Server monitoring stopped')
|
|
184
249
|
|
|
185
250
|
// Record server stop metric
|
|
@@ -268,14 +333,14 @@ export const monitoringPlugin: Plugin = {
|
|
|
268
333
|
responseContext.size
|
|
269
334
|
)
|
|
270
335
|
|
|
271
|
-
|
|
272
|
-
if (
|
|
336
|
+
const options = resolveMonitoringOptions(responseContext)
|
|
337
|
+
if (options.thresholds.responseTime && duration > options.thresholds.responseTime) {
|
|
273
338
|
const logger = (responseContext as any).logger || console
|
|
274
339
|
logger.warn(`Slow request detected: ${responseContext.method} ${responseContext.path} took ${duration}ms`, {
|
|
275
340
|
method: responseContext.method,
|
|
276
341
|
path: responseContext.path,
|
|
277
342
|
duration,
|
|
278
|
-
threshold:
|
|
343
|
+
threshold: options.thresholds.responseTime
|
|
279
344
|
})
|
|
280
345
|
}
|
|
281
346
|
},
|
|
@@ -348,7 +413,7 @@ function initializeHttpMetrics(registry: MetricsRegistry, collector: MetricsColl
|
|
|
348
413
|
collector.createHistogram('http_response_size_bytes', 'HTTP response size in bytes', [100, 1000, 10000, 100000, 1000000])
|
|
349
414
|
}
|
|
350
415
|
|
|
351
|
-
function startSystemMetricsCollection(context: PluginContext, collector: MetricsCollector) {
|
|
416
|
+
function startSystemMetricsCollection(context: PluginContext, collector: MetricsCollector, options: MonitoringOptions) {
|
|
352
417
|
const intervals: NodeJS.Timeout[] = []
|
|
353
418
|
|
|
354
419
|
// Initialize system metrics in collector
|
|
@@ -422,16 +487,15 @@ function startSystemMetricsCollection(context: PluginContext, collector: Metrics
|
|
|
422
487
|
|
|
423
488
|
// Collect metrics immediately and then at intervals
|
|
424
489
|
collectSystemMetrics()
|
|
425
|
-
const interval = setInterval(collectSystemMetrics,
|
|
490
|
+
const interval = setInterval(collectSystemMetrics, options.collectInterval)
|
|
426
491
|
intervals.push(interval)
|
|
427
492
|
|
|
428
493
|
// Store intervals for cleanup
|
|
429
494
|
;(context as any).monitoringIntervals = intervals
|
|
430
495
|
}
|
|
431
496
|
|
|
432
|
-
function setupMetricsEndpoint(context: PluginContext, _registry: MetricsRegistry, collector: MetricsCollector) {
|
|
433
|
-
|
|
434
|
-
const prometheusExporter = DEFAULTS.exporters.find((e: any) => e.type === 'prometheus' && e.enabled)
|
|
497
|
+
function setupMetricsEndpoint(context: PluginContext, _registry: MetricsRegistry, collector: MetricsCollector, options: MonitoringOptions) {
|
|
498
|
+
const prometheusExporter = options.exporters.find((e: any) => e.type === 'prometheus' && e.enabled)
|
|
435
499
|
if (!prometheusExporter) return
|
|
436
500
|
|
|
437
501
|
const endpoint = prometheusExporter.endpoint || '/metrics'
|
|
@@ -451,10 +515,10 @@ function setupMetricsEndpoint(context: PluginContext, _registry: MetricsRegistry
|
|
|
451
515
|
}
|
|
452
516
|
}
|
|
453
517
|
|
|
454
|
-
function startMetricsExporters(context: PluginContext, registry: MetricsRegistry, collector: MetricsCollector) {
|
|
518
|
+
function startMetricsExporters(context: PluginContext, registry: MetricsRegistry, collector: MetricsCollector, options: MonitoringOptions) {
|
|
455
519
|
const intervals: NodeJS.Timeout[] = (context as any).monitoringIntervals || []
|
|
456
520
|
|
|
457
|
-
for (const exporterConfig of
|
|
521
|
+
for (const exporterConfig of options.exporters) {
|
|
458
522
|
if (!exporterConfig.enabled) continue
|
|
459
523
|
|
|
460
524
|
const exportMetrics = () => {
|
|
@@ -492,11 +556,11 @@ function startMetricsExporters(context: PluginContext, registry: MetricsRegistry
|
|
|
492
556
|
;(context as any).monitoringIntervals = intervals
|
|
493
557
|
}
|
|
494
558
|
|
|
495
|
-
function setupAlertMonitoring(context: PluginContext, registry: MetricsRegistry) {
|
|
559
|
+
function setupAlertMonitoring(context: PluginContext, registry: MetricsRegistry, alerts: AlertThreshold[]) {
|
|
496
560
|
const intervals: NodeJS.Timeout[] = (context as any).monitoringIntervals || []
|
|
497
561
|
|
|
498
562
|
const checkAlerts = () => {
|
|
499
|
-
for (const alert of
|
|
563
|
+
for (const alert of alerts) {
|
|
500
564
|
try {
|
|
501
565
|
const metricValue = getMetricValue(registry, alert.metric)
|
|
502
566
|
if (metricValue !== null && evaluateThreshold(metricValue, alert.operator, alert.value)) {
|
|
@@ -544,12 +608,12 @@ function setupAlertMonitoring(context: PluginContext, registry: MetricsRegistry)
|
|
|
544
608
|
;(context as any).monitoringIntervals = intervals
|
|
545
609
|
}
|
|
546
610
|
|
|
547
|
-
function setupMetricsCleanup(context: PluginContext, registry: MetricsRegistry) {
|
|
611
|
+
function setupMetricsCleanup(context: PluginContext, registry: MetricsRegistry, options: MonitoringOptions) {
|
|
548
612
|
const intervals: NodeJS.Timeout[] = (context as any).monitoringIntervals || []
|
|
549
613
|
|
|
550
614
|
const cleanup = () => {
|
|
551
615
|
const now = Date.now()
|
|
552
|
-
const cutoff = now -
|
|
616
|
+
const cutoff = now - (options.retentionPeriod ?? 3600000)
|
|
553
617
|
|
|
554
618
|
// Clean up old metrics
|
|
555
619
|
for (const [key, metric] of registry.counters.entries()) {
|
|
@@ -815,4 +879,4 @@ export function formatPrometheusLabels(labels?: Record<string, string>): string
|
|
|
815
879
|
return `{${labelPairs}}`
|
|
816
880
|
}
|
|
817
881
|
|
|
818
|
-
export default monitoringPlugin
|
|
882
|
+
export default monitoringPlugin
|
|
@@ -1,62 +1,25 @@
|
|
|
1
1
|
import { swagger } from '@elysiajs/swagger'
|
|
2
|
-
import type { FluxStack, PluginContext } from '
|
|
3
|
-
import { appConfig } from '
|
|
4
|
-
import { serverConfig } from '@/config/server.config'
|
|
5
|
-
import { pluginsConfig } from '@/config/plugins.config'
|
|
2
|
+
import type { FluxStack, PluginContext } from '@core/plugins/types'
|
|
3
|
+
import { appConfig, serverConfig, pluginsConfig } from '@config'
|
|
6
4
|
|
|
7
5
|
type Plugin = FluxStack.Plugin
|
|
8
6
|
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*/
|
|
13
|
-
function parseServersFromConfig(serversString: string): Array<{ url: string; description: string }> {
|
|
14
|
-
if (!serversString || serversString.trim() === '') {
|
|
15
|
-
return []
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return serversString.split(',').map(server => {
|
|
19
|
-
const [url, description] = server.split('|').map(s => s.trim())
|
|
20
|
-
return {
|
|
21
|
-
url: url || '',
|
|
22
|
-
description: description || 'Server'
|
|
23
|
-
}
|
|
24
|
-
}).filter(s => s.url !== '')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Configuration from config/plugins.config.ts (user editable)
|
|
28
|
-
const DEFAULTS = {
|
|
29
|
-
enabled: pluginsConfig.swaggerEnabled,
|
|
30
|
-
path: pluginsConfig.swaggerPath,
|
|
31
|
-
title: pluginsConfig.swaggerTitle,
|
|
32
|
-
description: pluginsConfig.swaggerDescription,
|
|
33
|
-
version: pluginsConfig.swaggerVersion,
|
|
34
|
-
excludePaths: pluginsConfig.swaggerExcludePaths,
|
|
35
|
-
servers: parseServersFromConfig(pluginsConfig.swaggerServers),
|
|
36
|
-
|
|
37
|
-
// Swagger UI options
|
|
38
|
-
swaggerOptions: {
|
|
39
|
-
persistAuthorization: pluginsConfig.swaggerPersistAuthorization,
|
|
40
|
-
displayRequestDuration: pluginsConfig.swaggerDisplayRequestDuration,
|
|
41
|
-
filter: pluginsConfig.swaggerEnableFilter,
|
|
42
|
-
showExtensions: pluginsConfig.swaggerShowExtensions,
|
|
43
|
-
tryItOutEnabled: pluginsConfig.swaggerTryItOutEnabled
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
// Authentication
|
|
47
|
-
authEnabled: pluginsConfig.swaggerAuthEnabled,
|
|
48
|
-
authUsername: pluginsConfig.swaggerAuthUsername,
|
|
49
|
-
authPassword: pluginsConfig.swaggerAuthPassword,
|
|
7
|
+
/** Parse servers from config string format: "url1|desc1,url2|desc2" */
|
|
8
|
+
function parseServers(str: string): Array<{ url: string; description: string }> {
|
|
9
|
+
if (!str?.trim()) return []
|
|
50
10
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
11
|
+
return str.split(',')
|
|
12
|
+
.map(s => {
|
|
13
|
+
const [url, desc] = s.split('|').map(p => p.trim())
|
|
14
|
+
return { url: url || '', description: desc || 'Server' }
|
|
15
|
+
})
|
|
16
|
+
.filter(s => s.url)
|
|
54
17
|
}
|
|
55
18
|
|
|
56
19
|
export const swaggerPlugin: Plugin = {
|
|
57
20
|
name: 'swagger',
|
|
58
21
|
version: '1.0.0',
|
|
59
|
-
description: 'Swagger documentation plugin for FluxStack
|
|
22
|
+
description: 'Swagger documentation plugin for FluxStack',
|
|
60
23
|
author: 'FluxStack Team',
|
|
61
24
|
priority: 500,
|
|
62
25
|
category: 'documentation',
|
|
@@ -64,238 +27,78 @@ export const swaggerPlugin: Plugin = {
|
|
|
64
27
|
dependencies: [],
|
|
65
28
|
|
|
66
29
|
setup: async (context: PluginContext) => {
|
|
67
|
-
if (!
|
|
68
|
-
context.logger.debug('Swagger plugin disabled
|
|
30
|
+
if (!pluginsConfig.swaggerEnabled) {
|
|
31
|
+
context.logger.debug('Swagger plugin disabled')
|
|
69
32
|
return
|
|
70
33
|
}
|
|
71
34
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// Add production server if in production
|
|
82
|
-
if (context.utils.isProduction()) {
|
|
83
|
-
servers.push({
|
|
84
|
-
url: 'https://api.example.com', // This would be configured
|
|
85
|
-
description: 'Production server'
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Simple Swagger configuration - all options from config/plugins.config.ts
|
|
90
|
-
const swaggerConfig = {
|
|
91
|
-
path: DEFAULTS.path,
|
|
92
|
-
documentation: {
|
|
93
|
-
info: {
|
|
94
|
-
title: DEFAULTS.title || appConfig.name || 'FluxStack API',
|
|
95
|
-
version: DEFAULTS.version || appConfig.version,
|
|
96
|
-
description: DEFAULTS.description || appConfig.description || 'Modern full-stack TypeScript framework with type-safe API endpoints'
|
|
97
|
-
},
|
|
98
|
-
servers,
|
|
99
|
-
|
|
100
|
-
// Add security schemes if defined
|
|
101
|
-
...(Object.keys(DEFAULTS.securitySchemes).length > 0 && {
|
|
102
|
-
components: {
|
|
103
|
-
securitySchemes: DEFAULTS.securitySchemes
|
|
104
|
-
}
|
|
105
|
-
}),
|
|
35
|
+
// Build servers list
|
|
36
|
+
const servers = parseServers(pluginsConfig.swaggerServers ?? '')
|
|
37
|
+
if (servers.length === 0) {
|
|
38
|
+
servers.push({
|
|
39
|
+
url: `http://${serverConfig.server.host}:${serverConfig.server.port}`,
|
|
40
|
+
description: 'Development server'
|
|
41
|
+
})
|
|
42
|
+
}
|
|
106
43
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
44
|
+
// Swagger configuration
|
|
45
|
+
const config = {
|
|
46
|
+
path: pluginsConfig.swaggerPath ?? '/swagger',
|
|
47
|
+
documentation: {
|
|
48
|
+
info: {
|
|
49
|
+
title: pluginsConfig.swaggerTitle ?? appConfig.name ?? 'FluxStack API',
|
|
50
|
+
version: pluginsConfig.swaggerVersion ?? appConfig.version ?? '1.0.0',
|
|
51
|
+
description: pluginsConfig.swaggerDescription ?? 'API documentation'
|
|
111
52
|
},
|
|
112
|
-
|
|
113
|
-
|
|
53
|
+
servers
|
|
54
|
+
},
|
|
55
|
+
exclude: pluginsConfig.swaggerExcludePaths ?? [],
|
|
56
|
+
swaggerOptions: {
|
|
57
|
+
persistAuthorization: pluginsConfig.swaggerPersistAuthorization,
|
|
58
|
+
displayRequestDuration: pluginsConfig.swaggerDisplayRequestDuration,
|
|
59
|
+
filter: pluginsConfig.swaggerEnableFilter,
|
|
60
|
+
showExtensions: pluginsConfig.swaggerShowExtensions,
|
|
61
|
+
tryItOutEnabled: pluginsConfig.swaggerTryItOutEnabled
|
|
114
62
|
}
|
|
63
|
+
}
|
|
115
64
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (!path.startsWith(DEFAULTS.path)) {
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const authHeader = request.headers.get('authorization')
|
|
125
|
-
|
|
126
|
-
if (!authHeader || !authHeader.startsWith('Basic ')) {
|
|
127
|
-
set.status = 401
|
|
128
|
-
set.headers['WWW-Authenticate'] = 'Basic realm="Swagger Documentation"'
|
|
129
|
-
return {
|
|
130
|
-
error: 'Authentication required',
|
|
131
|
-
message: 'Please provide valid credentials to access Swagger documentation'
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Decode Basic Auth credentials
|
|
136
|
-
const base64Credentials = authHeader.substring(6)
|
|
137
|
-
const credentials = Buffer.from(base64Credentials, 'base64').toString('utf-8')
|
|
138
|
-
const [username, password] = credentials.split(':')
|
|
139
|
-
|
|
140
|
-
// Validate credentials
|
|
141
|
-
if (username !== DEFAULTS.authUsername || password !== DEFAULTS.authPassword) {
|
|
142
|
-
set.status = 401
|
|
143
|
-
set.headers['WWW-Authenticate'] = 'Basic realm="Swagger Documentation"'
|
|
144
|
-
return {
|
|
145
|
-
error: 'Invalid credentials',
|
|
146
|
-
message: 'The provided username or password is incorrect'
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Credentials valid, continue to Swagger
|
|
151
|
-
})
|
|
65
|
+
// Add Basic Auth if enabled
|
|
66
|
+
if (pluginsConfig.swaggerAuthEnabled && pluginsConfig.swaggerAuthPassword) {
|
|
67
|
+
context.app.onBeforeHandle({ as: 'global' }, ({ request, set, path }: { request: Request; set: any; path: string }) => {
|
|
68
|
+
if (!path.startsWith(pluginsConfig.swaggerPath ?? '/swagger')) return
|
|
152
69
|
|
|
153
|
-
|
|
154
|
-
|
|
70
|
+
const authHeader = request.headers.get('authorization')
|
|
71
|
+
if (!authHeader?.startsWith('Basic ')) {
|
|
72
|
+
set.status = 401
|
|
73
|
+
set.headers['WWW-Authenticate'] = 'Basic realm="Swagger"'
|
|
74
|
+
return { error: 'Authentication required' }
|
|
75
|
+
}
|
|
155
76
|
|
|
156
|
-
|
|
157
|
-
|
|
77
|
+
const [username, password] = Buffer.from(authHeader.substring(6), 'base64')
|
|
78
|
+
.toString('utf-8')
|
|
79
|
+
.split(':')
|
|
158
80
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
81
|
+
if (username !== pluginsConfig.swaggerAuthUsername ||
|
|
82
|
+
password !== pluginsConfig.swaggerAuthPassword) {
|
|
83
|
+
set.status = 401
|
|
84
|
+
set.headers['WWW-Authenticate'] = 'Basic realm="Swagger"'
|
|
85
|
+
return { error: 'Invalid credentials' }
|
|
86
|
+
}
|
|
164
87
|
})
|
|
165
|
-
|
|
166
|
-
context.logger.
|
|
167
|
-
throw error
|
|
88
|
+
|
|
89
|
+
context.logger.debug(`Swagger auth enabled (user: ${pluginsConfig.swaggerAuthUsername})`)
|
|
168
90
|
}
|
|
91
|
+
|
|
92
|
+
context.app.use(swagger(config))
|
|
93
|
+
context.logger.debug(`Swagger enabled at ${pluginsConfig.swaggerPath}`)
|
|
169
94
|
},
|
|
170
95
|
|
|
171
96
|
onServerStart: async (context: PluginContext) => {
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
97
|
+
if (!pluginsConfig.swaggerEnabled) return
|
|
98
|
+
|
|
99
|
+
const url = `http://${serverConfig.server.host}:${serverConfig.server.port}${pluginsConfig.swaggerPath}`
|
|
100
|
+
context.logger.debug(`Swagger available at: ${url}`)
|
|
176
101
|
}
|
|
177
102
|
}
|
|
178
103
|
|
|
179
|
-
// ==========================================
|
|
180
|
-
// ⚙️ SWAGGER CONFIGURATION
|
|
181
|
-
// ==========================================
|
|
182
|
-
//
|
|
183
|
-
// All Swagger options are configurable via:
|
|
184
|
-
// 📁 config/plugins.config.ts (user editable)
|
|
185
|
-
// 🌍 Environment variables (.env file)
|
|
186
|
-
//
|
|
187
|
-
// Available configurations:
|
|
188
|
-
//
|
|
189
|
-
// 1️⃣ Basic Settings:
|
|
190
|
-
// - SWAGGER_ENABLED (default: true)
|
|
191
|
-
// - SWAGGER_TITLE (default: 'FluxStack API')
|
|
192
|
-
// - SWAGGER_VERSION (default: app version)
|
|
193
|
-
// - SWAGGER_DESCRIPTION (default: 'API documentation...')
|
|
194
|
-
// - SWAGGER_PATH (default: '/swagger')
|
|
195
|
-
//
|
|
196
|
-
// 2️⃣ Advanced Options:
|
|
197
|
-
// - SWAGGER_EXCLUDE_PATHS (array, default: [])
|
|
198
|
-
// - SWAGGER_SERVERS (format: "url1|desc1,url2|desc2")
|
|
199
|
-
// Example: "https://api.prod.com|Production,https://staging.com|Staging"
|
|
200
|
-
//
|
|
201
|
-
// 3️⃣ Swagger UI Options:
|
|
202
|
-
// - SWAGGER_PERSIST_AUTH (default: true)
|
|
203
|
-
// - SWAGGER_DISPLAY_DURATION (default: true)
|
|
204
|
-
// - SWAGGER_ENABLE_FILTER (default: true)
|
|
205
|
-
// - SWAGGER_SHOW_EXTENSIONS (default: true)
|
|
206
|
-
// - SWAGGER_TRY_IT_OUT (default: true)
|
|
207
|
-
//
|
|
208
|
-
// 4️⃣ Authentication (Basic Auth):
|
|
209
|
-
// - SWAGGER_AUTH_ENABLED (default: false)
|
|
210
|
-
// - SWAGGER_AUTH_USERNAME (default: 'admin')
|
|
211
|
-
// - SWAGGER_AUTH_PASSWORD (required if auth enabled)
|
|
212
|
-
//
|
|
213
|
-
// Example .env configuration:
|
|
214
|
-
// ```
|
|
215
|
-
// SWAGGER_ENABLED=true
|
|
216
|
-
// SWAGGER_TITLE=My API
|
|
217
|
-
// SWAGGER_PATH=/api-docs
|
|
218
|
-
// SWAGGER_SERVERS=https://api.myapp.com|Production,https://staging.myapp.com|Staging
|
|
219
|
-
// SWAGGER_EXCLUDE_PATHS=/internal,/admin
|
|
220
|
-
//
|
|
221
|
-
// # Protect Swagger with Basic Auth
|
|
222
|
-
// SWAGGER_AUTH_ENABLED=true
|
|
223
|
-
// SWAGGER_AUTH_USERNAME=admin
|
|
224
|
-
// SWAGGER_AUTH_PASSWORD=super-secret-password
|
|
225
|
-
// ```
|
|
226
|
-
//
|
|
227
|
-
// 🔒 When authentication is enabled:
|
|
228
|
-
// - Browser will prompt for username/password
|
|
229
|
-
// - All Swagger routes are protected (UI + JSON spec)
|
|
230
|
-
// - Credentials are validated using Basic Auth
|
|
231
|
-
// - Perfect for staging/production environments
|
|
232
|
-
//
|
|
233
|
-
// ==========================================
|
|
234
|
-
// 🏷️ AUTOMATIC TAG DISCOVERY
|
|
235
|
-
// ==========================================
|
|
236
|
-
//
|
|
237
|
-
// ✨ 100% AUTOMATIC - Just add tags to your routes!
|
|
238
|
-
//
|
|
239
|
-
// Example 1 - Simple route:
|
|
240
|
-
// app.get('/products', handler, {
|
|
241
|
-
// detail: {
|
|
242
|
-
// summary: 'Get all products',
|
|
243
|
-
// tags: ['Products', 'Catalog'] // ✅ Auto-discovered!
|
|
244
|
-
// }
|
|
245
|
-
// })
|
|
246
|
-
//
|
|
247
|
-
// Example 2 - Grouped routes:
|
|
248
|
-
// export const ordersRoutes = new Elysia({ prefix: '/orders', tags: ['Orders'] })
|
|
249
|
-
// .get('/', handler, {
|
|
250
|
-
// detail: {
|
|
251
|
-
// summary: 'List orders',
|
|
252
|
-
// tags: ['Orders', 'Management'] // ✅ Auto-discovered!
|
|
253
|
-
// }
|
|
254
|
-
// })
|
|
255
|
-
//
|
|
256
|
-
// Elysia Swagger automatically:
|
|
257
|
-
// - Discovers all tags from routes
|
|
258
|
-
// - Organizes endpoints by tag
|
|
259
|
-
// - Generates OpenAPI spec
|
|
260
|
-
// - Creates interactive UI
|
|
261
|
-
//
|
|
262
|
-
// ==========================================
|
|
263
|
-
// 🔐 SECURITY CONFIGURATION
|
|
264
|
-
// ==========================================
|
|
265
|
-
//
|
|
266
|
-
// To enable security in your FluxStack app, configure like this:
|
|
267
|
-
//
|
|
268
|
-
// plugins: {
|
|
269
|
-
// config: {
|
|
270
|
-
// swagger: {
|
|
271
|
-
// securitySchemes: {
|
|
272
|
-
// bearerAuth: {
|
|
273
|
-
// type: 'http',
|
|
274
|
-
// scheme: 'bearer',
|
|
275
|
-
// bearerFormat: 'JWT'
|
|
276
|
-
// },
|
|
277
|
-
// apiKeyAuth: {
|
|
278
|
-
// type: 'apiKey',
|
|
279
|
-
// in: 'header',
|
|
280
|
-
// name: 'X-API-Key'
|
|
281
|
-
// }
|
|
282
|
-
// },
|
|
283
|
-
// globalSecurity: [
|
|
284
|
-
// { bearerAuth: [] } // Apply JWT auth globally
|
|
285
|
-
// ]
|
|
286
|
-
// }
|
|
287
|
-
// }
|
|
288
|
-
// }
|
|
289
|
-
//
|
|
290
|
-
// Then in your routes, you can override per endpoint:
|
|
291
|
-
// app.get('/public', handler, {
|
|
292
|
-
// detail: { security: [] } // No auth required
|
|
293
|
-
// })
|
|
294
|
-
//
|
|
295
|
-
// app.get('/private', handler, {
|
|
296
|
-
// detail: {
|
|
297
|
-
// security: [{ apiKeyAuth: [] }] // API key required
|
|
298
|
-
// }
|
|
299
|
-
// })
|
|
300
|
-
|
|
301
104
|
export default swaggerPlugin
|