create-fluxstack 1.10.1 → 1.12.1
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 +161 -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 +127 -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
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// LiveUpload - Estado de upload chunked + sincronização UI
|
|
2
|
+
|
|
3
|
+
import { LiveComponent } from '@core/types/types'
|
|
4
|
+
|
|
5
|
+
// Componente Cliente (Ctrl+Click para navegar)
|
|
6
|
+
import type { UploadDemo as _Client } from '@client/src/live/UploadDemo'
|
|
7
|
+
|
|
8
|
+
export class LiveUpload extends LiveComponent<typeof LiveUpload.defaultState> {
|
|
9
|
+
static componentName = 'LiveUpload'
|
|
10
|
+
static defaultState = {
|
|
11
|
+
status: 'idle' as 'idle' | 'uploading' | 'complete' | 'error',
|
|
12
|
+
progress: 0,
|
|
13
|
+
fileName: '',
|
|
14
|
+
fileSize: 0,
|
|
15
|
+
fileType: '',
|
|
16
|
+
fileUrl: '',
|
|
17
|
+
bytesUploaded: 0,
|
|
18
|
+
totalBytes: 0,
|
|
19
|
+
error: null as string | null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async startUpload(payload: { fileName: string; fileSize: number; fileType: string }) {
|
|
23
|
+
const normalized = payload.fileName.toLowerCase()
|
|
24
|
+
if (normalized.includes('..') || normalized.includes('/') || normalized.includes('\\')) {
|
|
25
|
+
throw new Error('Invalid file name')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// All file types allowed - no extension blocking
|
|
29
|
+
// Security note: Configure allowed extensions per your application needs
|
|
30
|
+
|
|
31
|
+
this.setState({
|
|
32
|
+
status: 'uploading',
|
|
33
|
+
progress: 0,
|
|
34
|
+
fileName: payload.fileName,
|
|
35
|
+
fileSize: payload.fileSize,
|
|
36
|
+
fileType: payload.fileType,
|
|
37
|
+
fileUrl: '',
|
|
38
|
+
bytesUploaded: 0,
|
|
39
|
+
totalBytes: payload.fileSize,
|
|
40
|
+
error: null
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
return { success: true }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async updateProgress(payload: { progress: number; bytesUploaded: number; totalBytes: number }) {
|
|
47
|
+
const progress = Math.max(0, Math.min(100, payload.progress))
|
|
48
|
+
this.setState({
|
|
49
|
+
progress,
|
|
50
|
+
bytesUploaded: payload.bytesUploaded,
|
|
51
|
+
totalBytes: payload.totalBytes
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return { success: true, progress }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async completeUpload(payload: { fileUrl: string }) {
|
|
58
|
+
this.setState({
|
|
59
|
+
status: 'complete',
|
|
60
|
+
progress: 100,
|
|
61
|
+
fileUrl: payload.fileUrl,
|
|
62
|
+
error: null
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return { success: true }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async failUpload(payload: { error: string }) {
|
|
69
|
+
this.setState({
|
|
70
|
+
status: 'error',
|
|
71
|
+
error: payload.error || 'Upload failed'
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
return { success: true }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async reset() {
|
|
78
|
+
this.setState({ ...LiveUpload.defaultState })
|
|
79
|
+
return { success: true }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Elysia, t } from "elysia"
|
|
2
2
|
import { usersRoutes } from "./users.routes"
|
|
3
|
+
import { roomRoutes } from "./room.routes"
|
|
3
4
|
|
|
4
5
|
export const apiRoutes = new Elysia({ prefix: "/api" })
|
|
5
6
|
.get("/", () => ({ message: "🔥 Hot Reload funcionando! FluxStack API v1.4.0 ⚡" }), {
|
|
@@ -32,5 +33,6 @@ export const apiRoutes = new Elysia({ prefix: "/api" })
|
|
|
32
33
|
description: 'Returns the current health status of the API server'
|
|
33
34
|
}
|
|
34
35
|
})
|
|
35
|
-
// Register
|
|
36
|
+
// Register routes
|
|
36
37
|
.use(usersRoutes)
|
|
38
|
+
.use(roomRoutes)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// 🔥 Room API Routes - Enviar mensagens para salas via HTTP
|
|
2
|
+
//
|
|
3
|
+
// Permite que sistemas externos (webhooks, outros serviços, etc)
|
|
4
|
+
// enviem mensagens para salas de chat via API REST
|
|
5
|
+
|
|
6
|
+
import { Elysia, t } from 'elysia'
|
|
7
|
+
import { liveRoomManager } from '@core/server/live/LiveRoomManager'
|
|
8
|
+
import { roomEvents } from '@core/server/live/RoomEventBus'
|
|
9
|
+
|
|
10
|
+
export const roomRoutes = new Elysia({ prefix: '/rooms' })
|
|
11
|
+
|
|
12
|
+
// Enviar mensagem para uma sala
|
|
13
|
+
.post('/:roomId/messages', ({ params, body }) => {
|
|
14
|
+
const { roomId } = params
|
|
15
|
+
const { user, text } = body
|
|
16
|
+
|
|
17
|
+
const message = {
|
|
18
|
+
id: `api-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
19
|
+
user: user || 'API Bot',
|
|
20
|
+
text,
|
|
21
|
+
timestamp: Date.now()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Emitir evento para a sala
|
|
25
|
+
// Isso vai:
|
|
26
|
+
// 1. Notificar handlers server-side via roomEvents
|
|
27
|
+
// 2. Broadcast via WebSocket para frontends
|
|
28
|
+
const notified = liveRoomManager.emitToRoom(roomId, 'message:new', message)
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
success: true,
|
|
32
|
+
message,
|
|
33
|
+
notified,
|
|
34
|
+
roomId
|
|
35
|
+
}
|
|
36
|
+
}, {
|
|
37
|
+
params: t.Object({
|
|
38
|
+
roomId: t.String({ description: 'ID da sala (ex: geral, tech, random)' })
|
|
39
|
+
}),
|
|
40
|
+
body: t.Object({
|
|
41
|
+
user: t.Optional(t.String({ description: 'Nome do usuário (opcional, default: API Bot)' })),
|
|
42
|
+
text: t.String({ description: 'Texto da mensagem', minLength: 1, maxLength: 1000 })
|
|
43
|
+
}),
|
|
44
|
+
response: t.Object({
|
|
45
|
+
success: t.Boolean(),
|
|
46
|
+
message: t.Object({
|
|
47
|
+
id: t.String(),
|
|
48
|
+
user: t.String(),
|
|
49
|
+
text: t.String(),
|
|
50
|
+
timestamp: t.Number()
|
|
51
|
+
}),
|
|
52
|
+
notified: t.Number(),
|
|
53
|
+
roomId: t.String()
|
|
54
|
+
}),
|
|
55
|
+
detail: {
|
|
56
|
+
tags: ['Rooms'],
|
|
57
|
+
summary: 'Enviar mensagem para sala',
|
|
58
|
+
description: 'Envia uma mensagem para todos os usuários conectados em uma sala específica'
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// Emitir evento customizado para uma sala
|
|
63
|
+
.post('/:roomId/emit', ({ params, body }) => {
|
|
64
|
+
const { roomId } = params
|
|
65
|
+
const { event, data } = body
|
|
66
|
+
|
|
67
|
+
const notified = liveRoomManager.emitToRoom(roomId, event, data)
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
success: true,
|
|
71
|
+
event,
|
|
72
|
+
notified,
|
|
73
|
+
roomId
|
|
74
|
+
}
|
|
75
|
+
}, {
|
|
76
|
+
params: t.Object({
|
|
77
|
+
roomId: t.String({ description: 'ID da sala' })
|
|
78
|
+
}),
|
|
79
|
+
body: t.Object({
|
|
80
|
+
event: t.String({ description: 'Nome do evento (ex: user:typing, notification)' }),
|
|
81
|
+
data: t.Any({ description: 'Dados do evento' })
|
|
82
|
+
}),
|
|
83
|
+
response: t.Object({
|
|
84
|
+
success: t.Boolean(),
|
|
85
|
+
event: t.String(),
|
|
86
|
+
notified: t.Number(),
|
|
87
|
+
roomId: t.String()
|
|
88
|
+
}),
|
|
89
|
+
detail: {
|
|
90
|
+
tags: ['Rooms'],
|
|
91
|
+
summary: 'Emitir evento customizado',
|
|
92
|
+
description: 'Emite um evento customizado para todos os componentes em uma sala'
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// Obter estatísticas das salas
|
|
97
|
+
.get('/stats', () => {
|
|
98
|
+
const roomStats = liveRoomManager.getStats()
|
|
99
|
+
const eventStats = roomEvents.getStats()
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
rooms: roomStats,
|
|
104
|
+
events: eventStats
|
|
105
|
+
}
|
|
106
|
+
}, {
|
|
107
|
+
response: t.Object({
|
|
108
|
+
success: t.Boolean(),
|
|
109
|
+
rooms: t.Any(),
|
|
110
|
+
events: t.Any()
|
|
111
|
+
}),
|
|
112
|
+
detail: {
|
|
113
|
+
tags: ['Rooms'],
|
|
114
|
+
summary: 'Estatísticas das salas',
|
|
115
|
+
description: 'Retorna estatísticas sobre salas ativas e eventos registrados'
|
|
116
|
+
}
|
|
117
|
+
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Elysia, t } from 'elysia'
|
|
2
|
-
import { UsersController } from '
|
|
2
|
+
import { UsersController } from '@app/server/controllers/users.controller'
|
|
3
|
+
import type { CreateUserRequest } from '@app/shared/types'
|
|
3
4
|
|
|
4
5
|
// ===== Request/Response Schemas =====
|
|
5
6
|
|
|
@@ -77,9 +78,7 @@ const ErrorResponseSchema = t.Object({
|
|
|
77
78
|
*/
|
|
78
79
|
export const usersRoutes = new Elysia({ prefix: '/users', tags: ['Users'] })
|
|
79
80
|
// GET /users - Get all users
|
|
80
|
-
.get('/', async () => {
|
|
81
|
-
return await UsersController.getUsers()
|
|
82
|
-
}, {
|
|
81
|
+
.get('/', async () => UsersController.getUsers(), {
|
|
83
82
|
detail: {
|
|
84
83
|
summary: 'Get All Users',
|
|
85
84
|
description: 'Retrieves a list of all registered users',
|
|
@@ -90,19 +89,18 @@ export const usersRoutes = new Elysia({ prefix: '/users', tags: ['Users'] })
|
|
|
90
89
|
|
|
91
90
|
// GET /users/:id - Get user by ID
|
|
92
91
|
.get('/:id', async ({ params, set }) => {
|
|
93
|
-
const id =
|
|
92
|
+
const id = Number(params.id)
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
if (isNaN(id)) {
|
|
94
|
+
if (!Number.isFinite(id)) {
|
|
97
95
|
set.status = 400
|
|
98
|
-
return { error: 'ID
|
|
96
|
+
return { success: false, error: 'ID invalido' }
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
const result = await UsersController.getUserById(id)
|
|
102
100
|
|
|
103
|
-
if (!result) {
|
|
101
|
+
if (!result.success) {
|
|
104
102
|
set.status = 404
|
|
105
|
-
return
|
|
103
|
+
return result
|
|
106
104
|
}
|
|
107
105
|
|
|
108
106
|
return result
|
|
@@ -124,17 +122,17 @@ export const usersRoutes = new Elysia({ prefix: '/users', tags: ['Users'] })
|
|
|
124
122
|
|
|
125
123
|
// POST /users - Create new user
|
|
126
124
|
.post('/', async ({ body, set }) => {
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
const payload = body as CreateUserRequest
|
|
126
|
+
|
|
127
|
+
if (!payload.name || !payload.email) {
|
|
129
128
|
set.status = 400
|
|
130
129
|
return {
|
|
131
130
|
success: false,
|
|
132
|
-
error: 'Nome e email
|
|
131
|
+
error: 'Nome e email sao obrigatorios'
|
|
133
132
|
}
|
|
134
133
|
}
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
if (body.name.length < 2) {
|
|
135
|
+
if (payload.name.trim().length < 2) {
|
|
138
136
|
set.status = 400
|
|
139
137
|
return {
|
|
140
138
|
success: false,
|
|
@@ -142,23 +140,28 @@ export const usersRoutes = new Elysia({ prefix: '/users', tags: ['Users'] })
|
|
|
142
140
|
}
|
|
143
141
|
}
|
|
144
142
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (!emailRegex.test(body.email)) {
|
|
143
|
+
const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/
|
|
144
|
+
if (!emailRegex.test(payload.email)) {
|
|
148
145
|
set.status = 400
|
|
149
146
|
return {
|
|
150
147
|
success: false,
|
|
151
|
-
error: 'Email
|
|
148
|
+
error: 'Email invalido'
|
|
152
149
|
}
|
|
153
150
|
}
|
|
154
151
|
|
|
155
|
-
const
|
|
152
|
+
const sanitizedPayload: CreateUserRequest = {
|
|
153
|
+
name: payload.name.trim(),
|
|
154
|
+
email: payload.email.trim()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const result = await UsersController.createUser(sanitizedPayload)
|
|
156
158
|
|
|
157
|
-
// If email is duplicate, still return 200 but with success: false
|
|
158
159
|
if (!result.success) {
|
|
160
|
+
set.status = 409
|
|
159
161
|
return result
|
|
160
162
|
}
|
|
161
163
|
|
|
164
|
+
set.status = 201
|
|
162
165
|
return result
|
|
163
166
|
}, {
|
|
164
167
|
detail: {
|
|
@@ -168,30 +171,34 @@ export const usersRoutes = new Elysia({ prefix: '/users', tags: ['Users'] })
|
|
|
168
171
|
},
|
|
169
172
|
body: CreateUserRequestSchema,
|
|
170
173
|
response: {
|
|
171
|
-
|
|
174
|
+
201: CreateUserResponseSchema,
|
|
172
175
|
400: t.Object({
|
|
173
176
|
success: t.Literal(false),
|
|
174
177
|
error: t.String()
|
|
178
|
+
}),
|
|
179
|
+
409: t.Object({
|
|
180
|
+
success: t.Literal(false),
|
|
181
|
+
error: t.String()
|
|
175
182
|
})
|
|
176
183
|
}
|
|
177
184
|
})
|
|
178
185
|
|
|
179
186
|
// DELETE /users/:id - Delete user
|
|
180
187
|
.delete('/:id', async ({ params, set }) => {
|
|
181
|
-
const id =
|
|
188
|
+
const id = Number(params.id)
|
|
182
189
|
|
|
183
|
-
if (
|
|
190
|
+
if (!Number.isFinite(id)) {
|
|
184
191
|
set.status = 400
|
|
185
192
|
return {
|
|
186
193
|
success: false,
|
|
187
|
-
message: 'ID
|
|
194
|
+
message: 'ID invalido'
|
|
188
195
|
}
|
|
189
196
|
}
|
|
190
197
|
|
|
191
198
|
const result = await UsersController.deleteUser(id)
|
|
192
199
|
|
|
193
200
|
if (!result.success) {
|
|
194
|
-
|
|
201
|
+
set.status = 404
|
|
195
202
|
return result
|
|
196
203
|
}
|
|
197
204
|
|
|
@@ -210,6 +217,7 @@ export const usersRoutes = new Elysia({ prefix: '/users', tags: ['Users'] })
|
|
|
210
217
|
400: t.Object({
|
|
211
218
|
success: t.Literal(false),
|
|
212
219
|
message: t.String()
|
|
213
|
-
})
|
|
220
|
+
}),
|
|
221
|
+
404: DeleteUserResponseSchema
|
|
214
222
|
}
|
|
215
223
|
})
|
|
@@ -11,8 +11,20 @@ export interface CreateUserRequest {
|
|
|
11
11
|
email: string
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export interface
|
|
14
|
+
export interface UserListResponse {
|
|
15
|
+
success: true
|
|
16
|
+
users: User[]
|
|
17
|
+
count: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface UserDetailResponse {
|
|
15
21
|
success: boolean
|
|
16
22
|
user?: User
|
|
23
|
+
error?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface MutationResponse {
|
|
27
|
+
success: boolean
|
|
17
28
|
message?: string
|
|
18
|
-
|
|
29
|
+
error?: string
|
|
30
|
+
}
|
package/config/app.config.ts
CHANGED
|
@@ -1,62 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Core application metadata and global settings
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { defineConfig, config } from '@/core/utils/config-schema'
|
|
7
|
-
import { FLUXSTACK_VERSION } from '@/core/utils/version'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* App configuration schema
|
|
11
|
-
* Contains only app-level metadata and global feature flags
|
|
12
|
-
*/
|
|
13
|
-
const appConfigSchema = {
|
|
14
|
-
// App basics
|
|
15
|
-
name: config.string('APP_NAME', 'FluxStack', true),
|
|
16
|
-
|
|
17
|
-
version: {
|
|
18
|
-
type: 'string' as const,
|
|
19
|
-
env: 'APP_VERSION',
|
|
20
|
-
default: FLUXSTACK_VERSION,
|
|
21
|
-
validate: (value: string) => /^\d+\.\d+\.\d+$/.test(value) || 'Version must be semver format (e.g., 1.0.0)'
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
description: config.string('APP_DESCRIPTION', 'A FluxStack application'),
|
|
25
|
-
|
|
26
|
-
// Environment
|
|
27
|
-
env: config.enum('NODE_ENV', ['development', 'production', 'test'] as const, 'development', true),
|
|
28
|
-
|
|
29
|
-
// URLs
|
|
30
|
-
url: config.string('APP_URL', undefined, false),
|
|
31
|
-
|
|
32
|
-
// Security
|
|
33
|
-
trustProxy: config.boolean('TRUST_PROXY', false),
|
|
34
|
-
|
|
35
|
-
sessionSecret: {
|
|
36
|
-
type: 'string' as const,
|
|
37
|
-
env: 'SESSION_SECRET',
|
|
38
|
-
default: undefined,
|
|
39
|
-
required: false,
|
|
40
|
-
validate: (value: string) => {
|
|
41
|
-
if (!value) return true // Optional
|
|
42
|
-
if (value.length < 32) {
|
|
43
|
-
return 'Session secret must be at least 32 characters'
|
|
44
|
-
}
|
|
45
|
-
return true
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
} as const
|
|
49
|
-
|
|
50
|
-
export const appConfig = defineConfig(appConfigSchema)
|
|
51
|
-
|
|
52
|
-
// Export type for use in other files
|
|
53
|
-
export type AppConfig = typeof appConfig
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Type-safe environment type
|
|
57
|
-
* Use this when you need the literal type explicitly
|
|
58
|
-
*/
|
|
59
|
-
export type Environment = typeof appConfig.env
|
|
60
|
-
|
|
61
|
-
// Export default
|
|
62
|
-
export default appConfig
|
|
1
|
+
export { appConfig as default, appConfig } from './system/app.config'
|
|
2
|
+
export type { AppConfig } from './system/app.config'
|
package/config/client.config.ts
CHANGED
|
@@ -1,95 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Declarative client, proxy and Vite dev server configuration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { defineConfig, defineNestedConfig, config } from '@/core/utils/config-schema'
|
|
7
|
-
import { env, helpers } from '@/core/utils/env'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Vite Dev Server Configuration
|
|
11
|
-
*/
|
|
12
|
-
const viteSchema = {
|
|
13
|
-
port: config.number('VITE_PORT', 5173, true),
|
|
14
|
-
|
|
15
|
-
host: config.string('VITE_HOST', 'localhost'),
|
|
16
|
-
|
|
17
|
-
strictPort: config.boolean('VITE_STRICT_PORT', false),
|
|
18
|
-
|
|
19
|
-
open: config.boolean('VITE_OPEN', false),
|
|
20
|
-
|
|
21
|
-
enableLogging: config.boolean('ENABLE_VITE_PROXY_LOGS', false)
|
|
22
|
-
} as const
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* API Proxy Configuration
|
|
26
|
-
*/
|
|
27
|
-
const proxySchema = {
|
|
28
|
-
target: {
|
|
29
|
-
type: 'string' as const,
|
|
30
|
-
env: 'PROXY_TARGET',
|
|
31
|
-
default: helpers.getServerUrl(),
|
|
32
|
-
required: false,
|
|
33
|
-
validate: (value: string) => {
|
|
34
|
-
if (!value) return true
|
|
35
|
-
try {
|
|
36
|
-
new URL(value)
|
|
37
|
-
return true
|
|
38
|
-
} catch {
|
|
39
|
-
return 'Proxy target must be a valid URL'
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
changeOrigin: config.boolean('PROXY_CHANGE_ORIGIN', true),
|
|
45
|
-
|
|
46
|
-
secure: config.boolean('PROXY_SECURE', false),
|
|
47
|
-
|
|
48
|
-
ws: config.boolean('PROXY_WS', true), // WebSocket support
|
|
49
|
-
|
|
50
|
-
rewrite: {
|
|
51
|
-
type: 'object' as const,
|
|
52
|
-
env: 'PROXY_REWRITE',
|
|
53
|
-
default: {},
|
|
54
|
-
required: false
|
|
55
|
-
}
|
|
56
|
-
} as const
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Client Build Configuration
|
|
60
|
-
*/
|
|
61
|
-
const buildSchema = {
|
|
62
|
-
outDir: config.string('CLIENT_OUTDIR', 'dist/client'),
|
|
63
|
-
|
|
64
|
-
sourceMaps: config.boolean('CLIENT_SOURCEMAPS', helpers.isDevelopment()),
|
|
65
|
-
|
|
66
|
-
minify: config.boolean('CLIENT_MINIFY', helpers.isProduction()),
|
|
67
|
-
|
|
68
|
-
target: config.string('CLIENT_TARGET', 'esnext'),
|
|
69
|
-
|
|
70
|
-
assetsDir: config.string('CLIENT_ASSETS_DIR', 'assets'),
|
|
71
|
-
|
|
72
|
-
cssCodeSplit: config.boolean('CLIENT_CSS_CODE_SPLIT', true),
|
|
73
|
-
|
|
74
|
-
chunkSizeWarningLimit: config.number('CLIENT_CHUNK_SIZE_WARNING', 500), // KB
|
|
75
|
-
|
|
76
|
-
emptyOutDir: config.boolean('CLIENT_EMPTY_OUTDIR', true)
|
|
77
|
-
} as const
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Client Configuration (nested)
|
|
81
|
-
*/
|
|
82
|
-
export const clientConfig = defineNestedConfig({
|
|
83
|
-
vite: viteSchema,
|
|
84
|
-
proxy: proxySchema,
|
|
85
|
-
build: buildSchema
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
// Export types
|
|
89
|
-
export type ViteConfig = typeof clientConfig.vite
|
|
90
|
-
export type ProxyConfig = typeof clientConfig.proxy
|
|
91
|
-
export type ClientBuildConfig = typeof clientConfig.build
|
|
92
|
-
export type ClientConfig = typeof clientConfig
|
|
93
|
-
|
|
94
|
-
// Export default
|
|
95
|
-
export default clientConfig
|
|
1
|
+
export { clientConfig as default, clientConfig } from './system/client.config'
|
|
2
|
+
export type { ClientConfig, ViteConfig, ClientBuildConfig } from './system/client.config'
|
|
@@ -1,99 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Laravel-style declarative config with validation
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { defineConfig, config } from '@/core/utils/config-schema'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Database configuration schema
|
|
10
|
-
*/
|
|
11
|
-
export const databaseConfig = defineConfig({
|
|
12
|
-
// Connection
|
|
13
|
-
url: {
|
|
14
|
-
type: 'string',
|
|
15
|
-
env: 'DATABASE_URL',
|
|
16
|
-
default: undefined,
|
|
17
|
-
required: false,
|
|
18
|
-
validate: (value) => {
|
|
19
|
-
if (!value) return true // Optional
|
|
20
|
-
if (!value.includes('://')) {
|
|
21
|
-
return 'DATABASE_URL must be a valid connection string (e.g., postgres://...)'
|
|
22
|
-
}
|
|
23
|
-
return true
|
|
24
|
-
},
|
|
25
|
-
description: 'Full database connection URL (overrides individual settings)'
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
host: config.string('DB_HOST', 'localhost'),
|
|
29
|
-
|
|
30
|
-
port: config.number('DB_PORT', 5432),
|
|
31
|
-
|
|
32
|
-
database: {
|
|
33
|
-
type: 'string',
|
|
34
|
-
env: 'DB_NAME',
|
|
35
|
-
default: undefined,
|
|
36
|
-
required: false,
|
|
37
|
-
description: 'Database name'
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
user: {
|
|
41
|
-
type: 'string',
|
|
42
|
-
env: 'DB_USER',
|
|
43
|
-
default: undefined,
|
|
44
|
-
required: false
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
password: {
|
|
48
|
-
type: 'string',
|
|
49
|
-
env: 'DB_PASSWORD',
|
|
50
|
-
default: undefined,
|
|
51
|
-
required: false
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
// Connection pool
|
|
55
|
-
poolMin: {
|
|
56
|
-
type: 'number',
|
|
57
|
-
env: 'DB_POOL_MIN',
|
|
58
|
-
default: 2,
|
|
59
|
-
validate: (value) => value >= 0 || 'Pool min must be >= 0'
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
poolMax: {
|
|
63
|
-
type: 'number',
|
|
64
|
-
env: 'DB_POOL_MAX',
|
|
65
|
-
default: 10,
|
|
66
|
-
validate: (value) => value > 0 || 'Pool max must be > 0'
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
// SSL
|
|
70
|
-
ssl: config.boolean('DB_SSL', false),
|
|
71
|
-
|
|
72
|
-
// Timeouts
|
|
73
|
-
connectionTimeout: {
|
|
74
|
-
type: 'number',
|
|
75
|
-
env: 'DB_CONNECTION_TIMEOUT',
|
|
76
|
-
default: 30000,
|
|
77
|
-
description: 'Connection timeout in milliseconds'
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
queryTimeout: {
|
|
81
|
-
type: 'number',
|
|
82
|
-
env: 'DB_QUERY_TIMEOUT',
|
|
83
|
-
default: 60000,
|
|
84
|
-
description: 'Query timeout in milliseconds'
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
// Features
|
|
88
|
-
enableLogging: config.boolean('DB_ENABLE_LOGGING', false),
|
|
89
|
-
|
|
90
|
-
enableMigrations: config.boolean('DB_ENABLE_MIGRATIONS', true),
|
|
91
|
-
|
|
92
|
-
migrationsTable: config.string('DB_MIGRATIONS_TABLE', 'migrations')
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
// Export type
|
|
96
|
-
export type DatabaseConfig = typeof databaseConfig
|
|
97
|
-
|
|
98
|
-
// Export default
|
|
99
|
-
export default databaseConfig
|
|
1
|
+
export { databaseConfig as default, databaseConfig } from './system/database.config'
|
|
2
|
+
export type { DatabaseConfig } from './system/database.config'
|