create-fluxstack 1.12.1 → 1.14.0
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/LLMD/INDEX.md +8 -1
- package/LLMD/agent.md +867 -0
- package/LLMD/config/environment-vars.md +30 -0
- package/LLMD/patterns/anti-patterns.md +100 -0
- package/LLMD/reference/routing.md +39 -39
- package/LLMD/resources/live-auth.md +465 -0
- package/LLMD/resources/live-components.md +168 -26
- package/LLMD/resources/live-logging.md +220 -0
- package/LLMD/resources/live-upload.md +59 -8
- package/LLMD/resources/rest-auth.md +290 -0
- package/README.md +520 -340
- package/app/client/index.html +2 -2
- package/app/client/public/favicon.svg +46 -0
- package/app/client/src/App.tsx +13 -1
- package/app/client/src/assets/fluxstack-static.svg +46 -0
- package/app/client/src/assets/fluxstack.svg +183 -0
- package/app/client/src/components/AppLayout.tsx +139 -9
- package/app/client/src/components/BackButton.tsx +13 -13
- package/app/client/src/components/DemoPage.tsx +4 -4
- package/app/client/src/live/AuthDemo.tsx +334 -0
- package/app/client/src/live/ChatDemo.tsx +2 -2
- package/app/client/src/live/CounterDemo.tsx +12 -12
- package/app/client/src/live/FormDemo.tsx +2 -2
- package/app/client/src/live/LiveDebuggerPanel.tsx +779 -0
- package/app/client/src/live/RoomChatDemo.tsx +24 -16
- package/app/client/src/main.tsx +13 -13
- package/app/client/src/pages/ApiTestPage.tsx +6 -6
- package/app/client/src/pages/HomePage.tsx +80 -52
- package/app/server/auth/AuthManager.ts +213 -0
- package/app/server/auth/DevAuthProvider.ts +66 -0
- package/app/server/auth/HashManager.ts +123 -0
- package/app/server/auth/JWTAuthProvider.example.ts +101 -0
- package/app/server/auth/RateLimiter.ts +106 -0
- package/app/server/auth/contracts.ts +192 -0
- package/app/server/auth/guards/SessionGuard.ts +167 -0
- package/app/server/auth/guards/TokenGuard.ts +202 -0
- package/app/server/auth/index.ts +174 -0
- package/app/server/auth/middleware.ts +163 -0
- package/app/server/auth/providers/InMemoryProvider.ts +162 -0
- package/app/server/auth/sessions/SessionManager.ts +164 -0
- package/app/server/cache/CacheManager.ts +81 -0
- package/app/server/cache/MemoryDriver.ts +112 -0
- package/app/server/cache/contracts.ts +49 -0
- package/app/server/cache/index.ts +42 -0
- package/app/server/index.ts +14 -0
- package/app/server/live/LiveAdminPanel.ts +174 -0
- package/app/server/live/LiveChat.ts +78 -77
- package/app/server/live/LiveCounter.ts +1 -0
- package/app/server/live/LiveForm.ts +1 -0
- package/app/server/live/LiveLocalCounter.ts +38 -32
- package/app/server/live/LiveProtectedChat.ts +151 -0
- package/app/server/live/LiveRoomChat.ts +1 -0
- package/app/server/live/LiveUpload.ts +1 -0
- package/app/server/live/register-components.ts +19 -19
- package/app/server/routes/auth.routes.ts +278 -0
- package/app/server/routes/index.ts +2 -0
- package/config/index.ts +8 -0
- package/config/system/auth.config.ts +49 -0
- package/config/system/runtime.config.ts +4 -0
- package/config/system/session.config.ts +33 -0
- package/core/build/optimizer.ts +235 -235
- package/core/client/LiveComponentsProvider.tsx +76 -5
- package/core/client/components/Live.tsx +17 -10
- package/core/client/components/LiveDebugger.tsx +1324 -0
- package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
- package/core/client/hooks/useLiveComponent.ts +58 -5
- package/core/client/hooks/useLiveDebugger.ts +392 -0
- package/core/client/index.ts +16 -1
- package/core/framework/server.ts +36 -4
- package/core/plugins/built-in/index.ts +134 -134
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +19 -8
- package/core/plugins/built-in/monitoring/index.ts +10 -3
- package/core/plugins/built-in/vite/index.ts +151 -20
- package/core/plugins/config.ts +5 -4
- package/core/plugins/discovery.ts +11 -2
- package/core/plugins/manager.ts +11 -5
- package/core/plugins/module-resolver.ts +1 -1
- package/core/plugins/registry.ts +53 -25
- package/core/server/index.ts +15 -15
- package/core/server/live/ComponentRegistry.ts +134 -50
- package/core/server/live/FileUploadManager.ts +188 -24
- package/core/server/live/LiveComponentPerformanceMonitor.ts +9 -8
- package/core/server/live/LiveDebugger.ts +462 -0
- package/core/server/live/LiveLogger.ts +144 -0
- package/core/server/live/LiveRoomManager.ts +22 -5
- package/core/server/live/StateSignature.ts +704 -643
- package/core/server/live/WebSocketConnectionManager.ts +11 -10
- package/core/server/live/auth/LiveAuthContext.ts +71 -0
- package/core/server/live/auth/LiveAuthManager.ts +304 -0
- package/core/server/live/auth/index.ts +19 -0
- package/core/server/live/auth/types.ts +179 -0
- package/core/server/live/auto-generated-components.ts +8 -2
- package/core/server/live/index.ts +16 -0
- package/core/server/live/websocket-plugin.ts +323 -22
- package/core/server/plugins/static-files-plugin.ts +179 -69
- package/core/templates/create-project.ts +0 -3
- package/core/types/build.ts +219 -219
- package/core/types/plugin.ts +107 -107
- package/core/types/types.ts +278 -22
- package/core/utils/index.ts +17 -17
- package/core/utils/logger/index.ts +5 -2
- package/core/utils/logger/startup-banner.ts +82 -82
- package/core/utils/version.ts +6 -6
- package/package.json +1 -8
- package/plugins/crypto-auth/index.ts +6 -0
- package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +58 -0
- package/plugins/crypto-auth/server/index.ts +24 -21
- package/rest-tests/README.md +57 -0
- package/rest-tests/auth-token.http +113 -0
- package/rest-tests/auth.http +112 -0
- package/rest-tests/rooms-token.http +69 -0
- package/rest-tests/users-token.http +62 -0
- package/.dockerignore +0 -81
- package/Dockerfile +0 -70
- package/LIVE_COMPONENTS_REVIEW.md +0 -781
- package/app/client/src/assets/react.svg +0 -1
|
@@ -47,11 +47,11 @@ export function RoomChatDemo() {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
return (
|
|
50
|
-
<div className="flex h-[600px] bg-gray-900 rounded-2xl overflow-hidden border border-white/10">
|
|
50
|
+
<div className="flex flex-col md:flex-row h-[calc(100vh-200px)] md:h-[600px] w-full max-w-4xl mx-auto bg-gray-900 rounded-2xl overflow-hidden border border-white/10">
|
|
51
51
|
{/* Sidebar */}
|
|
52
|
-
<div className=
|
|
52
|
+
<div className={`${activeRoom ? 'hidden md:flex' : 'flex'} w-full md:w-64 bg-gray-800/50 md:border-r border-white/10 flex-col ${!activeRoom ? 'flex-1 md:flex-initial' : ''}`}>
|
|
53
53
|
<div className="p-4 border-b border-white/10">
|
|
54
|
-
<h2 className="text-lg font-bold text-white mb-2"
|
|
54
|
+
<h2 className="text-lg font-bold text-white mb-2">Room Chat</h2>
|
|
55
55
|
<div className="flex items-center gap-2">
|
|
56
56
|
<div className={`w-2 h-2 rounded-full ${chat.$connected ? 'bg-emerald-400' : 'bg-red-400'}`} />
|
|
57
57
|
<span className="text-sm text-gray-400">{chat.$state.username}</span>
|
|
@@ -94,15 +94,23 @@ export function RoomChatDemo() {
|
|
|
94
94
|
</div>
|
|
95
95
|
|
|
96
96
|
{/* Chat Area */}
|
|
97
|
-
<div className=
|
|
97
|
+
<div className={`${!activeRoom ? 'hidden md:flex' : 'flex'} flex-1 flex-col`}>
|
|
98
98
|
{activeRoom ? (
|
|
99
99
|
<>
|
|
100
100
|
<div className="px-4 py-3 border-b border-white/10 flex items-center justify-between">
|
|
101
|
-
<div>
|
|
102
|
-
<
|
|
103
|
-
{
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
<div className="flex items-center gap-3">
|
|
102
|
+
<button
|
|
103
|
+
onClick={() => chat.switchRoom({ roomId: '' })}
|
|
104
|
+
className="md:hidden px-2 py-1 text-sm text-gray-400 hover:text-white"
|
|
105
|
+
>
|
|
106
|
+
←
|
|
107
|
+
</button>
|
|
108
|
+
<div>
|
|
109
|
+
<h3 className="text-white font-semibold">
|
|
110
|
+
{chat.$state.rooms.find(r => r.id === activeRoom)?.name || activeRoom}
|
|
111
|
+
</h3>
|
|
112
|
+
<p className="text-xs text-gray-500">{activeMessages.length} mensagens</p>
|
|
113
|
+
</div>
|
|
106
114
|
</div>
|
|
107
115
|
<button
|
|
108
116
|
onClick={() => chat.leaveRoom({ roomId: activeRoom })}
|
|
@@ -110,7 +118,7 @@ export function RoomChatDemo() {
|
|
|
110
118
|
>Sair</button>
|
|
111
119
|
</div>
|
|
112
120
|
|
|
113
|
-
<div className="flex-1 overflow-auto p-4 space-y-3">
|
|
121
|
+
<div className="flex-1 overflow-auto p-3 sm:p-4 space-y-3">
|
|
114
122
|
{activeMessages.length === 0 ? (
|
|
115
123
|
<div className="text-center text-gray-500 py-8">
|
|
116
124
|
<p>Nenhuma mensagem ainda</p>
|
|
@@ -119,9 +127,9 @@ export function RoomChatDemo() {
|
|
|
119
127
|
) : (
|
|
120
128
|
activeMessages.map(msg => (
|
|
121
129
|
<div key={msg.id} className={`flex flex-col ${msg.user === chat.$state.username ? 'items-end' : 'items-start'}`}>
|
|
122
|
-
<div className={`max-w-[80%] rounded-2xl px-4 py-2 ${msg.user === chat.$state.username ? 'bg-purple-500/30 text-purple-100' : 'bg-white/10 text-gray-200'}`}>
|
|
130
|
+
<div className={`max-w-[85%] sm:max-w-[80%] rounded-2xl px-3 sm:px-4 py-2 ${msg.user === chat.$state.username ? 'bg-purple-500/30 text-purple-100' : 'bg-white/10 text-gray-200'}`}>
|
|
123
131
|
<p className="text-xs text-gray-400 mb-1">{msg.user}</p>
|
|
124
|
-
<p>{msg.text}</p>
|
|
132
|
+
<p className="text-sm sm:text-base">{msg.text}</p>
|
|
125
133
|
</div>
|
|
126
134
|
<span className="text-xs text-gray-600 mt-1">{new Date(msg.timestamp).toLocaleTimeString()}</span>
|
|
127
135
|
</div>
|
|
@@ -130,19 +138,19 @@ export function RoomChatDemo() {
|
|
|
130
138
|
<div ref={messagesEndRef} />
|
|
131
139
|
</div>
|
|
132
140
|
|
|
133
|
-
<div className="p-4 border-t border-white/10">
|
|
141
|
+
<div className="p-3 sm:p-4 border-t border-white/10">
|
|
134
142
|
<div className="flex gap-2">
|
|
135
143
|
<input
|
|
136
144
|
value={text}
|
|
137
145
|
onChange={(e) => setText(e.target.value)}
|
|
138
146
|
onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSendMessage() } }}
|
|
139
147
|
placeholder="Digite uma mensagem..."
|
|
140
|
-
className="flex-1 px-4 py-2 rounded-xl bg-white/10 border border-white/20 text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500/50"
|
|
148
|
+
className="flex-1 px-3 sm:px-4 py-2 rounded-xl bg-white/10 border border-white/20 text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500/50 text-sm sm:text-base"
|
|
141
149
|
/>
|
|
142
150
|
<button
|
|
143
151
|
onClick={handleSendMessage}
|
|
144
152
|
disabled={!text.trim()}
|
|
145
|
-
className="px-6 py-2 rounded-xl bg-purple-500/30 text-purple-200 hover:bg-purple-500/40 disabled:opacity-50"
|
|
153
|
+
className="px-4 sm:px-6 py-2 rounded-xl bg-purple-500/30 text-purple-200 hover:bg-purple-500/40 disabled:opacity-50 text-sm sm:text-base"
|
|
146
154
|
>Enviar</button>
|
|
147
155
|
</div>
|
|
148
156
|
</div>
|
|
@@ -150,7 +158,7 @@ export function RoomChatDemo() {
|
|
|
150
158
|
) : (
|
|
151
159
|
<div className="flex-1 flex items-center justify-center text-gray-500">
|
|
152
160
|
<div className="text-center">
|
|
153
|
-
<p className="text-4xl mb-4"
|
|
161
|
+
<p className="text-4xl mb-4">←</p>
|
|
154
162
|
<p>Selecione uma sala para começar</p>
|
|
155
163
|
</div>
|
|
156
164
|
</div>
|
package/app/client/src/main.tsx
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { StrictMode } from 'react'
|
|
2
|
-
import { createRoot } from 'react-dom/client'
|
|
3
|
-
import { BrowserRouter } from 'react-router'
|
|
4
|
-
import './index.css'
|
|
5
|
-
import App from './App.tsx'
|
|
6
|
-
|
|
7
|
-
createRoot(document.getElementById('root')!).render(
|
|
8
|
-
<StrictMode>
|
|
9
|
-
<BrowserRouter>
|
|
10
|
-
<App />
|
|
11
|
-
</BrowserRouter>
|
|
12
|
-
</StrictMode>,
|
|
13
|
-
)
|
|
1
|
+
import { StrictMode } from 'react'
|
|
2
|
+
import { createRoot } from 'react-dom/client'
|
|
3
|
+
import { BrowserRouter } from 'react-router'
|
|
4
|
+
import './index.css'
|
|
5
|
+
import App from './App.tsx'
|
|
6
|
+
|
|
7
|
+
createRoot(document.getElementById('root')!).render(
|
|
8
|
+
<StrictMode>
|
|
9
|
+
<BrowserRouter>
|
|
10
|
+
<App />
|
|
11
|
+
</BrowserRouter>
|
|
12
|
+
</StrictMode>,
|
|
13
|
+
)
|
|
@@ -15,10 +15,10 @@ export function ApiTestPage({
|
|
|
15
15
|
onCreateUser: () => void
|
|
16
16
|
}) {
|
|
17
17
|
return (
|
|
18
|
-
<div className="container mx-auto px-4 py-8 max-w-4xl">
|
|
19
|
-
<div className="flex items-center gap-4 mb-8">
|
|
18
|
+
<div className="container mx-auto px-3 sm:px-4 py-6 sm:py-8 max-w-4xl">
|
|
19
|
+
<div className="flex items-center gap-3 sm:gap-4 mb-6 sm:mb-8">
|
|
20
20
|
<BackButton />
|
|
21
|
-
<h1 className="text-3xl font-bold bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">
|
|
21
|
+
<h1 className="text-2xl sm:text-3xl font-bold bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent">
|
|
22
22
|
Eden Treaty API Test
|
|
23
23
|
</h1>
|
|
24
24
|
</div>
|
|
@@ -50,7 +50,7 @@ export function ApiTestPage({
|
|
|
50
50
|
/>
|
|
51
51
|
</div>
|
|
52
52
|
|
|
53
|
-
<div className="bg-black/40 backdrop-blur-sm border border-white/10 rounded-2xl p-6">
|
|
53
|
+
<div className="bg-black/40 backdrop-blur-sm border border-white/10 rounded-2xl p-4 sm:p-6">
|
|
54
54
|
<div className="flex items-center justify-between mb-4">
|
|
55
55
|
<h2 className="text-lg font-semibold text-white">Response</h2>
|
|
56
56
|
{isLoading && (
|
|
@@ -67,7 +67,7 @@ export function ApiTestPage({
|
|
|
67
67
|
</pre>
|
|
68
68
|
</div>
|
|
69
69
|
|
|
70
|
-
<div className="mt-8 bg-white/5 border border-white/10 rounded-xl p-6">
|
|
70
|
+
<div className="mt-6 sm:mt-8 bg-white/5 border border-white/10 rounded-xl p-4 sm:p-6">
|
|
71
71
|
<h3 className="text-lg font-semibold text-white mb-3">How it works</h3>
|
|
72
72
|
<div className="text-gray-400 text-sm space-y-2">
|
|
73
73
|
<p>OK <code className="text-purple-400">api.health.get()</code> - Full type inference from server</p>
|
|
@@ -98,7 +98,7 @@ function ActionCard({
|
|
|
98
98
|
<button
|
|
99
99
|
onClick={onClick}
|
|
100
100
|
disabled={disabled}
|
|
101
|
-
className={`px-6 py-4 border rounded-xl font-medium transition-all disabled:opacity-50 ${className}`}
|
|
101
|
+
className={`px-4 sm:px-6 py-3 sm:py-4 border rounded-xl font-medium transition-all disabled:opacity-50 text-sm sm:text-base ${className}`}
|
|
102
102
|
>
|
|
103
103
|
<div className="text-2xl mb-2">{icon}</div>
|
|
104
104
|
<div>{title}</div>
|
|
@@ -1,76 +1,104 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { FaFire } from 'react-icons/fa'
|
|
1
|
+
import FluxStack from '@client/src/assets/fluxstack.svg'
|
|
3
2
|
|
|
4
3
|
export function HomePage({ apiStatus }: { apiStatus: 'checking' | 'online' | 'offline' }) {
|
|
5
4
|
return (
|
|
6
|
-
<div className="flex flex-col items-center justify-center min-h-[calc(100vh-72px)] px-6 text-center">
|
|
7
|
-
<div className="mb-8 animate-pulse-slow">
|
|
8
|
-
<FaFire className="text-8xl text-orange-500 drop-shadow-2xl" />
|
|
9
|
-
</div>
|
|
5
|
+
<div className="flex flex-col items-center justify-center min-h-[calc(100vh-72px)] px-4 sm:px-6 py-12 text-center relative overflow-hidden">
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
</h1>
|
|
7
|
+
{/* Background glow */}
|
|
8
|
+
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[400px] h-[400px] bg-purple-500/10 rounded-full blur-[120px] pointer-events-none" />
|
|
14
9
|
|
|
15
|
-
<
|
|
16
|
-
Full-stack TypeScript framework with{' '}
|
|
17
|
-
<span className="text-purple-400 font-semibold">Bun</span>,{' '}
|
|
18
|
-
<span className="text-blue-400 font-semibold">Elysia</span>, and{' '}
|
|
19
|
-
<span className="text-cyan-400 font-semibold">React</span>
|
|
20
|
-
</p>
|
|
10
|
+
<div className="relative z-10 flex flex-col items-center w-full max-w-3xl">
|
|
21
11
|
|
|
22
|
-
|
|
23
|
-
<div className=
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<div className={`w-2 h-2 rounded-full ${
|
|
31
|
-
apiStatus === 'online' ? 'bg-emerald-400' : apiStatus === 'offline' ? 'bg-red-400' : 'bg-yellow-400'
|
|
32
|
-
}`}></div>
|
|
33
|
-
<span>{apiStatus === 'checking' ? 'Checking API...' : apiStatus === 'online' ? 'API Online' : 'API Offline'}</span>
|
|
12
|
+
{/* Icon */}
|
|
13
|
+
<div className="relative mb-5">
|
|
14
|
+
<div className="absolute inset-0 bg-purple-500/20 rounded-full blur-2xl animate-pulse-slow" />
|
|
15
|
+
<img
|
|
16
|
+
src={FluxStack}
|
|
17
|
+
alt="FluxStack"
|
|
18
|
+
className="relative w-16 h-16 sm:w-20 sm:h-20 md:w-24 md:h-24 drop-shadow-[0_0_24px_rgba(168,85,247,0.35)] animate-float"
|
|
19
|
+
/>
|
|
34
20
|
</div>
|
|
35
|
-
</div>
|
|
36
21
|
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<p className="text-gray-400 text-sm">Bun runtime 3x mais rápido que Node.js</p>
|
|
42
|
-
</div>
|
|
22
|
+
{/* Title */}
|
|
23
|
+
<h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-extrabold mb-3 bg-gradient-to-r from-cyan-400 via-purple-400 to-pink-400 bg-clip-text text-transparent tracking-tight leading-none">
|
|
24
|
+
FluxStack
|
|
25
|
+
</h1>
|
|
43
26
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
27
|
+
{/* Subtitle */}
|
|
28
|
+
<p className="text-sm sm:text-base md:text-lg text-gray-400 mb-6 leading-relaxed">
|
|
29
|
+
<span className="text-purple-400 font-semibold">Bun</span>
|
|
30
|
+
{' + '}
|
|
31
|
+
<span className="text-indigo-400 font-semibold">Elysia</span>
|
|
32
|
+
{' + '}
|
|
33
|
+
<span className="text-cyan-400 font-semibold">React</span>
|
|
34
|
+
{' = '}
|
|
35
|
+
<span className="text-white font-semibold">FluxStack</span>
|
|
36
|
+
</p>
|
|
49
37
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<
|
|
53
|
-
|
|
38
|
+
{/* API Status */}
|
|
39
|
+
<div className="mb-10 md:mb-12">
|
|
40
|
+
<div className={`inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-[10px] sm:text-xs font-medium tracking-wide uppercase transition-all ${
|
|
41
|
+
apiStatus === 'online'
|
|
42
|
+
? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'
|
|
43
|
+
: apiStatus === 'offline'
|
|
44
|
+
? 'bg-red-500/10 text-red-400 border border-red-500/20'
|
|
45
|
+
: 'bg-yellow-500/10 text-yellow-400 border border-yellow-500/20'
|
|
46
|
+
}`}>
|
|
47
|
+
<div className={`w-1.5 h-1.5 rounded-full animate-pulse ${
|
|
48
|
+
apiStatus === 'online' ? 'bg-emerald-400' : apiStatus === 'offline' ? 'bg-red-400' : 'bg-yellow-400'
|
|
49
|
+
}`} />
|
|
50
|
+
<span>{apiStatus === 'checking' ? 'Checking API...' : apiStatus === 'online' ? 'API Online' : 'API Offline'}</span>
|
|
51
|
+
</div>
|
|
54
52
|
</div>
|
|
55
|
-
</div>
|
|
56
53
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
{/* Feature cards */}
|
|
55
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 sm:gap-4 w-full mb-12">
|
|
56
|
+
<div className="group bg-white/[0.03] border border-white/[0.06] rounded-xl p-4 sm:p-5 hover:bg-white/[0.06] hover:border-purple-500/20 transition-all duration-300">
|
|
57
|
+
<div className="w-8 h-8 rounded-lg bg-purple-500/10 flex items-center justify-center mb-3 group-hover:bg-purple-500/20 transition-colors">
|
|
58
|
+
<span className="text-sm">⚡</span>
|
|
59
|
+
</div>
|
|
60
|
+
<h3 className="text-xs sm:text-sm font-semibold text-white mb-1 text-left">Ultra Rápido</h3>
|
|
61
|
+
<p className="text-gray-500 text-[11px] sm:text-xs text-left leading-relaxed">Bun runtime com performance 3x superior ao Node.js</p>
|
|
62
|
+
</div>
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
<div className="group bg-white/[0.03] border border-white/[0.06] rounded-xl p-4 sm:p-5 hover:bg-white/[0.06] hover:border-indigo-500/20 transition-all duration-300">
|
|
65
|
+
<div className="w-8 h-8 rounded-lg bg-indigo-500/10 flex items-center justify-center mb-3 group-hover:bg-indigo-500/20 transition-colors">
|
|
66
|
+
<span className="text-sm">🔒</span>
|
|
67
|
+
</div>
|
|
68
|
+
<h3 className="text-xs sm:text-sm font-semibold text-white mb-1 text-left">Type Safe</h3>
|
|
69
|
+
<p className="text-gray-500 text-[11px] sm:text-xs text-left leading-relaxed">Eden Treaty com inferência end-to-end automática</p>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div className="group bg-white/[0.03] border border-white/[0.06] rounded-xl p-4 sm:p-5 hover:bg-white/[0.06] hover:border-cyan-500/20 transition-all duration-300">
|
|
73
|
+
<div className="w-8 h-8 rounded-lg bg-cyan-500/10 flex items-center justify-center mb-3 group-hover:bg-cyan-500/20 transition-colors">
|
|
74
|
+
<span className="text-sm">🔥</span>
|
|
75
|
+
</div>
|
|
76
|
+
<h3 className="text-xs sm:text-sm font-semibold text-white mb-1 text-left">Live Components</h3>
|
|
77
|
+
<p className="text-gray-500 text-[11px] sm:text-xs text-left leading-relaxed">Estado reativo no servidor inspirado no Livewire</p>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<p className="text-gray-600 text-[11px] tracking-wide">
|
|
82
|
+
Desenvolvido com ❤️ usando TypeScript
|
|
83
|
+
</p>
|
|
63
84
|
</div>
|
|
64
85
|
|
|
65
86
|
<style>{`
|
|
66
87
|
@keyframes pulse-slow {
|
|
67
88
|
0%, 100% { opacity: 1; }
|
|
68
|
-
50% { opacity: 0.
|
|
89
|
+
50% { opacity: 0.5; }
|
|
90
|
+
}
|
|
91
|
+
@keyframes float {
|
|
92
|
+
0%, 100% { transform: translateY(0); }
|
|
93
|
+
50% { transform: translateY(-6px); }
|
|
69
94
|
}
|
|
70
95
|
.animate-pulse-slow {
|
|
71
|
-
animation: pulse-slow
|
|
96
|
+
animation: pulse-slow 4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
97
|
+
}
|
|
98
|
+
.animate-float {
|
|
99
|
+
animation: float 5s ease-in-out infinite;
|
|
72
100
|
}
|
|
73
101
|
`}</style>
|
|
74
102
|
</div>
|
|
75
103
|
)
|
|
76
|
-
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack Auth - Auth Manager
|
|
3
|
+
*
|
|
4
|
+
* Orquestrador central do sistema de autenticação.
|
|
5
|
+
* Factory pattern com lazy resolution e cache de guards.
|
|
6
|
+
*
|
|
7
|
+
* Inspirado no AuthManager do Laravel:
|
|
8
|
+
* - Resolve guards por nome (ou default)
|
|
9
|
+
* - Extensível com extend() para guards customizados
|
|
10
|
+
* - Resolve providers automaticamente da config
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* // Usar guard padrão
|
|
14
|
+
* const user = await auth.guard().user()
|
|
15
|
+
*
|
|
16
|
+
* // Usar guard específico
|
|
17
|
+
* const apiUser = await auth.guard('api').user()
|
|
18
|
+
*
|
|
19
|
+
* // Registrar guard customizado
|
|
20
|
+
* auth.extend('jwt', (name, config, provider) => new JWTGuard(...))
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type {
|
|
25
|
+
Guard,
|
|
26
|
+
UserProvider,
|
|
27
|
+
GuardConfig,
|
|
28
|
+
GuardFactory,
|
|
29
|
+
RequestContext,
|
|
30
|
+
} from './contracts'
|
|
31
|
+
import { SessionGuard } from './guards/SessionGuard'
|
|
32
|
+
import { TokenGuard } from './guards/TokenGuard'
|
|
33
|
+
import { SessionManager } from './sessions/SessionManager'
|
|
34
|
+
import { cacheManager } from '@server/cache'
|
|
35
|
+
|
|
36
|
+
export interface AuthManagerConfig {
|
|
37
|
+
defaults: {
|
|
38
|
+
guard: string
|
|
39
|
+
provider: string
|
|
40
|
+
}
|
|
41
|
+
guards: Record<string, GuardConfig>
|
|
42
|
+
providers: Record<string, ProviderConfig>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ProviderConfig {
|
|
46
|
+
driver: string
|
|
47
|
+
[key: string]: unknown
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class AuthManager {
|
|
51
|
+
private config: AuthManagerConfig
|
|
52
|
+
private guards = new Map<string, Guard>()
|
|
53
|
+
private customGuardFactories = new Map<string, GuardFactory>()
|
|
54
|
+
private providerInstances = new Map<string, UserProvider>()
|
|
55
|
+
private customProviderFactories = new Map<string, (config: ProviderConfig) => UserProvider>()
|
|
56
|
+
private sessionManager: SessionManager
|
|
57
|
+
|
|
58
|
+
constructor(config: AuthManagerConfig, sessionManager: SessionManager) {
|
|
59
|
+
this.config = config
|
|
60
|
+
this.sessionManager = sessionManager
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Retorna um guard por nome (ou o default).
|
|
65
|
+
* Guards são criados uma vez e reutilizados.
|
|
66
|
+
*/
|
|
67
|
+
guard(name?: string): Guard {
|
|
68
|
+
const guardName = name ?? this.config.defaults.guard
|
|
69
|
+
|
|
70
|
+
if (this.guards.has(guardName)) {
|
|
71
|
+
return this.guards.get(guardName)!
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const guard = this.resolve(guardName)
|
|
75
|
+
this.guards.set(guardName, guard)
|
|
76
|
+
return guard
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Inicializa todos os guards com o contexto da request.
|
|
81
|
+
* Deve ser chamado no middleware, antes de qualquer uso.
|
|
82
|
+
*/
|
|
83
|
+
setRequest(context: RequestContext): void {
|
|
84
|
+
for (const guard of this.guards.values()) {
|
|
85
|
+
guard.setRequest(context)
|
|
86
|
+
}
|
|
87
|
+
// Também resetar guards não-instanciados para forçar re-resolve com novo context
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Cria um guard novo (sem cache) com o context da request.
|
|
92
|
+
* Útil quando precisa de um guard fresco para cada request.
|
|
93
|
+
*/
|
|
94
|
+
freshGuard(name?: string, context?: RequestContext): Guard {
|
|
95
|
+
const guardName = name ?? this.config.defaults.guard
|
|
96
|
+
const guard = this.resolve(guardName)
|
|
97
|
+
if (context) {
|
|
98
|
+
guard.setRequest(context)
|
|
99
|
+
}
|
|
100
|
+
return guard
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Registra um guard driver customizado.
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* auth.extend('jwt', (name, config, provider) => {
|
|
108
|
+
* return new JWTGuard(name, provider, config.secret)
|
|
109
|
+
* })
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
extend(driver: string, factory: GuardFactory): void {
|
|
113
|
+
this.customGuardFactories.set(driver, factory)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Registra um provider customizado.
|
|
118
|
+
*
|
|
119
|
+
* ```ts
|
|
120
|
+
* auth.extendProvider('drizzle', (config) => {
|
|
121
|
+
* return new DrizzleUserProvider(db, config.table)
|
|
122
|
+
* })
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
extendProvider(driver: string, factory: (config: ProviderConfig) => UserProvider): void {
|
|
126
|
+
this.customProviderFactories.set(driver, factory)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Registra uma instância de provider diretamente.
|
|
131
|
+
*/
|
|
132
|
+
registerProvider(name: string, provider: UserProvider): void {
|
|
133
|
+
this.providerInstances.set(name, provider)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Retorna o nome do guard padrão */
|
|
137
|
+
getDefaultGuardName(): string {
|
|
138
|
+
return this.config.defaults.guard
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Retorna a config */
|
|
142
|
+
getConfig(): AuthManagerConfig {
|
|
143
|
+
return this.config
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Resolve um guard por nome */
|
|
147
|
+
private resolve(name: string): Guard {
|
|
148
|
+
const guardConfig = this.config.guards[name]
|
|
149
|
+
if (!guardConfig) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Auth guard '${name}' not configured. ` +
|
|
152
|
+
`Available: ${Object.keys(this.config.guards).join(', ')}`
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Resolver provider
|
|
157
|
+
const provider = this.resolveProvider(guardConfig.provider)
|
|
158
|
+
|
|
159
|
+
// Verificar custom factory primeiro
|
|
160
|
+
if (this.customGuardFactories.has(guardConfig.driver)) {
|
|
161
|
+
return this.customGuardFactories.get(guardConfig.driver)!(name, guardConfig, provider)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Built-in drivers
|
|
165
|
+
switch (guardConfig.driver) {
|
|
166
|
+
case 'session':
|
|
167
|
+
return new SessionGuard(name, provider, this.sessionManager)
|
|
168
|
+
|
|
169
|
+
case 'token':
|
|
170
|
+
return new TokenGuard(
|
|
171
|
+
name,
|
|
172
|
+
provider,
|
|
173
|
+
cacheManager.driver(),
|
|
174
|
+
guardConfig.tokenTtl as number | undefined
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
default:
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Auth guard driver '${guardConfig.driver}' not supported. ` +
|
|
180
|
+
`Use auth.extend('${guardConfig.driver}', factory) to register it.`
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Resolve um provider por nome */
|
|
186
|
+
private resolveProvider(name: string): UserProvider {
|
|
187
|
+
// Cache de instâncias
|
|
188
|
+
if (this.providerInstances.has(name)) {
|
|
189
|
+
return this.providerInstances.get(name)!
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const providerConfig = this.config.providers[name]
|
|
193
|
+
if (!providerConfig) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`Auth provider '${name}' not configured. ` +
|
|
196
|
+
`Available: ${Object.keys(this.config.providers).join(', ')}`
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Custom factory
|
|
201
|
+
if (this.customProviderFactories.has(providerConfig.driver)) {
|
|
202
|
+
const provider = this.customProviderFactories.get(providerConfig.driver)!(providerConfig)
|
|
203
|
+
this.providerInstances.set(name, provider)
|
|
204
|
+
return provider
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
throw new Error(
|
|
208
|
+
`Auth provider driver '${providerConfig.driver}' not supported. ` +
|
|
209
|
+
`Use auth.extendProvider('${providerConfig.driver}', factory) to register it, ` +
|
|
210
|
+
`or use auth.registerProvider('${name}', providerInstance) to register directly.`
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// 🧪 DevAuthProvider - Provider de desenvolvimento para testes de auth
|
|
2
|
+
//
|
|
3
|
+
// Aceita tokens simples para facilitar testes da demo de autenticação.
|
|
4
|
+
// NÃO USAR EM PRODUÇÃO!
|
|
5
|
+
//
|
|
6
|
+
// Tokens válidos:
|
|
7
|
+
// - "admin-token" → role: admin, permissions: all
|
|
8
|
+
// - "user-token" → role: user, permissions: básicas
|
|
9
|
+
// - "mod-token" → role: moderator, permissions: moderação
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
LiveAuthProvider,
|
|
13
|
+
LiveAuthCredentials,
|
|
14
|
+
LiveAuthContext,
|
|
15
|
+
} from '@core/server/live/auth/types'
|
|
16
|
+
import { AuthenticatedContext } from '@core/server/live/auth/LiveAuthContext'
|
|
17
|
+
|
|
18
|
+
interface DevUser {
|
|
19
|
+
id: string
|
|
20
|
+
name: string
|
|
21
|
+
roles: string[]
|
|
22
|
+
permissions: string[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const DEV_USERS: Record<string, DevUser> = {
|
|
26
|
+
'admin-token': {
|
|
27
|
+
id: 'admin-1',
|
|
28
|
+
name: 'Admin User',
|
|
29
|
+
roles: ['admin', 'user'],
|
|
30
|
+
permissions: ['users.read', 'users.write', 'users.delete', 'chat.read', 'chat.write', 'chat.admin'],
|
|
31
|
+
},
|
|
32
|
+
'user-token': {
|
|
33
|
+
id: 'user-1',
|
|
34
|
+
name: 'Regular User',
|
|
35
|
+
roles: ['user'],
|
|
36
|
+
permissions: ['chat.read', 'chat.write'],
|
|
37
|
+
},
|
|
38
|
+
'mod-token': {
|
|
39
|
+
id: 'mod-1',
|
|
40
|
+
name: 'Moderator',
|
|
41
|
+
roles: ['moderator', 'user'],
|
|
42
|
+
permissions: ['chat.read', 'chat.write', 'chat.moderate'],
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class DevAuthProvider implements LiveAuthProvider {
|
|
47
|
+
readonly name = 'dev'
|
|
48
|
+
|
|
49
|
+
async authenticate(credentials: LiveAuthCredentials): Promise<LiveAuthContext | null> {
|
|
50
|
+
const token = credentials.token as string
|
|
51
|
+
if (!token) return null
|
|
52
|
+
|
|
53
|
+
const user = DEV_USERS[token]
|
|
54
|
+
if (!user) return null
|
|
55
|
+
|
|
56
|
+
return new AuthenticatedContext(
|
|
57
|
+
{
|
|
58
|
+
id: user.id,
|
|
59
|
+
name: user.name,
|
|
60
|
+
roles: user.roles,
|
|
61
|
+
permissions: user.permissions,
|
|
62
|
+
},
|
|
63
|
+
token
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
}
|