create-fluxstack 1.7.5 → 1.8.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.
Files changed (154) hide show
  1. package/.dockerignore +82 -0
  2. package/.env.example +19 -0
  3. package/Dockerfile +70 -0
  4. package/README.md +6 -3
  5. package/app/client/SIMPLIFICATION.md +140 -0
  6. package/app/client/frontend-only.ts +1 -1
  7. package/app/client/src/App.tsx +148 -283
  8. package/app/client/src/index.css +5 -20
  9. package/app/client/src/lib/eden-api.ts +53 -220
  10. package/app/client/src/main.tsx +2 -3
  11. package/app/server/app.ts +20 -5
  12. package/app/server/backend-only.ts +15 -12
  13. package/app/server/controllers/users.controller.ts +57 -31
  14. package/app/server/index.ts +86 -96
  15. package/app/server/live/register-components.ts +18 -7
  16. package/app/server/routes/env-test.ts +110 -0
  17. package/app/server/routes/index.ts +1 -8
  18. package/app/server/routes/users.routes.ts +192 -91
  19. package/config/app.config.ts +2 -54
  20. package/config/client.config.ts +95 -0
  21. package/config/fluxstack.config.ts +2 -2
  22. package/config/index.ts +57 -22
  23. package/config/monitoring.config.ts +114 -0
  24. package/config/plugins.config.ts +80 -0
  25. package/config/runtime.config.ts +0 -17
  26. package/config/server.config.ts +50 -30
  27. package/core/build/bundler.ts +17 -16
  28. package/core/build/flux-plugins-generator.ts +34 -23
  29. package/core/build/index.ts +32 -31
  30. package/core/build/live-components-generator.ts +44 -30
  31. package/core/build/optimizer.ts +37 -17
  32. package/core/cli/command-registry.ts +4 -14
  33. package/core/cli/commands/plugin-deps.ts +8 -8
  34. package/core/cli/generators/component.ts +3 -3
  35. package/core/cli/generators/controller.ts +4 -4
  36. package/core/cli/generators/index.ts +8 -8
  37. package/core/cli/generators/interactive.ts +4 -4
  38. package/core/cli/generators/plugin.ts +3 -3
  39. package/core/cli/generators/prompts.ts +1 -1
  40. package/core/cli/generators/route.ts +27 -11
  41. package/core/cli/generators/service.ts +5 -5
  42. package/core/cli/generators/template-engine.ts +1 -1
  43. package/core/cli/generators/types.ts +1 -1
  44. package/core/cli/index.ts +158 -189
  45. package/core/cli/plugin-discovery.ts +3 -3
  46. package/core/client/hooks/index.ts +2 -2
  47. package/core/client/hooks/state-validator.ts +1 -1
  48. package/core/client/hooks/useAuth.ts +1 -1
  49. package/core/client/hooks/useChunkedUpload.ts +1 -1
  50. package/core/client/hooks/useHybridLiveComponent.ts +1 -1
  51. package/core/client/hooks/useWebSocket.ts +1 -1
  52. package/core/config/env.ts +5 -1
  53. package/core/config/runtime-config.ts +12 -10
  54. package/core/config/schema.ts +33 -2
  55. package/core/framework/server.ts +30 -14
  56. package/core/framework/types.ts +2 -2
  57. package/core/index.ts +31 -23
  58. package/core/live/ComponentRegistry.ts +1 -1
  59. package/core/plugins/built-in/live-components/commands/create-live-component.ts +1 -1
  60. package/core/plugins/built-in/live-components/index.ts +1 -1
  61. package/core/plugins/built-in/monitoring/index.ts +65 -161
  62. package/core/plugins/built-in/static/index.ts +75 -277
  63. package/core/plugins/built-in/swagger/index.ts +301 -231
  64. package/core/plugins/built-in/vite/index.ts +342 -377
  65. package/core/plugins/config.ts +2 -2
  66. package/core/plugins/dependency-manager.ts +2 -2
  67. package/core/plugins/discovery.ts +1 -1
  68. package/core/plugins/executor.ts +2 -2
  69. package/core/plugins/manager.ts +19 -4
  70. package/core/plugins/module-resolver.ts +1 -1
  71. package/core/plugins/registry.ts +25 -21
  72. package/core/plugins/types.ts +147 -5
  73. package/core/server/backend-entry.ts +51 -0
  74. package/core/server/framework.ts +2 -2
  75. package/core/server/live/ComponentRegistry.ts +9 -26
  76. package/core/server/live/FileUploadManager.ts +1 -1
  77. package/core/server/live/auto-generated-components.ts +26 -0
  78. package/core/server/live/websocket-plugin.ts +211 -19
  79. package/core/server/middleware/errorHandling.ts +1 -1
  80. package/core/server/middleware/index.ts +4 -4
  81. package/core/server/plugins/database.ts +1 -2
  82. package/core/server/plugins/static-files-plugin.ts +259 -231
  83. package/core/server/plugins/swagger.ts +1 -1
  84. package/core/server/services/BaseService.ts +1 -1
  85. package/core/server/services/ServiceContainer.ts +1 -1
  86. package/core/server/services/index.ts +4 -4
  87. package/core/server/standalone.ts +16 -1
  88. package/core/testing/index.ts +1 -1
  89. package/core/testing/setup.ts +1 -1
  90. package/core/types/plugin.ts +6 -0
  91. package/core/utils/build-logger.ts +324 -0
  92. package/core/utils/config-schema.ts +2 -6
  93. package/core/utils/helpers.ts +14 -9
  94. package/core/utils/logger/startup-banner.ts +7 -33
  95. package/core/utils/regenerate-files.ts +69 -0
  96. package/core/utils/version.ts +6 -6
  97. package/create-fluxstack.ts +68 -25
  98. package/fluxstack.config.ts +138 -252
  99. package/package.json +3 -18
  100. package/plugins/crypto-auth/index.ts +52 -47
  101. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  102. package/plugins/crypto-auth/server/middlewares/helpers.ts +16 -1
  103. package/vitest.config.ts +17 -26
  104. package/app/client/src/App.css +0 -883
  105. package/app/client/src/components/ErrorBoundary.tsx +0 -107
  106. package/app/client/src/components/ErrorDisplay.css +0 -365
  107. package/app/client/src/components/ErrorDisplay.tsx +0 -258
  108. package/app/client/src/components/FluxStackConfig.tsx +0 -1321
  109. package/app/client/src/components/HybridLiveCounter.tsx +0 -140
  110. package/app/client/src/components/LiveClock.tsx +0 -286
  111. package/app/client/src/components/MainLayout.tsx +0 -388
  112. package/app/client/src/components/SidebarNavigation.tsx +0 -391
  113. package/app/client/src/components/StateDemo.tsx +0 -178
  114. package/app/client/src/components/SystemMonitor.tsx +0 -1044
  115. package/app/client/src/components/UserProfile.tsx +0 -809
  116. package/app/client/src/hooks/useAuth.ts +0 -39
  117. package/app/client/src/hooks/useNotifications.ts +0 -56
  118. package/app/client/src/lib/errors.ts +0 -340
  119. package/app/client/src/lib/hooks/useErrorHandler.ts +0 -258
  120. package/app/client/src/lib/index.ts +0 -45
  121. package/app/client/src/pages/ApiDocs.tsx +0 -182
  122. package/app/client/src/pages/CryptoAuthPage.tsx +0 -394
  123. package/app/client/src/pages/Demo.tsx +0 -174
  124. package/app/client/src/pages/HybridLive.tsx +0 -263
  125. package/app/client/src/pages/Overview.tsx +0 -155
  126. package/app/client/src/store/README.md +0 -43
  127. package/app/client/src/store/index.ts +0 -16
  128. package/app/client/src/store/slices/uiSlice.ts +0 -151
  129. package/app/client/src/store/slices/userSlice.ts +0 -161
  130. package/app/client/src/test/README.md +0 -257
  131. package/app/client/src/test/setup.ts +0 -70
  132. package/app/client/src/test/types.ts +0 -12
  133. package/app/server/live/CounterComponent.ts +0 -191
  134. package/app/server/live/FluxStackConfig.ts +0 -534
  135. package/app/server/live/SidebarNavigation.ts +0 -157
  136. package/app/server/live/SystemMonitor.ts +0 -595
  137. package/app/server/live/SystemMonitorIntegration.ts +0 -151
  138. package/app/server/live/UserProfileComponent.ts +0 -141
  139. package/app/server/middleware/auth.ts +0 -136
  140. package/app/server/middleware/errorHandling.ts +0 -252
  141. package/app/server/middleware/index.ts +0 -10
  142. package/app/server/middleware/rateLimit.ts +0 -193
  143. package/app/server/middleware/requestLogging.ts +0 -215
  144. package/app/server/middleware/validation.ts +0 -270
  145. package/app/server/routes/config.ts +0 -145
  146. package/app/server/routes/crypto-auth-demo.routes.ts +0 -167
  147. package/app/server/routes/example-with-crypto-auth.routes.ts +0 -235
  148. package/app/server/routes/exemplo-posts.routes.ts +0 -161
  149. package/app/server/routes/upload.ts +0 -92
  150. package/app/server/services/NotificationService.ts +0 -302
  151. package/app/server/services/UserService.ts +0 -222
  152. package/app/server/services/index.ts +0 -46
  153. package/app/server/types/index.ts +0 -1
  154. package/config/build.config.ts +0 -24
