create-fluxstack 1.5.4 → 1.7.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.
Files changed (61) hide show
  1. package/README.md +172 -215
  2. package/app/client/src/App.tsx +45 -19
  3. package/app/client/src/components/FluxStackConfig.tsx +1 -1
  4. package/app/client/src/components/HybridLiveCounter.tsx +1 -1
  5. package/app/client/src/components/LiveClock.tsx +1 -1
  6. package/app/client/src/components/MainLayout.tsx +0 -2
  7. package/app/client/src/components/SidebarNavigation.tsx +1 -1
  8. package/app/client/src/components/SystemMonitor.tsx +16 -10
  9. package/app/client/src/components/UserProfile.tsx +1 -1
  10. package/app/server/live/FluxStackConfig.ts +2 -1
  11. package/app/server/live/LiveClockComponent.ts +8 -7
  12. package/app/server/live/SidebarNavigation.ts +2 -1
  13. package/app/server/live/SystemMonitor.ts +1 -0
  14. package/app/server/live/UserProfileComponent.ts +36 -30
  15. package/config/server.config.ts +1 -0
  16. package/core/cli/command-registry.ts +10 -10
  17. package/core/cli/commands/plugin-deps.ts +13 -5
  18. package/core/cli/plugin-discovery.ts +1 -1
  19. package/core/client/LiveComponentsProvider.tsx +414 -0
  20. package/core/client/hooks/useHybridLiveComponent.ts +194 -530
  21. package/core/client/index.ts +16 -0
  22. package/core/framework/server.ts +144 -63
  23. package/core/index.ts +4 -1
  24. package/core/plugins/built-in/monitoring/index.ts +1 -1
  25. package/core/plugins/built-in/static/index.ts +1 -1
  26. package/core/plugins/built-in/swagger/index.ts +1 -1
  27. package/core/plugins/built-in/vite/index.ts +1 -1
  28. package/core/plugins/config.ts +1 -1
  29. package/core/plugins/discovery.ts +1 -1
  30. package/core/plugins/executor.ts +1 -1
  31. package/core/plugins/index.ts +1 -0
  32. package/core/server/live/ComponentRegistry.ts +3 -1
  33. package/core/server/live/WebSocketConnectionManager.ts +14 -4
  34. package/core/server/live/websocket-plugin.ts +453 -434
  35. package/core/server/middleware/elysia-helpers.ts +3 -5
  36. package/core/server/plugins/database.ts +1 -1
  37. package/core/server/plugins/static-files-plugin.ts +1 -1
  38. package/core/types/index.ts +1 -1
  39. package/core/types/plugin.ts +1 -1
  40. package/core/types/types.ts +6 -2
  41. package/core/utils/logger/colors.ts +4 -4
  42. package/core/utils/logger/index.ts +37 -4
  43. package/core/utils/logger/winston-logger.ts +1 -1
  44. package/core/utils/sync-version.ts +61 -0
  45. package/core/utils/version.ts +6 -5
  46. package/create-fluxstack.ts +1 -1
  47. package/package.json +5 -3
  48. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +1 -1
  49. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
  50. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +1 -1
  51. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +1 -1
  52. package/vite.config.ts +8 -0
  53. package/.dockerignore +0 -50
  54. package/CRYPTO-AUTH-MIDDLEWARE-GUIDE.md +0 -475
  55. package/CRYPTO-AUTH-MIDDLEWARES.md +0 -473
  56. package/CRYPTO-AUTH-USAGE.md +0 -491
  57. package/EXEMPLO-ROTA-PROTEGIDA.md +0 -347
  58. package/QUICK-START-CRYPTO-AUTH.md +0 -221
  59. package/app/client/src/components/Teste.tsx +0 -104
  60. package/app/server/live/TesteComponent.ts +0 -87
  61. package/test-crypto-auth.ts +0 -101
