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.
- package/.dockerignore +82 -0
- package/.env.example +19 -0
- package/Dockerfile +70 -0
- package/README.md +6 -3
- package/app/client/SIMPLIFICATION.md +140 -0
- package/app/client/frontend-only.ts +1 -1
- package/app/client/src/App.tsx +148 -283
- package/app/client/src/index.css +5 -20
- package/app/client/src/lib/eden-api.ts +53 -220
- package/app/client/src/main.tsx +2 -3
- package/app/server/app.ts +20 -5
- package/app/server/backend-only.ts +15 -12
- package/app/server/controllers/users.controller.ts +57 -31
- package/app/server/index.ts +86 -96
- package/app/server/live/register-components.ts +18 -7
- package/app/server/routes/env-test.ts +110 -0
- package/app/server/routes/index.ts +1 -8
- package/app/server/routes/users.routes.ts +192 -91
- package/config/app.config.ts +2 -54
- package/config/client.config.ts +95 -0
- package/config/fluxstack.config.ts +2 -2
- package/config/index.ts +57 -22
- package/config/monitoring.config.ts +114 -0
- package/config/plugins.config.ts +80 -0
- package/config/runtime.config.ts +0 -17
- package/config/server.config.ts +50 -30
- package/core/build/bundler.ts +17 -16
- package/core/build/flux-plugins-generator.ts +34 -23
- package/core/build/index.ts +32 -31
- package/core/build/live-components-generator.ts +44 -30
- package/core/build/optimizer.ts +37 -17
- package/core/cli/command-registry.ts +4 -14
- package/core/cli/commands/plugin-deps.ts +8 -8
- package/core/cli/generators/component.ts +3 -3
- package/core/cli/generators/controller.ts +4 -4
- package/core/cli/generators/index.ts +8 -8
- package/core/cli/generators/interactive.ts +4 -4
- package/core/cli/generators/plugin.ts +3 -3
- package/core/cli/generators/prompts.ts +1 -1
- package/core/cli/generators/route.ts +27 -11
- package/core/cli/generators/service.ts +5 -5
- package/core/cli/generators/template-engine.ts +1 -1
- package/core/cli/generators/types.ts +1 -1
- package/core/cli/index.ts +158 -189
- package/core/cli/plugin-discovery.ts +3 -3
- package/core/client/hooks/index.ts +2 -2
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +1 -1
- package/core/client/hooks/useChunkedUpload.ts +1 -1
- package/core/client/hooks/useHybridLiveComponent.ts +1 -1
- package/core/client/hooks/useWebSocket.ts +1 -1
- package/core/config/env.ts +5 -1
- package/core/config/runtime-config.ts +12 -10
- package/core/config/schema.ts +33 -2
- package/core/framework/server.ts +30 -14
- package/core/framework/types.ts +2 -2
- package/core/index.ts +31 -23
- package/core/live/ComponentRegistry.ts +1 -1
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +1 -1
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +65 -161
- package/core/plugins/built-in/static/index.ts +75 -277
- package/core/plugins/built-in/swagger/index.ts +301 -231
- package/core/plugins/built-in/vite/index.ts +342 -377
- package/core/plugins/config.ts +2 -2
- package/core/plugins/dependency-manager.ts +2 -2
- package/core/plugins/discovery.ts +1 -1
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/manager.ts +19 -4
- package/core/plugins/module-resolver.ts +1 -1
- package/core/plugins/registry.ts +25 -21
- package/core/plugins/types.ts +147 -5
- package/core/server/backend-entry.ts +51 -0
- package/core/server/framework.ts +2 -2
- package/core/server/live/ComponentRegistry.ts +9 -26
- package/core/server/live/FileUploadManager.ts +1 -1
- package/core/server/live/auto-generated-components.ts +26 -0
- package/core/server/live/websocket-plugin.ts +211 -19
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +4 -4
- package/core/server/plugins/database.ts +1 -2
- package/core/server/plugins/static-files-plugin.ts +259 -231
- package/core/server/plugins/swagger.ts +1 -1
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +4 -4
- package/core/server/standalone.ts +16 -1
- package/core/testing/index.ts +1 -1
- package/core/testing/setup.ts +1 -1
- package/core/types/plugin.ts +6 -0
- package/core/utils/build-logger.ts +324 -0
- package/core/utils/config-schema.ts +2 -6
- package/core/utils/helpers.ts +14 -9
- package/core/utils/logger/startup-banner.ts +7 -33
- package/core/utils/regenerate-files.ts +69 -0
- package/core/utils/version.ts +6 -6
- package/create-fluxstack.ts +68 -25
- package/fluxstack.config.ts +138 -252
- package/package.json +3 -18
- package/plugins/crypto-auth/index.ts +52 -47
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/helpers.ts +16 -1
- package/vitest.config.ts +17 -26
- package/app/client/src/App.css +0 -883
- package/app/client/src/components/ErrorBoundary.tsx +0 -107
- package/app/client/src/components/ErrorDisplay.css +0 -365
- package/app/client/src/components/ErrorDisplay.tsx +0 -258
- package/app/client/src/components/FluxStackConfig.tsx +0 -1321
- package/app/client/src/components/HybridLiveCounter.tsx +0 -140
- package/app/client/src/components/LiveClock.tsx +0 -286
- package/app/client/src/components/MainLayout.tsx +0 -388
- package/app/client/src/components/SidebarNavigation.tsx +0 -391
- package/app/client/src/components/StateDemo.tsx +0 -178
- package/app/client/src/components/SystemMonitor.tsx +0 -1044
- package/app/client/src/components/UserProfile.tsx +0 -809
- package/app/client/src/hooks/useAuth.ts +0 -39
- package/app/client/src/hooks/useNotifications.ts +0 -56
- package/app/client/src/lib/errors.ts +0 -340
- package/app/client/src/lib/hooks/useErrorHandler.ts +0 -258
- package/app/client/src/lib/index.ts +0 -45
- package/app/client/src/pages/ApiDocs.tsx +0 -182
- package/app/client/src/pages/CryptoAuthPage.tsx +0 -394
- package/app/client/src/pages/Demo.tsx +0 -174
- package/app/client/src/pages/HybridLive.tsx +0 -263
- package/app/client/src/pages/Overview.tsx +0 -155
- package/app/client/src/store/README.md +0 -43
- package/app/client/src/store/index.ts +0 -16
- package/app/client/src/store/slices/uiSlice.ts +0 -151
- package/app/client/src/store/slices/userSlice.ts +0 -161
- package/app/client/src/test/README.md +0 -257
- package/app/client/src/test/setup.ts +0 -70
- package/app/client/src/test/types.ts +0 -12
- package/app/server/live/CounterComponent.ts +0 -191
- package/app/server/live/FluxStackConfig.ts +0 -534
- package/app/server/live/SidebarNavigation.ts +0 -157
- package/app/server/live/SystemMonitor.ts +0 -595
- package/app/server/live/SystemMonitorIntegration.ts +0 -151
- package/app/server/live/UserProfileComponent.ts +0 -141
- package/app/server/middleware/auth.ts +0 -136
- package/app/server/middleware/errorHandling.ts +0 -252
- package/app/server/middleware/index.ts +0 -10
- package/app/server/middleware/rateLimit.ts +0 -193
- package/app/server/middleware/requestLogging.ts +0 -215
- package/app/server/middleware/validation.ts +0 -270
- package/app/server/routes/config.ts +0 -145
- package/app/server/routes/crypto-auth-demo.routes.ts +0 -167
- package/app/server/routes/example-with-crypto-auth.routes.ts +0 -235
- package/app/server/routes/exemplo-posts.routes.ts +0 -161
- package/app/server/routes/upload.ts +0 -92
- package/app/server/services/NotificationService.ts +0 -302
- package/app/server/services/UserService.ts +0 -222
- package/app/server/services/index.ts +0 -46
- package/app/server/types/index.ts +0 -1
- 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
|
-
}
|