create-fluxstack 1.8.1 → 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/.env.example +19 -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/controllers/users.controller.ts +57 -31
- package/app/server/index.ts +5 -2
- package/app/server/live/register-components.ts +18 -7
- package/app/server/routes/env-test.ts +53 -2
- package/app/server/routes/index.ts +1 -8
- package/app/server/routes/users.routes.ts +192 -91
- package/config/fluxstack.config.ts +2 -2
- package/config/plugins.config.ts +22 -1
- package/core/build/flux-plugins-generator.ts +5 -5
- package/core/build/live-components-generator.ts +15 -12
- 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 -193
- 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 +1 -1
- package/core/config/runtime-config.ts +5 -5
- package/core/config/schema.ts +9 -0
- package/core/framework/server.ts +30 -15
- package/core/framework/types.ts +2 -2
- 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 +18 -47
- package/core/plugins/built-in/swagger/index.ts +301 -231
- package/core/plugins/built-in/vite/index.ts +74 -109
- 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 +3 -3
- package/core/plugins/types.ts +147 -5
- 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/utils/logger/startup-banner.ts +7 -33
- package/core/utils/version.ts +6 -6
- package/create-fluxstack.ts +68 -25
- package/package.json +2 -2
- 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 +11 -2
- 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
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Config Management Routes
|
|
3
|
-
* Allows runtime configuration reload and inspection
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Elysia, t } from 'elysia'
|
|
7
|
-
import { appRuntimeConfig } from '@/config/runtime.config'
|
|
8
|
-
|
|
9
|
-
export const configRoutes = new Elysia({ prefix: '/config' })
|
|
10
|
-
/**
|
|
11
|
-
* Get current runtime configuration
|
|
12
|
-
*/
|
|
13
|
-
.get('/', () => {
|
|
14
|
-
return {
|
|
15
|
-
success: true,
|
|
16
|
-
config: appRuntimeConfig.values,
|
|
17
|
-
timestamp: new Date().toISOString()
|
|
18
|
-
}
|
|
19
|
-
}, {
|
|
20
|
-
detail: {
|
|
21
|
-
summary: 'Get current runtime configuration',
|
|
22
|
-
tags: ['Config']
|
|
23
|
-
}
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Reload configuration from environment
|
|
28
|
-
*/
|
|
29
|
-
.post('/reload', () => {
|
|
30
|
-
try {
|
|
31
|
-
const oldConfig = { ...appRuntimeConfig.values }
|
|
32
|
-
const newConfig = appRuntimeConfig.reload()
|
|
33
|
-
|
|
34
|
-
// Find changed fields
|
|
35
|
-
const changes: Record<string, { old: any, new: any }> = {}
|
|
36
|
-
for (const key in newConfig) {
|
|
37
|
-
if (oldConfig[key] !== newConfig[key]) {
|
|
38
|
-
changes[key] = {
|
|
39
|
-
old: oldConfig[key],
|
|
40
|
-
new: newConfig[key]
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
success: true,
|
|
47
|
-
message: 'Configuration reloaded successfully',
|
|
48
|
-
changes,
|
|
49
|
-
timestamp: new Date().toISOString()
|
|
50
|
-
}
|
|
51
|
-
} catch (error: any) {
|
|
52
|
-
return {
|
|
53
|
-
success: false,
|
|
54
|
-
error: error.message,
|
|
55
|
-
timestamp: new Date().toISOString()
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}, {
|
|
59
|
-
detail: {
|
|
60
|
-
summary: 'Reload configuration from environment variables',
|
|
61
|
-
description: 'Reloads configuration without restarting the server. Validates new values before applying.',
|
|
62
|
-
tags: ['Config']
|
|
63
|
-
}
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Get specific config field
|
|
68
|
-
*/
|
|
69
|
-
.get('/:field', ({ params: { field } }) => {
|
|
70
|
-
const value = appRuntimeConfig.get(field as any)
|
|
71
|
-
|
|
72
|
-
if (value === undefined) {
|
|
73
|
-
return {
|
|
74
|
-
success: false,
|
|
75
|
-
error: `Field '${field}' not found`,
|
|
76
|
-
timestamp: new Date().toISOString()
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
success: true,
|
|
82
|
-
field,
|
|
83
|
-
value,
|
|
84
|
-
type: typeof value,
|
|
85
|
-
timestamp: new Date().toISOString()
|
|
86
|
-
}
|
|
87
|
-
}, {
|
|
88
|
-
detail: {
|
|
89
|
-
summary: 'Get specific configuration field',
|
|
90
|
-
tags: ['Config']
|
|
91
|
-
},
|
|
92
|
-
params: t.Object({
|
|
93
|
-
field: t.String()
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Check if config field exists
|
|
99
|
-
*/
|
|
100
|
-
.get('/:field/exists', ({ params: { field } }) => {
|
|
101
|
-
const exists = appRuntimeConfig.has(field as any)
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
success: true,
|
|
105
|
-
field,
|
|
106
|
-
exists,
|
|
107
|
-
timestamp: new Date().toISOString()
|
|
108
|
-
}
|
|
109
|
-
}, {
|
|
110
|
-
detail: {
|
|
111
|
-
summary: 'Check if configuration field exists',
|
|
112
|
-
tags: ['Config']
|
|
113
|
-
},
|
|
114
|
-
params: t.Object({
|
|
115
|
-
field: t.String()
|
|
116
|
-
})
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Health check for config system
|
|
121
|
-
*/
|
|
122
|
-
.get('/health', () => {
|
|
123
|
-
try {
|
|
124
|
-
const config = appRuntimeConfig.values
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
success: true,
|
|
128
|
-
status: 'healthy',
|
|
129
|
-
fieldsLoaded: Object.keys(config).length,
|
|
130
|
-
timestamp: new Date().toISOString()
|
|
131
|
-
}
|
|
132
|
-
} catch (error: any) {
|
|
133
|
-
return {
|
|
134
|
-
success: false,
|
|
135
|
-
status: 'unhealthy',
|
|
136
|
-
error: error.message,
|
|
137
|
-
timestamp: new Date().toISOString()
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}, {
|
|
141
|
-
detail: {
|
|
142
|
-
summary: 'Config system health check',
|
|
143
|
-
tags: ['Config']
|
|
144
|
-
}
|
|
145
|
-
})
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rotas de demonstração do Crypto Auth Plugin
|
|
3
|
-
* ✅ NOVA ABORDAGEM: Middlewares declarativos nas rotas
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Elysia, t } from 'elysia'
|
|
7
|
-
import {
|
|
8
|
-
cryptoAuthRequired,
|
|
9
|
-
cryptoAuthAdmin,
|
|
10
|
-
cryptoAuthOptional,
|
|
11
|
-
cryptoAuthPermissions,
|
|
12
|
-
getCryptoAuthUser,
|
|
13
|
-
isCryptoAuthAdmin
|
|
14
|
-
} from '@/plugins/crypto-auth/server'
|
|
15
|
-
|
|
16
|
-
export const cryptoAuthDemoRoutes = new Elysia({ prefix: '/crypto-auth' })
|
|
17
|
-
|
|
18
|
-
// ========================================
|
|
19
|
-
// 🌐 ROTAS PÚBLICAS (sem middleware)
|
|
20
|
-
// ========================================
|
|
21
|
-
|
|
22
|
-
.get('/public', () => ({
|
|
23
|
-
success: true,
|
|
24
|
-
message: 'Esta é uma rota pública - acessível sem autenticação',
|
|
25
|
-
timestamp: new Date().toISOString(),
|
|
26
|
-
note: 'Não usa nenhum middleware crypto-auth'
|
|
27
|
-
}))
|
|
28
|
-
|
|
29
|
-
.get('/status', ({ request, headers }) => {
|
|
30
|
-
const user = getCryptoAuthUser(request)
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
authenticated: !!user,
|
|
34
|
-
headers: {
|
|
35
|
-
hasSignature: !!headers['x-signature'],
|
|
36
|
-
hasPublicKey: !!headers['x-public-key'],
|
|
37
|
-
hasTimestamp: !!headers['x-timestamp'],
|
|
38
|
-
hasNonce: !!headers['x-nonce']
|
|
39
|
-
},
|
|
40
|
-
user: user ? {
|
|
41
|
-
publicKey: user.publicKey.substring(0, 16) + '...',
|
|
42
|
-
isAdmin: user.isAdmin,
|
|
43
|
-
permissions: user.permissions
|
|
44
|
-
} : null,
|
|
45
|
-
timestamp: new Date().toISOString()
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
// ========================================
|
|
50
|
-
// 🌓 ROTA COM AUTH OPCIONAL
|
|
51
|
-
// ========================================
|
|
52
|
-
|
|
53
|
-
.guard({}, (app) =>
|
|
54
|
-
app.use(cryptoAuthOptional())
|
|
55
|
-
.get('/feed', ({ request }) => {
|
|
56
|
-
const user = getCryptoAuthUser(request)
|
|
57
|
-
const isAuthenticated = !!user
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
success: true,
|
|
61
|
-
message: isAuthenticated
|
|
62
|
-
? `Feed personalizado para ${user.publicKey.substring(0, 8)}...`
|
|
63
|
-
: 'Feed público geral',
|
|
64
|
-
posts: [
|
|
65
|
-
{
|
|
66
|
-
id: 1,
|
|
67
|
-
title: 'Post público',
|
|
68
|
-
canEdit: isAuthenticated && isCryptoAuthAdmin(request),
|
|
69
|
-
premium: false
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id: 2,
|
|
73
|
-
title: 'Post premium',
|
|
74
|
-
content: isAuthenticated ? 'Conteúdo completo' : 'Conteúdo bloqueado - faça login',
|
|
75
|
-
premium: true
|
|
76
|
-
}
|
|
77
|
-
],
|
|
78
|
-
user: user ? {
|
|
79
|
-
publicKey: user.publicKey.substring(0, 8) + '...',
|
|
80
|
-
isAdmin: user.isAdmin
|
|
81
|
-
} : null
|
|
82
|
-
}
|
|
83
|
-
})
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
// ========================================
|
|
87
|
-
// 🔒 ROTAS PROTEGIDAS (require auth)
|
|
88
|
-
// ========================================
|
|
89
|
-
|
|
90
|
-
.guard({}, (app) =>
|
|
91
|
-
app.use(cryptoAuthRequired())
|
|
92
|
-
.get('/protected', ({ request }) => {
|
|
93
|
-
const user = getCryptoAuthUser(request)!
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
success: true,
|
|
97
|
-
message: 'Acesso autorizado! Assinatura validada.',
|
|
98
|
-
user: {
|
|
99
|
-
publicKey: user.publicKey.substring(0, 16) + '...',
|
|
100
|
-
isAdmin: user.isAdmin,
|
|
101
|
-
permissions: user.permissions
|
|
102
|
-
},
|
|
103
|
-
data: {
|
|
104
|
-
secretInfo: 'Dados protegidos - só acessível com assinatura válida',
|
|
105
|
-
userLevel: 'authenticated',
|
|
106
|
-
timestamp: new Date().toISOString()
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
.post('/protected/data', async ({ request, body }) => {
|
|
112
|
-
const user = getCryptoAuthUser(request)!
|
|
113
|
-
const postBody = body as { query: string }
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
success: true,
|
|
117
|
-
message: 'Dados processados com segurança',
|
|
118
|
-
receivedFrom: user.publicKey.substring(0, 8) + '...',
|
|
119
|
-
receivedData: postBody,
|
|
120
|
-
processedAt: new Date().toISOString()
|
|
121
|
-
}
|
|
122
|
-
}, {
|
|
123
|
-
body: t.Object({
|
|
124
|
-
query: t.String()
|
|
125
|
-
})
|
|
126
|
-
})
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
// ========================================
|
|
130
|
-
// 👑 ROTAS ADMIN (require admin)
|
|
131
|
-
// ========================================
|
|
132
|
-
|
|
133
|
-
.guard({}, (app) =>
|
|
134
|
-
app.use(cryptoAuthAdmin())
|
|
135
|
-
.get('/admin', ({ request }) => {
|
|
136
|
-
const user = getCryptoAuthUser(request)!
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
success: true,
|
|
140
|
-
message: 'Acesso admin autorizado',
|
|
141
|
-
user: {
|
|
142
|
-
publicKey: user.publicKey.substring(0, 16) + '...',
|
|
143
|
-
isAdmin: true,
|
|
144
|
-
permissions: user.permissions
|
|
145
|
-
},
|
|
146
|
-
adminData: {
|
|
147
|
-
systemHealth: 'optimal',
|
|
148
|
-
totalUsers: 42,
|
|
149
|
-
message: 'Dados sensíveis de administração'
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
.delete('/admin/users/:id', ({ request, params }) => {
|
|
155
|
-
const user = getCryptoAuthUser(request)!
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
success: true,
|
|
159
|
-
message: `Usuário ${params.id} deletado por admin`,
|
|
160
|
-
deletedBy: {
|
|
161
|
-
publicKey: user.publicKey.substring(0, 8) + '...',
|
|
162
|
-
isAdmin: true
|
|
163
|
-
},
|
|
164
|
-
timestamp: new Date().toISOString()
|
|
165
|
-
}
|
|
166
|
-
})
|
|
167
|
-
)
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exemplo de uso dos middlewares crypto-auth
|
|
3
|
-
* Demonstra como proteger rotas com autenticação criptográfica
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Elysia, t } from 'elysia'
|
|
7
|
-
import {
|
|
8
|
-
cryptoAuthRequired,
|
|
9
|
-
cryptoAuthAdmin,
|
|
10
|
-
cryptoAuthPermissions,
|
|
11
|
-
cryptoAuthOptional,
|
|
12
|
-
getCryptoAuthUser,
|
|
13
|
-
isCryptoAuthAdmin
|
|
14
|
-
} from '@/plugins/crypto-auth/server'
|
|
15
|
-
|
|
16
|
-
// ========================================
|
|
17
|
-
// 1️⃣ ROTAS QUE REQUEREM AUTENTICAÇÃO
|
|
18
|
-
// ========================================
|
|
19
|
-
|
|
20
|
-
export const protectedRoutes = new Elysia()
|
|
21
|
-
// ✅ Aplica middleware a TODAS as rotas deste grupo
|
|
22
|
-
.use(cryptoAuthRequired())
|
|
23
|
-
|
|
24
|
-
// Agora TODAS as rotas abaixo requerem autenticação
|
|
25
|
-
.get('/users/me', ({ request }) => {
|
|
26
|
-
const user = getCryptoAuthUser(request)!
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
profile: {
|
|
30
|
-
publicKey: user.publicKey,
|
|
31
|
-
isAdmin: user.isAdmin,
|
|
32
|
-
permissions: user.permissions
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
.get('/users', ({ request }) => {
|
|
38
|
-
const user = getCryptoAuthUser(request)!
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
users: [
|
|
42
|
-
{ id: 1, name: 'João' },
|
|
43
|
-
{ id: 2, name: 'Maria' }
|
|
44
|
-
],
|
|
45
|
-
requestedBy: user.publicKey.substring(0, 16) + '...'
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
.post('/posts', ({ request, body }) => {
|
|
50
|
-
const user = getCryptoAuthUser(request)!
|
|
51
|
-
const { title, content } = body as { title: string; content: string }
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
success: true,
|
|
55
|
-
post: {
|
|
56
|
-
title,
|
|
57
|
-
content,
|
|
58
|
-
author: user.publicKey,
|
|
59
|
-
createdAt: new Date()
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}, {
|
|
63
|
-
body: t.Object({
|
|
64
|
-
title: t.String(),
|
|
65
|
-
content: t.String()
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
// ========================================
|
|
70
|
-
// 2️⃣ ROTAS QUE REQUEREM ADMIN
|
|
71
|
-
// ========================================
|
|
72
|
-
|
|
73
|
-
export const adminRoutes = new Elysia()
|
|
74
|
-
// ✅ Apenas admins podem acessar
|
|
75
|
-
.use(cryptoAuthAdmin())
|
|
76
|
-
|
|
77
|
-
.get('/admin/stats', () => ({
|
|
78
|
-
totalUsers: 100,
|
|
79
|
-
totalPosts: 500,
|
|
80
|
-
systemHealth: 'optimal'
|
|
81
|
-
}))
|
|
82
|
-
|
|
83
|
-
.delete('/admin/users/:id', ({ params, request }) => {
|
|
84
|
-
const user = getCryptoAuthUser(request)!
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
success: true,
|
|
88
|
-
message: `Usuário ${params.id} deletado`,
|
|
89
|
-
deletedBy: user.publicKey
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
.post('/admin/broadcast', ({ body }) => ({
|
|
94
|
-
success: true,
|
|
95
|
-
message: 'Mensagem enviada para todos os usuários',
|
|
96
|
-
content: body
|
|
97
|
-
}), {
|
|
98
|
-
body: t.Object({
|
|
99
|
-
message: t.String()
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
// ========================================
|
|
104
|
-
// 3️⃣ ROTAS COM PERMISSÕES ESPECÍFICAS
|
|
105
|
-
// ========================================
|
|
106
|
-
|
|
107
|
-
export const writeRoutes = new Elysia()
|
|
108
|
-
// ✅ Requer permissão 'write'
|
|
109
|
-
.use(cryptoAuthPermissions(['write']))
|
|
110
|
-
|
|
111
|
-
.put('/posts/:id', ({ params, body }) => ({
|
|
112
|
-
success: true,
|
|
113
|
-
message: `Post ${params.id} atualizado`,
|
|
114
|
-
data: body
|
|
115
|
-
}), {
|
|
116
|
-
body: t.Object({
|
|
117
|
-
title: t.Optional(t.String()),
|
|
118
|
-
content: t.Optional(t.String())
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
.patch('/posts/:id/publish', ({ params }) => ({
|
|
123
|
-
success: true,
|
|
124
|
-
message: `Post ${params.id} publicado`
|
|
125
|
-
}))
|
|
126
|
-
|
|
127
|
-
// ========================================
|
|
128
|
-
// 4️⃣ ROTAS MISTAS (Opcional)
|
|
129
|
-
// ========================================
|
|
130
|
-
|
|
131
|
-
export const mixedRoutes = new Elysia()
|
|
132
|
-
// ✅ Autenticação OPCIONAL - adiciona user se autenticado
|
|
133
|
-
.use(cryptoAuthOptional())
|
|
134
|
-
|
|
135
|
-
// Comportamento diferente se autenticado
|
|
136
|
-
.get('/posts/:id', ({ request, params }) => {
|
|
137
|
-
const user = getCryptoAuthUser(request)
|
|
138
|
-
const isAdmin = isCryptoAuthAdmin(request)
|
|
139
|
-
|
|
140
|
-
return {
|
|
141
|
-
post: {
|
|
142
|
-
id: params.id,
|
|
143
|
-
title: 'Título do Post',
|
|
144
|
-
// Mostra conteúdo completo apenas se autenticado
|
|
145
|
-
content: user ? 'Conteúdo completo do post...' : 'Prévia...',
|
|
146
|
-
author: 'João'
|
|
147
|
-
},
|
|
148
|
-
viewer: user ? {
|
|
149
|
-
publicKey: user.publicKey.substring(0, 16) + '...',
|
|
150
|
-
canEdit: isAdmin,
|
|
151
|
-
canComment: true
|
|
152
|
-
} : {
|
|
153
|
-
canEdit: false,
|
|
154
|
-
canComment: false
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
.get('/posts', ({ request }) => {
|
|
160
|
-
const user = getCryptoAuthUser(request)
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
posts: [
|
|
164
|
-
{ id: 1, title: 'Post 1' },
|
|
165
|
-
{ id: 2, title: 'Post 2' }
|
|
166
|
-
],
|
|
167
|
-
authenticated: !!user
|
|
168
|
-
}
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
// ========================================
|
|
172
|
-
// 5️⃣ ROTAS COM VERIFICAÇÃO MANUAL
|
|
173
|
-
// ========================================
|
|
174
|
-
|
|
175
|
-
export const customRoutes = new Elysia()
|
|
176
|
-
.use(cryptoAuthRequired())
|
|
177
|
-
|
|
178
|
-
.get('/posts/:id/edit', ({ request, params, set }) => {
|
|
179
|
-
const user = getCryptoAuthUser(request)!
|
|
180
|
-
|
|
181
|
-
// Verificação customizada - apenas autor ou admin
|
|
182
|
-
const post = { id: params.id, authorKey: 'abc123...' } // Buscar do DB
|
|
183
|
-
|
|
184
|
-
const canEdit = user.isAdmin || user.publicKey === post.authorKey
|
|
185
|
-
|
|
186
|
-
if (!canEdit) {
|
|
187
|
-
set.status = 403
|
|
188
|
-
return {
|
|
189
|
-
error: 'Apenas o autor ou admin podem editar este post'
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
post,
|
|
195
|
-
canEdit: true
|
|
196
|
-
}
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
// ========================================
|
|
200
|
-
// 6️⃣ COMBINANDO MÚLTIPLOS MIDDLEWARES
|
|
201
|
-
// ========================================
|
|
202
|
-
|
|
203
|
-
export const combinedRoutes = new Elysia({ prefix: '/api/v1' })
|
|
204
|
-
// Primeiro grupo - rotas públicas
|
|
205
|
-
.get('/health', () => ({ status: 'ok' }))
|
|
206
|
-
|
|
207
|
-
.get('/posts', () => ({
|
|
208
|
-
posts: [/* ... */]
|
|
209
|
-
}))
|
|
210
|
-
|
|
211
|
-
// Segundo grupo - rotas protegidas
|
|
212
|
-
.group('/users', (app) => app
|
|
213
|
-
.use(cryptoAuthRequired())
|
|
214
|
-
.get('/', () => ({ users: [] }))
|
|
215
|
-
.post('/', ({ body }) => ({ created: body }))
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
// Terceiro grupo - rotas admin
|
|
219
|
-
.group('/admin', (app) => app
|
|
220
|
-
.use(cryptoAuthAdmin())
|
|
221
|
-
.get('/stats', () => ({ stats: {} }))
|
|
222
|
-
.delete('/users/:id', ({ params }) => ({ deleted: params.id }))
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
// ========================================
|
|
226
|
-
// 7️⃣ EXPORTAR TODAS AS ROTAS
|
|
227
|
-
// ========================================
|
|
228
|
-
|
|
229
|
-
export const allExampleRoutes = new Elysia()
|
|
230
|
-
.use(protectedRoutes)
|
|
231
|
-
.use(adminRoutes)
|
|
232
|
-
.use(writeRoutes)
|
|
233
|
-
.use(mixedRoutes)
|
|
234
|
-
.use(customRoutes)
|
|
235
|
-
.use(combinedRoutes)
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 🎓 EXEMPLO PRÁTICO: Como criar rotas com Crypto-Auth
|
|
3
|
-
*
|
|
4
|
-
* Este arquivo demonstra como um desenvolvedor cria rotas usando
|
|
5
|
-
* o sistema de autenticação crypto-auth do FluxStack.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { Elysia, t } from 'elysia'
|
|
9
|
-
import {
|
|
10
|
-
cryptoAuthRequired,
|
|
11
|
-
cryptoAuthAdmin,
|
|
12
|
-
cryptoAuthOptional,
|
|
13
|
-
getCryptoAuthUser
|
|
14
|
-
} from '@/plugins/crypto-auth/server'
|
|
15
|
-
|
|
16
|
-
// Mock database (simulação)
|
|
17
|
-
const posts = [
|
|
18
|
-
{ id: 1, title: 'Post Público 1', content: 'Conteúdo aberto', public: true },
|
|
19
|
-
{ id: 2, title: 'Post Público 2', content: 'Conteúdo aberto', public: true }
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
export const exemploPostsRoutes = new Elysia({ prefix: '/exemplo-posts' })
|
|
23
|
-
|
|
24
|
-
// ========================================
|
|
25
|
-
// 🌐 ROTA PÚBLICA - Qualquer um acessa
|
|
26
|
-
// ========================================
|
|
27
|
-
.get('/', () => {
|
|
28
|
-
return {
|
|
29
|
-
success: true,
|
|
30
|
-
message: 'Lista pública de posts',
|
|
31
|
-
posts: posts.filter(p => p.public)
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
// ========================================
|
|
36
|
-
// 🌓 ROTA COM AUTH OPCIONAL
|
|
37
|
-
// Funciona com ou sem autenticação
|
|
38
|
-
// ========================================
|
|
39
|
-
.guard({}, (app) =>
|
|
40
|
-
app.use(cryptoAuthOptional())
|
|
41
|
-
|
|
42
|
-
.get('/:id', ({ request, params }) => {
|
|
43
|
-
const user = getCryptoAuthUser(request)
|
|
44
|
-
const isAuthenticated = !!user
|
|
45
|
-
const post = posts.find(p => p.id === parseInt(params.id))
|
|
46
|
-
|
|
47
|
-
if (!post) {
|
|
48
|
-
return { success: false, error: 'Post não encontrado' }
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
success: true,
|
|
53
|
-
post: {
|
|
54
|
-
...post,
|
|
55
|
-
// ✅ Conteúdo extra apenas para autenticados
|
|
56
|
-
premiumContent: isAuthenticated
|
|
57
|
-
? 'Conteúdo premium exclusivo para usuários autenticados!'
|
|
58
|
-
: null,
|
|
59
|
-
viewer: isAuthenticated
|
|
60
|
-
? `Autenticado: ${user.publicKey.substring(0, 8)}...`
|
|
61
|
-
: 'Visitante anônimo'
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
})
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
// ========================================
|
|
68
|
-
// 🔒 ROTAS PROTEGIDAS - Requer autenticação
|
|
69
|
-
// ========================================
|
|
70
|
-
.guard({}, (app) =>
|
|
71
|
-
app.use(cryptoAuthRequired())
|
|
72
|
-
|
|
73
|
-
// GET /api/exemplo-posts/meus-posts
|
|
74
|
-
.get('/meus-posts', ({ request }) => {
|
|
75
|
-
const user = getCryptoAuthUser(request)!
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
success: true,
|
|
79
|
-
message: `Posts criados por você`,
|
|
80
|
-
user: {
|
|
81
|
-
publicKey: user.publicKey.substring(0, 16) + '...',
|
|
82
|
-
isAdmin: user.isAdmin
|
|
83
|
-
},
|
|
84
|
-
posts: [
|
|
85
|
-
{
|
|
86
|
-
id: 999,
|
|
87
|
-
title: 'Meu Post Privado',
|
|
88
|
-
content: 'Só eu posso ver',
|
|
89
|
-
author: user.publicKey
|
|
90
|
-
}
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
// POST /api/exemplo-posts/criar
|
|
96
|
-
.post('/criar', ({ request, body }) => {
|
|
97
|
-
const user = getCryptoAuthUser(request)!
|
|
98
|
-
const { title, content } = body as { title: string; content: string }
|
|
99
|
-
|
|
100
|
-
const newPost = {
|
|
101
|
-
id: Date.now(),
|
|
102
|
-
title,
|
|
103
|
-
content,
|
|
104
|
-
author: user.publicKey,
|
|
105
|
-
createdAt: new Date().toISOString(),
|
|
106
|
-
public: false
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
posts.push(newPost)
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
success: true,
|
|
113
|
-
message: 'Post criado com sucesso!',
|
|
114
|
-
post: newPost
|
|
115
|
-
}
|
|
116
|
-
}, {
|
|
117
|
-
body: t.Object({
|
|
118
|
-
title: t.String({ minLength: 3 }),
|
|
119
|
-
content: t.String({ minLength: 10 })
|
|
120
|
-
})
|
|
121
|
-
})
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
// ========================================
|
|
125
|
-
// 👑 ROTAS ADMIN - Apenas administradores
|
|
126
|
-
// ========================================
|
|
127
|
-
.guard({}, (app) =>
|
|
128
|
-
app.use(cryptoAuthAdmin())
|
|
129
|
-
|
|
130
|
-
// GET /api/exemplo-posts/admin/todos
|
|
131
|
-
.get('/admin/todos', ({ request }) => {
|
|
132
|
-
const user = getCryptoAuthUser(request)!
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
success: true,
|
|
136
|
-
message: 'Painel administrativo',
|
|
137
|
-
admin: user.publicKey.substring(0, 8) + '...',
|
|
138
|
-
totalPosts: posts.length,
|
|
139
|
-
posts: posts // Admin vê tudo
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
// DELETE /api/exemplo-posts/admin/:id
|
|
144
|
-
.delete('/admin/:id', ({ request, params }) => {
|
|
145
|
-
const user = getCryptoAuthUser(request)!
|
|
146
|
-
const postIndex = posts.findIndex(p => p.id === parseInt(params.id))
|
|
147
|
-
|
|
148
|
-
if (postIndex === -1) {
|
|
149
|
-
return { success: false, error: 'Post não encontrado' }
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const deletedPost = posts.splice(postIndex, 1)[0]
|
|
153
|
-
|
|
154
|
-
return {
|
|
155
|
-
success: true,
|
|
156
|
-
message: `Post "${deletedPost.title}" deletado pelo admin`,
|
|
157
|
-
deletedBy: user.publicKey.substring(0, 8) + '...',
|
|
158
|
-
deletedPost
|
|
159
|
-
}
|
|
160
|
-
})
|
|
161
|
-
)
|