create-fluxstack 1.10.1 → 1.12.1
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 +1 -2
- package/Dockerfile +8 -8
- package/LLMD/INDEX.md +64 -0
- package/LLMD/MAINTENANCE.md +197 -0
- package/LLMD/MIGRATION.md +156 -0
- package/LLMD/config/.gitkeep +1 -0
- package/LLMD/config/declarative-system.md +268 -0
- package/LLMD/config/environment-vars.md +327 -0
- package/LLMD/config/runtime-reload.md +401 -0
- package/LLMD/core/.gitkeep +1 -0
- package/LLMD/core/build-system.md +599 -0
- package/LLMD/core/framework-lifecycle.md +229 -0
- package/LLMD/core/plugin-system.md +451 -0
- package/LLMD/patterns/.gitkeep +1 -0
- package/LLMD/patterns/anti-patterns.md +297 -0
- package/LLMD/patterns/project-structure.md +264 -0
- package/LLMD/patterns/type-safety.md +440 -0
- package/LLMD/reference/.gitkeep +1 -0
- package/LLMD/reference/cli-commands.md +250 -0
- package/LLMD/reference/plugin-hooks.md +357 -0
- package/LLMD/reference/routing.md +39 -0
- package/LLMD/reference/troubleshooting.md +364 -0
- package/LLMD/resources/.gitkeep +1 -0
- package/LLMD/resources/controllers.md +465 -0
- package/LLMD/resources/live-components.md +703 -0
- package/LLMD/resources/live-rooms.md +482 -0
- package/LLMD/resources/live-upload.md +130 -0
- package/LLMD/resources/plugins-external.md +617 -0
- package/LLMD/resources/routes-eden.md +254 -0
- package/README.md +37 -17
- package/app/client/index.html +0 -1
- package/app/client/src/App.tsx +107 -150
- package/app/client/src/components/AppLayout.tsx +68 -0
- package/app/client/src/components/BackButton.tsx +13 -0
- package/app/client/src/components/DemoPage.tsx +20 -0
- package/app/client/src/components/LiveUploadWidget.tsx +204 -0
- package/app/client/src/lib/eden-api.ts +85 -60
- package/app/client/src/live/ChatDemo.tsx +107 -0
- package/app/client/src/live/CounterDemo.tsx +206 -0
- package/app/client/src/live/FormDemo.tsx +119 -0
- package/app/client/src/live/RoomChatDemo.tsx +161 -0
- package/app/client/src/live/UploadDemo.tsx +21 -0
- package/app/client/src/main.tsx +4 -1
- package/app/client/src/pages/ApiTestPage.tsx +108 -0
- package/app/client/src/pages/HomePage.tsx +76 -0
- package/app/server/app.ts +1 -4
- package/app/server/controllers/users.controller.ts +36 -44
- package/app/server/index.ts +25 -35
- package/app/server/live/LiveChat.ts +77 -0
- package/app/server/live/LiveCounter.ts +67 -0
- package/app/server/live/LiveForm.ts +63 -0
- package/app/server/live/LiveLocalCounter.ts +32 -0
- package/app/server/live/LiveRoomChat.ts +127 -0
- package/app/server/live/LiveUpload.ts +81 -0
- package/app/server/routes/index.ts +3 -1
- package/app/server/routes/room.routes.ts +117 -0
- package/app/server/routes/users.routes.ts +35 -27
- package/app/shared/types/index.ts +14 -2
- package/config/app.config.ts +2 -62
- package/config/client.config.ts +2 -95
- package/config/database.config.ts +2 -99
- package/config/fluxstack.config.ts +25 -45
- package/config/index.ts +57 -38
- package/config/monitoring.config.ts +2 -114
- package/config/plugins.config.ts +2 -80
- package/config/server.config.ts +2 -68
- package/config/services.config.ts +2 -130
- package/config/system/app.config.ts +29 -0
- package/config/system/build.config.ts +49 -0
- package/config/system/client.config.ts +68 -0
- package/config/system/database.config.ts +17 -0
- package/config/system/fluxstack.config.ts +114 -0
- package/config/{logger.config.ts → system/logger.config.ts} +3 -1
- package/config/system/monitoring.config.ts +114 -0
- package/config/system/plugins.config.ts +84 -0
- package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
- package/config/system/server.config.ts +68 -0
- package/config/system/services.config.ts +46 -0
- package/config/{system.config.ts → system/system.config.ts} +1 -1
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +39 -27
- package/core/build/live-components-generator.ts +3 -3
- package/core/build/optimizer.ts +235 -235
- package/core/cli/command-registry.ts +6 -4
- package/core/cli/commands/build.ts +79 -0
- package/core/cli/commands/create.ts +54 -0
- package/core/cli/commands/dev.ts +101 -0
- package/core/cli/commands/help.ts +34 -0
- package/core/cli/commands/index.ts +34 -0
- package/core/cli/commands/make-plugin.ts +90 -0
- package/core/cli/commands/plugin-add.ts +197 -0
- package/core/cli/commands/plugin-deps.ts +2 -2
- package/core/cli/commands/plugin-list.ts +208 -0
- package/core/cli/commands/plugin-remove.ts +170 -0
- package/core/cli/generators/component.ts +769 -769
- package/core/cli/generators/controller.ts +1 -1
- package/core/cli/generators/index.ts +146 -146
- package/core/cli/generators/interactive.ts +227 -227
- package/core/cli/generators/plugin.ts +2 -2
- package/core/cli/generators/prompts.ts +82 -82
- package/core/cli/generators/route.ts +6 -6
- package/core/cli/generators/service.ts +2 -2
- package/core/cli/generators/template-engine.ts +4 -3
- package/core/cli/generators/types.ts +2 -2
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +115 -686
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/LiveComponentsProvider.tsx +60 -8
- package/core/client/api/eden.ts +183 -0
- package/core/client/api/index.ts +11 -0
- package/core/client/components/Live.tsx +104 -0
- package/core/client/fluxstack.ts +1 -9
- package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/hooks/useChunkedUpload.ts +85 -35
- package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
- package/core/client/hooks/useLiveComponent.ts +800 -0
- package/core/client/hooks/useLiveUpload.ts +71 -0
- package/core/client/hooks/useRoom.ts +409 -0
- package/core/client/hooks/useRoomProxy.ts +382 -0
- package/core/client/index.ts +17 -68
- package/core/client/standalone-entry.ts +8 -0
- package/core/client/standalone.ts +74 -53
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +70 -291
- package/core/config/schema.ts +42 -723
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +47 -40
- package/core/framework/types.ts +2 -2
- package/core/index.ts +23 -4
- package/core/live/ComponentRegistry.ts +3 -3
- package/core/live/types.ts +77 -0
- package/core/plugins/built-in/index.ts +134 -134
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +111 -47
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +68 -265
- package/core/plugins/built-in/vite/index.ts +85 -185
- package/core/plugins/built-in/vite/vite-dev.ts +10 -16
- package/core/plugins/config.ts +9 -7
- package/core/plugins/dependency-manager.ts +31 -1
- package/core/plugins/discovery.ts +19 -7
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +203 -203
- package/core/plugins/manager.ts +27 -39
- package/core/plugins/module-resolver.ts +19 -8
- package/core/plugins/registry.ts +255 -19
- package/core/plugins/types.ts +20 -53
- package/core/server/framework.ts +66 -43
- package/core/server/index.ts +15 -15
- package/core/server/live/ComponentRegistry.ts +78 -71
- package/core/server/live/FileUploadManager.ts +23 -10
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/LiveRoomManager.ts +261 -0
- package/core/server/live/RoomEventBus.ts +234 -0
- package/core/server/live/RoomStateManager.ts +172 -0
- package/core/server/live/StateSignature.ts +643 -643
- package/core/server/live/WebSocketConnectionManager.ts +30 -19
- package/core/server/live/auto-generated-components.ts +21 -9
- package/core/server/live/index.ts +14 -0
- package/core/server/live/websocket-plugin.ts +214 -67
- package/core/server/middleware/elysia-helpers.ts +7 -2
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +180 -180
- package/core/server/plugins/static-files-plugin.ts +69 -69
- package/core/server/plugins/swagger.ts +1 -1
- package/core/server/rooms/RoomBroadcaster.ts +357 -0
- package/core/server/rooms/RoomSystem.ts +463 -0
- package/core/server/rooms/index.ts +13 -0
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +12 -12
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/build.ts +219 -219
- package/core/types/config.ts +56 -26
- package/core/types/index.ts +4 -4
- package/core/types/plugin.ts +107 -107
- package/core/types/types.ts +353 -14
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +2 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +36 -1
- package/core/utils/errors/index.ts +49 -5
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +6 -16
- package/core/utils/index.ts +17 -17
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +13 -9
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +6 -1
- package/core/utils/logger/stack-trace.ts +3 -1
- package/core/utils/logger/startup-banner.ts +82 -82
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +66 -66
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +8 -7
- package/package.json +12 -13
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
- package/plugins/crypto-auth/client/components/index.ts +11 -11
- package/plugins/crypto-auth/client/index.ts +11 -11
- package/plugins/crypto-auth/config/index.ts +1 -1
- package/plugins/crypto-auth/index.ts +4 -4
- package/plugins/crypto-auth/package.json +65 -65
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/index.ts +21 -21
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/tsconfig.api-strict.json +16 -0
- package/tsconfig.json +48 -52
- package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
- package/types/global.d.ts +29 -29
- package/types/vitest.d.ts +8 -8
- package/vite.config.ts +38 -62
- package/vitest.config.live.ts +10 -9
- package/vitest.config.ts +29 -17
- package/app/client/README.md +0 -69
- package/app/client/SIMPLIFICATION.md +0 -140
- package/app/client/frontend-only.ts +0 -12
- package/app/client/src/live/FileUploadExample.tsx +0 -359
- package/app/client/src/live/MinimalLiveClock.tsx +0 -47
- package/app/client/src/live/QuickUploadTest.tsx +0 -193
- package/app/client/tsconfig.app.json +0 -45
- package/app/client/tsconfig.json +0 -7
- package/app/client/zustand-setup.md +0 -65
- package/app/server/backend-only.ts +0 -18
- package/app/server/live/LiveClockComponent.ts +0 -215
- package/app/server/live/LiveFileUploadComponent.ts +0 -77
- package/app/server/routes/env-test.ts +0 -110
- package/core/client/hooks/index.ts +0 -7
- package/core/client/hooks/useHybridLiveComponent.ts +0 -685
- package/core/client/hooks/useTypedLiveComponent.ts +0 -133
- package/core/client/hooks/useWebSocket.ts +0 -361
- package/core/config/env.ts +0 -546
- package/core/config/loader.ts +0 -522
- package/core/config/runtime-config.ts +0 -327
- package/core/config/validator.ts +0 -540
- package/core/server/backend-entry.ts +0 -51
- package/core/server/standalone.ts +0 -106
- package/core/utils/regenerate-files.ts +0 -69
- package/fluxstack.config.ts +0 -354
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Gera rotas protegidas automaticamente
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { CliCommand, CliContext } from '
|
|
6
|
+
import type { CliCommand, CliContext } from '@core/plugins/types'
|
|
7
7
|
import { writeFileSync, existsSync, mkdirSync } from 'fs'
|
|
8
8
|
import { join } from 'path'
|
|
9
9
|
|
|
@@ -1,302 +1,302 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cliente de Autenticação Criptográfica
|
|
3
|
-
* Sistema baseado em assinatura Ed25519 SEM sessões no servidor
|
|
4
|
-
*
|
|
5
|
-
* Funcionamento:
|
|
6
|
-
* 1. Cliente gera par de chaves Ed25519 localmente
|
|
7
|
-
* 2. Chave privada NUNCA sai do navegador
|
|
8
|
-
* 3. Cada requisição é assinada automaticamente
|
|
9
|
-
* 4. Servidor valida assinatura usando chave pública recebida
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { ed25519 } from '@noble/curves/ed25519'
|
|
13
|
-
import { sha256 } from '@noble/hashes/sha256'
|
|
14
|
-
import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
|
|
15
|
-
|
|
16
|
-
export interface KeyPair {
|
|
17
|
-
publicKey: string
|
|
18
|
-
privateKey: string
|
|
19
|
-
createdAt: Date
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface AuthConfig {
|
|
23
|
-
storage?: 'localStorage' | 'sessionStorage' | 'memory'
|
|
24
|
-
autoInit?: boolean
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface SignedRequestOptions extends RequestInit {
|
|
28
|
-
skipAuth?: boolean
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export class CryptoAuthClient {
|
|
32
|
-
private keys: KeyPair | null = null
|
|
33
|
-
private config: AuthConfig
|
|
34
|
-
private storage: Storage | Map<string, string>
|
|
35
|
-
private readonly STORAGE_KEY = 'fluxstack_crypto_keys'
|
|
36
|
-
|
|
37
|
-
constructor(config: AuthConfig = {}) {
|
|
38
|
-
this.config = {
|
|
39
|
-
storage: 'localStorage',
|
|
40
|
-
autoInit: true,
|
|
41
|
-
...config
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Configurar storage
|
|
45
|
-
if (this.config.storage === 'localStorage' && typeof localStorage !== 'undefined') {
|
|
46
|
-
this.storage = localStorage
|
|
47
|
-
} else if (this.config.storage === 'sessionStorage' && typeof sessionStorage !== 'undefined') {
|
|
48
|
-
this.storage = sessionStorage
|
|
49
|
-
} else {
|
|
50
|
-
this.storage = new Map<string, string>()
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Auto-inicializar se configurado
|
|
54
|
-
if (this.config.autoInit) {
|
|
55
|
-
this.initialize()
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Inicializar (gerar ou carregar chaves)
|
|
61
|
-
*/
|
|
62
|
-
initialize(): KeyPair {
|
|
63
|
-
// Tentar carregar chaves existentes
|
|
64
|
-
const existingKeys = this.loadKeys()
|
|
65
|
-
if (existingKeys) {
|
|
66
|
-
this.keys = existingKeys
|
|
67
|
-
return existingKeys
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Criar novo par de chaves
|
|
71
|
-
return this.createNewKeys()
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Criar novo par de chaves
|
|
76
|
-
* NUNCA envia chave privada ao servidor!
|
|
77
|
-
*/
|
|
78
|
-
createNewKeys(): KeyPair {
|
|
79
|
-
// Gerar par de chaves Ed25519
|
|
80
|
-
const privateKey = ed25519.utils.randomPrivateKey()
|
|
81
|
-
const publicKey = ed25519.getPublicKey(privateKey)
|
|
82
|
-
|
|
83
|
-
const keys: KeyPair = {
|
|
84
|
-
publicKey: bytesToHex(publicKey),
|
|
85
|
-
privateKey: bytesToHex(privateKey),
|
|
86
|
-
createdAt: new Date()
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
this.keys = keys
|
|
90
|
-
this.saveKeys(keys)
|
|
91
|
-
|
|
92
|
-
return keys
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Fazer requisição autenticada com assinatura
|
|
97
|
-
*/
|
|
98
|
-
async fetch(url: string, options: SignedRequestOptions = {}): Promise<Response> {
|
|
99
|
-
const { skipAuth = false, ...fetchOptions } = options
|
|
100
|
-
|
|
101
|
-
if (skipAuth) {
|
|
102
|
-
return fetch(url, fetchOptions)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (!this.keys) {
|
|
106
|
-
this.initialize()
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (!this.keys) {
|
|
110
|
-
throw new Error('Chaves não inicializadas')
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Preparar dados de autenticação
|
|
114
|
-
const timestamp = Date.now()
|
|
115
|
-
const nonce = this.generateNonce()
|
|
116
|
-
const message = this.buildMessage(fetchOptions.method || 'GET', url, fetchOptions.body)
|
|
117
|
-
const signature = this.signMessage(message, timestamp, nonce)
|
|
118
|
-
|
|
119
|
-
// Adicionar headers de autenticação
|
|
120
|
-
const headers = {
|
|
121
|
-
'Content-Type': 'application/json',
|
|
122
|
-
...fetchOptions.headers,
|
|
123
|
-
'x-public-key': this.keys.publicKey,
|
|
124
|
-
'x-timestamp': timestamp.toString(),
|
|
125
|
-
'x-nonce': nonce,
|
|
126
|
-
'x-signature': signature
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return fetch(url, {
|
|
130
|
-
...fetchOptions,
|
|
131
|
-
headers
|
|
132
|
-
})
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Obter chaves atuais
|
|
137
|
-
*/
|
|
138
|
-
getKeys(): KeyPair | null {
|
|
139
|
-
return this.keys
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Verificar se tem chaves
|
|
144
|
-
*/
|
|
145
|
-
isInitialized(): boolean {
|
|
146
|
-
return this.keys !== null
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Limpar chaves (logout)
|
|
151
|
-
*/
|
|
152
|
-
clearKeys(): void {
|
|
153
|
-
this.keys = null
|
|
154
|
-
if (this.storage instanceof Map) {
|
|
155
|
-
this.storage.delete(this.STORAGE_KEY)
|
|
156
|
-
} else {
|
|
157
|
-
this.storage.removeItem(this.STORAGE_KEY)
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Importar chave privada existente
|
|
163
|
-
* @param privateKeyHex - Chave privada em formato hexadecimal (64 caracteres)
|
|
164
|
-
* @returns KeyPair com as chaves importadas
|
|
165
|
-
* @throws Error se a chave privada for inválida
|
|
166
|
-
*/
|
|
167
|
-
importPrivateKey(privateKeyHex: string): KeyPair {
|
|
168
|
-
// Validar formato
|
|
169
|
-
if (!/^[a-fA-F0-9]{64}$/.test(privateKeyHex)) {
|
|
170
|
-
throw new Error('Chave privada inválida. Deve ter 64 caracteres hexadecimais.')
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
// Converter hex para bytes
|
|
175
|
-
const privateKeyBytes = hexToBytes(privateKeyHex)
|
|
176
|
-
|
|
177
|
-
// Derivar chave pública da privada
|
|
178
|
-
const publicKeyBytes = ed25519.getPublicKey(privateKeyBytes)
|
|
179
|
-
|
|
180
|
-
const keys: KeyPair = {
|
|
181
|
-
publicKey: bytesToHex(publicKeyBytes),
|
|
182
|
-
privateKey: privateKeyHex.toLowerCase(),
|
|
183
|
-
createdAt: new Date()
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
this.keys = keys
|
|
187
|
-
this.saveKeys(keys)
|
|
188
|
-
|
|
189
|
-
return keys
|
|
190
|
-
} catch (error) {
|
|
191
|
-
throw new Error('Erro ao importar chave privada: ' + (error as Error).message)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Exportar chave privada (para backup)
|
|
197
|
-
* @returns Chave privada em formato hexadecimal
|
|
198
|
-
* @throws Error se não houver chaves inicializadas
|
|
199
|
-
*/
|
|
200
|
-
exportPrivateKey(): string {
|
|
201
|
-
if (!this.keys) {
|
|
202
|
-
throw new Error('Nenhuma chave inicializada para exportar')
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return this.keys.privateKey
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Assinar mensagem
|
|
210
|
-
*/
|
|
211
|
-
private signMessage(message: string, timestamp: number, nonce: string): string {
|
|
212
|
-
if (!this.keys) {
|
|
213
|
-
throw new Error('Chaves não inicializadas')
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Construir mensagem completa: publicKey:timestamp:nonce:message
|
|
217
|
-
const fullMessage = `${this.keys.publicKey}:${timestamp}:${nonce}:${message}`
|
|
218
|
-
const messageHash = sha256(new TextEncoder().encode(fullMessage))
|
|
219
|
-
|
|
220
|
-
const privateKeyBytes = hexToBytes(this.keys.privateKey)
|
|
221
|
-
const signature = ed25519.sign(messageHash, privateKeyBytes)
|
|
222
|
-
|
|
223
|
-
return bytesToHex(signature)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Construir mensagem para assinatura
|
|
228
|
-
*/
|
|
229
|
-
private buildMessage(method: string, url: string, body?: BodyInit | null): string {
|
|
230
|
-
let message = `${method}:${url}`
|
|
231
|
-
|
|
232
|
-
if (body) {
|
|
233
|
-
if (typeof body === 'string') {
|
|
234
|
-
message += `:${body}`
|
|
235
|
-
} else {
|
|
236
|
-
message += `:${JSON.stringify(body)}`
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return message
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Gerar nonce aleatório
|
|
245
|
-
*/
|
|
246
|
-
private generateNonce(): string {
|
|
247
|
-
const bytes = new Uint8Array(16)
|
|
248
|
-
crypto.getRandomValues(bytes)
|
|
249
|
-
return bytesToHex(bytes)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Carregar chaves do storage
|
|
254
|
-
*/
|
|
255
|
-
private loadKeys(): KeyPair | null {
|
|
256
|
-
try {
|
|
257
|
-
let data: string | null
|
|
258
|
-
|
|
259
|
-
if (this.storage instanceof Map) {
|
|
260
|
-
data = this.storage.get(this.STORAGE_KEY) || null
|
|
261
|
-
} else {
|
|
262
|
-
data = this.storage.getItem(this.STORAGE_KEY)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (!data) {
|
|
266
|
-
return null
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const parsed = JSON.parse(data)
|
|
270
|
-
|
|
271
|
-
return {
|
|
272
|
-
publicKey: parsed.publicKey,
|
|
273
|
-
privateKey: parsed.privateKey,
|
|
274
|
-
createdAt: new Date(parsed.createdAt)
|
|
275
|
-
}
|
|
276
|
-
} catch (error) {
|
|
277
|
-
console.error('Erro ao carregar chaves:', error)
|
|
278
|
-
return null
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Salvar chaves no storage
|
|
284
|
-
*/
|
|
285
|
-
private saveKeys(keys: KeyPair): void {
|
|
286
|
-
try {
|
|
287
|
-
const data = JSON.stringify({
|
|
288
|
-
publicKey: keys.publicKey,
|
|
289
|
-
privateKey: keys.privateKey,
|
|
290
|
-
createdAt: keys.createdAt.toISOString()
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
if (this.storage instanceof Map) {
|
|
294
|
-
this.storage.set(this.STORAGE_KEY, data)
|
|
295
|
-
} else {
|
|
296
|
-
this.storage.setItem(this.STORAGE_KEY, data)
|
|
297
|
-
}
|
|
298
|
-
} catch (error) {
|
|
299
|
-
console.error('Erro ao salvar chaves:', error)
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Cliente de Autenticação Criptográfica
|
|
3
|
+
* Sistema baseado em assinatura Ed25519 SEM sessões no servidor
|
|
4
|
+
*
|
|
5
|
+
* Funcionamento:
|
|
6
|
+
* 1. Cliente gera par de chaves Ed25519 localmente
|
|
7
|
+
* 2. Chave privada NUNCA sai do navegador
|
|
8
|
+
* 3. Cada requisição é assinada automaticamente
|
|
9
|
+
* 4. Servidor valida assinatura usando chave pública recebida
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { ed25519 } from '@noble/curves/ed25519'
|
|
13
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
14
|
+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
|
|
15
|
+
|
|
16
|
+
export interface KeyPair {
|
|
17
|
+
publicKey: string
|
|
18
|
+
privateKey: string
|
|
19
|
+
createdAt: Date
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface AuthConfig {
|
|
23
|
+
storage?: 'localStorage' | 'sessionStorage' | 'memory'
|
|
24
|
+
autoInit?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SignedRequestOptions extends RequestInit {
|
|
28
|
+
skipAuth?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class CryptoAuthClient {
|
|
32
|
+
private keys: KeyPair | null = null
|
|
33
|
+
private config: AuthConfig
|
|
34
|
+
private storage: Storage | Map<string, string>
|
|
35
|
+
private readonly STORAGE_KEY = 'fluxstack_crypto_keys'
|
|
36
|
+
|
|
37
|
+
constructor(config: AuthConfig = {}) {
|
|
38
|
+
this.config = {
|
|
39
|
+
storage: 'localStorage',
|
|
40
|
+
autoInit: true,
|
|
41
|
+
...config
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Configurar storage
|
|
45
|
+
if (this.config.storage === 'localStorage' && typeof localStorage !== 'undefined') {
|
|
46
|
+
this.storage = localStorage
|
|
47
|
+
} else if (this.config.storage === 'sessionStorage' && typeof sessionStorage !== 'undefined') {
|
|
48
|
+
this.storage = sessionStorage
|
|
49
|
+
} else {
|
|
50
|
+
this.storage = new Map<string, string>()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Auto-inicializar se configurado
|
|
54
|
+
if (this.config.autoInit) {
|
|
55
|
+
this.initialize()
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Inicializar (gerar ou carregar chaves)
|
|
61
|
+
*/
|
|
62
|
+
initialize(): KeyPair {
|
|
63
|
+
// Tentar carregar chaves existentes
|
|
64
|
+
const existingKeys = this.loadKeys()
|
|
65
|
+
if (existingKeys) {
|
|
66
|
+
this.keys = existingKeys
|
|
67
|
+
return existingKeys
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Criar novo par de chaves
|
|
71
|
+
return this.createNewKeys()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Criar novo par de chaves
|
|
76
|
+
* NUNCA envia chave privada ao servidor!
|
|
77
|
+
*/
|
|
78
|
+
createNewKeys(): KeyPair {
|
|
79
|
+
// Gerar par de chaves Ed25519
|
|
80
|
+
const privateKey = ed25519.utils.randomPrivateKey()
|
|
81
|
+
const publicKey = ed25519.getPublicKey(privateKey)
|
|
82
|
+
|
|
83
|
+
const keys: KeyPair = {
|
|
84
|
+
publicKey: bytesToHex(publicKey),
|
|
85
|
+
privateKey: bytesToHex(privateKey),
|
|
86
|
+
createdAt: new Date()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.keys = keys
|
|
90
|
+
this.saveKeys(keys)
|
|
91
|
+
|
|
92
|
+
return keys
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Fazer requisição autenticada com assinatura
|
|
97
|
+
*/
|
|
98
|
+
async fetch(url: string, options: SignedRequestOptions = {}): Promise<Response> {
|
|
99
|
+
const { skipAuth = false, ...fetchOptions } = options
|
|
100
|
+
|
|
101
|
+
if (skipAuth) {
|
|
102
|
+
return fetch(url, fetchOptions)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!this.keys) {
|
|
106
|
+
this.initialize()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!this.keys) {
|
|
110
|
+
throw new Error('Chaves não inicializadas')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Preparar dados de autenticação
|
|
114
|
+
const timestamp = Date.now()
|
|
115
|
+
const nonce = this.generateNonce()
|
|
116
|
+
const message = this.buildMessage(fetchOptions.method || 'GET', url, fetchOptions.body)
|
|
117
|
+
const signature = this.signMessage(message, timestamp, nonce)
|
|
118
|
+
|
|
119
|
+
// Adicionar headers de autenticação
|
|
120
|
+
const headers = {
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
...fetchOptions.headers,
|
|
123
|
+
'x-public-key': this.keys.publicKey,
|
|
124
|
+
'x-timestamp': timestamp.toString(),
|
|
125
|
+
'x-nonce': nonce,
|
|
126
|
+
'x-signature': signature
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return fetch(url, {
|
|
130
|
+
...fetchOptions,
|
|
131
|
+
headers
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Obter chaves atuais
|
|
137
|
+
*/
|
|
138
|
+
getKeys(): KeyPair | null {
|
|
139
|
+
return this.keys
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Verificar se tem chaves
|
|
144
|
+
*/
|
|
145
|
+
isInitialized(): boolean {
|
|
146
|
+
return this.keys !== null
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Limpar chaves (logout)
|
|
151
|
+
*/
|
|
152
|
+
clearKeys(): void {
|
|
153
|
+
this.keys = null
|
|
154
|
+
if (this.storage instanceof Map) {
|
|
155
|
+
this.storage.delete(this.STORAGE_KEY)
|
|
156
|
+
} else {
|
|
157
|
+
this.storage.removeItem(this.STORAGE_KEY)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Importar chave privada existente
|
|
163
|
+
* @param privateKeyHex - Chave privada em formato hexadecimal (64 caracteres)
|
|
164
|
+
* @returns KeyPair com as chaves importadas
|
|
165
|
+
* @throws Error se a chave privada for inválida
|
|
166
|
+
*/
|
|
167
|
+
importPrivateKey(privateKeyHex: string): KeyPair {
|
|
168
|
+
// Validar formato
|
|
169
|
+
if (!/^[a-fA-F0-9]{64}$/.test(privateKeyHex)) {
|
|
170
|
+
throw new Error('Chave privada inválida. Deve ter 64 caracteres hexadecimais.')
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
// Converter hex para bytes
|
|
175
|
+
const privateKeyBytes = hexToBytes(privateKeyHex)
|
|
176
|
+
|
|
177
|
+
// Derivar chave pública da privada
|
|
178
|
+
const publicKeyBytes = ed25519.getPublicKey(privateKeyBytes)
|
|
179
|
+
|
|
180
|
+
const keys: KeyPair = {
|
|
181
|
+
publicKey: bytesToHex(publicKeyBytes),
|
|
182
|
+
privateKey: privateKeyHex.toLowerCase(),
|
|
183
|
+
createdAt: new Date()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.keys = keys
|
|
187
|
+
this.saveKeys(keys)
|
|
188
|
+
|
|
189
|
+
return keys
|
|
190
|
+
} catch (error) {
|
|
191
|
+
throw new Error('Erro ao importar chave privada: ' + (error as Error).message)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Exportar chave privada (para backup)
|
|
197
|
+
* @returns Chave privada em formato hexadecimal
|
|
198
|
+
* @throws Error se não houver chaves inicializadas
|
|
199
|
+
*/
|
|
200
|
+
exportPrivateKey(): string {
|
|
201
|
+
if (!this.keys) {
|
|
202
|
+
throw new Error('Nenhuma chave inicializada para exportar')
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return this.keys.privateKey
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Assinar mensagem
|
|
210
|
+
*/
|
|
211
|
+
private signMessage(message: string, timestamp: number, nonce: string): string {
|
|
212
|
+
if (!this.keys) {
|
|
213
|
+
throw new Error('Chaves não inicializadas')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Construir mensagem completa: publicKey:timestamp:nonce:message
|
|
217
|
+
const fullMessage = `${this.keys.publicKey}:${timestamp}:${nonce}:${message}`
|
|
218
|
+
const messageHash = sha256(new TextEncoder().encode(fullMessage))
|
|
219
|
+
|
|
220
|
+
const privateKeyBytes = hexToBytes(this.keys.privateKey)
|
|
221
|
+
const signature = ed25519.sign(messageHash, privateKeyBytes)
|
|
222
|
+
|
|
223
|
+
return bytesToHex(signature)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Construir mensagem para assinatura
|
|
228
|
+
*/
|
|
229
|
+
private buildMessage(method: string, url: string, body?: BodyInit | null): string {
|
|
230
|
+
let message = `${method}:${url}`
|
|
231
|
+
|
|
232
|
+
if (body) {
|
|
233
|
+
if (typeof body === 'string') {
|
|
234
|
+
message += `:${body}`
|
|
235
|
+
} else {
|
|
236
|
+
message += `:${JSON.stringify(body)}`
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return message
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Gerar nonce aleatório
|
|
245
|
+
*/
|
|
246
|
+
private generateNonce(): string {
|
|
247
|
+
const bytes = new Uint8Array(16)
|
|
248
|
+
crypto.getRandomValues(bytes)
|
|
249
|
+
return bytesToHex(bytes)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Carregar chaves do storage
|
|
254
|
+
*/
|
|
255
|
+
private loadKeys(): KeyPair | null {
|
|
256
|
+
try {
|
|
257
|
+
let data: string | null
|
|
258
|
+
|
|
259
|
+
if (this.storage instanceof Map) {
|
|
260
|
+
data = this.storage.get(this.STORAGE_KEY) || null
|
|
261
|
+
} else {
|
|
262
|
+
data = this.storage.getItem(this.STORAGE_KEY)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (!data) {
|
|
266
|
+
return null
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const parsed = JSON.parse(data)
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
publicKey: parsed.publicKey,
|
|
273
|
+
privateKey: parsed.privateKey,
|
|
274
|
+
createdAt: new Date(parsed.createdAt)
|
|
275
|
+
}
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error('Erro ao carregar chaves:', error)
|
|
278
|
+
return null
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Salvar chaves no storage
|
|
284
|
+
*/
|
|
285
|
+
private saveKeys(keys: KeyPair): void {
|
|
286
|
+
try {
|
|
287
|
+
const data = JSON.stringify({
|
|
288
|
+
publicKey: keys.publicKey,
|
|
289
|
+
privateKey: keys.privateKey,
|
|
290
|
+
createdAt: keys.createdAt.toISOString()
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
if (this.storage instanceof Map) {
|
|
294
|
+
this.storage.set(this.STORAGE_KEY, data)
|
|
295
|
+
} else {
|
|
296
|
+
this.storage.setItem(this.STORAGE_KEY, data)
|
|
297
|
+
}
|
|
298
|
+
} catch (error) {
|
|
299
|
+
console.error('Erro ao salvar chaves:', error)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exportações dos componentes do cliente
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { LoginButton } from './LoginButton'
|
|
6
|
-
export type { LoginButtonProps } from './LoginButton'
|
|
7
|
-
|
|
8
|
-
export { AuthProvider, useAuth } from './AuthProvider'
|
|
9
|
-
export type { AuthProviderProps, AuthContextValue } from './AuthProvider'
|
|
10
|
-
|
|
11
|
-
export { ProtectedRoute, withAuth } from './ProtectedRoute'
|
|
1
|
+
/**
|
|
2
|
+
* Exportações dos componentes do cliente
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { LoginButton } from './LoginButton'
|
|
6
|
+
export type { LoginButtonProps } from './LoginButton'
|
|
7
|
+
|
|
8
|
+
export { AuthProvider, useAuth } from './AuthProvider'
|
|
9
|
+
export type { AuthProviderProps, AuthContextValue } from './AuthProvider'
|
|
10
|
+
|
|
11
|
+
export { ProtectedRoute, withAuth } from './ProtectedRoute'
|
|
12
12
|
export type { ProtectedRouteProps } from './ProtectedRoute'
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exportações principais do cliente de autenticação
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { CryptoAuthClient } from './CryptoAuthClient'
|
|
6
|
-
export type { KeyPair, AuthConfig, SignedRequestOptions } from './CryptoAuthClient'
|
|
7
|
-
|
|
8
|
-
// Componentes React
|
|
9
|
-
export * from './components'
|
|
10
|
-
|
|
11
|
-
// Re-exportar para compatibilidade
|
|
1
|
+
/**
|
|
2
|
+
* Exportações principais do cliente de autenticação
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { CryptoAuthClient } from './CryptoAuthClient'
|
|
6
|
+
export type { KeyPair, AuthConfig, SignedRequestOptions } from './CryptoAuthClient'
|
|
7
|
+
|
|
8
|
+
// Componentes React
|
|
9
|
+
export * from './components'
|
|
10
|
+
|
|
11
|
+
// Re-exportar para compatibilidade
|
|
12
12
|
export { CryptoAuthClient as default } from './CryptoAuthClient'
|