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,473 @@
|
|
|
1
|
+
# 🔐 Crypto Auth - Middlewares Elysia
|
|
2
|
+
|
|
3
|
+
## 🚀 Guia Rápido
|
|
4
|
+
|
|
5
|
+
### Uso Básico
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { Elysia } from 'elysia'
|
|
9
|
+
import { cryptoAuthRequired, cryptoAuthAdmin } from '@/plugins/crypto-auth/server'
|
|
10
|
+
|
|
11
|
+
export const myRoutes = new Elysia()
|
|
12
|
+
// ✅ Aplica autenticação a todas as rotas
|
|
13
|
+
.use(cryptoAuthRequired())
|
|
14
|
+
|
|
15
|
+
.get('/users', ({ request }) => {
|
|
16
|
+
const user = (request as any).user
|
|
17
|
+
return { users: [], requestedBy: user.publicKey }
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
.post('/users', ({ request, body }) => {
|
|
21
|
+
const user = (request as any).user
|
|
22
|
+
return { created: body, by: user.publicKey }
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 📚 Middlewares Disponíveis
|
|
29
|
+
|
|
30
|
+
### 1️⃣ `cryptoAuthRequired()` - Requer Autenticação
|
|
31
|
+
|
|
32
|
+
Valida assinatura e bloqueia acesso se não autenticado.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { cryptoAuthRequired, getCryptoAuthUser } from '@/plugins/crypto-auth/server'
|
|
36
|
+
|
|
37
|
+
export const protectedRoutes = new Elysia()
|
|
38
|
+
.use(cryptoAuthRequired()) // ✅ Todas as rotas protegidas
|
|
39
|
+
|
|
40
|
+
.get('/profile', ({ request }) => {
|
|
41
|
+
const user = getCryptoAuthUser(request)!
|
|
42
|
+
return {
|
|
43
|
+
publicKey: user.publicKey,
|
|
44
|
+
isAdmin: user.isAdmin,
|
|
45
|
+
permissions: user.permissions
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Retorno se não autenticado**:
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"error": {
|
|
54
|
+
"message": "Authentication required",
|
|
55
|
+
"code": "CRYPTO_AUTH_REQUIRED",
|
|
56
|
+
"statusCode": 401
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### 2️⃣ `cryptoAuthAdmin()` - Requer Admin
|
|
64
|
+
|
|
65
|
+
Valida autenticação E verifica se usuário é admin.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { cryptoAuthAdmin } from '@/plugins/crypto-auth/server'
|
|
69
|
+
|
|
70
|
+
export const adminRoutes = new Elysia()
|
|
71
|
+
.use(cryptoAuthAdmin()) // ✅ Apenas admins
|
|
72
|
+
|
|
73
|
+
.get('/admin/stats', () => ({
|
|
74
|
+
totalUsers: 100,
|
|
75
|
+
systemHealth: 'optimal'
|
|
76
|
+
}))
|
|
77
|
+
|
|
78
|
+
.delete('/admin/users/:id', ({ params, request }) => {
|
|
79
|
+
const user = (request as any).user
|
|
80
|
+
return {
|
|
81
|
+
deleted: params.id,
|
|
82
|
+
by: user.publicKey
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Retorno se não for admin**:
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"error": {
|
|
91
|
+
"message": "Admin privileges required",
|
|
92
|
+
"code": "ADMIN_REQUIRED",
|
|
93
|
+
"statusCode": 403,
|
|
94
|
+
"yourPermissions": ["read"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### 3️⃣ `cryptoAuthPermissions(permissions)` - Requer Permissões
|
|
102
|
+
|
|
103
|
+
Valida autenticação E verifica permissões específicas.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { cryptoAuthPermissions } from '@/plugins/crypto-auth/server'
|
|
107
|
+
|
|
108
|
+
export const writeRoutes = new Elysia()
|
|
109
|
+
.use(cryptoAuthPermissions(['write'])) // ✅ Requer permissão 'write'
|
|
110
|
+
|
|
111
|
+
.put('/posts/:id', ({ params, body }) => ({
|
|
112
|
+
updated: params.id,
|
|
113
|
+
data: body
|
|
114
|
+
}))
|
|
115
|
+
|
|
116
|
+
.patch('/posts/:id/publish', ({ params }) => ({
|
|
117
|
+
published: params.id
|
|
118
|
+
}))
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Múltiplas permissões**:
|
|
122
|
+
```typescript
|
|
123
|
+
.use(cryptoAuthPermissions(['write', 'publish']))
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Retorno se sem permissão**:
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"error": {
|
|
130
|
+
"message": "Insufficient permissions",
|
|
131
|
+
"code": "PERMISSION_DENIED",
|
|
132
|
+
"statusCode": 403,
|
|
133
|
+
"required": ["write"],
|
|
134
|
+
"yours": ["read"]
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
### 4️⃣ `cryptoAuthOptional()` - Autenticação Opcional
|
|
142
|
+
|
|
143
|
+
Adiciona `user` se autenticado, mas NÃO bloqueia se não autenticado.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { cryptoAuthOptional, getCryptoAuthUser } from '@/plugins/crypto-auth/server'
|
|
147
|
+
|
|
148
|
+
export const mixedRoutes = new Elysia()
|
|
149
|
+
.use(cryptoAuthOptional()) // ✅ Opcional
|
|
150
|
+
|
|
151
|
+
// Comportamento diferente se autenticado
|
|
152
|
+
.get('/posts/:id', ({ request, params }) => {
|
|
153
|
+
const user = getCryptoAuthUser(request)
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
post: {
|
|
157
|
+
id: params.id,
|
|
158
|
+
title: 'Post Title',
|
|
159
|
+
// Conteúdo completo apenas se autenticado
|
|
160
|
+
content: user ? 'Full content...' : 'Preview...'
|
|
161
|
+
},
|
|
162
|
+
viewer: user ? {
|
|
163
|
+
publicKey: user.publicKey,
|
|
164
|
+
canEdit: user.isAdmin
|
|
165
|
+
} : null
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 🛠️ Helper Functions
|
|
173
|
+
|
|
174
|
+
### `getCryptoAuthUser(request)` - Obter Usuário
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { getCryptoAuthUser } from '@/plugins/crypto-auth/server'
|
|
178
|
+
|
|
179
|
+
.get('/me', ({ request }) => {
|
|
180
|
+
const user = getCryptoAuthUser(request)
|
|
181
|
+
|
|
182
|
+
if (!user) {
|
|
183
|
+
return { error: 'Not authenticated' }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return { user }
|
|
187
|
+
})
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### `isCryptoAuthAuthenticated(request)` - Verificar se Autenticado
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { isCryptoAuthAuthenticated } from '@/plugins/crypto-auth/server'
|
|
196
|
+
|
|
197
|
+
.get('/posts/:id', ({ request, params }) => {
|
|
198
|
+
const isAuth = isCryptoAuthAuthenticated(request)
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
post: { id: params.id },
|
|
202
|
+
canComment: isAuth
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### `isCryptoAuthAdmin(request)` - Verificar se é Admin
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
import { isCryptoAuthAdmin } from '@/plugins/crypto-auth/server'
|
|
213
|
+
|
|
214
|
+
.get('/posts/:id', ({ request, params, set }) => {
|
|
215
|
+
if (!isCryptoAuthAdmin(request)) {
|
|
216
|
+
set.status = 403
|
|
217
|
+
return { error: 'Admin only' }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return { post: params.id }
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### `hasCryptoAuthPermission(request, permission)` - Verificar Permissão
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { hasCryptoAuthPermission } from '@/plugins/crypto-auth/server'
|
|
230
|
+
|
|
231
|
+
.put('/posts/:id', ({ request, params, set }) => {
|
|
232
|
+
if (!hasCryptoAuthPermission(request, 'write')) {
|
|
233
|
+
set.status = 403
|
|
234
|
+
return { error: 'Write permission required' }
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return { updated: params.id }
|
|
238
|
+
})
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 🎯 Padrões de Uso
|
|
244
|
+
|
|
245
|
+
### Padrão 1: Grupo de Rotas Protegidas
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
export const apiRoutes = new Elysia()
|
|
249
|
+
// Rotas públicas
|
|
250
|
+
.get('/health', () => ({ status: 'ok' }))
|
|
251
|
+
.get('/posts', () => ({ posts: [] }))
|
|
252
|
+
|
|
253
|
+
// Grupo protegido
|
|
254
|
+
.group('/users', (app) => app
|
|
255
|
+
.use(cryptoAuthRequired())
|
|
256
|
+
.get('/', () => ({ users: [] }))
|
|
257
|
+
.post('/', ({ body }) => ({ created: body }))
|
|
258
|
+
.delete('/:id', ({ params }) => ({ deleted: params.id }))
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
// Grupo admin
|
|
262
|
+
.group('/admin', (app) => app
|
|
263
|
+
.use(cryptoAuthAdmin())
|
|
264
|
+
.get('/stats', () => ({ stats: {} }))
|
|
265
|
+
)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
### Padrão 2: Middleware Cascata
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
export const routes = new Elysia()
|
|
274
|
+
// Aplica a todas as rotas
|
|
275
|
+
.use(cryptoAuthRequired())
|
|
276
|
+
|
|
277
|
+
.get('/profile', () => ({ profile: {} }))
|
|
278
|
+
|
|
279
|
+
// Sub-grupo com restrição adicional
|
|
280
|
+
.group('/admin', (app) => app
|
|
281
|
+
.use(cryptoAuthAdmin()) // Admin adicional ao required
|
|
282
|
+
.get('/users', () => ({ users: [] }))
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
### Padrão 3: Verificação Manual Combinada
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
export const routes = new Elysia()
|
|
292
|
+
.use(cryptoAuthRequired())
|
|
293
|
+
|
|
294
|
+
.delete('/posts/:id', ({ request, params, set }) => {
|
|
295
|
+
const user = getCryptoAuthUser(request)!
|
|
296
|
+
|
|
297
|
+
// Buscar post do DB
|
|
298
|
+
const post = { id: params.id, authorKey: 'abc...' }
|
|
299
|
+
|
|
300
|
+
// Apenas autor ou admin pode deletar
|
|
301
|
+
const canDelete = user.isAdmin ||
|
|
302
|
+
user.publicKey === post.authorKey
|
|
303
|
+
|
|
304
|
+
if (!canDelete) {
|
|
305
|
+
set.status = 403
|
|
306
|
+
return {
|
|
307
|
+
error: 'Apenas o autor ou admin podem deletar'
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return { deleted: params.id }
|
|
312
|
+
})
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
### Padrão 4: Rotas Condicionais
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
export const routes = new Elysia()
|
|
321
|
+
.use(cryptoAuthOptional())
|
|
322
|
+
|
|
323
|
+
.get('/posts/:id/download', ({ request, params, set }) => {
|
|
324
|
+
const user = getCryptoAuthUser(request)
|
|
325
|
+
|
|
326
|
+
// Usuários autenticados: download ilimitado
|
|
327
|
+
// Não autenticados: limite de 3 por dia
|
|
328
|
+
if (!user) {
|
|
329
|
+
const dailyLimit = checkRateLimit(request.headers.get('x-forwarded-for'))
|
|
330
|
+
if (dailyLimit > 3) {
|
|
331
|
+
set.status = 429
|
|
332
|
+
return { error: 'Rate limit exceeded. Authenticate for unlimited access.' }
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return { download: `post-${params.id}.pdf` }
|
|
337
|
+
})
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## 🔍 Debugging
|
|
343
|
+
|
|
344
|
+
### Log de Autenticação
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
import { cryptoAuthRequired } from '@/plugins/crypto-auth/server'
|
|
348
|
+
|
|
349
|
+
export const routes = new Elysia()
|
|
350
|
+
.use(cryptoAuthRequired({
|
|
351
|
+
logger: yourLogger // ✅ Passar logger para debug
|
|
352
|
+
}))
|
|
353
|
+
|
|
354
|
+
.get('/users', ({ request }) => {
|
|
355
|
+
const user = (request as any).user
|
|
356
|
+
console.log('User:', user)
|
|
357
|
+
return { users: [] }
|
|
358
|
+
})
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
### Rota de Debug
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
export const debugRoutes = new Elysia()
|
|
367
|
+
.use(cryptoAuthOptional())
|
|
368
|
+
|
|
369
|
+
.get('/debug/auth', ({ request }) => {
|
|
370
|
+
const user = getCryptoAuthUser(request)
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
authenticated: !!user,
|
|
374
|
+
user: user || null,
|
|
375
|
+
headers: {
|
|
376
|
+
publicKey: request.headers.get('x-public-key'),
|
|
377
|
+
timestamp: request.headers.get('x-timestamp'),
|
|
378
|
+
nonce: request.headers.get('x-nonce'),
|
|
379
|
+
signature: request.headers.get('x-signature')?.substring(0, 16) + '...'
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
})
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## ⚠️ Importante
|
|
388
|
+
|
|
389
|
+
1. **Ordem importa**: Aplique middlewares antes de definir rotas
|
|
390
|
+
```typescript
|
|
391
|
+
.use(cryptoAuthRequired()) // ✅ Primeiro
|
|
392
|
+
.get('/users', ...) // ✅ Depois
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
2. **Grupos herdam middlewares**:
|
|
396
|
+
```typescript
|
|
397
|
+
.use(cryptoAuthRequired())
|
|
398
|
+
.group('/api', ...) // ✅ Herda cryptoAuthRequired
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
3. **User object sempre disponível**: Em rotas com `cryptoAuthRequired`, `cryptoAuthAdmin` ou `cryptoAuthPermissions`
|
|
402
|
+
|
|
403
|
+
4. **Null check necessário**: Em rotas com `cryptoAuthOptional`:
|
|
404
|
+
```typescript
|
|
405
|
+
const user = getCryptoAuthUser(request)
|
|
406
|
+
if (user) { // ✅ Verificar antes de usar
|
|
407
|
+
...
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## 📦 TypeScript Types
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
interface CryptoAuthUser {
|
|
417
|
+
publicKey: string // Chave pública (ID único)
|
|
418
|
+
isAdmin: boolean // Se é administrador
|
|
419
|
+
permissions: string[] // ["read"] ou ["admin", "read", "write", "delete"]
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## 🆚 Comparação com Config
|
|
426
|
+
|
|
427
|
+
### ❌ Antes (Config Global)
|
|
428
|
+
```typescript
|
|
429
|
+
// config/app.config.ts
|
|
430
|
+
plugins: {
|
|
431
|
+
config: {
|
|
432
|
+
'crypto-auth': {
|
|
433
|
+
protectedRoutes: ["/api/users/*"]
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// routes/users.routes.ts
|
|
439
|
+
.get('/users', ({ request }) => {
|
|
440
|
+
// Protegido automaticamente
|
|
441
|
+
})
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### ✅ Agora (Middlewares)
|
|
445
|
+
```typescript
|
|
446
|
+
// routes/users.routes.ts
|
|
447
|
+
import { cryptoAuthRequired } from '@/plugins/crypto-auth/server'
|
|
448
|
+
|
|
449
|
+
export const routes = new Elysia()
|
|
450
|
+
.use(cryptoAuthRequired()) // ✅ Explícito
|
|
451
|
+
.get('/users', ({ request }) => {
|
|
452
|
+
// Protegido
|
|
453
|
+
})
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**Vantagens**:
|
|
457
|
+
- ✅ Explícito e visível
|
|
458
|
+
- ✅ Type-safe
|
|
459
|
+
- ✅ Mais flexível
|
|
460
|
+
- ✅ Melhor autocomplete
|
|
461
|
+
- ✅ Não depende de config global
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## 📚 Ver Mais
|
|
466
|
+
|
|
467
|
+
- **Exemplo completo**: `app/server/routes/example-with-crypto-auth.routes.ts`
|
|
468
|
+
- **Documentação AI**: `plugins/crypto-auth/ai-context.md`
|
|
469
|
+
- **Demo rotas**: `app/server/routes/crypto-auth-demo.routes.ts`
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
**Última atualização**: Janeiro 2025
|