create-fluxstack 1.5.0 → 1.5.2

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 (50) hide show
  1. package/.env.example +1 -8
  2. package/app/client/src/App.tsx +1 -4
  3. package/app/server/index.ts +0 -4
  4. package/app/server/routes/index.ts +1 -5
  5. package/config/index.ts +1 -9
  6. package/core/cli/generators/plugin.ts +34 -324
  7. package/core/cli/generators/template-engine.ts +0 -5
  8. package/core/cli/plugin-discovery.ts +12 -33
  9. package/core/framework/server.ts +0 -10
  10. package/core/plugins/dependency-manager.ts +22 -89
  11. package/core/plugins/index.ts +0 -4
  12. package/core/plugins/manager.ts +2 -3
  13. package/core/plugins/registry.ts +1 -28
  14. package/core/utils/logger/index.ts +0 -4
  15. package/core/utils/version.ts +1 -1
  16. package/fluxstack.config.ts +114 -253
  17. package/package.json +117 -117
  18. package/CRYPTO-AUTH-MIDDLEWARE-GUIDE.md +0 -475
  19. package/CRYPTO-AUTH-MIDDLEWARES.md +0 -473
  20. package/CRYPTO-AUTH-USAGE.md +0 -491
  21. package/EXEMPLO-ROTA-PROTEGIDA.md +0 -347
  22. package/QUICK-START-CRYPTO-AUTH.md +0 -221
  23. package/app/client/src/pages/CryptoAuthPage.tsx +0 -394
  24. package/app/server/routes/crypto-auth-demo.routes.ts +0 -167
  25. package/app/server/routes/example-with-crypto-auth.routes.ts +0 -235
  26. package/app/server/routes/exemplo-posts.routes.ts +0 -161
  27. package/core/plugins/module-resolver.ts +0 -216
  28. package/plugins/crypto-auth/README.md +0 -788
  29. package/plugins/crypto-auth/ai-context.md +0 -1282
  30. package/plugins/crypto-auth/cli/make-protected-route.command.ts +0 -383
  31. package/plugins/crypto-auth/client/CryptoAuthClient.ts +0 -302
  32. package/plugins/crypto-auth/client/components/AuthProvider.tsx +0 -131
  33. package/plugins/crypto-auth/client/components/LoginButton.tsx +0 -138
  34. package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +0 -89
  35. package/plugins/crypto-auth/client/components/index.ts +0 -12
  36. package/plugins/crypto-auth/client/index.ts +0 -12
  37. package/plugins/crypto-auth/config/index.ts +0 -34
  38. package/plugins/crypto-auth/index.ts +0 -162
  39. package/plugins/crypto-auth/package.json +0 -66
  40. package/plugins/crypto-auth/server/AuthMiddleware.ts +0 -181
  41. package/plugins/crypto-auth/server/CryptoAuthService.ts +0 -186
  42. package/plugins/crypto-auth/server/index.ts +0 -22
  43. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +0 -65
  44. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +0 -26
  45. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +0 -76
  46. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +0 -45
  47. package/plugins/crypto-auth/server/middlewares/helpers.ts +0 -140
  48. package/plugins/crypto-auth/server/middlewares/index.ts +0 -22
  49. package/plugins/crypto-auth/server/middlewares.ts +0 -19
  50. package/test-crypto-auth.ts +0 -101
