create-fluxstack 1.7.5 → 1.8.3
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 +82 -0
- package/.env.example +19 -0
- package/Dockerfile +70 -0
- package/README.md +6 -3
- package/app/client/SIMPLIFICATION.md +140 -0
- package/app/client/frontend-only.ts +1 -1
- package/app/client/src/App.tsx +148 -283
- package/app/client/src/index.css +5 -20
- package/app/client/src/lib/eden-api.ts +53 -220
- package/app/client/src/main.tsx +2 -3
- package/app/server/app.ts +20 -5
- package/app/server/backend-only.ts +15 -12
- package/app/server/controllers/users.controller.ts +57 -31
- package/app/server/index.ts +86 -96
- package/app/server/live/register-components.ts +18 -7
- package/app/server/routes/env-test.ts +110 -0
- package/app/server/routes/index.ts +1 -8
- package/app/server/routes/users.routes.ts +192 -91
- package/config/app.config.ts +2 -54
- package/config/client.config.ts +95 -0
- package/config/fluxstack.config.ts +2 -2
- package/config/index.ts +57 -22
- package/config/monitoring.config.ts +114 -0
- package/config/plugins.config.ts +80 -0
- package/config/runtime.config.ts +0 -17
- package/config/server.config.ts +50 -30
- package/core/build/bundler.ts +17 -16
- package/core/build/flux-plugins-generator.ts +34 -23
- package/core/build/index.ts +32 -31
- package/core/build/live-components-generator.ts +44 -30
- package/core/build/optimizer.ts +37 -17
- package/core/cli/command-registry.ts +4 -14
- package/core/cli/commands/plugin-deps.ts +8 -8
- package/core/cli/generators/component.ts +3 -3
- package/core/cli/generators/controller.ts +4 -4
- package/core/cli/generators/index.ts +8 -8
- package/core/cli/generators/interactive.ts +4 -4
- package/core/cli/generators/plugin.ts +3 -3
- package/core/cli/generators/prompts.ts +1 -1
- package/core/cli/generators/route.ts +27 -11
- package/core/cli/generators/service.ts +5 -5
- package/core/cli/generators/template-engine.ts +1 -1
- package/core/cli/generators/types.ts +1 -1
- package/core/cli/index.ts +158 -189
- package/core/cli/plugin-discovery.ts +3 -3
- package/core/client/hooks/index.ts +2 -2
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +1 -1
- package/core/client/hooks/useChunkedUpload.ts +1 -1
- package/core/client/hooks/useHybridLiveComponent.ts +1 -1
- package/core/client/hooks/useWebSocket.ts +1 -1
- package/core/config/env.ts +5 -1
- package/core/config/runtime-config.ts +12 -10
- package/core/config/schema.ts +33 -2
- package/core/framework/server.ts +30 -14
- package/core/framework/types.ts +2 -2
- package/core/index.ts +31 -23
- package/core/live/ComponentRegistry.ts +1 -1
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +1 -1
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +65 -161
- package/core/plugins/built-in/static/index.ts +75 -277
- package/core/plugins/built-in/swagger/index.ts +301 -231
- package/core/plugins/built-in/vite/index.ts +342 -377
- package/core/plugins/config.ts +2 -2
- package/core/plugins/dependency-manager.ts +2 -2
- package/core/plugins/discovery.ts +1 -1
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/manager.ts +19 -4
- package/core/plugins/module-resolver.ts +1 -1
- package/core/plugins/registry.ts +25 -21
- package/core/plugins/types.ts +147 -5
- package/core/server/backend-entry.ts +51 -0
- package/core/server/framework.ts +2 -2
- package/core/server/live/ComponentRegistry.ts +9 -26
- package/core/server/live/FileUploadManager.ts +1 -1
- package/core/server/live/auto-generated-components.ts +26 -0
- package/core/server/live/websocket-plugin.ts +211 -19
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +4 -4
- package/core/server/plugins/database.ts +1 -2
- package/core/server/plugins/static-files-plugin.ts +259 -231
- package/core/server/plugins/swagger.ts +1 -1
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +4 -4
- package/core/server/standalone.ts +16 -1
- package/core/testing/index.ts +1 -1
- package/core/testing/setup.ts +1 -1
- package/core/types/plugin.ts +6 -0
- package/core/utils/build-logger.ts +324 -0
- package/core/utils/config-schema.ts +2 -6
- package/core/utils/helpers.ts +14 -9
- package/core/utils/logger/startup-banner.ts +7 -33
- package/core/utils/regenerate-files.ts +69 -0
- package/core/utils/version.ts +6 -6
- package/create-fluxstack.ts +68 -25
- package/fluxstack.config.ts +138 -252
- package/package.json +3 -18
- package/plugins/crypto-auth/index.ts +52 -47
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/helpers.ts +16 -1
- package/vitest.config.ts +17 -26
- package/app/client/src/App.css +0 -883
- package/app/client/src/components/ErrorBoundary.tsx +0 -107
- package/app/client/src/components/ErrorDisplay.css +0 -365
- package/app/client/src/components/ErrorDisplay.tsx +0 -258
- package/app/client/src/components/FluxStackConfig.tsx +0 -1321
- package/app/client/src/components/HybridLiveCounter.tsx +0 -140
- package/app/client/src/components/LiveClock.tsx +0 -286
- package/app/client/src/components/MainLayout.tsx +0 -388
- package/app/client/src/components/SidebarNavigation.tsx +0 -391
- package/app/client/src/components/StateDemo.tsx +0 -178
- package/app/client/src/components/SystemMonitor.tsx +0 -1044
- package/app/client/src/components/UserProfile.tsx +0 -809
- package/app/client/src/hooks/useAuth.ts +0 -39
- package/app/client/src/hooks/useNotifications.ts +0 -56
- package/app/client/src/lib/errors.ts +0 -340
- package/app/client/src/lib/hooks/useErrorHandler.ts +0 -258
- package/app/client/src/lib/index.ts +0 -45
- package/app/client/src/pages/ApiDocs.tsx +0 -182
- package/app/client/src/pages/CryptoAuthPage.tsx +0 -394
- package/app/client/src/pages/Demo.tsx +0 -174
- package/app/client/src/pages/HybridLive.tsx +0 -263
- package/app/client/src/pages/Overview.tsx +0 -155
- package/app/client/src/store/README.md +0 -43
- package/app/client/src/store/index.ts +0 -16
- package/app/client/src/store/slices/uiSlice.ts +0 -151
- package/app/client/src/store/slices/userSlice.ts +0 -161
- package/app/client/src/test/README.md +0 -257
- package/app/client/src/test/setup.ts +0 -70
- package/app/client/src/test/types.ts +0 -12
- package/app/server/live/CounterComponent.ts +0 -191
- package/app/server/live/FluxStackConfig.ts +0 -534
- package/app/server/live/SidebarNavigation.ts +0 -157
- package/app/server/live/SystemMonitor.ts +0 -595
- package/app/server/live/SystemMonitorIntegration.ts +0 -151
- package/app/server/live/UserProfileComponent.ts +0 -141
- package/app/server/middleware/auth.ts +0 -136
- package/app/server/middleware/errorHandling.ts +0 -252
- package/app/server/middleware/index.ts +0 -10
- package/app/server/middleware/rateLimit.ts +0 -193
- package/app/server/middleware/requestLogging.ts +0 -215
- package/app/server/middleware/validation.ts +0 -270
- package/app/server/routes/config.ts +0 -145
- package/app/server/routes/crypto-auth-demo.routes.ts +0 -167
- package/app/server/routes/example-with-crypto-auth.routes.ts +0 -235
- package/app/server/routes/exemplo-posts.routes.ts +0 -161
- package/app/server/routes/upload.ts +0 -92
- package/app/server/services/NotificationService.ts +0 -302
- package/app/server/services/UserService.ts +0 -222
- package/app/server/services/index.ts +0 -46
- package/app/server/types/index.ts +0 -1
- package/config/build.config.ts +0 -24
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
// 🔥 System Monitor Integration - Auto-capture metrics from Live Components
|
|
2
|
-
|
|
3
|
-
import { componentRegistry } from '../../../core/server/live/ComponentRegistry'
|
|
4
|
-
|
|
5
|
-
export class SystemMonitorIntegration {
|
|
6
|
-
private static instance: SystemMonitorIntegration | null = null
|
|
7
|
-
private systemMonitorId: string | null = null
|
|
8
|
-
|
|
9
|
-
private constructor() {
|
|
10
|
-
this.setupHooks()
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
public static getInstance(): SystemMonitorIntegration {
|
|
14
|
-
if (!SystemMonitorIntegration.instance) {
|
|
15
|
-
SystemMonitorIntegration.instance = new SystemMonitorIntegration()
|
|
16
|
-
}
|
|
17
|
-
return SystemMonitorIntegration.instance
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Set the system monitor component ID
|
|
21
|
-
public setSystemMonitorId(componentId: string) {
|
|
22
|
-
this.systemMonitorId = componentId
|
|
23
|
-
console.log(`📊 SystemMonitor integration activated: ${componentId}`)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Get system monitor component
|
|
27
|
-
private getSystemMonitor() {
|
|
28
|
-
if (!this.systemMonitorId) return null
|
|
29
|
-
return componentRegistry.getComponent(this.systemMonitorId)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Setup hooks to capture Live Component events
|
|
33
|
-
private setupHooks() {
|
|
34
|
-
// Override the original mountComponent method
|
|
35
|
-
const originalMountComponent = componentRegistry.mountComponent.bind(componentRegistry)
|
|
36
|
-
componentRegistry.mountComponent = async (...args) => {
|
|
37
|
-
const result = await originalMountComponent(...args)
|
|
38
|
-
|
|
39
|
-
// Record connection event
|
|
40
|
-
this.recordConnection(args[1], 'connected')
|
|
41
|
-
|
|
42
|
-
return result
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Override the original unmountComponent method
|
|
46
|
-
const originalUnmountComponent = componentRegistry.unmountComponent.bind(componentRegistry)
|
|
47
|
-
componentRegistry.unmountComponent = async (componentId) => {
|
|
48
|
-
const component = componentRegistry.getComponent(componentId)
|
|
49
|
-
const componentType = component?.constructor.name || 'Unknown'
|
|
50
|
-
|
|
51
|
-
await originalUnmountComponent(componentId)
|
|
52
|
-
|
|
53
|
-
// Record disconnection event
|
|
54
|
-
this.recordConnection(componentType, 'disconnected')
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Override the original rehydrateComponent method
|
|
58
|
-
const originalRehydrateComponent = componentRegistry.rehydrateComponent.bind(componentRegistry)
|
|
59
|
-
componentRegistry.rehydrateComponent = async (...args) => {
|
|
60
|
-
const result = await originalRehydrateComponent(...args)
|
|
61
|
-
|
|
62
|
-
if (result.success) {
|
|
63
|
-
// Record rehydration event
|
|
64
|
-
this.recordConnection(args[1], 'rehydrated')
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return result
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Override the original executeAction method
|
|
71
|
-
const originalExecuteAction = componentRegistry.executeAction.bind(componentRegistry)
|
|
72
|
-
componentRegistry.executeAction = async (componentId, action, payload) => {
|
|
73
|
-
const startTime = Date.now()
|
|
74
|
-
let success = false
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
const result = await originalExecuteAction(componentId, action, payload)
|
|
78
|
-
success = true
|
|
79
|
-
return result
|
|
80
|
-
} catch (error) {
|
|
81
|
-
success = false
|
|
82
|
-
throw error
|
|
83
|
-
} finally {
|
|
84
|
-
const responseTime = Date.now() - startTime
|
|
85
|
-
|
|
86
|
-
// Record message event
|
|
87
|
-
this.recordMessage(action, componentId, success, responseTime)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Setup completed - logged in auto-discovery group
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Record a connection event
|
|
95
|
-
private recordConnection(componentType: string, status: 'connected' | 'disconnected' | 'rehydrated') {
|
|
96
|
-
const monitor = this.getSystemMonitor()
|
|
97
|
-
if (!monitor) return
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
monitor.executeAction('recordConnection', {
|
|
101
|
-
componentType,
|
|
102
|
-
status
|
|
103
|
-
}).catch(error => {
|
|
104
|
-
console.warn('⚠️ Failed to record connection event:', error.message)
|
|
105
|
-
})
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.warn('⚠️ Error recording connection event:', error)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Record a message event
|
|
112
|
-
private recordMessage(type: string, componentId: string, success: boolean, responseTime?: number) {
|
|
113
|
-
const monitor = this.getSystemMonitor()
|
|
114
|
-
if (!monitor) return
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
monitor.executeAction('recordMessage', {
|
|
118
|
-
type,
|
|
119
|
-
componentId,
|
|
120
|
-
success,
|
|
121
|
-
responseTime
|
|
122
|
-
}).catch(error => {
|
|
123
|
-
console.warn('⚠️ Failed to record message event:', error.message)
|
|
124
|
-
})
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.warn('⚠️ Error recording message event:', error)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Manually trigger metrics collection for all monitors
|
|
131
|
-
public triggerMetricsCollection() {
|
|
132
|
-
const monitor = this.getSystemMonitor()
|
|
133
|
-
if (!monitor) return
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
monitor.executeAction('collectMetrics', {}).catch(error => {
|
|
137
|
-
console.warn('⚠️ Failed to trigger metrics collection:', error.message)
|
|
138
|
-
})
|
|
139
|
-
} catch (error) {
|
|
140
|
-
console.warn('⚠️ Error triggering metrics collection:', error)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Get current system stats
|
|
145
|
-
public getSystemStats() {
|
|
146
|
-
return componentRegistry.getStats()
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Export singleton instance
|
|
151
|
-
export const systemMonitorIntegration = SystemMonitorIntegration.getInstance()
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
// 🔥 User Profile - Live Component
|
|
2
|
-
import { LiveComponent } from "@/core/types/types";
|
|
3
|
-
|
|
4
|
-
interface UserProfileState {
|
|
5
|
-
name: string;
|
|
6
|
-
email: string;
|
|
7
|
-
avatar: string;
|
|
8
|
-
status: 'online' | 'away' | 'busy' | 'offline';
|
|
9
|
-
bio: string;
|
|
10
|
-
location: string;
|
|
11
|
-
joinedDate: string;
|
|
12
|
-
followers: number;
|
|
13
|
-
following: number;
|
|
14
|
-
posts: number;
|
|
15
|
-
isEditing: boolean;
|
|
16
|
-
lastUpdated: Date; // ✅ Padronizado para Date
|
|
17
|
-
theme: 'light' | 'dark';
|
|
18
|
-
notifications: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class UserProfileComponent extends LiveComponent<UserProfileState> {
|
|
22
|
-
constructor(initialState: UserProfileState, ws: any, options?: { room?: string; userId?: string }) {
|
|
23
|
-
const defaultState: UserProfileState = {
|
|
24
|
-
name: "João Silva",
|
|
25
|
-
email: "joao.silva@example.com",
|
|
26
|
-
avatar: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face",
|
|
27
|
-
status: "online",
|
|
28
|
-
bio: "Desenvolvedor Full-Stack apaixonado por tecnologia e inovação. Sempre em busca de novos desafios!",
|
|
29
|
-
location: "São Paulo, Brasil",
|
|
30
|
-
joinedDate: "Janeiro 2023",
|
|
31
|
-
followers: 1234,
|
|
32
|
-
following: 567,
|
|
33
|
-
posts: 89,
|
|
34
|
-
isEditing: false,
|
|
35
|
-
lastUpdated: new Date(),
|
|
36
|
-
theme: "light",
|
|
37
|
-
notifications: 5,
|
|
38
|
-
...initialState
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
super(defaultState, ws, options);
|
|
42
|
-
console.log('👤 UserProfile component created:', this.id, { initialState: defaultState });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async updateProfile(payload: { name?: string; bio?: string; location?: string }) {
|
|
46
|
-
const updates: Partial<UserProfileState> = {
|
|
47
|
-
lastUpdated: new Date()
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
if (payload.name) updates.name = payload.name;
|
|
51
|
-
if (payload.bio) updates.bio = payload.bio;
|
|
52
|
-
if (payload.location) updates.location = payload.location;
|
|
53
|
-
|
|
54
|
-
this.setState(updates);
|
|
55
|
-
console.log('👤 Profile updated:', this.id, updates);
|
|
56
|
-
return { success: true, updated: Object.keys(updates) };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async toggleStatus() {
|
|
60
|
-
const statuses: UserProfileState['status'][] = ['online', 'away', 'busy', 'offline'];
|
|
61
|
-
const currentIndex = statuses.indexOf(this.state.status);
|
|
62
|
-
const nextStatus = statuses[(currentIndex + 1) % statuses.length];
|
|
63
|
-
|
|
64
|
-
this.setState({
|
|
65
|
-
status: nextStatus,
|
|
66
|
-
lastUpdated: new Date()
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
console.log('👤 Status changed:', this.id, { from: this.state.status, to: nextStatus });
|
|
70
|
-
return { success: true, newStatus: nextStatus };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async toggleTheme() {
|
|
74
|
-
const newTheme = this.state.theme === 'light' ? 'dark' : 'light';
|
|
75
|
-
this.setState({
|
|
76
|
-
theme: newTheme,
|
|
77
|
-
lastUpdated: new Date()
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
console.log('👤 Theme changed:', this.id, { to: newTheme });
|
|
81
|
-
return { success: true, theme: newTheme };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async followUser() {
|
|
85
|
-
this.setState({
|
|
86
|
-
followers: this.state.followers + 1,
|
|
87
|
-
lastUpdated: new Date()
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
console.log('👤 New follower:', this.id, { followers: this.state.followers + 1 });
|
|
91
|
-
return { success: true, followers: this.state.followers };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async toggleEdit() {
|
|
95
|
-
this.setState({
|
|
96
|
-
isEditing: !this.state.isEditing,
|
|
97
|
-
lastUpdated: new Date()
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
console.log('👤 Edit mode toggled:', this.id, { isEditing: !this.state.isEditing });
|
|
101
|
-
return { success: true, isEditing: this.state.isEditing };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async clearNotifications() {
|
|
105
|
-
this.setState({
|
|
106
|
-
notifications: 0,
|
|
107
|
-
lastUpdated: new Date()
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
console.log('👤 Notifications cleared:', this.id);
|
|
111
|
-
return { success: true };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
async updateAvatar(payload: { imageUrl: string }) {
|
|
115
|
-
if (!payload.imageUrl) {
|
|
116
|
-
throw new Error('Invalid image URL');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
this.setState({
|
|
120
|
-
avatar: payload.imageUrl,
|
|
121
|
-
lastUpdated: new Date()
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
console.log('👤 Avatar updated:', this.id, {
|
|
125
|
-
newAvatar: payload.imageUrl,
|
|
126
|
-
previousAvatar: this.state.avatar
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
success: true,
|
|
131
|
-
avatar: payload.imageUrl,
|
|
132
|
-
message: 'Avatar updated successfully!'
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Override destroy for cleanup
|
|
137
|
-
public destroy() {
|
|
138
|
-
console.log(`🗑️ UserProfile component ${this.id} destroyed`)
|
|
139
|
-
super.destroy()
|
|
140
|
-
}
|
|
141
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Authentication Middleware
|
|
3
|
-
* Handles user authentication and authorization
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Context } from 'elysia'
|
|
7
|
-
|
|
8
|
-
export interface AuthUser {
|
|
9
|
-
id: number
|
|
10
|
-
email: string
|
|
11
|
-
name: string
|
|
12
|
-
role: 'admin' | 'user'
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface AuthContext extends Context {
|
|
16
|
-
user?: AuthUser
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Authentication middleware
|
|
21
|
-
* Validates JWT tokens and sets user context
|
|
22
|
-
*/
|
|
23
|
-
export const authMiddleware = {
|
|
24
|
-
name: 'auth',
|
|
25
|
-
|
|
26
|
-
beforeHandle: async (context: AuthContext) => {
|
|
27
|
-
const authHeader = context.headers.authorization
|
|
28
|
-
|
|
29
|
-
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
30
|
-
return new Response(
|
|
31
|
-
JSON.stringify({ error: 'Missing or invalid authorization header' }),
|
|
32
|
-
{ status: 401, headers: { 'Content-Type': 'application/json' } }
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const token = authHeader.substring(7) // Remove 'Bearer ' prefix
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
// In a real application, you would validate the JWT token here
|
|
40
|
-
// For demo purposes, we'll simulate token validation
|
|
41
|
-
const user = await validateToken(token)
|
|
42
|
-
|
|
43
|
-
if (!user) {
|
|
44
|
-
return new Response(
|
|
45
|
-
JSON.stringify({ error: 'Invalid or expired token' }),
|
|
46
|
-
{ status: 401, headers: { 'Content-Type': 'application/json' } }
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Set user in context
|
|
51
|
-
context.user = user
|
|
52
|
-
} catch (error) {
|
|
53
|
-
return new Response(
|
|
54
|
-
JSON.stringify({ error: 'Token validation failed' }),
|
|
55
|
-
{ status: 401, headers: { 'Content-Type': 'application/json' } }
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Optional authentication middleware
|
|
63
|
-
* Sets user context if token is present, but doesn't require it
|
|
64
|
-
*/
|
|
65
|
-
export const optionalAuthMiddleware = {
|
|
66
|
-
name: 'optional-auth',
|
|
67
|
-
|
|
68
|
-
beforeHandle: async (context: AuthContext) => {
|
|
69
|
-
const authHeader = context.headers.authorization
|
|
70
|
-
|
|
71
|
-
if (authHeader && authHeader.startsWith('Bearer ')) {
|
|
72
|
-
const token = authHeader.substring(7)
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const user = await validateToken(token)
|
|
76
|
-
if (user) {
|
|
77
|
-
context.user = user
|
|
78
|
-
}
|
|
79
|
-
} catch (error) {
|
|
80
|
-
// Ignore errors for optional auth
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Role-based authorization middleware
|
|
88
|
-
*/
|
|
89
|
-
export const requireRole = (requiredRole: 'admin' | 'user') => ({
|
|
90
|
-
name: `require-${requiredRole}`,
|
|
91
|
-
|
|
92
|
-
beforeHandle: async (context: AuthContext) => {
|
|
93
|
-
if (!context.user) {
|
|
94
|
-
return new Response(
|
|
95
|
-
JSON.stringify({ error: 'Authentication required' }),
|
|
96
|
-
{ status: 401, headers: { 'Content-Type': 'application/json' } }
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (requiredRole === 'admin' && context.user.role !== 'admin') {
|
|
101
|
-
return new Response(
|
|
102
|
-
JSON.stringify({ error: 'Admin access required' }),
|
|
103
|
-
{ status: 403, headers: { 'Content-Type': 'application/json' } }
|
|
104
|
-
)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Validate JWT token (mock implementation)
|
|
111
|
-
* In a real application, this would use a proper JWT library
|
|
112
|
-
*/
|
|
113
|
-
async function validateToken(token: string): Promise<AuthUser | null> {
|
|
114
|
-
// Mock token validation
|
|
115
|
-
// In reality, you would decode and verify the JWT
|
|
116
|
-
|
|
117
|
-
if (token === 'valid-admin-token') {
|
|
118
|
-
return {
|
|
119
|
-
id: 1,
|
|
120
|
-
email: 'admin@example.com',
|
|
121
|
-
name: 'Admin User',
|
|
122
|
-
role: 'admin'
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (token === 'valid-user-token') {
|
|
127
|
-
return {
|
|
128
|
-
id: 2,
|
|
129
|
-
email: 'user@example.com',
|
|
130
|
-
name: 'Regular User',
|
|
131
|
-
role: 'user'
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return null
|
|
136
|
-
}
|
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error Handling Middleware
|
|
3
|
-
* Provides centralized error handling for the application
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Context } from 'elysia'
|
|
7
|
-
import { appConfig } from '@/config/app.config'
|
|
8
|
-
|
|
9
|
-
export interface ErrorResponse {
|
|
10
|
-
error: string
|
|
11
|
-
message: string
|
|
12
|
-
code?: string
|
|
13
|
-
details?: any
|
|
14
|
-
timestamp: string
|
|
15
|
-
requestId?: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Custom application errors
|
|
20
|
-
*/
|
|
21
|
-
export class AppError extends Error {
|
|
22
|
-
constructor(
|
|
23
|
-
message: string,
|
|
24
|
-
public statusCode: number = 500,
|
|
25
|
-
public code?: string,
|
|
26
|
-
public details?: any
|
|
27
|
-
) {
|
|
28
|
-
super(message)
|
|
29
|
-
this.name = 'AppError'
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export class ValidationError extends AppError {
|
|
34
|
-
constructor(message: string, details?: any) {
|
|
35
|
-
super(message, 400, 'VALIDATION_ERROR', details)
|
|
36
|
-
this.name = 'ValidationError'
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class NotFoundError extends AppError {
|
|
41
|
-
constructor(resource: string = 'Resource') {
|
|
42
|
-
super(`${resource} not found`, 404, 'NOT_FOUND')
|
|
43
|
-
this.name = 'NotFoundError'
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export class UnauthorizedError extends AppError {
|
|
48
|
-
constructor(message: string = 'Unauthorized') {
|
|
49
|
-
super(message, 401, 'UNAUTHORIZED')
|
|
50
|
-
this.name = 'UnauthorizedError'
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export class ForbiddenError extends AppError {
|
|
55
|
-
constructor(message: string = 'Forbidden') {
|
|
56
|
-
super(message, 403, 'FORBIDDEN')
|
|
57
|
-
this.name = 'ForbiddenError'
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export class ConflictError extends AppError {
|
|
62
|
-
constructor(message: string, details?: any) {
|
|
63
|
-
super(message, 409, 'CONFLICT', details)
|
|
64
|
-
this.name = 'ConflictError'
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Error handling middleware
|
|
70
|
-
*/
|
|
71
|
-
export const errorHandlingMiddleware = {
|
|
72
|
-
name: 'error-handling',
|
|
73
|
-
|
|
74
|
-
onError: async (context: Context, error: Error): Promise<Response> => {
|
|
75
|
-
const requestId = context.store?.requestId || 'unknown'
|
|
76
|
-
|
|
77
|
-
// Log the error
|
|
78
|
-
console.error('🚨 Error occurred', {
|
|
79
|
-
requestId,
|
|
80
|
-
method: context.request.method,
|
|
81
|
-
path: context.path,
|
|
82
|
-
error: error.message,
|
|
83
|
-
stack: error.stack,
|
|
84
|
-
timestamp: new Date().toISOString()
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
// Handle different error types
|
|
88
|
-
if (error instanceof AppError) {
|
|
89
|
-
return createErrorResponse(
|
|
90
|
-
error.statusCode,
|
|
91
|
-
error.message,
|
|
92
|
-
error.code,
|
|
93
|
-
error.details,
|
|
94
|
-
requestId
|
|
95
|
-
)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Handle validation errors from Elysia
|
|
99
|
-
if (error.name === 'ValidationError' || error.message.includes('validation')) {
|
|
100
|
-
return createErrorResponse(
|
|
101
|
-
400,
|
|
102
|
-
'Validation failed',
|
|
103
|
-
'VALIDATION_ERROR',
|
|
104
|
-
{ originalError: error.message },
|
|
105
|
-
requestId
|
|
106
|
-
)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Handle syntax errors (malformed JSON, etc.)
|
|
110
|
-
if (error instanceof SyntaxError) {
|
|
111
|
-
return createErrorResponse(
|
|
112
|
-
400,
|
|
113
|
-
'Invalid request format',
|
|
114
|
-
'SYNTAX_ERROR',
|
|
115
|
-
undefined,
|
|
116
|
-
requestId
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Handle network/timeout errors
|
|
121
|
-
if (error.name === 'TimeoutError' || error.message.includes('timeout')) {
|
|
122
|
-
return createErrorResponse(
|
|
123
|
-
408,
|
|
124
|
-
'Request timeout',
|
|
125
|
-
'TIMEOUT_ERROR',
|
|
126
|
-
undefined,
|
|
127
|
-
requestId
|
|
128
|
-
)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Handle database/external service errors
|
|
132
|
-
if (error.message.includes('ECONNREFUSED') || error.message.includes('connection')) {
|
|
133
|
-
return createErrorResponse(
|
|
134
|
-
503,
|
|
135
|
-
'Service temporarily unavailable',
|
|
136
|
-
'SERVICE_UNAVAILABLE',
|
|
137
|
-
undefined,
|
|
138
|
-
requestId
|
|
139
|
-
)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Default to internal server error
|
|
143
|
-
const isProduction = appConfig.env === 'production'
|
|
144
|
-
return createErrorResponse(
|
|
145
|
-
500,
|
|
146
|
-
isProduction
|
|
147
|
-
? 'Internal server error'
|
|
148
|
-
: error.message,
|
|
149
|
-
'INTERNAL_ERROR',
|
|
150
|
-
isProduction
|
|
151
|
-
? undefined
|
|
152
|
-
: { stack: error.stack },
|
|
153
|
-
requestId
|
|
154
|
-
)
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Create standardized error response
|
|
160
|
-
*/
|
|
161
|
-
function createErrorResponse(
|
|
162
|
-
statusCode: number,
|
|
163
|
-
message: string,
|
|
164
|
-
code?: string,
|
|
165
|
-
details?: any,
|
|
166
|
-
requestId?: string
|
|
167
|
-
): Response {
|
|
168
|
-
const errorResponse: ErrorResponse = {
|
|
169
|
-
error: getErrorName(statusCode),
|
|
170
|
-
message,
|
|
171
|
-
code,
|
|
172
|
-
details,
|
|
173
|
-
timestamp: new Date().toISOString(),
|
|
174
|
-
requestId
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Remove undefined fields
|
|
178
|
-
Object.keys(errorResponse).forEach(key => {
|
|
179
|
-
if (errorResponse[key as keyof ErrorResponse] === undefined) {
|
|
180
|
-
delete errorResponse[key as keyof ErrorResponse]
|
|
181
|
-
}
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
return new Response(
|
|
185
|
-
JSON.stringify(errorResponse),
|
|
186
|
-
{
|
|
187
|
-
status: statusCode,
|
|
188
|
-
headers: {
|
|
189
|
-
'Content-Type': 'application/json'
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Get error name from status code
|
|
197
|
-
*/
|
|
198
|
-
function getErrorName(statusCode: number): string {
|
|
199
|
-
const errorNames: Record<number, string> = {
|
|
200
|
-
400: 'Bad Request',
|
|
201
|
-
401: 'Unauthorized',
|
|
202
|
-
403: 'Forbidden',
|
|
203
|
-
404: 'Not Found',
|
|
204
|
-
405: 'Method Not Allowed',
|
|
205
|
-
408: 'Request Timeout',
|
|
206
|
-
409: 'Conflict',
|
|
207
|
-
422: 'Unprocessable Entity',
|
|
208
|
-
429: 'Too Many Requests',
|
|
209
|
-
500: 'Internal Server Error',
|
|
210
|
-
502: 'Bad Gateway',
|
|
211
|
-
503: 'Service Unavailable',
|
|
212
|
-
504: 'Gateway Timeout'
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return errorNames[statusCode] || 'Unknown Error'
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Async error wrapper for route handlers
|
|
220
|
-
*/
|
|
221
|
-
export const asyncHandler = (fn: Function) => {
|
|
222
|
-
return async (context: Context) => {
|
|
223
|
-
try {
|
|
224
|
-
return await fn(context)
|
|
225
|
-
} catch (error) {
|
|
226
|
-
throw error // Let the error middleware handle it
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Create error response helper
|
|
233
|
-
*/
|
|
234
|
-
export const createError = {
|
|
235
|
-
badRequest: (message: string, details?: any) =>
|
|
236
|
-
new ValidationError(message, details),
|
|
237
|
-
|
|
238
|
-
unauthorized: (message?: string) =>
|
|
239
|
-
new UnauthorizedError(message),
|
|
240
|
-
|
|
241
|
-
forbidden: (message?: string) =>
|
|
242
|
-
new ForbiddenError(message),
|
|
243
|
-
|
|
244
|
-
notFound: (resource?: string) =>
|
|
245
|
-
new NotFoundError(resource),
|
|
246
|
-
|
|
247
|
-
conflict: (message: string, details?: any) =>
|
|
248
|
-
new ConflictError(message, details),
|
|
249
|
-
|
|
250
|
-
internal: (message: string, details?: any) =>
|
|
251
|
-
new AppError(message, 500, 'INTERNAL_ERROR', details)
|
|
252
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Middleware Index
|
|
3
|
-
* Exports all custom middleware for the application
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export { authMiddleware } from './auth'
|
|
7
|
-
export { validationMiddleware } from './validation'
|
|
8
|
-
export { rateLimitMiddleware } from './rateLimit'
|
|
9
|
-
export { requestLoggingMiddleware } from './requestLogging'
|
|
10
|
-
export { errorHandlingMiddleware } from './errorHandling'
|