@lastbrain/ai-ui-react 1.0.37 → 1.0.39
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/dist/components/AiPromptPanel.js +1 -6
- package/dist/components/ErrorToast.js +14 -14
- package/dist/components/LBConnectButton.d.ts +20 -0
- package/dist/components/LBConnectButton.d.ts.map +1 -0
- package/dist/components/LBConnectButton.js +87 -0
- package/dist/components/LBKeyPicker.d.ts +10 -0
- package/dist/components/LBKeyPicker.d.ts.map +1 -0
- package/dist/components/LBKeyPicker.js +61 -0
- package/dist/context/LBAuthProvider.d.ts +38 -0
- package/dist/context/LBAuthProvider.d.ts.map +1 -0
- package/dist/context/LBAuthProvider.js +194 -0
- package/dist/hooks/useAiModels.d.ts.map +1 -1
- package/dist/hooks/useAiModels.js +1 -2
- package/dist/hooks/useLB.d.ts +5 -0
- package/dist/hooks/useLB.d.ts.map +1 -0
- package/dist/hooks/useLB.js +5 -0
- package/dist/hooks/usePrompts.d.ts.map +1 -1
- package/dist/hooks/usePrompts.js +12 -3
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/package.json +2 -2
- package/src/components/AiPromptPanel.tsx +5 -6
- package/src/components/ErrorToast.tsx +16 -16
- package/src/components/LBConnectButton.tsx +230 -0
- package/src/components/LBKeyPicker.tsx +145 -0
- package/src/context/LBAuthProvider.tsx +269 -0
- package/src/hooks/useAiModels.ts +1 -2
- package/src/hooks/useLB.ts +7 -0
- package/src/hooks/usePrompts.ts +13 -3
- package/src/index.ts +4 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Provider d'authentification LastBrain
|
|
5
|
+
* Gère l'état de connexion, la session et les appels IA
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
createContext,
|
|
10
|
+
useContext,
|
|
11
|
+
useEffect,
|
|
12
|
+
useCallback,
|
|
13
|
+
useState,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
} from "react";
|
|
16
|
+
import type {
|
|
17
|
+
LBAuthState,
|
|
18
|
+
LBApiKey,
|
|
19
|
+
LBSession,
|
|
20
|
+
LBLoginResult,
|
|
21
|
+
LBSessionResult,
|
|
22
|
+
} from "@lastbrain/ai-ui-core";
|
|
23
|
+
|
|
24
|
+
interface LBProviderProps {
|
|
25
|
+
children: ReactNode;
|
|
26
|
+
/** URL de l'API LastBrain (ex: https://api.lastbrain.io) */
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
/** URL du proxy local (ex: /api/lb) */
|
|
29
|
+
proxyUrl?: string;
|
|
30
|
+
/** Fonction appelée lors des changements d'état */
|
|
31
|
+
onStatusChange?: (status: LBAuthState["status"]) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface LBContextValue extends LBAuthState {
|
|
35
|
+
/** Fonction de connexion */
|
|
36
|
+
login: (email: string, password: string) => Promise<LBLoginResult>;
|
|
37
|
+
/** Fonction de déconnexion */
|
|
38
|
+
logout: () => Promise<void>;
|
|
39
|
+
/** Récupère les clés API de l'utilisateur */
|
|
40
|
+
fetchApiKeys: (accessToken: string) => Promise<LBApiKey[]>;
|
|
41
|
+
/** Sélectionne une clé API et crée une session */
|
|
42
|
+
selectApiKey: (accessToken: string, apiKeyId: string) => Promise<void>;
|
|
43
|
+
/** Recharge l'état de la session */
|
|
44
|
+
refreshSession: () => Promise<void>;
|
|
45
|
+
/** Clés API disponibles */
|
|
46
|
+
apiKeys: LBApiKey[];
|
|
47
|
+
/** Access token temporaire (après login) */
|
|
48
|
+
accessToken?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const LBContext = createContext<LBContextValue | undefined>(undefined);
|
|
52
|
+
|
|
53
|
+
export function LBProvider({
|
|
54
|
+
children,
|
|
55
|
+
baseUrl: _baseUrl = "/api/lb",
|
|
56
|
+
proxyUrl = "/api/lb",
|
|
57
|
+
onStatusChange,
|
|
58
|
+
}: LBProviderProps) {
|
|
59
|
+
const [state, setState] = useState<LBAuthState>({
|
|
60
|
+
status: "loading",
|
|
61
|
+
});
|
|
62
|
+
const [apiKeys, setApiKeys] = useState<LBApiKey[]>([]);
|
|
63
|
+
const [accessToken, setAccessToken] = useState<string>();
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Vérifie si une session existe au chargement
|
|
67
|
+
*/
|
|
68
|
+
const checkSession = useCallback(async () => {
|
|
69
|
+
try {
|
|
70
|
+
const response = await fetch(`${proxyUrl}/auth/session/verify`, {
|
|
71
|
+
credentials: "include",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (response.ok) {
|
|
75
|
+
const session: LBSession = await response.json();
|
|
76
|
+
setState({
|
|
77
|
+
status: "ready",
|
|
78
|
+
session,
|
|
79
|
+
user: {
|
|
80
|
+
id: session.userId,
|
|
81
|
+
email: "", // Sera rempli par une autre requête si nécessaire
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
onStatusChange?.("ready");
|
|
85
|
+
} else {
|
|
86
|
+
setState({ status: "needs_auth" });
|
|
87
|
+
onStatusChange?.("needs_auth");
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error("[LBProvider] Session check failed:", error);
|
|
91
|
+
setState({ status: "needs_auth" });
|
|
92
|
+
onStatusChange?.("needs_auth");
|
|
93
|
+
}
|
|
94
|
+
}, [proxyUrl, onStatusChange]);
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
checkSession();
|
|
98
|
+
}, [checkSession]);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Connexion utilisateur
|
|
102
|
+
*/
|
|
103
|
+
const login = useCallback(
|
|
104
|
+
async (email: string, password: string): Promise<LBLoginResult> => {
|
|
105
|
+
try {
|
|
106
|
+
setState((prev: LBAuthState) => ({ ...prev, status: "loading" }));
|
|
107
|
+
|
|
108
|
+
const response = await fetch(`${proxyUrl}/auth/login`, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: { "Content-Type": "application/json" },
|
|
111
|
+
body: JSON.stringify({ email, password }),
|
|
112
|
+
credentials: "include",
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
const error = await response.json();
|
|
117
|
+
throw new Error(error.message || "Login failed");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const result: LBLoginResult = await response.json();
|
|
121
|
+
setAccessToken(result.accessToken);
|
|
122
|
+
|
|
123
|
+
setState({
|
|
124
|
+
status: "ready",
|
|
125
|
+
user: result.user,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return result;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
const message = error instanceof Error ? error.message : "Login failed";
|
|
131
|
+
setState({
|
|
132
|
+
status: "error",
|
|
133
|
+
error: message,
|
|
134
|
+
});
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
[proxyUrl]
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Récupère les clés API de l'utilisateur
|
|
143
|
+
*/
|
|
144
|
+
const fetchApiKeys = useCallback(
|
|
145
|
+
async (token: string): Promise<LBApiKey[]> => {
|
|
146
|
+
try {
|
|
147
|
+
const response = await fetch(`${proxyUrl}/user/api-keys`, {
|
|
148
|
+
headers: {
|
|
149
|
+
Authorization: `Bearer ${token}`,
|
|
150
|
+
},
|
|
151
|
+
credentials: "include",
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
throw new Error("Failed to fetch API keys");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const keys: LBApiKey[] = await response.json();
|
|
159
|
+
setApiKeys(keys);
|
|
160
|
+
return keys;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.error("[LBProvider] Failed to fetch API keys:", error);
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
[proxyUrl]
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Sélectionne une clé API et crée une session
|
|
171
|
+
*/
|
|
172
|
+
const selectApiKey = useCallback(
|
|
173
|
+
async (token: string, apiKeyId: string): Promise<void> => {
|
|
174
|
+
try {
|
|
175
|
+
setState((prev: LBAuthState) => ({ ...prev, status: "loading" }));
|
|
176
|
+
|
|
177
|
+
const response = await fetch(`${proxyUrl}/auth/session`, {
|
|
178
|
+
method: "POST",
|
|
179
|
+
headers: {
|
|
180
|
+
"Content-Type": "application/json",
|
|
181
|
+
Authorization: `Bearer ${token}`,
|
|
182
|
+
},
|
|
183
|
+
body: JSON.stringify({ api_key_id: apiKeyId }),
|
|
184
|
+
credentials: "include",
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (!response.ok) {
|
|
188
|
+
throw new Error("Failed to create session");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const sessionResult: LBSessionResult = await response.json();
|
|
192
|
+
|
|
193
|
+
setState({
|
|
194
|
+
status: "ready",
|
|
195
|
+
user: state.user,
|
|
196
|
+
selectedKey: sessionResult.apiKey,
|
|
197
|
+
session: {
|
|
198
|
+
sessionToken: sessionResult.sessionToken,
|
|
199
|
+
userId: state.user?.id || "",
|
|
200
|
+
apiKeyId,
|
|
201
|
+
expiresAt: Date.now() + sessionResult.expiresIn * 1000,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
setAccessToken(undefined); // Nettoyer l'access token temporaire
|
|
206
|
+
onStatusChange?.("ready");
|
|
207
|
+
} catch (error) {
|
|
208
|
+
const message =
|
|
209
|
+
error instanceof Error ? error.message : "Failed to select API key";
|
|
210
|
+
setState({
|
|
211
|
+
status: "error",
|
|
212
|
+
error: message,
|
|
213
|
+
});
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
[proxyUrl, state.user, onStatusChange]
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Déconnexion
|
|
222
|
+
*/
|
|
223
|
+
const logout = useCallback(async (): Promise<void> => {
|
|
224
|
+
try {
|
|
225
|
+
await fetch(`${proxyUrl}/auth/session/logout`, {
|
|
226
|
+
method: "POST",
|
|
227
|
+
credentials: "include",
|
|
228
|
+
});
|
|
229
|
+
} catch (error) {
|
|
230
|
+
console.error("[LBProvider] Logout failed:", error);
|
|
231
|
+
} finally {
|
|
232
|
+
setState({ status: "needs_auth" });
|
|
233
|
+
setApiKeys([]);
|
|
234
|
+
setAccessToken(undefined);
|
|
235
|
+
onStatusChange?.("needs_auth");
|
|
236
|
+
}
|
|
237
|
+
}, [proxyUrl, onStatusChange]);
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Recharge la session
|
|
241
|
+
*/
|
|
242
|
+
const refreshSession = useCallback(async (): Promise<void> => {
|
|
243
|
+
await checkSession();
|
|
244
|
+
}, [checkSession]);
|
|
245
|
+
|
|
246
|
+
const value: LBContextValue = {
|
|
247
|
+
...state,
|
|
248
|
+
login,
|
|
249
|
+
logout,
|
|
250
|
+
fetchApiKeys,
|
|
251
|
+
selectApiKey,
|
|
252
|
+
refreshSession,
|
|
253
|
+
apiKeys,
|
|
254
|
+
accessToken,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
return <LBContext.Provider value={value}>{children}</LBContext.Provider>;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Hook pour accéder au contexte LastBrain
|
|
262
|
+
*/
|
|
263
|
+
export function useLB(): LBContextValue {
|
|
264
|
+
const context = useContext(LBContext);
|
|
265
|
+
if (!context) {
|
|
266
|
+
throw new Error("useLB must be used within LBProvider");
|
|
267
|
+
}
|
|
268
|
+
return context;
|
|
269
|
+
}
|
package/src/hooks/useAiModels.ts
CHANGED
|
@@ -87,8 +87,7 @@ export function useAiModels(options?: UseAiModelsOptions): UseAiModelsResult {
|
|
|
87
87
|
filtered.length
|
|
88
88
|
);
|
|
89
89
|
return filtered;
|
|
90
|
-
|
|
91
|
-
}, [useContextData, context, options?.modelType]);
|
|
90
|
+
}, [useContextData, context, options]);
|
|
92
91
|
|
|
93
92
|
const refetch = useCallback(() => {
|
|
94
93
|
if (useContextData) {
|
package/src/hooks/usePrompts.ts
CHANGED
|
@@ -79,7 +79,10 @@ export function usePrompts(): UsePromptsReturn {
|
|
|
79
79
|
// Check cache first (60 seconds TTL)
|
|
80
80
|
const cached = getCached<Prompt[] | PublicPrompt[]>(cacheKey, 60000);
|
|
81
81
|
if (cached) {
|
|
82
|
-
console.log("[usePrompts] Using cached data",
|
|
82
|
+
console.log("[usePrompts] Using cached data:", {
|
|
83
|
+
count: cached.length,
|
|
84
|
+
prompts: cached,
|
|
85
|
+
});
|
|
83
86
|
setPrompts(cached);
|
|
84
87
|
setLoading(false);
|
|
85
88
|
return;
|
|
@@ -141,6 +144,13 @@ export function usePrompts(): UsePromptsReturn {
|
|
|
141
144
|
|
|
142
145
|
const data = await response.json();
|
|
143
146
|
|
|
147
|
+
console.log("[usePrompts] Server response:", {
|
|
148
|
+
ok: response.ok,
|
|
149
|
+
status: response.status,
|
|
150
|
+
promptsCount: data.prompts?.length || 0,
|
|
151
|
+
data,
|
|
152
|
+
});
|
|
153
|
+
|
|
144
154
|
if (response.ok) {
|
|
145
155
|
const promptsData = data.prompts || [];
|
|
146
156
|
setPrompts(promptsData);
|
|
@@ -155,7 +165,7 @@ export function usePrompts(): UsePromptsReturn {
|
|
|
155
165
|
setLoading(false);
|
|
156
166
|
}
|
|
157
167
|
},
|
|
158
|
-
[baseUrl]
|
|
168
|
+
[baseUrl, apiKeyId]
|
|
159
169
|
);
|
|
160
170
|
|
|
161
171
|
const createPrompt = useCallback(
|
|
@@ -314,7 +324,7 @@ export function usePrompts(): UsePromptsReturn {
|
|
|
314
324
|
body: JSON.stringify({ prompt_id: promptId, stat_type: statType }),
|
|
315
325
|
credentials: isExternalProxy ? "include" : "same-origin",
|
|
316
326
|
});
|
|
317
|
-
} catch
|
|
327
|
+
} catch {
|
|
318
328
|
// Silent fail for stats
|
|
319
329
|
}
|
|
320
330
|
},
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./types";
|
|
|
3
3
|
|
|
4
4
|
// Context
|
|
5
5
|
export * from "./context/AiProvider";
|
|
6
|
+
export * from "./context/LBAuthProvider";
|
|
6
7
|
|
|
7
8
|
// Hooks
|
|
8
9
|
export * from "./hooks/useAiClient";
|
|
@@ -12,6 +13,7 @@ export * from "./hooks/useAiCallText";
|
|
|
12
13
|
export * from "./hooks/useAiCallImage";
|
|
13
14
|
export * from "./hooks/usePrompts";
|
|
14
15
|
export * from "./hooks/useModelManagement";
|
|
16
|
+
export * from "./hooks/useLB";
|
|
15
17
|
|
|
16
18
|
// Components
|
|
17
19
|
export * from "./components/AiPromptPanel";
|
|
@@ -24,6 +26,8 @@ export * from "./components/AiImageButton";
|
|
|
24
26
|
export * from "./components/AiContextButton";
|
|
25
27
|
export * from "./components/AiSettingsButton";
|
|
26
28
|
export * from "./components/AiStatusButton";
|
|
29
|
+
export * from "./components/LBConnectButton";
|
|
30
|
+
export * from "./components/LBKeyPicker";
|
|
27
31
|
|
|
28
32
|
// Toast system
|
|
29
33
|
export * from "./components/ErrorToast";
|