create-fluxstack 1.17.0 → 1.18.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/LLMD/resources/live-auth.md +462 -465
- package/app/client/.live-stubs/LiveAdminPanel.js +15 -0
- package/app/client/.live-stubs/LiveCounter.js +9 -0
- package/app/client/.live-stubs/LiveForm.js +11 -0
- package/app/client/.live-stubs/LiveLocalCounter.js +8 -0
- package/app/client/.live-stubs/LivePingPong.js +10 -0
- package/app/client/.live-stubs/LiveRoomChat.js +11 -0
- package/app/client/.live-stubs/LiveSharedCounter.js +10 -0
- package/app/client/.live-stubs/LiveUpload.js +15 -0
- package/app/client/src/App.tsx +45 -3
- package/app/client/src/components/AppLayout.tsx +10 -1
- package/app/client/src/components/ErrorBoundary.tsx +117 -0
- package/app/client/src/components/LiveErrorBoundary.tsx +87 -0
- package/app/client/src/components/LiveUploadWidget.tsx +10 -14
- package/app/client/src/lib/eden-api.ts +6 -0
- package/app/client/src/lib/plugin-hooks.ts +82 -0
- package/app/client/src/live/AuthDemo.tsx +0 -1
- package/app/client/src/live/FormDemo.tsx +1 -1
- package/app/client/src/live/PingPongDemo.tsx +4 -1
- package/app/client/src/live/RoomChatDemo.tsx +90 -50
- package/app/client/src/live/SharedCounterDemo.tsx +5 -0
- package/app/server/auth/AuthManager.ts +24 -0
- package/app/server/auth/contracts.ts +12 -1
- package/app/server/auth/errors.ts +84 -0
- package/app/server/auth/guards/TokenGuard.ts +5 -2
- package/app/server/auth/index.ts +1 -1
- package/app/server/auth/providers/InMemoryProvider.ts +1 -1
- package/app/server/index.ts +3 -4
- package/app/server/live/LiveAdminPanel.ts +8 -8
- package/app/server/live/LiveForm.ts +1 -1
- package/app/server/live/LiveProtectedChat.ts +5 -5
- package/app/server/live/LiveRoomChat.ts +50 -28
- package/app/server/live/LiveUpload.ts +17 -3
- package/app/server/live/auto-generated-components.ts +26 -0
- package/app/server/live/rooms/ChatRoom.ts +17 -2
- package/app/server/routes/auth.routes.ts +29 -20
- package/app/server/routes/index.ts +9 -0
- package/app/server/routes/room.routes.ts +6 -6
- package/config/index.ts +3 -3
- package/config/system/app.config.ts +1 -1
- package/config/system/auth.config.ts +1 -1
- package/config/system/build.config.ts +8 -6
- package/config/system/client.config.ts +6 -4
- package/config/system/database.config.ts +1 -1
- package/config/system/logger.config.ts +1 -1
- package/config/system/monitoring.config.ts +6 -4
- package/config/system/plugins.config.ts +1 -1
- package/config/system/runtime.config.ts +1 -1
- package/config/system/server.config.ts +1 -1
- package/config/system/services.config.ts +1 -1
- package/config/system/session.config.ts +3 -3
- package/config/system/system.config.ts +1 -1
- package/core/build/vite-plugins.ts +3 -2
- package/core/cli/generators/plugin.ts +1 -1
- package/core/config/index.ts +8 -1
- package/core/framework/server.ts +9 -5
- package/core/index.ts +1 -1
- package/core/plugins/index.ts +1 -1
- package/core/plugins/manager.ts +5 -1
- package/core/plugins/types.ts +17 -1
- package/core/server/index.ts +5 -2
- package/core/server/live/index.ts +8 -71
- package/core/server/plugin-client-hooks.ts +97 -0
- package/core/types/types.ts +1 -0
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +1 -1
- package/package.json +8 -5
- package/src/client/components/ui/StatusBadge.tsx +23 -0
- package/core/utils/config-schema.ts +0 -480
- package/core/utils/env.ts +0 -305
- package/plugins/crypto-auth/README.md +0 -788
- package/plugins/crypto-auth/ai-context.md +0 -1282
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +0 -383
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +0 -302
- package/plugins/crypto-auth/client/components/AuthProvider.tsx +0 -131
- package/plugins/crypto-auth/client/components/LoginButton.tsx +0 -138
- package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +0 -89
- package/plugins/crypto-auth/client/components/index.ts +0 -12
- package/plugins/crypto-auth/client/index.ts +0 -12
- package/plugins/crypto-auth/config/index.ts +0 -34
- package/plugins/crypto-auth/index.ts +0 -173
- package/plugins/crypto-auth/package.json +0 -66
- package/plugins/crypto-auth/server/AuthMiddleware.ts +0 -181
- package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +0 -58
- package/plugins/crypto-auth/server/CryptoAuthService.ts +0 -186
- package/plugins/crypto-auth/server/index.ts +0 -25
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +0 -66
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +0 -26
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +0 -77
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +0 -45
- package/plugins/crypto-auth/server/middlewares/helpers.ts +0 -155
- package/plugins/crypto-auth/server/middlewares/index.ts +0 -22
- package/plugins/crypto-auth/server/middlewares.ts +0 -19
|
@@ -38,20 +38,28 @@ export class LiveRoomChat extends LiveComponent<typeof LiveRoomChat.defaultState
|
|
|
38
38
|
// Load existing custom rooms from directory state
|
|
39
39
|
this.setState({ customRooms: dir.state.rooms || [] })
|
|
40
40
|
|
|
41
|
-
// Listen for
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
41
|
+
// Listen for directory events with safe cleanup on error
|
|
42
|
+
const unsubs: (() => void)[] = []
|
|
43
|
+
try {
|
|
44
|
+
// Listen for new rooms being added
|
|
45
|
+
unsubs.push(dir.on('room:added', (entry: DirectoryEntry) => {
|
|
46
|
+
const current = this.state.customRooms.filter(r => r.id !== entry.id)
|
|
47
|
+
this.setState({ customRooms: [...current, entry] })
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
// Listen for rooms being removed
|
|
51
|
+
unsubs.push(dir.on('room:removed', (data: { id: string }) => {
|
|
52
|
+
this.setState({
|
|
53
|
+
customRooms: this.state.customRooms.filter(r => r.id !== data.id)
|
|
54
|
+
})
|
|
55
|
+
}))
|
|
56
|
+
|
|
57
|
+
this.directoryUnsubs = unsubs
|
|
58
|
+
} catch (error) {
|
|
59
|
+
// Cleanup any listeners that were successfully registered
|
|
60
|
+
unsubs.forEach(fn => fn())
|
|
61
|
+
throw error
|
|
62
|
+
}
|
|
55
63
|
}
|
|
56
64
|
|
|
57
65
|
async createRoom(payload: { roomId: string; roomName: string; password?: string }) {
|
|
@@ -83,14 +91,21 @@ export class LiveRoomChat extends LiveComponent<typeof LiveRoomChat.defaultState
|
|
|
83
91
|
createdBy: this.state.username || 'Anonymous'
|
|
84
92
|
})
|
|
85
93
|
|
|
86
|
-
// Listen for messages
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
// Listen for messages with safe cleanup
|
|
95
|
+
let unsub: (() => void) | undefined
|
|
96
|
+
try {
|
|
97
|
+
unsub = room.on('chat:message', (msg: ChatMessage) => {
|
|
98
|
+
const msgs = this.state.messages[roomId] || []
|
|
99
|
+
this.setState({
|
|
100
|
+
messages: { ...this.state.messages, [roomId]: [...msgs, msg].slice(-100) }
|
|
101
|
+
})
|
|
91
102
|
})
|
|
92
|
-
|
|
93
|
-
|
|
103
|
+
this.roomListeners.set(roomId, [unsub])
|
|
104
|
+
} catch (error) {
|
|
105
|
+
unsub?.()
|
|
106
|
+
room.leave()
|
|
107
|
+
throw error
|
|
108
|
+
}
|
|
94
109
|
|
|
95
110
|
this.setState({
|
|
96
111
|
activeRoom: roomId,
|
|
@@ -118,14 +133,21 @@ export class LiveRoomChat extends LiveComponent<typeof LiveRoomChat.defaultState
|
|
|
118
133
|
return { success: false, error: 'Senha incorreta' }
|
|
119
134
|
}
|
|
120
135
|
|
|
121
|
-
// Listen for chat messages from other members
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
// Listen for chat messages from other members with safe cleanup
|
|
137
|
+
let unsub: (() => void) | undefined
|
|
138
|
+
try {
|
|
139
|
+
unsub = room.on('chat:message', (msg: ChatMessage) => {
|
|
140
|
+
const msgs = this.state.messages[roomId] || []
|
|
141
|
+
this.setState({
|
|
142
|
+
messages: { ...this.state.messages, [roomId]: [...msgs, msg].slice(-100) }
|
|
143
|
+
})
|
|
126
144
|
})
|
|
127
|
-
|
|
128
|
-
|
|
145
|
+
this.roomListeners.set(roomId, [unsub])
|
|
146
|
+
} catch (error) {
|
|
147
|
+
unsub?.()
|
|
148
|
+
room.leave()
|
|
149
|
+
throw error
|
|
150
|
+
}
|
|
129
151
|
|
|
130
152
|
// Update component state — load existing messages from room state
|
|
131
153
|
this.setState({
|
|
@@ -21,9 +21,23 @@ export class LiveUpload extends LiveComponent<typeof LiveUpload.defaultState> {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
async startUpload(payload: { fileName: string; fileSize: number; fileType: string }) {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const fileName = payload.fileName
|
|
25
|
+
|
|
26
|
+
// Validate filename length
|
|
27
|
+
if (!fileName || fileName.length > 255) {
|
|
28
|
+
throw new Error('Invalid file name: must be 1-255 characters')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Block path traversal, null bytes, and control characters
|
|
32
|
+
if (/[\x00-\x1f]/.test(fileName) || fileName.includes('..') || fileName.includes('/') || fileName.includes('\\')) {
|
|
33
|
+
throw new Error('Invalid file name: contains forbidden characters')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Block Windows reserved names
|
|
37
|
+
const baseName = fileName.split('.')[0].toUpperCase()
|
|
38
|
+
const reserved = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'LPT1', 'LPT2', 'LPT3']
|
|
39
|
+
if (reserved.includes(baseName)) {
|
|
40
|
+
throw new Error('Invalid file name: reserved name')
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
// All file types allowed - no extension blocking
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Auto-generated Live Components Registration
|
|
2
|
+
// Generated by @fluxstack/live — DO NOT EDIT MANUALLY
|
|
3
|
+
// Generated at: 2026-04-09T02:31:46.153Z
|
|
4
|
+
|
|
5
|
+
import { LiveAdminPanel } from "./LiveAdminPanel"
|
|
6
|
+
import { LiveCounter } from "./LiveCounter"
|
|
7
|
+
import { LiveForm } from "./LiveForm"
|
|
8
|
+
import { LiveLocalCounter } from "./LiveLocalCounter"
|
|
9
|
+
import { LivePingPong } from "./LivePingPong"
|
|
10
|
+
import { LiveProtectedChat } from "./LiveProtectedChat"
|
|
11
|
+
import { LiveRoomChat } from "./LiveRoomChat"
|
|
12
|
+
import { LiveSharedCounter } from "./LiveSharedCounter"
|
|
13
|
+
import { LiveUpload } from "./LiveUpload"
|
|
14
|
+
|
|
15
|
+
// Component classes array for LiveServer({ components }) option
|
|
16
|
+
export const liveComponentClasses = [
|
|
17
|
+
LiveAdminPanel,
|
|
18
|
+
LiveCounter,
|
|
19
|
+
LiveForm,
|
|
20
|
+
LiveLocalCounter,
|
|
21
|
+
LivePingPong,
|
|
22
|
+
LiveProtectedChat,
|
|
23
|
+
LiveRoomChat,
|
|
24
|
+
LiveSharedCounter,
|
|
25
|
+
LiveUpload,
|
|
26
|
+
]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { LiveRoom } from '@fluxstack/live'
|
|
4
4
|
import type { RoomJoinContext, RoomLeaveContext } from '@fluxstack/live'
|
|
5
|
+
import { createHash, timingSafeEqual } from 'crypto'
|
|
5
6
|
|
|
6
7
|
export interface ChatMessage {
|
|
7
8
|
id: string
|
|
@@ -32,16 +33,30 @@ export class ChatRoom extends LiveRoom<ChatState, ChatMeta, ChatEvents> {
|
|
|
32
33
|
static defaultMeta: ChatMeta = { password: null, createdBy: null }
|
|
33
34
|
static $options = { maxMembers: 100 }
|
|
34
35
|
|
|
36
|
+
/** Hash a password using SHA-256. */
|
|
37
|
+
private static hashPassword(password: string): string {
|
|
38
|
+
return createHash('sha256').update(password).digest('hex')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Constant-time comparison of two hex strings. */
|
|
42
|
+
private static safeCompare(a: string, b: string): boolean {
|
|
43
|
+
const bufA = Buffer.from(a, 'hex')
|
|
44
|
+
const bufB = Buffer.from(b, 'hex')
|
|
45
|
+
if (bufA.length !== bufB.length) return false
|
|
46
|
+
return timingSafeEqual(bufA, bufB)
|
|
47
|
+
}
|
|
48
|
+
|
|
35
49
|
/** Set a password for this room. Pass null to remove. */
|
|
36
50
|
setPassword(password: string | null) {
|
|
37
|
-
this.meta.password = password
|
|
51
|
+
this.meta.password = password ? ChatRoom.hashPassword(password) : null
|
|
38
52
|
this.setState({ isPrivate: password !== null })
|
|
39
53
|
}
|
|
40
54
|
|
|
41
55
|
onJoin(ctx: RoomJoinContext) {
|
|
42
56
|
// Validate password if room is protected
|
|
43
57
|
if (this.meta.password) {
|
|
44
|
-
|
|
58
|
+
const provided = ctx.payload?.password
|
|
59
|
+
if (!provided || !ChatRoom.safeCompare(ChatRoom.hashPassword(provided), this.meta.password)) {
|
|
45
60
|
return false // Rejected — wrong or missing password
|
|
46
61
|
}
|
|
47
62
|
}
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
guest,
|
|
17
17
|
buildRequestContext,
|
|
18
18
|
} from '@server/auth'
|
|
19
|
+
import type { Guard, Authenticatable } from '@server/auth'
|
|
20
|
+
import { classifyAuthError } from '@server/auth/errors'
|
|
19
21
|
import { authConfig } from '@config/system/auth.config'
|
|
20
22
|
|
|
21
23
|
// ===== Schemas TypeBox (para validação + Swagger) =====
|
|
@@ -87,9 +89,8 @@ export const authRoutes = new Elysia({
|
|
|
87
89
|
// Solução: usar o InMemoryProvider diretamente ou o guard.
|
|
88
90
|
// Por segurança, vamos usar attempt após criar o user.
|
|
89
91
|
|
|
90
|
-
// Buscar provider
|
|
91
|
-
const
|
|
92
|
-
const provider = providers.get(providerName)
|
|
92
|
+
// Buscar provider via public API
|
|
93
|
+
const provider = authManager.getProvider(providerName)
|
|
93
94
|
|
|
94
95
|
if (!provider) {
|
|
95
96
|
set.status = 500
|
|
@@ -116,14 +117,15 @@ export const authRoutes = new Elysia({
|
|
|
116
117
|
set.status = 201
|
|
117
118
|
return {
|
|
118
119
|
success: true as const,
|
|
119
|
-
user: user.toJSON()
|
|
120
|
+
user: user.toJSON(),
|
|
120
121
|
}
|
|
121
|
-
} catch (error:
|
|
122
|
-
|
|
122
|
+
} catch (error: unknown) {
|
|
123
|
+
const classified = classifyAuthError(error)
|
|
124
|
+
set.status = classified.status as 422 | 500
|
|
123
125
|
return {
|
|
124
126
|
success: false as const,
|
|
125
|
-
error:
|
|
126
|
-
message:
|
|
127
|
+
error: classified.error,
|
|
128
|
+
message: classified.message,
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
}, {
|
|
@@ -187,26 +189,32 @@ export const authRoutes = new Elysia({
|
|
|
187
189
|
await rateLimiter.clear(throttleKey)
|
|
188
190
|
|
|
189
191
|
// Montar response
|
|
190
|
-
const response:
|
|
192
|
+
const response: {
|
|
193
|
+
success: true
|
|
194
|
+
user: ReturnType<typeof user.toJSON>
|
|
195
|
+
token?: string
|
|
196
|
+
} = {
|
|
191
197
|
success: true as const,
|
|
192
198
|
user: user.toJSON(),
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
// Se for token guard, incluir token na response
|
|
196
202
|
if ('getLastGeneratedToken' in guard) {
|
|
197
|
-
const
|
|
203
|
+
const tokenGuard = guard as Guard & { getLastGeneratedToken(): string | null }
|
|
204
|
+
const token = tokenGuard.getLastGeneratedToken()
|
|
198
205
|
if (token) {
|
|
199
206
|
response.token = token
|
|
200
207
|
}
|
|
201
208
|
}
|
|
202
209
|
|
|
203
210
|
return response
|
|
204
|
-
} catch (error:
|
|
205
|
-
|
|
211
|
+
} catch (error: unknown) {
|
|
212
|
+
const classified = classifyAuthError(error)
|
|
213
|
+
set.status = classified.status as 500
|
|
206
214
|
return {
|
|
207
215
|
success: false as const,
|
|
208
|
-
error:
|
|
209
|
-
message:
|
|
216
|
+
error: classified.error,
|
|
217
|
+
message: classified.message,
|
|
210
218
|
}
|
|
211
219
|
}
|
|
212
220
|
}, {
|
|
@@ -226,7 +234,7 @@ export const authRoutes = new Elysia({
|
|
|
226
234
|
// ───── Logout ─────
|
|
227
235
|
.use(auth())
|
|
228
236
|
.post('/logout', async (ctx) => {
|
|
229
|
-
const { auth:
|
|
237
|
+
const guard = (ctx as unknown as { auth: Guard | null }).auth
|
|
230
238
|
|
|
231
239
|
try {
|
|
232
240
|
if (guard) {
|
|
@@ -237,12 +245,13 @@ export const authRoutes = new Elysia({
|
|
|
237
245
|
success: true as const,
|
|
238
246
|
message: 'Logged out successfully.',
|
|
239
247
|
}
|
|
240
|
-
} catch (error:
|
|
241
|
-
|
|
248
|
+
} catch (error: unknown) {
|
|
249
|
+
const classified = classifyAuthError(error)
|
|
250
|
+
ctx.set.status = classified.status as 500
|
|
242
251
|
return {
|
|
243
252
|
success: false as const,
|
|
244
|
-
error:
|
|
245
|
-
message:
|
|
253
|
+
error: classified.error,
|
|
254
|
+
message: classified.message,
|
|
246
255
|
}
|
|
247
256
|
}
|
|
248
257
|
}, {
|
|
@@ -261,7 +270,7 @@ export const authRoutes = new Elysia({
|
|
|
261
270
|
|
|
262
271
|
// ───── Me (current user) ─────
|
|
263
272
|
.get('/me', async (ctx) => {
|
|
264
|
-
const
|
|
273
|
+
const user = (ctx as unknown as { user: Authenticatable }).user
|
|
265
274
|
|
|
266
275
|
return {
|
|
267
276
|
success: true as const,
|
|
@@ -2,6 +2,7 @@ import { Elysia, t } from "elysia"
|
|
|
2
2
|
import { usersRoutes } from "./users.routes"
|
|
3
3
|
import { roomRoutes } from "./room.routes"
|
|
4
4
|
import { authRoutes } from "./auth.routes"
|
|
5
|
+
import { pluginClientHooks } from "@core/server/plugin-client-hooks"
|
|
5
6
|
|
|
6
7
|
export const apiRoutes = new Elysia({ prefix: "/api" })
|
|
7
8
|
.get("/", () => ({ message: "🔥 Hot Reload funcionando! FluxStack API v1.4.0 ⚡" }), {
|
|
@@ -34,6 +35,14 @@ export const apiRoutes = new Elysia({ prefix: "/api" })
|
|
|
34
35
|
description: 'Returns the current health status of the API server'
|
|
35
36
|
}
|
|
36
37
|
})
|
|
38
|
+
// Plugin client hooks endpoint
|
|
39
|
+
.get("/__plugins/client-hooks", () => pluginClientHooks.getSignedResponse(), {
|
|
40
|
+
detail: {
|
|
41
|
+
tags: ['Plugins'],
|
|
42
|
+
summary: 'Get plugin client hooks',
|
|
43
|
+
description: 'Returns JavaScript code registered by server-side plugins to be executed on the client'
|
|
44
|
+
}
|
|
45
|
+
})
|
|
37
46
|
// Register routes
|
|
38
47
|
.use(authRoutes)
|
|
39
48
|
.use(usersRoutes)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// enviem mensagens para salas de chat via API REST
|
|
5
5
|
|
|
6
6
|
import { Elysia, t } from 'elysia'
|
|
7
|
-
import {
|
|
7
|
+
import { liveServer } from '@core/server/live'
|
|
8
8
|
|
|
9
9
|
export const roomRoutes = new Elysia({ prefix: '/rooms' })
|
|
10
10
|
|
|
@@ -22,9 +22,9 @@ export const roomRoutes = new Elysia({ prefix: '/rooms' })
|
|
|
22
22
|
|
|
23
23
|
// Emitir evento para a sala
|
|
24
24
|
// Isso vai:
|
|
25
|
-
// 1. Notificar handlers server-side via roomEvents
|
|
25
|
+
// 1. Notificar handlers server-side via liveServer!.roomEvents
|
|
26
26
|
// 2. Broadcast via WebSocket para frontends
|
|
27
|
-
const notified =
|
|
27
|
+
const notified = liveServer!.roomManager.emitToRoom(roomId, 'message:new', message)
|
|
28
28
|
|
|
29
29
|
return {
|
|
30
30
|
success: true,
|
|
@@ -63,7 +63,7 @@ export const roomRoutes = new Elysia({ prefix: '/rooms' })
|
|
|
63
63
|
const { roomId } = params
|
|
64
64
|
const { event, data } = body
|
|
65
65
|
|
|
66
|
-
const notified =
|
|
66
|
+
const notified = liveServer!.roomManager.emitToRoom(roomId, event, data)
|
|
67
67
|
|
|
68
68
|
return {
|
|
69
69
|
success: true,
|
|
@@ -94,8 +94,8 @@ export const roomRoutes = new Elysia({ prefix: '/rooms' })
|
|
|
94
94
|
|
|
95
95
|
// Obter estatísticas das salas
|
|
96
96
|
.get('/stats', () => {
|
|
97
|
-
const roomStats =
|
|
98
|
-
const eventStats = roomEvents.getStats()
|
|
97
|
+
const roomStats = liveServer!.roomManager.getStats()
|
|
98
|
+
const eventStats = liveServer!.roomEvents.getStats()
|
|
99
99
|
|
|
100
100
|
return {
|
|
101
101
|
success: true,
|
package/config/index.ts
CHANGED
|
@@ -47,7 +47,7 @@ export { sessionConfig } from './system/session.config'
|
|
|
47
47
|
export { fluxStackConfig, config as fluxConfig, type FluxStackConfig } from './fluxstack.config'
|
|
48
48
|
|
|
49
49
|
// Plugin configs (re-exported for convenience)
|
|
50
|
-
export { cryptoAuthConfig } from '
|
|
50
|
+
export { cryptoAuthConfig } from '@fluxstack/plugin-crypto-auth/config'
|
|
51
51
|
|
|
52
52
|
// ============================================================================
|
|
53
53
|
// 📝 TYPE EXPORTS
|
|
@@ -86,7 +86,7 @@ export type { AuthConfig } from './system/auth.config'
|
|
|
86
86
|
export type { SessionConfig } from './system/session.config'
|
|
87
87
|
|
|
88
88
|
// Plugin types
|
|
89
|
-
export type { CryptoAuthConfig } from '
|
|
89
|
+
export type { CryptoAuthConfig } from '@fluxstack/plugin-crypto-auth/config'
|
|
90
90
|
|
|
91
91
|
// ============================================================================
|
|
92
92
|
// 🎯 UNIFIED CONFIG OBJECT
|
|
@@ -105,7 +105,7 @@ import { databaseConfig } from './system/database.config'
|
|
|
105
105
|
import { servicesConfig } from './system/services.config'
|
|
106
106
|
import { authConfig } from './system/auth.config'
|
|
107
107
|
import { sessionConfig } from './system/session.config'
|
|
108
|
-
import { cryptoAuthConfig } from '
|
|
108
|
+
import { cryptoAuthConfig } from '@fluxstack/plugin-crypto-auth/config'
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
111
|
* All configs in one object
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Providers: ONDE buscar usuários (memory, database...)
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { defineConfig, defineNestedConfig, config } from '@
|
|
11
|
+
import { defineConfig, defineNestedConfig, config } from '@fluxstack/config'
|
|
12
12
|
|
|
13
13
|
// ===== Defaults =====
|
|
14
14
|
|
|
@@ -3,16 +3,18 @@
|
|
|
3
3
|
* Server build and optimization settings
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { defineConfig, defineNestedConfig, config } from '@
|
|
7
|
-
|
|
6
|
+
import { defineConfig, defineNestedConfig, config } from '@fluxstack/config'
|
|
7
|
+
|
|
8
|
+
const isProduction = process.env.NODE_ENV === 'production'
|
|
9
|
+
const isDevelopment = process.env.NODE_ENV !== 'production'
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Build optimization schema
|
|
11
13
|
*/
|
|
12
14
|
const optimizationSchema = {
|
|
13
|
-
minify: config.boolean('BUILD_MINIFY',
|
|
15
|
+
minify: config.boolean('BUILD_MINIFY', isProduction),
|
|
14
16
|
treeshake: config.boolean('BUILD_TREESHAKE', true),
|
|
15
|
-
compress: config.boolean('BUILD_COMPRESS',
|
|
17
|
+
compress: config.boolean('BUILD_COMPRESS', isProduction),
|
|
16
18
|
splitChunks: config.boolean('BUILD_SPLIT_CHUNKS', true),
|
|
17
19
|
bundleAnalyzer: config.boolean('BUILD_BUNDLE_ANALYZER', false),
|
|
18
20
|
removeUnusedCSS: config.boolean('BUILD_REMOVE_UNUSED_CSS', false),
|
|
@@ -25,9 +27,9 @@ const optimizationSchema = {
|
|
|
25
27
|
const buildSchema = {
|
|
26
28
|
target: config.enum('BUILD_TARGET', ['bun', 'node', 'docker'] as const, 'bun', true),
|
|
27
29
|
outDir: config.string('BUILD_OUT_DIR', 'dist', true),
|
|
28
|
-
sourceMaps: config.boolean('BUILD_SOURCE_MAPS',
|
|
30
|
+
sourceMaps: config.boolean('BUILD_SOURCE_MAPS', isDevelopment),
|
|
29
31
|
clean: config.boolean('BUILD_CLEAN', true),
|
|
30
|
-
mode: config.enum('BUILD_MODE', ['development', 'production'] as const,
|
|
32
|
+
mode: config.enum('BUILD_MODE', ['development', 'production'] as const, isProduction ? 'production' : 'development'),
|
|
31
33
|
external: config.array('BUILD_EXTERNAL', []),
|
|
32
34
|
optimize: config.boolean('BUILD_OPTIMIZE', true)
|
|
33
35
|
} as const
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* Declarative client, proxy and Vite dev server configuration
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { defineConfig, defineNestedConfig, config } from '
|
|
7
|
-
|
|
6
|
+
import { defineConfig, defineNestedConfig, config } from '@fluxstack/config'
|
|
7
|
+
|
|
8
|
+
const isProduction = process.env.NODE_ENV === 'production'
|
|
9
|
+
const isDevelopment = process.env.NODE_ENV !== 'production'
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Vite Dev Server Configuration
|
|
@@ -31,9 +33,9 @@ const viteSchema = {
|
|
|
31
33
|
const buildSchema = {
|
|
32
34
|
outDir: config.string('CLIENT_OUTDIR', 'dist/client'),
|
|
33
35
|
|
|
34
|
-
sourceMaps: config.boolean('CLIENT_SOURCEMAPS',
|
|
36
|
+
sourceMaps: config.boolean('CLIENT_SOURCEMAPS', isDevelopment),
|
|
35
37
|
|
|
36
|
-
minify: config.boolean('CLIENT_MINIFY',
|
|
38
|
+
minify: config.boolean('CLIENT_MINIFY', isProduction),
|
|
37
39
|
|
|
38
40
|
target: config.string('CLIENT_TARGET', 'esnext'),
|
|
39
41
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Optional database settings for backward compatibility
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { defineConfig, config } from '@
|
|
6
|
+
import { defineConfig, config } from '@fluxstack/config'
|
|
7
7
|
|
|
8
8
|
export const databaseConfig = defineConfig({
|
|
9
9
|
url: config.string('DATABASE_URL', ''),
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* Declarative monitoring, metrics and profiling configuration
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { defineConfig, defineNestedConfig, config } from '@
|
|
7
|
-
|
|
6
|
+
import { defineConfig, defineNestedConfig, config } from '@fluxstack/config'
|
|
7
|
+
|
|
8
|
+
const isProduction = process.env.NODE_ENV === 'production'
|
|
9
|
+
const isDevelopment = process.env.NODE_ENV !== 'production'
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Metrics Configuration Schema
|
|
@@ -31,7 +33,7 @@ const metricsSchema = {
|
|
|
31
33
|
customMetrics: config.boolean('CUSTOM_METRICS', false),
|
|
32
34
|
|
|
33
35
|
// Metric exporters
|
|
34
|
-
exportToConsole: config.boolean('METRICS_EXPORT_CONSOLE',
|
|
36
|
+
exportToConsole: config.boolean('METRICS_EXPORT_CONSOLE', isDevelopment),
|
|
35
37
|
|
|
36
38
|
exportToFile: config.boolean('METRICS_EXPORT_FILE', false),
|
|
37
39
|
|
|
@@ -54,7 +56,7 @@ const profilingSchema = {
|
|
|
54
56
|
sampleRate: {
|
|
55
57
|
type: 'number' as const,
|
|
56
58
|
env: 'PROFILING_SAMPLE_RATE',
|
|
57
|
-
default:
|
|
59
|
+
default: isProduction ? 0.01 : 0.1,
|
|
58
60
|
validate: (value: number) => {
|
|
59
61
|
if (value < 0 || value > 1) {
|
|
60
62
|
return 'Sample rate must be between 0 and 1'
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Declarative plugin management configuration
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { defineConfig, config } from '@
|
|
6
|
+
import { defineConfig, config } from '@fluxstack/config'
|
|
7
7
|
import { FLUXSTACK_VERSION } from '@core/utils/version'
|
|
8
8
|
|
|
9
9
|
const defaultPluginConfigs = {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Server-specific settings (port, host, CORS, middleware)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { defineConfig, defineNestedConfig, config } from '@
|
|
6
|
+
import { defineConfig, defineNestedConfig, config } from '@fluxstack/config'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* CORS configuration schema
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Optional service settings (email, JWT, storage, redis) for compatibility
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { defineNestedConfig, config } from '@
|
|
6
|
+
import { defineNestedConfig, config } from '@fluxstack/config'
|
|
7
7
|
|
|
8
8
|
const emailSchema = {
|
|
9
9
|
host: config.string('MAIL_HOST', 'smtp.example.com'),
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Inspirado no config/session.php do Laravel.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { defineConfig, config } from '@
|
|
8
|
+
import { defineConfig, config } from '@fluxstack/config'
|
|
9
9
|
|
|
10
10
|
const sessionConfigSchema = {
|
|
11
11
|
/** Driver de sessão (memory = processo, extensível para redis, database) */
|
|
@@ -16,8 +16,8 @@ const sessionConfigSchema = {
|
|
|
16
16
|
cookieName: config.string('SESSION_COOKIE', 'fluxstack_session'),
|
|
17
17
|
/** Cookie httpOnly (default: true) */
|
|
18
18
|
httpOnly: config.boolean('SESSION_HTTP_ONLY', true),
|
|
19
|
-
/** Cookie secure -
|
|
20
|
-
secure: config.boolean('SESSION_SECURE',
|
|
19
|
+
/** Cookie secure - auto-detects production (default: true in production, false in dev) */
|
|
20
|
+
secure: config.boolean('SESSION_SECURE', process.env.NODE_ENV === 'production'),
|
|
21
21
|
/** Cookie sameSite */
|
|
22
22
|
sameSite: config.enum('SESSION_SAME_SITE', ['strict', 'lax', 'none'] as const, 'lax'),
|
|
23
23
|
/** Cookie path */
|
|
@@ -11,7 +11,8 @@ import { resolve } from 'path'
|
|
|
11
11
|
import tsconfigPaths from 'vite-tsconfig-paths'
|
|
12
12
|
import checker from 'vite-plugin-checker'
|
|
13
13
|
import { liveStripPlugin } from '@fluxstack/live/build'
|
|
14
|
-
import {
|
|
14
|
+
import { env } from '@fluxstack/config'
|
|
15
|
+
const isDevelopment = env.get('NODE_ENV', 'development') === 'development'
|
|
15
16
|
|
|
16
17
|
export function fluxstackVitePlugins(): Plugin[] {
|
|
17
18
|
return [
|
|
@@ -20,7 +21,7 @@ export function fluxstackVitePlugins(): Plugin[] {
|
|
|
20
21
|
projects: [resolve(import.meta.dirname, '..', '..', 'tsconfig.json')]
|
|
21
22
|
}),
|
|
22
23
|
// Only run type checker in development (saves ~5+ minutes in Docker builds)
|
|
23
|
-
|
|
24
|
+
isDevelopment && checker({
|
|
24
25
|
typescript: true,
|
|
25
26
|
overlay: true
|
|
26
27
|
}),
|
|
@@ -111,7 +111,7 @@ export class PluginGenerator implements Generator {
|
|
|
111
111
|
* Declarative config using FluxStack config system
|
|
112
112
|
*/
|
|
113
113
|
|
|
114
|
-
import { defineConfig, config } from '@
|
|
114
|
+
import { defineConfig, config } from '@fluxstack/config'
|
|
115
115
|
|
|
116
116
|
const {{camelName}}ConfigSchema = {
|
|
117
117
|
// Enable/disable plugin
|