@@ -1,45 +0,0 @@
1
- // Enhanced error handling exports
2
- export * from './errors'
3
- export * from './eden-api'
4
- export * from './hooks/useErrorHandler'
5
-
6
- // Re-export components
7
- export { ErrorBoundary, useErrorBoundary } from '../components/ErrorBoundary'
8
- export {
9
- ErrorDisplay,
10
- ErrorToast,
11
- InlineError,
12
- LoadingWithError,
13
- ErrorSummary
14
- } from '../components/ErrorDisplay'
15
-
16
- // Convenience exports for common patterns
17
- export {
18
- apiCall,
19
- simpleApiCall,
20
- criticalApiCall,
21
- backgroundApiCall,
22
- userActionApiCall,
23
- getErrorMessage,
24
- isRetryableError,
25
- shouldShowErrorToUser,
26
- getCircuitBreakerState,
27
- resetCircuitBreaker
28
- } from './eden-api'
29
-
30
- export {
31
- ClientAPIError,
32
- NetworkError,
33
- TimeoutError,
34
- withRetry,
35
- withFallback,
36
- CircuitBreaker,
37
- getDefaultUserMessage,
38
- logClientError
39
- } from './errors'
40
-
41
- export {
42
- useErrorHandler,
43
- useApiCall,
44
- useFormSubmission
45
- } from './hooks/useErrorHandler'
@@ -1,182 +0,0 @@
1
- import {
2
- FaBook, FaClipboardList, FaRocket, FaFileAlt, FaCog, FaLock, FaSync, FaCode, FaEye,
3
- FaBolt
4
- } from 'react-icons/fa';
5
-
6
-
7
- export function ApiDocsPage() {
8
- return (
9
- <div className="space-y-8">
10
- {/* Header */}
11
- <div className="text-center">
12
- <div className="flex items-center justify-center gap-3 mb-4">
13
- <FaBook className="text-3xl text-blue-500" />
14
- <h2 className="text-3xl font-bold text-gray-900">Documentação da API</h2>
15
- </div>
16
- <p className="text-lg text-gray-600 max-w-2xl mx-auto">
17
- Documentação interativa gerada automaticamente com Swagger UI
18
- </p>
19
- </div>
20
-
21
- {/* Quick Links */}
22
- <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
23
- <div className="group bg-white rounded-2xl p-6 shadow-lg border border-gray-200 hover:shadow-xl hover:border-blue-300 transition-all duration-300">
24
- <div className="text-center">
25
- <div className="text-4xl mb-4">
26
- <FaClipboardList className="text-blue-500 mx-auto" />
27
- </div>
28
- <h3 className="text-xl font-semibold text-gray-900 mb-2">Swagger UI Interativo</h3>
29
- <p className="text-gray-600 mb-6">Interface completa para testar todos os endpoints da API</p>
30
- <a
31
- href="/swagger"
32
- target="_blank"
33
- rel="noopener noreferrer"
34
- className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white font-medium rounded-xl hover:from-blue-700 hover:to-blue-800 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all duration-200 shadow-lg hover:shadow-xl"
35
- >
36
- <FaRocket className="w-4 h-4" />
37
- Abrir em Nova Aba
38
- </a>
39
- </div>
40
- </div>
41
-
42
- <div className="group bg-white rounded-2xl p-6 shadow-lg border border-gray-200 hover:shadow-xl hover:border-purple-300 transition-all duration-300">
43
- <div className="text-center">
44
- <div className="text-4xl mb-4">
45
- <FaFileAlt className="text-purple-500 mx-auto" />
46
- </div>
47
- <h3 className="text-xl font-semibold text-gray-900 mb-2">OpenAPI Spec (JSON)</h3>
48
- <p className="text-gray-600 mb-6">Especificação OpenAPI em formato JSON para integração</p>
49
- <a
50
- href="/swagger/json"
51
- target="_blank"
52
- rel="noopener noreferrer"
53
- className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-purple-600 to-purple-700 text-white font-medium rounded-xl hover:from-purple-700 hover:to-purple-800 focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 transition-all duration-200 shadow-lg hover:shadow-xl"
54
- >
55
- <FaClipboardList className="w-4 h-4" />
56
- Ver JSON
57
- </a>
58
- </div>
59
- </div>
60
- </div>
61
-
62
- {/* Embedded Swagger */}
63
- <div className="bg-white rounded-2xl shadow-lg border border-gray-200 overflow-hidden">
64
- <div className="bg-gradient-to-r from-gray-50 to-slate-100 px-6 py-4 border-b border-gray-200">
65
- <div className="flex items-center gap-2">
66
- <FaCog className="text-gray-900" />
67
- <h3 className="text-lg font-semibold text-gray-900">Documentação Integrada</h3>
68
- </div>
69
- </div>
70
- <iframe
71
- src="/swagger"
72
- className="w-full h-[600px] border-0"
73
- title="Swagger UI"
74
- />
75
- </div>
76
-
77
- {/* Eden Treaty Guide */}
78
- <div className="bg-white rounded-2xl shadow-lg border border-gray-200 overflow-hidden">
79
- <div className="bg-gradient-to-r from-gray-50 to-slate-100 px-6 py-4 border-b border-gray-200">
80
- <div className="flex items-center gap-2">
81
- <FaCog className="text-gray-900" />
82
- <h3 className="text-lg font-semibold text-gray-900">Como usar Eden Treaty</h3>
83
- </div>
84
- </div>
85
- <div className="p-6 space-y-6">
86
- <div>
87
- <h4 className="text-base font-semibold text-gray-900 mb-3">Configuração do Cliente:</h4>
88
- <pre className="bg-gray-900 text-gray-100 p-4 rounded-xl overflow-x-auto text-sm border border-gray-700">
89
- {`import { treaty } from '@elysiajs/eden'
90
- import type { App } from './server'
91
-
92
- const client = treaty<App>('http://localhost:3000')
93
- export const api = client.api`}
94
- </pre>
95
- </div>
96
-
97
- <div>
98
- <h4 className="text-base font-semibold text-gray-900 mb-3">Exemplos de Uso:</h4>
99
- <pre className="bg-gray-900 text-gray-100 p-4 rounded-xl overflow-x-auto text-sm border border-gray-700">
100
- {`// Listar usuários
101
- const users = await api.users.get()
102
-
103
- // Criar usuário
104
- const newUser = await api.users.post({
105
- name: "João Silva",
106
- email: "joao@example.com"
107
- })
108
-
109
- // Deletar usuário
110
- await api.users["1"].delete()
111
-
112
- // Health check
113
- const health = await api.health.get()`}
114
- </pre>
115
- </div>
116
-
117
- <div>
118
- <h4 className="text-base font-semibold text-gray-900 mb-3">Com tratamento de erros:</h4>
119
- <pre className="bg-gray-900 text-gray-100 p-4 rounded-xl overflow-x-auto text-sm border border-gray-700">
120
- {`try {
121
- const result = await apiCall(api.users.post({
122
- name: "Maria Silva",
123
- email: "maria@example.com"
124
- }))
125
-
126
- if (result.success) {
127
- console.log('Usuário criado:', result.user)
128
- }
129
- } catch (error) {
130
- console.error('Erro:', getErrorMessage(error))
131
- }`}
132
- </pre>
133
- </div>
134
- </div>
135
- </div>
136
-
137
- {/* Features */}
138
- <div className="bg-white rounded-2xl shadow-lg border border-gray-200 overflow-hidden">
139
- <div className="bg-gradient-to-r from-gray-50 to-slate-100 px-6 py-4 border-b border-gray-200">
140
- <div className="flex items-center justify-center gap-2">
141
- <FaBolt className="text-gray-900" />
142
- <h3 className="text-lg font-semibold text-gray-900">Funcionalidades</h3>
143
- </div>
144
- </div>
145
- <div className="p-6">
146
- <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
147
- {[
148
- {
149
- icon: <FaLock className="text-blue-500" />,
150
- title: "Type Safety",
151
- description: "Tipos TypeScript inferidos automaticamente"
152
- },
153
- {
154
- icon: <FaBolt className="text-yellow-500" />,
155
- title: "Auto-complete",
156
- description: "IntelliSense completo no editor"
157
- },
158
- {
159
- icon: <FaSync className="text-green-500" />,
160
- title: "Sincronização",
161
- description: "Mudanças no backend refletem automaticamente no frontend"
162
- },
163
- {
164
- icon: <FaCode className="text-purple-500" />,
165
- title: "Debugging",
166
- description: "Erros de tipo detectados em tempo de compilação"
167
- }
168
- ].map((feature, index) => (
169
- <div key={index} className="flex items-start gap-4 p-4 rounded-xl bg-gray-50 hover:bg-gray-100 transition-colors duration-200">
170
- <div className="text-2xl">{feature.icon}</div>
171
- <div>
172
- <h4 className="font-semibold text-gray-900 mb-1">{feature.title}</h4>
173
- <p className="text-gray-600 text-sm">{feature.description}</p>
174
- </div>
175
- </div>
176
- ))}
177
- </div>
178
- </div>
179
- </div>
180
- </div>
181
- );
182
- }
@@ -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
- }