@@ -1,347 +0,0 @@
1
- # 🔐 Como Criar Rotas com Crypto-Auth
2
-
3
- Guia prático para desenvolvedores criarem rotas usando o sistema de autenticação.
4
-
5
- ## 📋 Passo a Passo
6
-
7
- ### 1️⃣ Criar Arquivo de Rotas
8
-
9
- Crie um arquivo em `app/server/routes/`:
10
-
11
- ```typescript
12
- // app/server/routes/posts.routes.ts
13
- import { Elysia, t } from 'elysia'
14
- import {
15
- cryptoAuthRequired,
16
- cryptoAuthAdmin,
17
- cryptoAuthOptional,
18
- getCryptoAuthUser
19
- } from '@/plugins/crypto-auth/server'
20
-
21
- export const postsRoutes = new Elysia({ prefix: '/posts' })
22
-
23
- // ========================================
24
- // 🌐 ROTA PÚBLICA - Qualquer um pode acessar
25
- // ========================================
26
- .get('/', () => {
27
- return {
28
- success: true,
29
- posts: [
30
- { id: 1, title: 'Post público' },
31
- { id: 2, title: 'Outro post' }
32
- ]
33
- }
34
- })
35
-
36
- // ========================================
37
- // 🔒 ROTA PROTEGIDA - Requer autenticação
38
- // ========================================
39
- .guard({}, (app) =>
40
- app.use(cryptoAuthRequired())
41
-
42
- // GET /api/posts/my-posts - Lista posts do usuário autenticado
43
- .get('/my-posts', ({ request }) => {
44
- const user = getCryptoAuthUser(request)!
45
-
46
- return {
47
- success: true,
48
- message: `Posts de ${user.publicKey.substring(0, 8)}...`,
49
- posts: [
50
- { id: 1, title: 'Meu post privado', author: user.publicKey }
51
- ]
52
- }
53
- })
54
-
55
- // POST /api/posts - Criar novo post (autenticação obrigatória)
56
- .post('/', ({ request, body }) => {
57
- const user = getCryptoAuthUser(request)!
58
- const { title, content } = body as { title: string; content: string }
59
-
60
- return {
61
- success: true,
62
- message: 'Post criado com sucesso',
63
- post: {
64
- id: Date.now(),
65
- title,
66
- content,
67
- author: user.publicKey,
68
- createdAt: new Date().toISOString()
69
- }
70
- }
71
- }, {
72
- body: t.Object({
73
- title: t.String(),
74
- content: t.String()
75
- })
76
- })
77
- )
78
-
79
- // ========================================
80
- // 👑 ROTA ADMIN - Apenas administradores
81
- // ========================================
82
- .guard({}, (app) =>
83
- app.use(cryptoAuthAdmin())
84
-
85
- // DELETE /api/posts/:id - Deletar qualquer post (só admin)
86
- .delete('/:id', ({ request, params }) => {
87
- const user = getCryptoAuthUser(request)!
88
-
89
- return {
90
- success: true,
91
- message: `Post ${params.id} deletado por admin`,
92
- deletedBy: user.publicKey.substring(0, 8) + '...'
93
- }
94
- })
95
-
96
- // GET /api/posts/moderation - Painel de moderação
97
- .get('/moderation', ({ request }) => {
98
- const user = getCryptoAuthUser(request)!
99
-
100
- return {
101
- success: true,
102
- message: 'Painel de moderação',
103
- admin: user.publicKey.substring(0, 8) + '...',
104
- pendingPosts: [],
105
- reportedPosts: []
106
- }
107
- })
108
- )
109
-
110
- // ========================================
111
- // 🌓 ROTA COM AUTH OPCIONAL - Funciona com/sem auth
112
- // ========================================
113
- .guard({}, (app) =>
114
- app.use(cryptoAuthOptional())
115
-
116
- // GET /api/posts/:id - Detalhes do post (conteúdo extra se autenticado)
117
- .get('/:id', ({ request, params }) => {
118
- const user = getCryptoAuthUser(request)
119
- const isAuthenticated = !!user
120
-
121
- return {
122
- success: true,
123
- post: {
124
- id: params.id,
125
- title: 'Post de exemplo',
126
- content: 'Conteúdo público',
127
- // ✅ Conteúdo extra apenas para autenticados
128
- extraContent: isAuthenticated
129
- ? 'Conteúdo premium para usuários autenticados'
130
- : null,
131
- canEdit: isAuthenticated,
132
- viewer: user ? user.publicKey.substring(0, 8) + '...' : 'anonymous'
133
- }
134
- }
135
- })
136
- )
137
- ```
138
-
139
- ### 2️⃣ Registrar no Router Principal
140
-
141
- ```typescript
142
- // app/server/routes/index.ts
143
- import { Elysia } from 'elysia'
144
- import { postsRoutes } from './posts.routes' // ✅ Importar suas rotas
145
-
146
- export const apiRoutes = new Elysia({ prefix: '/api' })
147
- .use(userRoutes)
148
- .use(postsRoutes) // ✅ Adicionar aqui
149
- // ... outras rotas
150
- ```
151
-
152
- ### 3️⃣ Como o Cliente Faz Login
153
-
154
- O cliente precisa enviar headers de autenticação criptográfica:
155
-
156
- ```typescript
157
- // Frontend/Cliente
158
- import { generateKeyPair, sign } from './crypto-utils'
159
-
160
- // 1. Gerar par de chaves (feito uma vez)
161
- const { publicKey, privateKey } = generateKeyPair()
162
-
163
- // 2. Fazer requisição autenticada
164
- const timestamp = Date.now()
165
- const nonce = crypto.randomUUID()
166
- const message = `GET:/api/posts/my-posts:${timestamp}:${nonce}`
167
- const signature = sign(message, privateKey)
168
-
169
- fetch('http://localhost:3000/api/posts/my-posts', {
170
- headers: {
171
- 'X-Public-Key': publicKey,
172
- 'X-Timestamp': timestamp.toString(),
173
- 'X-Nonce': nonce,
174
- 'X-Signature': signature
175
- }
176
- })
177
- ```
178
-
179
- ## 🧪 Testando as Rotas
180
-
181
- ### Rota Pública (sem auth)
182
- ```bash
183
- curl http://localhost:3000/api/posts
184
- # ✅ 200 OK
185
- ```
186
-
187
- ### Rota Protegida (sem auth)
188
- ```bash
189
- curl http://localhost:3000/api/posts/my-posts
190
- # ❌ 401 {"error": {"message": "Authentication required"}}
191
- ```
192
-
193
- ### Rota Protegida (com auth)
194
- ```bash
195
- curl http://localhost:3000/api/posts/my-posts \
196
- -H "X-Public-Key: abc123..." \
197
- -H "X-Timestamp: 1234567890" \
198
- -H "X-Nonce: uuid-here" \
199
- -H "X-Signature: signature-here"
200
- # ✅ 200 OK {"posts": [...]}
201
- ```
202
-
203
- ### Rota Admin (sem admin)
204
- ```bash
205
- curl http://localhost:3000/api/posts/moderation \
206
- -H "X-Public-Key: user-key..." \
207
- # ... outros headers
208
- # ❌ 403 {"error": {"message": "Admin privileges required"}}
209
- ```
210
-
211
- ### Rota Opcional (ambos funcionam)
212
- ```bash
213
- # Sem auth
214
- curl http://localhost:3000/api/posts/123
215
- # ✅ 200 OK {"post": {"extraContent": null}}
216
-
217
- # Com auth
218
- curl http://localhost:3000/api/posts/123 -H "X-Public-Key: ..."
219
- # ✅ 200 OK {"post": {"extraContent": "Conteúdo premium..."}}
220
- ```
221
-
222
- ## 📦 Middlewares Disponíveis
223
-
224
- ### `cryptoAuthRequired()`
225
- Bloqueia acesso se não autenticado.
226
- ```typescript
227
- .use(cryptoAuthRequired())
228
- .get('/protected', ({ request }) => {
229
- const user = getCryptoAuthUser(request)! // ✅ Sempre existe
230
- })
231
- ```
232
-
233
- ### `cryptoAuthAdmin()`
234
- Bloqueia se não for admin.
235
- ```typescript
236
- .use(cryptoAuthAdmin())
237
- .delete('/users/:id', ({ request }) => {
238
- const user = getCryptoAuthUser(request)! // ✅ Sempre admin
239
- })
240
- ```
241
-
242
- ### `cryptoAuthOptional()`
243
- Adiciona user se autenticado, mas não bloqueia.
244
- ```typescript
245
- .use(cryptoAuthOptional())
246
- .get('/feed', ({ request }) => {
247
- const user = getCryptoAuthUser(request) // ⚠️ Pode ser null
248
- if (user) {
249
- return { message: 'Feed personalizado' }
250
- }
251
- return { message: 'Feed público' }
252
- })
253
- ```
254
-
255
- ### `cryptoAuthPermissions(['write', 'delete'])`
256
- Bloqueia se não tiver permissões específicas.
257
- ```typescript
258
- .use(cryptoAuthPermissions(['write', 'delete']))
259
- .put('/posts/:id', ({ request }) => {
260
- const user = getCryptoAuthUser(request)! // ✅ Tem as permissões
261
- })
262
- ```
263
-
264
- ## 🔑 Helpers Úteis
265
-
266
- ```typescript
267
- import {
268
- getCryptoAuthUser,
269
- isCryptoAuthAuthenticated,
270
- isCryptoAuthAdmin,
271
- hasCryptoAuthPermission
272
- } from '@/plugins/crypto-auth/server'
273
-
274
- // Dentro de uma rota
275
- ({ request }) => {
276
- // Pegar usuário autenticado (null se não autenticado)
277
- const user = getCryptoAuthUser(request)
278
-
279
- // Verificar se está autenticado
280
- if (!isCryptoAuthAuthenticated(request)) {
281
- return { error: 'Login required' }
282
- }
283
-
284
- // Verificar se é admin
285
- if (isCryptoAuthAdmin(request)) {
286
- return { message: 'Admin panel' }
287
- }
288
-
289
- // Verificar permissão específica
290
- if (hasCryptoAuthPermission(request, 'delete')) {
291
- return { message: 'Can delete' }
292
- }
293
- }
294
- ```
295
-
296
- ## ⚠️ Boas Práticas
297
-
298
- ### ✅ Fazer
299
- - Usar `.guard({})` para isolar middlewares
300
- - Verificar `null` em rotas com `cryptoAuthOptional()`
301
- - Usar `!` apenas após middlewares obrigatórios
302
- - Separar rotas por nível de permissão
303
-
304
- ### ❌ Não Fazer
305
- - Usar `.use()` fora de `.guard()` (afeta TODAS as rotas seguintes)
306
- - Esquecer `.as('plugin')` ao criar middlewares customizados
307
- - Assumir que `user` existe sem middleware de proteção
308
-
309
- ## 🎯 Padrão Recomendado
310
-
311
- ```typescript
312
- export const myRoutes = new Elysia({ prefix: '/my-feature' })
313
-
314
- // Públicas primeiro
315
- .get('/public', () => ({ ... }))
316
-
317
- // Auth opcional (separado)
318
- .guard({}, (app) =>
319
- app.use(cryptoAuthOptional())
320
- .get('/optional', ({ request }) => {
321
- const user = getCryptoAuthUser(request)
322
- // ...
323
- })
324
- )
325
-
326
- // Protegidas (separado)
327
- .guard({}, (app) =>
328
- app.use(cryptoAuthRequired())
329
- .get('/protected', ({ request }) => {
330
- const user = getCryptoAuthUser(request)!
331
- // ...
332
- })
333
- )
334
-
335
- // Admin (separado)
336
- .guard({}, (app) =>
337
- app.use(cryptoAuthAdmin())
338
- .delete('/admin-only', ({ request }) => {
339
- const user = getCryptoAuthUser(request)!
340
- // ...
341
- })
342
- )
343
- ```
344
-
345
- ---
346
-
347
- **Pronto!** Agora você sabe criar rotas com autenticação crypto-auth no FluxStack. 🚀
@@ -1,221 +0,0 @@
1
- # ⚡ Quick Start: Crypto-Auth em 5 Minutos
2
-
3
- ## 🎯 Como Criar uma Rota Protegida
4
-
5
- ### 🚀 Opção 1: CLI (Recomendado)
6
-
7
- Use o comando `crypto-auth:make:route` para gerar rotas automaticamente:
8
-
9
- ```bash
10
- # Rota com autenticação obrigatória (padrão)
11
- bun flux crypto-auth:make:route users
12
-
13
- # Rota apenas para admins
14
- bun flux crypto-auth:make:route admin-panel --auth admin
15
-
16
- # Rota com autenticação opcional
17
- bun flux crypto-auth:make:route blog --auth optional
18
-
19
- # Rota pública (sem auth)
20
- bun flux crypto-auth:make:route public-api --auth public
21
- ```
22
-
23
- O comando cria automaticamente:
24
- - ✅ Arquivo de rotas em `app/server/routes/[nome].routes.ts`
25
- - ✅ Middlewares de autenticação configurados
26
- - ✅ Templates de CRUD completos
27
- - ✅ Exemplos de uso de `getCryptoAuthUser()`
28
-
29
- **Tipos de `--auth` disponíveis:**
30
- - `required` - Autenticação obrigatória (padrão)
31
- - `admin` - Apenas administradores
32
- - `optional` - Auth opcional (rota pública com conteúdo extra para autenticados)
33
- - `public` - Completamente pública (sem middleware)
34
-
35
- ---
36
-
37
- ### ⚙️ Opção 2: Manual
38
-
39
- #### 1️⃣ Criar Arquivo de Rotas
40
-
41
- ```typescript
42
- // app/server/routes/minhas-rotas.routes.ts
43
- import { Elysia } from 'elysia'
44
- import { cryptoAuthRequired, getCryptoAuthUser } from '@/plugins/crypto-auth/server'
45
-
46
- export const minhasRotas = new Elysia({ prefix: '/minhas-rotas' })
47
-
48
- // Rota pública
49
- .get('/publica', () => ({ message: 'Todos podem ver' }))
50
-
51
- // Rota protegida
52
- .guard({}, (app) =>
53
- app.use(cryptoAuthRequired())
54
- .get('/protegida', ({ request }) => {
55
- const user = getCryptoAuthUser(request)!
56
- return {
57
- message: 'Área restrita',
58
- user: user.publicKey.substring(0, 8) + '...'
59
- }
60
- })
61
- )
62
- ```
63
-
64
- #### 2️⃣ Registrar no Router
65
-
66
- ```typescript
67
- // app/server/routes/index.ts
68
- import { minhasRotas } from './minhas-rotas.routes'
69
-
70
- export const apiRoutes = new Elysia({ prefix: '/api' })
71
- .use(minhasRotas) // ✅ Adicionar aqui
72
- ```
73
-
74
- #### 3️⃣ Testar
75
-
76
- ```bash
77
- # Pública (funciona)
78
- curl http://localhost:3000/api/minhas-rotas/publica
79
- # ✅ {"message": "Todos podem ver"}
80
-
81
- # Protegida (sem auth)
82
- curl http://localhost:3000/api/minhas-rotas/protegida
83
- # ❌ {"error": {"message": "Authentication required", "code": "CRYPTO_AUTH_REQUIRED", "statusCode": 401}}
84
- ```
85
-
86
- ## 🔐 Tipos de Middleware
87
-
88
- ### `cryptoAuthRequired()` - Autenticação Obrigatória
89
- ```typescript
90
- .guard({}, (app) =>
91
- app.use(cryptoAuthRequired())
92
- .get('/protegida', ({ request }) => {
93
- const user = getCryptoAuthUser(request)! // ✅ Sempre existe
94
- return { user }
95
- })
96
- )
97
- ```
98
-
99
- ### `cryptoAuthAdmin()` - Apenas Administradores
100
- ```typescript
101
- .guard({}, (app) =>
102
- app.use(cryptoAuthAdmin())
103
- .delete('/deletar/:id', ({ request, params }) => {
104
- const user = getCryptoAuthUser(request)! // ✅ Sempre admin
105
- return { message: `${params.id} deletado` }
106
- })
107
- )
108
- ```
109
-
110
- ### `cryptoAuthOptional()` - Autenticação Opcional
111
- ```typescript
112
- .guard({}, (app) =>
113
- app.use(cryptoAuthOptional())
114
- .get('/feed', ({ request }) => {
115
- const user = getCryptoAuthUser(request) // ⚠️ Pode ser null
116
-
117
- if (user) {
118
- return { message: 'Feed personalizado', user }
119
- }
120
- return { message: 'Feed público' }
121
- })
122
- )
123
- ```
124
-
125
- ### `cryptoAuthPermissions([...])` - Permissões Específicas
126
- ```typescript
127
- .guard({}, (app) =>
128
- app.use(cryptoAuthPermissions(['write', 'delete']))
129
- .put('/editar/:id', ({ request }) => {
130
- const user = getCryptoAuthUser(request)! // ✅ Tem as permissões
131
- return { message: 'Editado' }
132
- })
133
- )
134
- ```
135
-
136
- ## 📊 Exemplo Real Funcionando
137
-
138
- Veja o arquivo criado: **`app/server/routes/exemplo-posts.routes.ts`**
139
-
140
- Rotas disponíveis:
141
- - ✅ `GET /api/exemplo-posts` - Pública
142
- - ✅ `GET /api/exemplo-posts/:id` - Auth opcional
143
- - ✅ `GET /api/exemplo-posts/meus-posts` - Protegida
144
- - ✅ `POST /api/exemplo-posts/criar` - Protegida
145
- - ✅ `GET /api/exemplo-posts/admin/todos` - Admin
146
- - ✅ `DELETE /api/exemplo-posts/admin/:id` - Admin
147
-
148
- ## 🧪 Testando Agora
149
-
150
- ```bash
151
- # Pública
152
- curl http://localhost:3000/api/exemplo-posts
153
- # ✅ {"success":true,"posts":[...]}
154
-
155
- # Auth opcional (sem auth)
156
- curl http://localhost:3000/api/exemplo-posts/1
157
- # ✅ {"success":true,"post":{"premiumContent":null,"viewer":"Visitante anônimo"}}
158
-
159
- # Protegida (sem auth)
160
- curl http://localhost:3000/api/exemplo-posts/meus-posts
161
- # ❌ {"error":{"message":"Authentication required"}}
162
-
163
- # Admin (sem auth)
164
- curl http://localhost:3000/api/exemplo-posts/admin/todos
165
- # ❌ {"error":{"message":"Authentication required"}}
166
- ```
167
-
168
- ## 🔑 Helpers Úteis
169
-
170
- ```typescript
171
- import {
172
- getCryptoAuthUser,
173
- isCryptoAuthAuthenticated,
174
- isCryptoAuthAdmin,
175
- hasCryptoAuthPermission
176
- } from '@/plugins/crypto-auth/server'
177
-
178
- ({ request }) => {
179
- const user = getCryptoAuthUser(request) // User | null
180
- const isAuth = isCryptoAuthAuthenticated(request) // boolean
181
- const isAdmin = isCryptoAuthAdmin(request) // boolean
182
- const canDelete = hasCryptoAuthPermission(request, 'delete') // boolean
183
- }
184
- ```
185
-
186
- ## ⚠️ Importante
187
-
188
- ### ✅ Fazer
189
- ```typescript
190
- // Isolar middlewares com .guard({})
191
- .guard({}, (app) =>
192
- app.use(cryptoAuthRequired())
193
- .get('/protected', () => {})
194
- )
195
-
196
- // Verificar null em auth opcional
197
- .guard({}, (app) =>
198
- app.use(cryptoAuthOptional())
199
- .get('/feed', ({ request }) => {
200
- const user = getCryptoAuthUser(request)
201
- if (user) { /* autenticado */ }
202
- })
203
- )
204
- ```
205
-
206
- ### ❌ Não Fazer
207
- ```typescript
208
- // ❌ Usar .use() sem .guard() (afeta TODAS as rotas seguintes)
209
- export const myRoutes = new Elysia()
210
- .use(cryptoAuthRequired()) // ❌ ERRADO
211
- .get('/publica', () => {}) // Esta rota ficará protegida!
212
- ```
213
-
214
- ## 📚 Documentação Completa
215
-
216
- - **Guia Detalhado**: `EXEMPLO-ROTA-PROTEGIDA.md`
217
- - **Referência de Middlewares**: `CRYPTO-AUTH-MIDDLEWARE-GUIDE.md`
218
-
219
- ---
220
-
221
- **Pronto!** Agora você pode criar rotas protegidas em minutos. 🚀
@@ -1,104 +0,0 @@
1
- // 🔥 Teste - Client Component
2
- import React from 'react';
3
- import { useHybridLiveComponent } from 'fluxstack';
4
-
5
- interface TesteState {
6
- message: string;
7
- count: number;
8
- lastUpdated: Date;
9
- }
10
-
11
- const initialState: TesteState = {
12
- message: "Loading...",
13
- count: 0,
14
- lastUpdated: new Date(),
15
- };
16
-
17
- export function Teste() {
18
- const { state, call, connected, loading } = useHybridLiveComponent<TesteState>('Teste', initialState, { room: 'testeroom' });
19
-
20
- if (!connected) {
21
- return (
22
- <div className="flex items-center justify-center p-8 border-2 border-dashed border-gray-300 rounded-lg">
23
- <div className="text-center">
24
- <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-2"></div>
25
- <p className="text-gray-600">Connecting to Teste...</p>
26
- </div>
27
- </div>
28
- );
29
- }
30
-
31
- return (
32
- <div className="bg-white border border-gray-200 rounded-lg shadow-sm p-6 m-4 relative">
33
- <div className="flex items-center justify-between mb-4">
34
- <h2 className="text-2xl font-bold text-gray-800">Teste Live Component</h2>
35
- <span className={
36
- `px-2 py-1 rounded-full text-xs font-medium ${
37
- connected ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
38
- }`
39
- }>
40
- {connected ? '🟢 Connected' : '🔴 Disconnected'}
41
- </span>
42
- </div>
43
-
44
- <div className="space-y-4">
45
- <div>
46
- <p className="text-gray-600 mb-2">Server message:</p>
47
- <p className="text-lg font-semibold text-blue-600">{state.message}</p>
48
- </div>
49
-
50
- <div>
51
- <p className="text-gray-600 mb-2">Counter: <span className="font-bold text-2xl">{state.count}</span></p>
52
- </div>
53
-
54
- <div className="flex gap-2 flex-wrap">
55
- <button
56
- onClick={() => call('updateMessage', { message: 'Hello from the client!' })}
57
- disabled={loading}
58
- className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed"
59
- >
60
- 📝 Update Message
61
- </button>
62
-
63
- <button
64
- onClick={() => call('incrementCounter')}
65
- disabled={loading}
66
- className="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 disabled:opacity-50 disabled:cursor-not-allowed"
67
- >
68
- ➕ Increment
69
- </button>
70
-
71
- <button
72
- onClick={() => call('resetData')}
73
- disabled={loading}
74
- className="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed"
75
- >
76
- 🔄 Reset
77
- </button>
78
-
79
- <button
80
- onClick={async () => {
81
- const result = await call('getData');
82
- console.log('Component data:', result);
83
- alert('Data logged to console');
84
- }}
85
- disabled={loading}
86
- className="px-4 py-2 bg-purple-500 text-white rounded-lg hover:bg-purple-600 disabled:opacity-50 disabled:cursor-not-allowed"
87
- >
88
- 📊 Get Data
89
- </button>
90
- </div>
91
- </div>
92
-
93
- <div className="mt-4 text-xs text-gray-500 text-center">
94
- Last updated: {new Date(state.lastUpdated).toLocaleTimeString()}
95
- </div>
96
-
97
- {loading && (
98
- <div className="absolute inset-0 bg-white bg-opacity-75 flex items-center justify-center rounded-lg">
99
- <div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-500"></div>
100
- </div>
101
- )}
102
- </div>
103
- );
104
- }