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
|
@@ -2,245 +2,78 @@
|
|
|
2
2
|
import { treaty } from '@elysiajs/eden'
|
|
3
3
|
import type { App } from '../../../server/app'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Get base URL dynamically
|
|
7
|
+
*/
|
|
8
|
+
export const getBaseUrl = () => {
|
|
7
9
|
if (typeof window === 'undefined') return 'http://localhost:3000'
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
+
|
|
11
|
+
// Production: use current origin
|
|
10
12
|
if (window.location.hostname !== 'localhost') {
|
|
11
13
|
return window.location.origin
|
|
12
14
|
}
|
|
13
|
-
|
|
14
|
-
// In development, use backend server (port 3000 for integrated mode)
|
|
15
|
-
return 'http://localhost:3000'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Create Eden Treaty client with proper typing
|
|
19
|
-
const client = treaty<App>(getBaseUrl())
|
|
20
|
-
|
|
21
|
-
// Export the client's API directly to get proper type inference
|
|
22
|
-
export const api = client.api
|
|
23
15
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
ClientAPIError,
|
|
27
|
-
NetworkError,
|
|
28
|
-
TimeoutError,
|
|
29
|
-
withRetry,
|
|
30
|
-
withFallback,
|
|
31
|
-
CircuitBreaker,
|
|
32
|
-
getDefaultUserMessage,
|
|
33
|
-
type RetryOptions,
|
|
34
|
-
type FallbackOptions
|
|
35
|
-
} from './errors'
|
|
36
|
-
|
|
37
|
-
// Legacy interface for backward compatibility
|
|
38
|
-
export interface APIError {
|
|
39
|
-
message: string
|
|
40
|
-
status: number
|
|
41
|
-
code?: string
|
|
42
|
-
details?: any
|
|
16
|
+
// Development: use backend server
|
|
17
|
+
return 'http://localhost:3000'
|
|
43
18
|
}
|
|
44
19
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
constructor(error: APIError) {
|
|
52
|
-
super(error.message)
|
|
53
|
-
this.name = 'APIException'
|
|
54
|
-
this.status = error.status
|
|
55
|
-
this.code = error.code
|
|
56
|
-
this.details = error.details
|
|
57
|
-
}
|
|
20
|
+
/**
|
|
21
|
+
* Get auth token from localStorage (optional)
|
|
22
|
+
*/
|
|
23
|
+
const getAuthToken = (): string | null => {
|
|
24
|
+
if (typeof window === 'undefined') return null
|
|
25
|
+
return localStorage.getItem('accessToken')
|
|
58
26
|
}
|
|
59
27
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
export
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// Add timeout to the API call
|
|
77
|
-
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
78
|
-
setTimeout(() => {
|
|
79
|
-
reject(new TimeoutError(timeout))
|
|
80
|
-
}, timeout)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
const result = await Promise.race([apiPromise, timeoutPromise])
|
|
85
|
-
const { data, error } = result
|
|
86
|
-
|
|
87
|
-
if (error) {
|
|
88
|
-
const correlationId = error.value?.correlationId
|
|
89
|
-
|
|
90
|
-
throw new ClientAPIError(
|
|
91
|
-
error.value?.message || 'API Error',
|
|
92
|
-
error.value?.code || 'API_ERROR',
|
|
93
|
-
error.status,
|
|
94
|
-
error.value?.details || error.value,
|
|
95
|
-
correlationId,
|
|
96
|
-
error.value?.userMessage
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return data
|
|
101
|
-
} catch (error) {
|
|
102
|
-
// Handle different types of errors
|
|
103
|
-
if (error instanceof ClientAPIError || error instanceof TimeoutError) {
|
|
104
|
-
throw error
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (error instanceof Error) {
|
|
108
|
-
// Check if it's a network error
|
|
109
|
-
if (error.message.includes('fetch') || error.message.includes('network')) {
|
|
110
|
-
throw new NetworkError(error.message)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
throw new ClientAPIError(
|
|
114
|
-
error.message,
|
|
115
|
-
'NETWORK_ERROR',
|
|
116
|
-
0,
|
|
117
|
-
undefined,
|
|
118
|
-
undefined,
|
|
119
|
-
'Unable to connect to the server. Please check your connection.'
|
|
120
|
-
)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
throw new ClientAPIError(
|
|
124
|
-
'Unknown error occurred',
|
|
125
|
-
'UNKNOWN_ERROR',
|
|
126
|
-
500,
|
|
127
|
-
error
|
|
128
|
-
)
|
|
28
|
+
/**
|
|
29
|
+
* Create Eden Treaty client with authentication and logging
|
|
30
|
+
* Based on official ElysiaJS Chan example
|
|
31
|
+
*/
|
|
32
|
+
export const api = treaty<App>(getBaseUrl(), {
|
|
33
|
+
// Dynamic headers for every request
|
|
34
|
+
headers(_path, _options) {
|
|
35
|
+
const token = getAuthToken()
|
|
36
|
+
return token ? { Authorization: `Bearer ${token}` } : undefined
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// Custom fetcher with logging and error handling
|
|
40
|
+
// Use 'as any' to bypass Bun's strict fetch type (includes preconnect property)
|
|
41
|
+
fetcher: (async (url, init) => {
|
|
42
|
+
if (import.meta.env.DEV) {
|
|
43
|
+
console.log(`🌐 ${init?.method ?? 'GET'} ${url}`)
|
|
129
44
|
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Wrap with circuit breaker if enabled
|
|
133
|
-
const callWithCircuitBreaker = useCircuitBreaker
|
|
134
|
-
? () => apiCircuitBreaker.execute(executeCall)
|
|
135
|
-
: executeCall
|
|
136
|
-
|
|
137
|
-
// Apply retry logic if specified
|
|
138
|
-
const callWithRetry = retry
|
|
139
|
-
? () => withRetry(callWithCircuitBreaker, retry)
|
|
140
|
-
: callWithCircuitBreaker
|
|
141
|
-
|
|
142
|
-
// Apply fallback if specified
|
|
143
|
-
if (fallback) {
|
|
144
|
-
return withFallback(callWithRetry, fallback)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return callWithRetry()
|
|
148
|
-
}
|
|
149
45
|
|
|
150
|
-
|
|
151
|
-
export async function simpleApiCall(apiPromise: Promise<any>) {
|
|
152
|
-
return apiCall(apiPromise)
|
|
153
|
-
}
|
|
46
|
+
const res = await fetch(url, init)
|
|
154
47
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
retry: {
|
|
159
|
-
maxRetries: 5,
|
|
160
|
-
baseDelay: 2000,
|
|
161
|
-
maxDelay: 30000
|
|
162
|
-
},
|
|
163
|
-
timeout: 60000,
|
|
164
|
-
useCircuitBreaker: true
|
|
165
|
-
})
|
|
166
|
-
}
|
|
48
|
+
if (import.meta.env.DEV) {
|
|
49
|
+
console.log(`📡 ${url} → ${res.status}`)
|
|
50
|
+
}
|
|
167
51
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
maxRetries: 2,
|
|
175
|
-
baseDelay: 1000
|
|
176
|
-
},
|
|
177
|
-
fallback: {
|
|
178
|
-
fallbackValue,
|
|
179
|
-
showFallbackMessage: false
|
|
180
|
-
},
|
|
181
|
-
timeout: 15000,
|
|
182
|
-
useCircuitBreaker: false
|
|
183
|
-
})
|
|
184
|
-
}
|
|
52
|
+
// Auto-logout on 401
|
|
53
|
+
if (res.status === 401) {
|
|
54
|
+
console.warn('🔒 Token expired')
|
|
55
|
+
localStorage.removeItem('accessToken')
|
|
56
|
+
// window.location.href = '/login' // Uncomment if you have auth
|
|
57
|
+
}
|
|
185
58
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
maxRetries: 3,
|
|
190
|
-
baseDelay: 1000,
|
|
191
|
-
retryableStatusCodes: [408, 429, 500, 502, 503, 504]
|
|
192
|
-
},
|
|
193
|
-
timeout: 30000,
|
|
194
|
-
useCircuitBreaker: true
|
|
195
|
-
})
|
|
196
|
-
}
|
|
59
|
+
return res
|
|
60
|
+
}) as typeof fetch
|
|
61
|
+
}).api // ← expose the generated API object
|
|
197
62
|
|
|
198
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Get user-friendly error message
|
|
65
|
+
*/
|
|
199
66
|
export function getErrorMessage(error: unknown): string {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (error instanceof APIException) {
|
|
205
|
-
return getDefaultUserMessage(error.code || 'UNKNOWN_ERROR', error.status)
|
|
67
|
+
// Eden Treaty error format
|
|
68
|
+
if (error && typeof error === 'object' && 'value' in error) {
|
|
69
|
+
const edenError = error as { value?: { message?: string; userMessage?: string } }
|
|
70
|
+
return edenError.value?.userMessage || edenError.value?.message || 'An error occurred'
|
|
206
71
|
}
|
|
207
|
-
|
|
72
|
+
|
|
73
|
+
// Standard Error
|
|
208
74
|
if (error instanceof Error) {
|
|
209
75
|
return error.message
|
|
210
76
|
}
|
|
211
|
-
|
|
212
|
-
return 'An unexpected error occurred'
|
|
213
|
-
}
|
|
214
77
|
|
|
215
|
-
|
|
216
|
-
export function isRetryableError(error: unknown): boolean {
|
|
217
|
-
if (error instanceof ClientAPIError) {
|
|
218
|
-
return error.isRetryable
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (error instanceof APIException) {
|
|
222
|
-
const retryableStatusCodes = [408, 429, 500, 502, 503, 504]
|
|
223
|
-
return retryableStatusCodes.includes(error.status)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return false
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export function shouldShowErrorToUser(error: unknown): boolean {
|
|
230
|
-
if (error instanceof ClientAPIError) {
|
|
231
|
-
// Don't show technical errors to users
|
|
232
|
-
const technicalCodes = ['DATABASE_ERROR', 'EXTERNAL_SERVICE_ERROR', 'INTERNAL_SERVER_ERROR']
|
|
233
|
-
return !technicalCodes.includes(error.code)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return true
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Circuit breaker utilities
|
|
240
|
-
export function getCircuitBreakerState(): string {
|
|
241
|
-
return apiCircuitBreaker.getState()
|
|
78
|
+
return 'An unexpected error occurred'
|
|
242
79
|
}
|
|
243
|
-
|
|
244
|
-
export function resetCircuitBreaker(): void {
|
|
245
|
-
apiCircuitBreaker.reset()
|
|
246
|
-
}
|
package/app/client/src/main.tsx
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { StrictMode } from 'react'
|
|
2
2
|
import { createRoot } from 'react-dom/client'
|
|
3
|
-
import { BrowserRouter } from 'react-router-dom'
|
|
4
3
|
import './index.css'
|
|
5
4
|
import App from './App.tsx'
|
|
6
5
|
|
|
7
6
|
createRoot(document.getElementById('root')!).render(
|
|
8
|
-
<
|
|
7
|
+
<StrictMode>
|
|
9
8
|
<App />
|
|
10
|
-
</
|
|
9
|
+
</StrictMode>,
|
|
11
10
|
)
|
package/app/server/app.ts
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 🎯 Application Instance - Single Source of Truth
|
|
3
|
+
*
|
|
4
|
+
* This instance is used by:
|
|
5
|
+
* - index.ts (full-stack mode)
|
|
6
|
+
* - backend-only.ts (backend standalone mode)
|
|
7
|
+
* - Eden Treaty client (type inference)
|
|
8
|
+
*
|
|
9
|
+
* This ensures that the type exported for Eden Treaty is exactly
|
|
10
|
+
* the same as what the server uses.
|
|
11
|
+
*/
|
|
12
|
+
|
|
3
13
|
import { Elysia } from "elysia"
|
|
14
|
+
import { apiRoutes } from "./routes"
|
|
15
|
+
import { envTestRoute } from "./routes/env-test"
|
|
4
16
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Main application instance with all routes registered
|
|
19
|
+
*/
|
|
20
|
+
export const appInstance = new Elysia()
|
|
21
|
+
.use(envTestRoute) // Environment test/debug endpoint
|
|
22
|
+
.use(apiRoutes) // Main application routes
|
|
8
23
|
|
|
9
24
|
// Export the type correctly for Eden Treaty
|
|
10
25
|
export type App = typeof appInstance
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Backend Standalone Entry Point
|
|
3
|
+
*
|
|
4
|
+
* This is a minimal wrapper for starting the backend in standalone mode.
|
|
5
|
+
* The core logic is protected in @/core/server/backend-entry.ts
|
|
6
|
+
*
|
|
7
|
+
* You can customize the configuration here if needed.
|
|
8
|
+
*/
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
apiPrefix: serverConfig.apiPrefix
|
|
10
|
-
}
|
|
10
|
+
import { startBackend, createBackendConfig } from "@/core/server/backend-entry"
|
|
11
|
+
import { appInstance } from "./app"
|
|
12
|
+
import { serverConfig } from "@/config/server.config"
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
// Create backend configuration from declarative config
|
|
15
|
+
const backendConfig = createBackendConfig(serverConfig)
|
|
13
16
|
|
|
14
|
-
//
|
|
15
|
-
|
|
17
|
+
// Start backend in standalone mode
|
|
18
|
+
startBackend(appInstance, backendConfig)
|
|
@@ -1,37 +1,56 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CreateUserRequest } from '@/app/shared/types'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
// User type
|
|
4
|
+
export interface User {
|
|
5
|
+
id: number
|
|
6
|
+
name: string
|
|
7
|
+
email: string
|
|
8
|
+
createdAt: Date
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// In-memory storage for users (for testing purposes)
|
|
12
|
+
let users: User[] = []
|
|
13
|
+
let nextId = 1
|
|
7
14
|
|
|
8
15
|
export class UsersController {
|
|
16
|
+
/**
|
|
17
|
+
* Get all users
|
|
18
|
+
*/
|
|
9
19
|
static async getUsers() {
|
|
10
|
-
return {
|
|
20
|
+
return {
|
|
21
|
+
users
|
|
22
|
+
}
|
|
11
23
|
}
|
|
12
24
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
25
|
+
/**
|
|
26
|
+
* Get user by ID
|
|
27
|
+
*/
|
|
28
|
+
static async getUserById(id: number) {
|
|
29
|
+
const user = users.find(u => u.id === id)
|
|
30
|
+
if (!user) {
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
return { user }
|
|
19
34
|
}
|
|
20
35
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Create a new user
|
|
38
|
+
*/
|
|
39
|
+
static async createUser(data: CreateUserRequest) {
|
|
40
|
+
// Check for duplicate email
|
|
41
|
+
const existingUser = users.find(u => u.email === data.email)
|
|
24
42
|
if (existingUser) {
|
|
25
43
|
return {
|
|
26
44
|
success: false,
|
|
27
|
-
message:
|
|
45
|
+
message: 'Email já está em uso'
|
|
28
46
|
}
|
|
29
47
|
}
|
|
30
48
|
|
|
49
|
+
// Create new user
|
|
31
50
|
const newUser: User = {
|
|
32
|
-
id:
|
|
33
|
-
name:
|
|
34
|
-
email:
|
|
51
|
+
id: nextId++,
|
|
52
|
+
name: data.name,
|
|
53
|
+
email: data.email,
|
|
35
54
|
createdAt: new Date()
|
|
36
55
|
}
|
|
37
56
|
|
|
@@ -43,27 +62,34 @@ export class UsersController {
|
|
|
43
62
|
}
|
|
44
63
|
}
|
|
45
64
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
static async deleteUser(id: number): Promise<UserResponse> {
|
|
65
|
+
/**
|
|
66
|
+
* Delete user by ID
|
|
67
|
+
*/
|
|
68
|
+
static async deleteUser(id: number) {
|
|
52
69
|
const userIndex = users.findIndex(u => u.id === id)
|
|
53
|
-
|
|
70
|
+
|
|
54
71
|
if (userIndex === -1) {
|
|
55
72
|
return {
|
|
56
73
|
success: false,
|
|
57
|
-
message:
|
|
74
|
+
message: 'Usuário não encontrado'
|
|
58
75
|
}
|
|
59
76
|
}
|
|
60
77
|
|
|
61
|
-
const deletedUser = users
|
|
62
|
-
|
|
78
|
+
const deletedUser = users[userIndex]
|
|
79
|
+
users.splice(userIndex, 1)
|
|
80
|
+
|
|
63
81
|
return {
|
|
64
82
|
success: true,
|
|
65
83
|
user: deletedUser,
|
|
66
|
-
message:
|
|
84
|
+
message: 'Usuário deletado com sucesso'
|
|
67
85
|
}
|
|
68
86
|
}
|
|
69
|
-
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Reset users array for testing purposes
|
|
90
|
+
*/
|
|
91
|
+
static resetForTesting() {
|
|
92
|
+
users = []
|
|
93
|
+
nextId = 1
|
|
94
|
+
}
|
|
95
|
+
}
|