@@ -1,394 +0,0 @@
1
- import { useState, useEffect } from 'react'
2
- import { FaKey, FaLock, FaUnlock, FaCheckCircle, FaTimesCircle, FaSync, FaShieldAlt, FaCopy, FaFileImport, FaExclamationTriangle } from 'react-icons/fa'
3
- import { CryptoAuthClient, type KeyPair } from '../../../../plugins/crypto-auth/client'
4
-
5
- export function CryptoAuthPage() {
6
- const [authClient] = useState(() => new CryptoAuthClient({
7
- storage: 'sessionStorage', // Usar sessionStorage ao invés de localStorage
8
- autoInit: true // Gerar automaticamente ao inicializar
9
- }))
10
- const [keys, setKeys] = useState<KeyPair | null>(null)
11
- const [loading, setLoading] = useState(false)
12
- const [publicDataResult, setPublicDataResult] = useState<any>(null)
13
- const [protectedDataResult, setProtectedDataResult] = useState<any>(null)
14
- const [copiedKey, setCopiedKey] = useState('')
15
- const [showImportModal, setShowImportModal] = useState(false)
16
- const [importKey, setImportKey] = useState('')
17
- const [importError, setImportError] = useState('')
18
-
19
- useEffect(() => {
20
- const existingKeys = authClient.getKeys()
21
- if (existingKeys) {
22
- setKeys(existingKeys)
23
- }
24
- }, [authClient])
25
-
26
- const handleCreateKeys = () => {
27
- setLoading(true)
28
- try {
29
- const newKeys = authClient.createNewKeys()
30
- setKeys(newKeys)
31
- } catch (error) {
32
- console.error('Erro ao criar chaves:', error)
33
- alert('Erro ao criar chaves: ' + (error as Error).message)
34
- } finally {
35
- setLoading(false)
36
- }
37
- }
38
-
39
- const handleClearKeys = () => {
40
- setLoading(true)
41
- try {
42
- authClient.clearKeys()
43
- setKeys(null)
44
- setPublicDataResult(null)
45
- setProtectedDataResult(null)
46
- } catch (error) {
47
- console.error('Erro ao limpar chaves:', error)
48
- } finally {
49
- setLoading(false)
50
- }
51
- }
52
-
53
- const handleImportKey = () => {
54
- setImportError('')
55
- setLoading(true)
56
- try {
57
- const trimmedKey = importKey.trim()
58
- const importedKeys = authClient.importPrivateKey(trimmedKey)
59
- setKeys(importedKeys)
60
- setShowImportModal(false)
61
- setImportKey('')
62
- } catch (error) {
63
- console.error('Erro ao importar chave:', error)
64
- setImportError((error as Error).message)
65
- } finally {
66
- setLoading(false)
67
- }
68
- }
69
-
70
- const openImportModal = () => {
71
- setShowImportModal(true)
72
- setImportKey('')
73
- setImportError('')
74
- }
75
-
76
- const handlePublicRequest = async () => {
77
- setLoading(true)
78
- try {
79
- const response = await fetch('/api/crypto-auth/public')
80
- const data = await response.json()
81
- setPublicDataResult(data)
82
- } catch (error) {
83
- console.error('Erro na requisição pública:', error)
84
- setPublicDataResult({ error: (error as Error).message })
85
- } finally {
86
- setLoading(false)
87
- }
88
- }
89
-
90
- const handleProtectedRequest = async () => {
91
- setLoading(true)
92
- try {
93
- const response = await authClient.fetch('/api/crypto-auth/protected')
94
- const data = await response.json()
95
- setProtectedDataResult(data)
96
- } catch (error) {
97
- console.error('Erro na requisição protegida:', error)
98
- setProtectedDataResult({ error: (error as Error).message })
99
- } finally {
100
- setLoading(false)
101
- }
102
- }
103
-
104
- const copyToClipboard = (text: string, type: string) => {
105
- navigator.clipboard.writeText(text)
106
- setCopiedKey(type)
107
- setTimeout(() => setCopiedKey(''), 2000)
108
- }
109
-
110
- return (
111
- <div className="space-y-6">
112
- {/* Header */}
113
- <div className="bg-gradient-to-r from-purple-500 to-indigo-600 rounded-xl p-6 text-white">
114
- <div className="flex items-center space-x-3 mb-2">
115
- <FaShieldAlt className="text-3xl" />
116
- <h1 className="text-3xl font-bold">🔐 Crypto Auth Demo</h1>
117
- </div>
118
- <p className="text-purple-100">
119
- Autenticação criptográfica usando Ed25519 - SEM sessões no servidor
120
- </p>
121
- </div>
122
-
123
- {/* Keys Status */}
124
- <div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
125
- <h2 className="text-xl font-bold text-gray-800 mb-4 flex items-center">
126
- <FaKey className="mr-2 text-purple-600" />
127
- Suas Chaves Criptográficas
128
- </h2>
129
-
130
- {!keys ? (
131
- <div className="text-center py-8">
132
- <FaUnlock className="text-6xl text-gray-300 mx-auto mb-4" />
133
- <p className="text-gray-600 mb-4">Nenhum par de chaves gerado</p>
134
- <div className="flex gap-3 justify-center">
135
- <button
136
- onClick={handleCreateKeys}
137
- disabled={loading}
138
- className="bg-purple-600 text-white px-6 py-3 rounded-lg hover:bg-purple-700 disabled:opacity-50 flex items-center"
139
- >
140
- {loading ? <FaSync className="animate-spin mr-2" /> : <FaKey className="mr-2" />}
141
- Gerar Novo Par de Chaves
142
- </button>
143
- <button
144
- onClick={openImportModal}
145
- disabled={loading}
146
- className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 disabled:opacity-50 flex items-center"
147
- >
148
- <FaFileImport className="mr-2" />
149
- Importar Chave Privada
150
- </button>
151
- </div>
152
- </div>
153
- ) : (
154
- <div className="space-y-4">
155
- <div className="flex items-center justify-between p-4 bg-green-50 rounded-lg">
156
- <div className="flex items-center">
157
- <FaLock className="text-green-600 text-2xl mr-3" />
158
- <div>
159
- <p className="font-semibold text-green-800 flex items-center gap-2">
160
- Chaves Ativas
161
- <span className="text-xs bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full">
162
- sessionStorage
163
- </span>
164
- </p>
165
- <p className="text-sm text-green-600">
166
- Criadas em: {keys.createdAt.toLocaleString()}
167
- </p>
168
- </div>
169
- </div>
170
- <div className="flex gap-2">
171
- <button
172
- onClick={openImportModal}
173
- disabled={loading}
174
- className="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 disabled:opacity-50 flex items-center text-sm"
175
- >
176
- <FaFileImport className="mr-2" />
177
- Importar
178
- </button>
179
- <button
180
- onClick={handleClearKeys}
181
- disabled={loading}
182
- className="bg-red-500 text-white px-4 py-2 rounded-lg hover:bg-red-600 disabled:opacity-50"
183
- >
184
- Limpar Chaves
185
- </button>
186
- </div>
187
- </div>
188
-
189
- <div className="grid grid-cols-1 gap-4">
190
- <div className="p-4 bg-gray-50 rounded-lg">
191
- <p className="text-sm text-gray-600 mb-1">Chave Pública (enviada ao servidor)</p>
192
- <div className="flex items-center space-x-2">
193
- <code className="text-xs bg-white px-2 py-1 rounded border flex-1 break-all">
194
- {keys.publicKey}
195
- </code>
196
- <button
197
- onClick={() => copyToClipboard(keys.publicKey, 'public')}
198
- className="text-purple-600 hover:text-purple-700 flex-shrink-0"
199
- >
200
- {copiedKey === 'public' ? <FaCheckCircle /> : <FaCopy />}
201
- </button>
202
- </div>
203
- </div>
204
-
205
- <div className="p-4 bg-red-50 rounded-lg border-2 border-red-200">
206
- <p className="text-sm text-red-600 mb-1 font-bold">⚠️ Chave Privada (NUNCA compartilhar!)</p>
207
- <div className="flex items-center space-x-2">
208
- <code className="text-xs bg-white px-2 py-1 rounded border flex-1 break-all blur-sm hover:blur-none transition">
209
- {keys.privateKey}
210
- </code>
211
- <button
212
- onClick={() => copyToClipboard(keys.privateKey, 'private')}
213
- className="text-red-600 hover:text-red-700 flex-shrink-0"
214
- >
215
- {copiedKey === 'private' ? <FaCheckCircle /> : <FaCopy />}
216
- </button>
217
- </div>
218
- <p className="text-xs text-red-500 mt-2">
219
- Esta chave fica APENAS no seu navegador e nunca é enviada ao servidor
220
- </p>
221
- </div>
222
- </div>
223
- </div>
224
- )}
225
- </div>
226
-
227
- {/* API Tests */}
228
- <div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
229
- <h2 className="text-xl font-bold text-gray-800 mb-4">🧪 Testes de API</h2>
230
-
231
- <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
232
- {/* Public Request */}
233
- <div className="border border-gray-200 rounded-lg p-4">
234
- <h3 className="font-semibold text-gray-800 mb-2">Rota Pública</h3>
235
- <p className="text-sm text-gray-600 mb-3">Não requer autenticação</p>
236
- <button
237
- onClick={handlePublicRequest}
238
- disabled={loading}
239
- className="w-full bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 disabled:opacity-50 mb-3"
240
- >
241
- GET /api/crypto-auth/public
242
- </button>
243
- {publicDataResult && (
244
- <pre className="text-xs bg-gray-900 text-green-400 p-3 rounded overflow-auto max-h-40">
245
- {JSON.stringify(publicDataResult, null, 2)}
246
- </pre>
247
- )}
248
- </div>
249
-
250
- {/* Protected Request */}
251
- <div className="border border-gray-200 rounded-lg p-4">
252
- <h3 className="font-semibold text-gray-800 mb-2">Rota Protegida</h3>
253
- <p className="text-sm text-gray-600 mb-3">Requer assinatura criptográfica</p>
254
- <button
255
- onClick={handleProtectedRequest}
256
- disabled={loading || !keys}
257
- className="w-full bg-purple-500 text-white px-4 py-2 rounded-lg hover:bg-purple-600 disabled:opacity-50 mb-3"
258
- >
259
- GET /api/crypto-auth/protected
260
- </button>
261
- {protectedDataResult && (
262
- <pre className="text-xs bg-gray-900 text-green-400 p-3 rounded overflow-auto max-h-40">
263
- {JSON.stringify(protectedDataResult, null, 2)}
264
- </pre>
265
- )}
266
- </div>
267
- </div>
268
- </div>
269
-
270
- {/* How it Works */}
271
- <div className="bg-blue-50 rounded-xl p-6">
272
- <h2 className="text-xl font-bold text-blue-900 mb-4">🔍 Como Funciona (SEM Sessões)</h2>
273
- <div className="space-y-3 text-sm text-blue-800">
274
- <div className="flex items-start">
275
- <FaCheckCircle className="text-blue-600 mt-1 mr-2 flex-shrink-0" />
276
- <div>
277
- <strong>1. Geração de Chaves:</strong> Par de chaves Ed25519 gerado LOCALMENTE no navegador
278
- </div>
279
- </div>
280
- <div className="flex items-start">
281
- <FaCheckCircle className="text-blue-600 mt-1 mr-2 flex-shrink-0" />
282
- <div>
283
- <strong>2. Chave Privada:</strong> NUNCA sai do navegador, armazenada em sessionStorage (válida apenas durante a sessão)
284
- </div>
285
- </div>
286
- <div className="flex items-start">
287
- <FaCheckCircle className="text-blue-600 mt-1 mr-2 flex-shrink-0" />
288
- <div>
289
- <strong>3. Assinatura:</strong> Cada requisição é assinada: publicKey + timestamp + nonce + mensagem
290
- </div>
291
- </div>
292
- <div className="flex items-start">
293
- <FaCheckCircle className="text-blue-600 mt-1 mr-2 flex-shrink-0" />
294
- <div>
295
- <strong>4. Validação:</strong> Servidor valida assinatura usando a chave pública recebida
296
- </div>
297
- </div>
298
- <div className="flex items-start">
299
- <FaCheckCircle className="text-blue-600 mt-1 mr-2 flex-shrink-0" />
300
- <div>
301
- <strong>5. Headers Enviados:</strong> x-public-key, x-timestamp, x-nonce, x-signature
302
- </div>
303
- </div>
304
- <div className="flex items-start">
305
- <FaCheckCircle className="text-blue-600 mt-1 mr-2 flex-shrink-0" />
306
- <div>
307
- <strong>6. Sem Sessões:</strong> Servidor NÃO armazena nada, apenas valida assinaturas
308
- </div>
309
- </div>
310
- </div>
311
- </div>
312
-
313
- {/* Import Modal */}
314
- {showImportModal && (
315
- <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
316
- <div className="bg-white rounded-xl shadow-2xl p-6 max-w-lg w-full mx-4">
317
- <div className="flex items-center justify-between mb-4">
318
- <h3 className="text-xl font-bold text-gray-800 flex items-center">
319
- <FaFileImport className="mr-2 text-blue-600" />
320
- Importar Chave Privada
321
- </h3>
322
- <button
323
- onClick={() => setShowImportModal(false)}
324
- className="text-gray-400 hover:text-gray-600"
325
- >
326
-
327
- </button>
328
- </div>
329
-
330
- <div className="mb-4">
331
- <label className="block text-sm font-medium text-gray-700 mb-2">
332
- Chave Privada (64 caracteres hexadecimais)
333
- </label>
334
- <textarea
335
- value={importKey}
336
- onChange={(e) => setImportKey(e.target.value)}
337
- placeholder="Digite ou cole sua chave privada aqui..."
338
- className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono text-sm resize-none"
339
- rows={4}
340
- />
341
- <p className="text-xs text-gray-500 mt-1">
342
- {importKey.trim().length}/64 caracteres
343
- </p>
344
- </div>
345
-
346
- {importError && (
347
- <div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg flex items-start">
348
- <FaExclamationTriangle className="text-red-600 mt-0.5 mr-2 flex-shrink-0" />
349
- <p className="text-sm text-red-700">{importError}</p>
350
- </div>
351
- )}
352
-
353
- <div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3 mb-4">
354
- <div className="flex items-start">
355
- <FaExclamationTriangle className="text-yellow-600 mt-0.5 mr-2 flex-shrink-0" />
356
- <div className="text-sm text-yellow-800">
357
- <strong>⚠️ Atenção:</strong> Importar uma chave privada substituirá suas chaves atuais.
358
- A chave pública será derivada automaticamente da chave privada.
359
- </div>
360
- </div>
361
- </div>
362
-
363
- <div className="flex gap-3">
364
- <button
365
- onClick={handleImportKey}
366
- disabled={loading || importKey.trim().length !== 64}
367
- className="flex-1 bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
368
- >
369
- {loading ? (
370
- <>
371
- <FaSync className="animate-spin mr-2" />
372
- Importando...
373
- </>
374
- ) : (
375
- <>
376
- <FaFileImport className="mr-2" />
377
- Importar Chave
378
- </>
379
- )}
380
- </button>
381
- <button
382
- onClick={() => setShowImportModal(false)}
383
- disabled={loading}
384
- className="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 disabled:opacity-50"
385
- >
386
- Cancelar
387
- </button>
388
- </div>
389
- </div>
390
- </div>
391
- )}
392
- </div>
393
- )
394
- }
@@ -1,167 +0,0 @@
1
- /**
2
- * Rotas de demonstração do Crypto Auth Plugin
3
- * ✅ NOVA ABORDAGEM: Middlewares declarativos nas rotas
4
- */
5
-
6
- import { Elysia, t } from 'elysia'
7
- import {
8
- cryptoAuthRequired,
9
- cryptoAuthAdmin,
10
- cryptoAuthOptional,
11
- cryptoAuthPermissions,
12
- getCryptoAuthUser,
13
- isCryptoAuthAdmin
14
- } from '@/plugins/crypto-auth/server'
15
-
16
- export const cryptoAuthDemoRoutes = new Elysia({ prefix: '/crypto-auth' })
17
-
18
- // ========================================
19
- // 🌐 ROTAS PÚBLICAS (sem middleware)
20
- // ========================================
21
-
22
- .get('/public', () => ({
23
- success: true,
24
- message: 'Esta é uma rota pública - acessível sem autenticação',
25
- timestamp: new Date().toISOString(),
26
- note: 'Não usa nenhum middleware crypto-auth'
27
- }))
28
-
29
- .get('/status', ({ request, headers }) => {
30
- const user = getCryptoAuthUser(request)
31
-
32
- return {
33
- authenticated: !!user,
34
- headers: {
35
- hasSignature: !!headers['x-signature'],
36
- hasPublicKey: !!headers['x-public-key'],
37
- hasTimestamp: !!headers['x-timestamp'],
38
- hasNonce: !!headers['x-nonce']
39
- },
40
- user: user ? {
41
- publicKey: user.publicKey.substring(0, 16) + '...',
42
- isAdmin: user.isAdmin,
43
- permissions: user.permissions
44
- } : null,
45
- timestamp: new Date().toISOString()
46
- }
47
- })
48
-
49
- // ========================================
50
- // 🌓 ROTA COM AUTH OPCIONAL
51
- // ========================================
52
-
53
- .guard({}, (app) =>
54
- app.use(cryptoAuthOptional())
55
- .get('/feed', ({ request }) => {
56
- const user = getCryptoAuthUser(request)
57
- const isAuthenticated = !!user
58
-
59
- return {
60
- success: true,
61
- message: isAuthenticated
62
- ? `Feed personalizado para ${user.publicKey.substring(0, 8)}...`
63
- : 'Feed público geral',
64
- posts: [
65
- {
66
- id: 1,
67
- title: 'Post público',
68
- canEdit: isAuthenticated && isCryptoAuthAdmin(request),
69
- premium: false
70
- },
71
- {
72
- id: 2,
73
- title: 'Post premium',
74
- content: isAuthenticated ? 'Conteúdo completo' : 'Conteúdo bloqueado - faça login',
75
- premium: true
76
- }
77
- ],
78
- user: user ? {
79
- publicKey: user.publicKey.substring(0, 8) + '...',
80
- isAdmin: user.isAdmin
81
- } : null
82
- }
83
- })
84
- )
85
-
86
- // ========================================
87
- // 🔒 ROTAS PROTEGIDAS (require auth)
88
- // ========================================
89
-
90
- .guard({}, (app) =>
91
- app.use(cryptoAuthRequired())
92
- .get('/protected', ({ request }) => {
93
- const user = getCryptoAuthUser(request)!
94
-
95
- return {
96
- success: true,
97
- message: 'Acesso autorizado! Assinatura validada.',
98
- user: {
99
- publicKey: user.publicKey.substring(0, 16) + '...',
100
- isAdmin: user.isAdmin,
101
- permissions: user.permissions
102
- },
103
- data: {
104
- secretInfo: 'Dados protegidos - só acessível com assinatura válida',
105
- userLevel: 'authenticated',
106
- timestamp: new Date().toISOString()
107
- }
108
- }
109
- })
110
-
111
- .post('/protected/data', async ({ request, body }) => {
112
- const user = getCryptoAuthUser(request)!
113
- const postBody = body as { query: string }
114
-
115
- return {
116
- success: true,
117
- message: 'Dados processados com segurança',
118
- receivedFrom: user.publicKey.substring(0, 8) + '...',
119
- receivedData: postBody,
120
- processedAt: new Date().toISOString()
121
- }
122
- }, {
123
- body: t.Object({
124
- query: t.String()
125
- })
126
- })
127
- )
128
-
129
- // ========================================
130
- // 👑 ROTAS ADMIN (require admin)
131
- // ========================================
132
-
133
- .guard({}, (app) =>
134
- app.use(cryptoAuthAdmin())
135
- .get('/admin', ({ request }) => {
136
- const user = getCryptoAuthUser(request)!
137
-
138
- return {
139
- success: true,
140
- message: 'Acesso admin autorizado',
141
- user: {
142
- publicKey: user.publicKey.substring(0, 16) + '...',
143
- isAdmin: true,
144
- permissions: user.permissions
145
- },
146
- adminData: {
147
- systemHealth: 'optimal',
148
- totalUsers: 42,
149
- message: 'Dados sensíveis de administração'
150
- }
151
- }
152
- })
153
-
154
- .delete('/admin/users/:id', ({ request, params }) => {
155
- const user = getCryptoAuthUser(request)!
156
-
157
- return {
158
- success: true,
159
- message: `Usuário ${params.id} deletado por admin`,
160
- deletedBy: {
161
- publicKey: user.publicKey.substring(0, 8) + '...',
162
- isAdmin: true
163
- },
164
- timestamp: new Date().toISOString()
165
- }
166
- })
167
- )