create-fluxstack 1.5.1 → 1.5.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/.env.example +8 -1
- package/CRYPTO-AUTH-MIDDLEWARE-GUIDE.md +475 -0
- package/CRYPTO-AUTH-MIDDLEWARES.md +473 -0
- package/CRYPTO-AUTH-USAGE.md +491 -0
- package/EXEMPLO-ROTA-PROTEGIDA.md +347 -0
- package/QUICK-START-CRYPTO-AUTH.md +221 -0
- package/app/client/src/App.tsx +4 -1
- package/app/client/src/pages/CryptoAuthPage.tsx +394 -0
- package/app/server/index.ts +4 -0
- package/app/server/live/FluxStackConfig.ts +1 -1
- package/app/server/routes/crypto-auth-demo.routes.ts +167 -0
- package/app/server/routes/example-with-crypto-auth.routes.ts +235 -0
- package/app/server/routes/exemplo-posts.routes.ts +161 -0
- package/app/server/routes/index.ts +5 -1
- package/config/index.ts +9 -1
- package/core/cli/generators/plugin.ts +324 -34
- package/core/cli/generators/template-engine.ts +5 -0
- package/core/cli/plugin-discovery.ts +33 -12
- package/core/framework/server.ts +10 -0
- package/core/plugins/dependency-manager.ts +89 -22
- package/core/plugins/index.ts +4 -0
- package/core/plugins/manager.ts +3 -2
- package/core/plugins/module-resolver.ts +216 -0
- package/core/plugins/registry.ts +28 -1
- package/core/utils/logger/index.ts +4 -0
- package/core/utils/version.ts +1 -1
- package/fluxstack.config.ts +253 -114
- package/package.json +3 -3
- package/plugins/crypto-auth/README.md +788 -0
- package/plugins/crypto-auth/ai-context.md +1282 -0
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +383 -0
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -0
- package/plugins/crypto-auth/client/components/AuthProvider.tsx +131 -0
- package/plugins/crypto-auth/client/components/LoginButton.tsx +138 -0
- package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +89 -0
- package/plugins/crypto-auth/client/components/index.ts +12 -0
- package/plugins/crypto-auth/client/index.ts +12 -0
- package/plugins/crypto-auth/config/index.ts +34 -0
- package/plugins/crypto-auth/index.ts +162 -0
- package/plugins/crypto-auth/package.json +66 -0
- package/plugins/crypto-auth/server/AuthMiddleware.ts +181 -0
- package/plugins/crypto-auth/server/CryptoAuthService.ts +186 -0
- package/plugins/crypto-auth/server/index.ts +22 -0
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +65 -0
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +26 -0
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +76 -0
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +45 -0
- package/plugins/crypto-auth/server/middlewares/helpers.ts +140 -0
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -0
- package/plugins/crypto-auth/server/middlewares.ts +19 -0
- package/test-crypto-auth.ts +101 -0
|
@@ -0,0 +1,347 @@
|
|
|
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. 🚀
|
|
@@ -0,0 +1,221 @@
|
|
|
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. 🚀
|
package/app/client/src/App.tsx
CHANGED
|
@@ -15,6 +15,7 @@ import { OverviewPage } from './pages/Overview'
|
|
|
15
15
|
import { DemoPage } from './pages/Demo'
|
|
16
16
|
import { HybridLivePage } from './pages/HybridLive'
|
|
17
17
|
import { ApiDocsPage } from './pages/ApiDocs'
|
|
18
|
+
import { CryptoAuthPage } from './pages/CryptoAuthPage'
|
|
18
19
|
import { MainLayout } from './components/MainLayout'
|
|
19
20
|
import { LiveClock } from './components/LiveClock'
|
|
20
21
|
|
|
@@ -166,6 +167,7 @@ function AppContent() {
|
|
|
166
167
|
{[
|
|
167
168
|
{ id: 'overview', label: 'Visão Geral', icon: <FaClipboardList />, path: '/' },
|
|
168
169
|
{ id: 'demo', label: 'Demo', icon: <FaRocket />, path: '/demo' },
|
|
170
|
+
{ id: 'crypto-auth', label: 'Crypto Auth', icon: <FaShieldAlt />, path: '/crypto-auth' },
|
|
169
171
|
{ id: 'hybrid-live', label: 'Hybrid Live', icon: <FaBolt />, path: '/hybrid-live' },
|
|
170
172
|
{ id: 'live-app', label: 'Live App', icon: <FaFire />, path: '/live-app' },
|
|
171
173
|
{ id: 'api-docs', label: 'API Docs', icon: <FaBook />, path: '/api-docs' },
|
|
@@ -208,8 +210,8 @@ function AppContent() {
|
|
|
208
210
|
{[
|
|
209
211
|
{ id: 'overview', label: 'Visão', icon: <FaClipboardList />, path: '/' },
|
|
210
212
|
{ id: 'demo', label: 'Demo', icon: <FaRocket />, path: '/demo' },
|
|
213
|
+
{ id: 'crypto-auth', label: 'Crypto', icon: <FaShieldAlt />, path: '/crypto-auth' },
|
|
211
214
|
{ id: 'hybrid-live', label: 'Hybrid', icon: <FaBolt />, path: '/hybrid-live' },
|
|
212
|
-
{ id: 'live-app', label: 'Live', icon: <FaFire />, path: '/live-app' },
|
|
213
215
|
{ id: 'api-docs', label: 'Docs', icon: <FaBook />, path: '/api-docs' },
|
|
214
216
|
{ id: 'tests', label: 'Testes', icon: <FaTest />, path: '/tests' }
|
|
215
217
|
].map(tab => (
|
|
@@ -256,6 +258,7 @@ function AppContent() {
|
|
|
256
258
|
/>
|
|
257
259
|
}
|
|
258
260
|
/>
|
|
261
|
+
<Route path="/crypto-auth" element={<CryptoAuthPage />} />
|
|
259
262
|
<Route path="/hybrid-live" element={<HybridLivePage />} />
|
|
260
263
|
<Route path="/api-docs" element={<ApiDocsPage />} />
|
|
261
264
|
<Route path="/tests" element={
|