@lastbrain/ai-ui-core 1.0.28 → 1.0.30
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/client/lb-client.d.ts +67 -0
- package/dist/client/lb-client.d.ts.map +1 -0
- package/dist/client/lb-client.js +168 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/route-handlers/nextjs/lb-proxy.d.ts +66 -0
- package/dist/route-handlers/nextjs/lb-proxy.d.ts.map +1 -0
- package/dist/route-handlers/nextjs/lb-proxy.js +147 -0
- package/dist/types/auth.d.ts +108 -0
- package/dist/types/auth.d.ts.map +1 -0
- package/dist/types/auth.js +5 -0
- package/package.json +1 -1
- package/src/client/lb-client.ts +220 -0
- package/src/index.ts +3 -0
- package/src/route-handlers/nextjs/lb-proxy.ts +189 -0
- package/src/types/auth.ts +116 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client LastBrain intelligent
|
|
3
|
+
* Supporte deux modes d'authentification :
|
|
4
|
+
* - env-key : utilise LB_API_KEY (côté serveur)
|
|
5
|
+
* - session : utilise cookie lb_session (user authentifié)
|
|
6
|
+
*/
|
|
7
|
+
import type { LBAuthConfig, LBSession, LBLoginResult, LBApiKey, LBSessionResult } from "../types/auth";
|
|
8
|
+
export declare class LBClient {
|
|
9
|
+
private config;
|
|
10
|
+
constructor(config?: LBAuthConfig);
|
|
11
|
+
/**
|
|
12
|
+
* Détecte le mode d'authentification à utiliser
|
|
13
|
+
*/
|
|
14
|
+
private getAuthMode;
|
|
15
|
+
/**
|
|
16
|
+
* Construit les headers d'authentification
|
|
17
|
+
*/
|
|
18
|
+
private getAuthHeaders;
|
|
19
|
+
/**
|
|
20
|
+
* Fait une requête à l'API LastBrain
|
|
21
|
+
*/
|
|
22
|
+
private request;
|
|
23
|
+
/**
|
|
24
|
+
* Login utilisateur (email + password)
|
|
25
|
+
* Retourne un access_token temporaire pour récupérer les clés API
|
|
26
|
+
*/
|
|
27
|
+
login(email: string, password: string): Promise<LBLoginResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Récupère les clés API de l'utilisateur
|
|
30
|
+
*/
|
|
31
|
+
getUserApiKeys(accessToken: string): Promise<LBApiKey[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Crée une session (72h) avec une clé API sélectionnée
|
|
34
|
+
*/
|
|
35
|
+
createSession(accessToken: string, apiKeyId: string): Promise<LBSessionResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Vérifie si une session est valide
|
|
38
|
+
*/
|
|
39
|
+
verifySession(sessionToken: string): Promise<LBSession | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Déconnexion (invalide la session)
|
|
42
|
+
*/
|
|
43
|
+
logout(sessionToken: string): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Fait un appel IA (génération de texte)
|
|
46
|
+
*/
|
|
47
|
+
generateText(params: {
|
|
48
|
+
prompt: string;
|
|
49
|
+
model?: string;
|
|
50
|
+
sessionToken?: string;
|
|
51
|
+
[key: string]: any;
|
|
52
|
+
}): Promise<any>;
|
|
53
|
+
/**
|
|
54
|
+
* Fait un appel IA (génération d'image)
|
|
55
|
+
*/
|
|
56
|
+
generateImage(params: {
|
|
57
|
+
prompt: string;
|
|
58
|
+
model?: string;
|
|
59
|
+
sessionToken?: string;
|
|
60
|
+
[key: string]: any;
|
|
61
|
+
}): Promise<any>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Factory pour créer un client LastBrain
|
|
65
|
+
*/
|
|
66
|
+
export declare function createLBClient(config?: LBAuthConfig): LBClient;
|
|
67
|
+
//# sourceMappingURL=lb-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lb-client.d.ts","sourceRoot":"","sources":["../../src/client/lb-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EAET,aAAa,EACb,QAAQ,EACR,eAAe,EAChB,MAAM,eAAe,CAAC;AAEvB,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAyB;gBAE3B,MAAM,GAAE,YAAiB;IAWrC;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;YACW,cAAc;IAsB5B;;OAEG;YACW,OAAO;IAyCrB;;;OAGG;IACG,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAOpE;;OAEG;IACG,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAQ9D;;OAEG;IACG,aAAa,CACjB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,CAAC;IAU3B;;OAEG;IACG,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAYpE;;OAEG;IACG,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASjD;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE;QACzB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,GAAG,OAAO,CAAC,GAAG,CAAC;IAWhB;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE;QAC1B,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,GAAG,OAAO,CAAC,GAAG,CAAC;CAUjB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,QAAQ,CAE9D"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client LastBrain intelligent
|
|
3
|
+
* Supporte deux modes d'authentification :
|
|
4
|
+
* - env-key : utilise LB_API_KEY (côté serveur)
|
|
5
|
+
* - session : utilise cookie lb_session (user authentifié)
|
|
6
|
+
*/
|
|
7
|
+
export class LBClient {
|
|
8
|
+
config;
|
|
9
|
+
constructor(config = {}) {
|
|
10
|
+
this.config = {
|
|
11
|
+
baseUrl: config.baseUrl ||
|
|
12
|
+
process.env.LB_BASE_URL ||
|
|
13
|
+
"https://prompt.lastbrain.io",
|
|
14
|
+
apiKey: config.apiKey || process.env.LB_API_KEY || "",
|
|
15
|
+
mode: config.mode || "auto",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Détecte le mode d'authentification à utiliser
|
|
20
|
+
*/
|
|
21
|
+
getAuthMode() {
|
|
22
|
+
if (this.config.mode === "env-key")
|
|
23
|
+
return "env-key";
|
|
24
|
+
if (this.config.mode === "session")
|
|
25
|
+
return "session";
|
|
26
|
+
// Mode auto : préfère env-key si disponible
|
|
27
|
+
return this.config.apiKey ? "env-key" : "session";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Construit les headers d'authentification
|
|
31
|
+
*/
|
|
32
|
+
async getAuthHeaders(sessionToken) {
|
|
33
|
+
const mode = this.getAuthMode();
|
|
34
|
+
if (mode === "env-key" && this.config.apiKey) {
|
|
35
|
+
return {
|
|
36
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Mode session : utilise le token fourni ou tente de lire le cookie
|
|
40
|
+
if (sessionToken) {
|
|
41
|
+
return {
|
|
42
|
+
Authorization: `Bearer ${sessionToken}`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Si aucun token fourni, la requête échouera (attendu pour déclencher auth)
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fait une requête à l'API LastBrain
|
|
50
|
+
*/
|
|
51
|
+
async request(endpoint, options = {}) {
|
|
52
|
+
const { baseUrl, headers: customHeaders, timeout, ...fetchOptions } = options;
|
|
53
|
+
const url = `${baseUrl || this.config.baseUrl}${endpoint}`;
|
|
54
|
+
const authHeaders = await this.getAuthHeaders();
|
|
55
|
+
const controller = new AbortController();
|
|
56
|
+
const timeoutId = timeout
|
|
57
|
+
? setTimeout(() => controller.abort(), timeout)
|
|
58
|
+
: undefined;
|
|
59
|
+
try {
|
|
60
|
+
const response = await fetch(url, {
|
|
61
|
+
...fetchOptions,
|
|
62
|
+
headers: {
|
|
63
|
+
"Content-Type": "application/json",
|
|
64
|
+
...authHeaders,
|
|
65
|
+
...customHeaders,
|
|
66
|
+
},
|
|
67
|
+
signal: controller.signal,
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
const error = await response.json().catch(() => ({}));
|
|
71
|
+
throw new Error(error.message || `HTTP ${response.status}`);
|
|
72
|
+
}
|
|
73
|
+
return await response.json();
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
if (timeoutId)
|
|
77
|
+
clearTimeout(timeoutId);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Login utilisateur (email + password)
|
|
82
|
+
* Retourne un access_token temporaire pour récupérer les clés API
|
|
83
|
+
*/
|
|
84
|
+
async login(email, password) {
|
|
85
|
+
return this.request("/auth/login", {
|
|
86
|
+
method: "POST",
|
|
87
|
+
body: JSON.stringify({ email, password }),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Récupère les clés API de l'utilisateur
|
|
92
|
+
*/
|
|
93
|
+
async getUserApiKeys(accessToken) {
|
|
94
|
+
return this.request("/user/api-keys", {
|
|
95
|
+
headers: {
|
|
96
|
+
Authorization: `Bearer ${accessToken}`,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Crée une session (72h) avec une clé API sélectionnée
|
|
102
|
+
*/
|
|
103
|
+
async createSession(accessToken, apiKeyId) {
|
|
104
|
+
return this.request("/auth/session", {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: {
|
|
107
|
+
Authorization: `Bearer ${accessToken}`,
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify({ api_key_id: apiKeyId }),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Vérifie si une session est valide
|
|
114
|
+
*/
|
|
115
|
+
async verifySession(sessionToken) {
|
|
116
|
+
try {
|
|
117
|
+
return await this.request("/auth/session/verify", {
|
|
118
|
+
headers: {
|
|
119
|
+
Authorization: `Bearer ${sessionToken}`,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Déconnexion (invalide la session)
|
|
129
|
+
*/
|
|
130
|
+
async logout(sessionToken) {
|
|
131
|
+
await this.request("/auth/session/logout", {
|
|
132
|
+
method: "POST",
|
|
133
|
+
headers: {
|
|
134
|
+
Authorization: `Bearer ${sessionToken}`,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Fait un appel IA (génération de texte)
|
|
140
|
+
*/
|
|
141
|
+
async generateText(params) {
|
|
142
|
+
const { sessionToken, ...body } = params;
|
|
143
|
+
const headers = await this.getAuthHeaders(sessionToken);
|
|
144
|
+
return this.request("/ai/generate-text", {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers,
|
|
147
|
+
body: JSON.stringify(body),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Fait un appel IA (génération d'image)
|
|
152
|
+
*/
|
|
153
|
+
async generateImage(params) {
|
|
154
|
+
const { sessionToken, ...body } = params;
|
|
155
|
+
const headers = await this.getAuthHeaders(sessionToken);
|
|
156
|
+
return this.request("/ai/generate-image", {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers,
|
|
159
|
+
body: JSON.stringify(body),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Factory pour créer un client LastBrain
|
|
165
|
+
*/
|
|
166
|
+
export function createLBClient(config) {
|
|
167
|
+
return new LBClient(config);
|
|
168
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
|
+
export * from "./types/auth";
|
|
2
3
|
export * from "./errors/errorCodes";
|
|
3
4
|
export * from "./errors/normalizeError";
|
|
4
5
|
export * from "./client/createClient";
|
|
6
|
+
export * from "./client/lb-client";
|
|
5
7
|
export * from "./route-handlers/nextjs/gateway";
|
|
6
8
|
export { GET as EnhancedGET, POST as EnhancedPOST, PUT as EnhancedPUT, } from "./route-handlers/nextjs/enhanced-gateway";
|
|
7
9
|
export * as enhancedGateway from "./route-handlers/nextjs/enhanced-gateway";
|
|
10
|
+
export * from "./route-handlers/nextjs/lb-proxy";
|
|
8
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,iCAAiC,CAAC;AAChD,OAAO,EACL,GAAG,IAAI,WAAW,EAClB,IAAI,IAAI,YAAY,EACpB,GAAG,IAAI,WAAW,GACnB,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,eAAe,MAAM,0CAA0C,CAAC;AAC5E,cAAc,kCAAkC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
|
+
export * from "./types/auth";
|
|
2
3
|
export * from "./errors/errorCodes";
|
|
3
4
|
export * from "./errors/normalizeError";
|
|
4
5
|
export * from "./client/createClient";
|
|
6
|
+
export * from "./client/lb-client";
|
|
5
7
|
// Route handlers
|
|
6
8
|
export * from "./route-handlers/nextjs/gateway";
|
|
7
9
|
export { GET as EnhancedGET, POST as EnhancedPOST, PUT as EnhancedPUT, } from "./route-handlers/nextjs/enhanced-gateway";
|
|
8
10
|
export * as enhancedGateway from "./route-handlers/nextjs/enhanced-gateway";
|
|
11
|
+
export * from "./route-handlers/nextjs/lb-proxy";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy handler pour Next.js App Router
|
|
3
|
+
* Permet de faire des appels IA LastBrain depuis le serveur
|
|
4
|
+
* avec support de LB_API_KEY ou session cookie
|
|
5
|
+
*/
|
|
6
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
7
|
+
/**
|
|
8
|
+
* Configuration du proxy LastBrain
|
|
9
|
+
*/
|
|
10
|
+
export interface LBProxyConfig {
|
|
11
|
+
/** URL de base de l'API LastBrain */
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
/** Clé API LastBrain (optionnel, peut utiliser env) */
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
/** Nom du cookie de session */
|
|
16
|
+
sessionCookieName?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Crée un handler de proxy unifié pour Next.js App Router
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* ```typescript
|
|
23
|
+
* // app/api/lastbrain/[...path]/route.ts
|
|
24
|
+
* import { createLBProxyHandler } from "@lastbrain/ai-ui-core";
|
|
25
|
+
*
|
|
26
|
+
* export const { GET, POST, PUT, DELETE, PATCH } = createLBProxyHandler({
|
|
27
|
+
* baseUrl: process.env.LB_BASE_URL,
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function createLBProxyHandler(config?: LBProxyConfig): {
|
|
32
|
+
GET: (request: NextRequest, context?: {
|
|
33
|
+
params: Promise<{
|
|
34
|
+
path?: string[];
|
|
35
|
+
}>;
|
|
36
|
+
}) => Promise<NextResponse>;
|
|
37
|
+
POST: (request: NextRequest, context?: {
|
|
38
|
+
params: Promise<{
|
|
39
|
+
path?: string[];
|
|
40
|
+
}>;
|
|
41
|
+
}) => Promise<NextResponse>;
|
|
42
|
+
PUT: (request: NextRequest, context?: {
|
|
43
|
+
params: Promise<{
|
|
44
|
+
path?: string[];
|
|
45
|
+
}>;
|
|
46
|
+
}) => Promise<NextResponse>;
|
|
47
|
+
DELETE: (request: NextRequest, context?: {
|
|
48
|
+
params: Promise<{
|
|
49
|
+
path?: string[];
|
|
50
|
+
}>;
|
|
51
|
+
}) => Promise<NextResponse>;
|
|
52
|
+
PATCH: (request: NextRequest, context?: {
|
|
53
|
+
params: Promise<{
|
|
54
|
+
path?: string[];
|
|
55
|
+
}>;
|
|
56
|
+
}) => Promise<NextResponse>;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Helper pour ajouter ou mettre à jour le cookie de session
|
|
60
|
+
*/
|
|
61
|
+
export declare function setLBSessionCookie(response: NextResponse, sessionToken: string, expiresIn?: number): NextResponse;
|
|
62
|
+
/**
|
|
63
|
+
* Helper pour supprimer le cookie de session
|
|
64
|
+
*/
|
|
65
|
+
export declare function clearLBSessionCookie(response: NextResponse): NextResponse;
|
|
66
|
+
//# sourceMappingURL=lb-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lb-proxy.d.ts","sourceRoot":"","sources":["../../../src/route-handlers/nextjs/lb-proxy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,aAAkB;mBAWlD,WAAW,YACV;QAAE,MAAM,EAAE,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAA;KAAE,KACjD,OAAO,CAAC,YAAY,CAAC;oBAFb,WAAW,YACV;QAAE,MAAM,EAAE,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAA;KAAE,KACjD,OAAO,CAAC,YAAY,CAAC;mBAFb,WAAW,YACV;QAAE,MAAM,EAAE,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAA;KAAE,KACjD,OAAO,CAAC,YAAY,CAAC;sBAFb,WAAW,YACV;QAAE,MAAM,EAAE,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAA;KAAE,KACjD,OAAO,CAAC,YAAY,CAAC;qBAFb,WAAW,YACV;QAAE,MAAM,EAAE,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAA;KAAE,KACjD,OAAO,CAAC,YAAY,CAAC;EAiHzB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,EACtB,YAAY,EAAE,MAAM,EACpB,SAAS,GAAE,MAAe,GACzB,YAAY,CAYd;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,YAAY,GAAG,YAAY,CAGzE"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy handler pour Next.js App Router
|
|
3
|
+
* Permet de faire des appels IA LastBrain depuis le serveur
|
|
4
|
+
* avec support de LB_API_KEY ou session cookie
|
|
5
|
+
*/
|
|
6
|
+
import { NextResponse } from "next/server";
|
|
7
|
+
/**
|
|
8
|
+
* Crée un handler de proxy unifié pour Next.js App Router
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // app/api/lastbrain/[...path]/route.ts
|
|
13
|
+
* import { createLBProxyHandler } from "@lastbrain/ai-ui-core";
|
|
14
|
+
*
|
|
15
|
+
* export const { GET, POST, PUT, DELETE, PATCH } = createLBProxyHandler({
|
|
16
|
+
* baseUrl: process.env.LB_BASE_URL,
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function createLBProxyHandler(config = {}) {
|
|
21
|
+
const { baseUrl = process.env.LB_BASE_URL || "https://lastbrain.io", apiKey = process.env.LB_API_KEY, sessionCookieName = "lb_session", } = config;
|
|
22
|
+
/**
|
|
23
|
+
* Handler unique pour toutes les méthodes HTTP et tous les paths
|
|
24
|
+
*/
|
|
25
|
+
async function handler(request, context) {
|
|
26
|
+
try {
|
|
27
|
+
// Extraire le path depuis l'URL ou les params
|
|
28
|
+
let path = "";
|
|
29
|
+
if (context?.params) {
|
|
30
|
+
const resolvedParams = await context.params;
|
|
31
|
+
path = resolvedParams.path?.join("/") || "";
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Fallback: extraire depuis l'URL
|
|
35
|
+
const url = new URL(request.url);
|
|
36
|
+
const pathMatch = url.pathname.match(/\/api\/lastbrain\/(.*)/);
|
|
37
|
+
path = pathMatch ? pathMatch[1] : "";
|
|
38
|
+
}
|
|
39
|
+
// Routes publiques qui ne nécessitent pas d'authentification
|
|
40
|
+
const publicPaths = [
|
|
41
|
+
"auth/login",
|
|
42
|
+
"auth/session/create",
|
|
43
|
+
"public/user/api-keys",
|
|
44
|
+
];
|
|
45
|
+
const isPublicPath = publicPaths.some((p) => path.startsWith(p));
|
|
46
|
+
// Construire l'URL complète vers LastBrain
|
|
47
|
+
const targetUrl = `${baseUrl}/api/ai/${path}`;
|
|
48
|
+
// Préparer les headers
|
|
49
|
+
const headers = {};
|
|
50
|
+
// Copier les headers pertinents de la requête originale
|
|
51
|
+
const contentType = request.headers.get("content-type");
|
|
52
|
+
if (contentType) {
|
|
53
|
+
headers["Content-Type"] = contentType;
|
|
54
|
+
}
|
|
55
|
+
const authorization = request.headers.get("authorization");
|
|
56
|
+
if (authorization) {
|
|
57
|
+
headers["Authorization"] = authorization;
|
|
58
|
+
}
|
|
59
|
+
// Authentification : LB_API_KEY ou session cookie
|
|
60
|
+
if (!authorization) {
|
|
61
|
+
if (apiKey) {
|
|
62
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Lire le cookie de session
|
|
66
|
+
const sessionToken = request.cookies.get(sessionCookieName)?.value;
|
|
67
|
+
if (sessionToken) {
|
|
68
|
+
headers["Authorization"] = `Bearer ${sessionToken}`;
|
|
69
|
+
}
|
|
70
|
+
else if (!isPublicPath) {
|
|
71
|
+
// Pas d'auth disponible et route protégée
|
|
72
|
+
return NextResponse.json({
|
|
73
|
+
error: "No authentication provided",
|
|
74
|
+
hint: "Set LB_API_KEY env variable or login via /api/lastbrain/auth/login",
|
|
75
|
+
}, { status: 401 });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Lire le body si c'est POST/PUT/PATCH
|
|
80
|
+
let body = undefined;
|
|
81
|
+
if (["POST", "PUT", "PATCH"].includes(request.method)) {
|
|
82
|
+
const text = await request.text();
|
|
83
|
+
if (text) {
|
|
84
|
+
body = text;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Faire la requête au backend LastBrain
|
|
88
|
+
const response = await fetch(targetUrl, {
|
|
89
|
+
method: request.method,
|
|
90
|
+
headers,
|
|
91
|
+
body,
|
|
92
|
+
});
|
|
93
|
+
// Lire la réponse
|
|
94
|
+
const responseText = await response.text();
|
|
95
|
+
let data;
|
|
96
|
+
try {
|
|
97
|
+
data = responseText ? JSON.parse(responseText) : {};
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
data = { error: "Invalid JSON response", raw: responseText };
|
|
101
|
+
}
|
|
102
|
+
// Créer la réponse Next.js
|
|
103
|
+
const nextResponse = NextResponse.json(data, { status: response.status });
|
|
104
|
+
// Copier les cookies de la réponse (notamment lb_session)
|
|
105
|
+
const setCookie = response.headers.get("set-cookie");
|
|
106
|
+
if (setCookie) {
|
|
107
|
+
nextResponse.headers.set("set-cookie", setCookie);
|
|
108
|
+
}
|
|
109
|
+
return nextResponse;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error("[LB Proxy] Error:", error);
|
|
113
|
+
return NextResponse.json({ error: error instanceof Error ? error.message : "Proxy error" }, { status: 500 });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Retourner un objet avec les handlers pour chaque méthode HTTP
|
|
117
|
+
return {
|
|
118
|
+
GET: handler,
|
|
119
|
+
POST: handler,
|
|
120
|
+
PUT: handler,
|
|
121
|
+
DELETE: handler,
|
|
122
|
+
PATCH: handler,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Helper pour ajouter ou mettre à jour le cookie de session
|
|
127
|
+
*/
|
|
128
|
+
export function setLBSessionCookie(response, sessionToken, expiresIn = 259200 // 72h en secondes
|
|
129
|
+
) {
|
|
130
|
+
response.cookies.set({
|
|
131
|
+
name: "lb_session",
|
|
132
|
+
value: sessionToken,
|
|
133
|
+
httpOnly: true,
|
|
134
|
+
secure: process.env.NODE_ENV === "production",
|
|
135
|
+
sameSite: "lax",
|
|
136
|
+
path: "/",
|
|
137
|
+
maxAge: expiresIn,
|
|
138
|
+
});
|
|
139
|
+
return response;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Helper pour supprimer le cookie de session
|
|
143
|
+
*/
|
|
144
|
+
export function clearLBSessionCookie(response) {
|
|
145
|
+
response.cookies.delete("lb_session");
|
|
146
|
+
return response;
|
|
147
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types pour le système d'authentification LastBrain
|
|
3
|
+
* Permet l'utilisation des services IA avec LB_API_KEY ou session utilisateur
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Configuration du client LastBrain
|
|
7
|
+
*/
|
|
8
|
+
export interface LBAuthConfig {
|
|
9
|
+
/** URL de base de l'API LastBrain (ex: https://api.lastbrain.io) */
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
/** Clé API LastBrain (côté serveur uniquement) */
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
/** Mode d'authentification */
|
|
14
|
+
mode?: "env-key" | "session" | "auto";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Session utilisateur LastBrain (stockée dans cookie HttpOnly)
|
|
18
|
+
*/
|
|
19
|
+
export interface LBSession {
|
|
20
|
+
/** Token de session opaque signé côté serveur */
|
|
21
|
+
sessionToken: string;
|
|
22
|
+
/** ID utilisateur associé */
|
|
23
|
+
userId: string;
|
|
24
|
+
/** ID de la clé API sélectionnée */
|
|
25
|
+
apiKeyId: string;
|
|
26
|
+
/** Timestamp d'expiration (72h après création) */
|
|
27
|
+
expiresAt: number;
|
|
28
|
+
/** Scopes/permissions accordés */
|
|
29
|
+
scopes?: string[];
|
|
30
|
+
/** Quotas restants (optionnel) */
|
|
31
|
+
quotas?: {
|
|
32
|
+
tokens?: number;
|
|
33
|
+
requests?: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Informations utilisateur LastBrain
|
|
38
|
+
*/
|
|
39
|
+
export interface LBUser {
|
|
40
|
+
id: string;
|
|
41
|
+
email: string;
|
|
42
|
+
name?: string;
|
|
43
|
+
avatar?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Clé API LastBrain de l'utilisateur
|
|
47
|
+
*/
|
|
48
|
+
export interface LBApiKey {
|
|
49
|
+
id: string;
|
|
50
|
+
name: string;
|
|
51
|
+
/** Préfixe visible de la clé (ex: "lb_live_abc...") */
|
|
52
|
+
keyPrefix: string;
|
|
53
|
+
scopes: string[];
|
|
54
|
+
isActive: boolean;
|
|
55
|
+
createdAt: string;
|
|
56
|
+
lastUsedAt?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Résultat de connexion
|
|
60
|
+
*/
|
|
61
|
+
export interface LBLoginResult {
|
|
62
|
+
/** Token d'accès temporaire pour récupérer les clés API */
|
|
63
|
+
accessToken: string;
|
|
64
|
+
/** Informations utilisateur */
|
|
65
|
+
user: LBUser;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Résultat de création de session
|
|
69
|
+
*/
|
|
70
|
+
export interface LBSessionResult {
|
|
71
|
+
/** Token de session opaque (72h) */
|
|
72
|
+
sessionToken: string;
|
|
73
|
+
/** Durée de validité en secondes (259200 = 72h) */
|
|
74
|
+
expiresIn: number;
|
|
75
|
+
/** Clé API sélectionnée */
|
|
76
|
+
apiKey: LBApiKey;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Statut d'authentification
|
|
80
|
+
*/
|
|
81
|
+
export type LBAuthStatus = "ready" | "needs_auth" | "loading" | "error";
|
|
82
|
+
/**
|
|
83
|
+
* État d'authentification complet
|
|
84
|
+
*/
|
|
85
|
+
export interface LBAuthState {
|
|
86
|
+
/** Statut actuel */
|
|
87
|
+
status: LBAuthStatus;
|
|
88
|
+
/** Utilisateur connecté (si session active) */
|
|
89
|
+
user?: LBUser;
|
|
90
|
+
/** Clé API sélectionnée (si session active) */
|
|
91
|
+
selectedKey?: LBApiKey;
|
|
92
|
+
/** Session active */
|
|
93
|
+
session?: LBSession;
|
|
94
|
+
/** Erreur éventuelle */
|
|
95
|
+
error?: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Options pour les requêtes IA
|
|
99
|
+
*/
|
|
100
|
+
export interface LBRequestOptions {
|
|
101
|
+
/** URL de base (override config) */
|
|
102
|
+
baseUrl?: string;
|
|
103
|
+
/** Headers supplémentaires */
|
|
104
|
+
headers?: Record<string, string>;
|
|
105
|
+
/** Timeout en ms */
|
|
106
|
+
timeout?: number;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/types/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kCAAkC;IAClC,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oBAAoB;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,qBAAqB;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client LastBrain intelligent
|
|
3
|
+
* Supporte deux modes d'authentification :
|
|
4
|
+
* - env-key : utilise LB_API_KEY (côté serveur)
|
|
5
|
+
* - session : utilise cookie lb_session (user authentifié)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
LBAuthConfig,
|
|
10
|
+
LBSession,
|
|
11
|
+
LBRequestOptions,
|
|
12
|
+
LBLoginResult,
|
|
13
|
+
LBApiKey,
|
|
14
|
+
LBSessionResult,
|
|
15
|
+
} from "../types/auth";
|
|
16
|
+
|
|
17
|
+
export class LBClient {
|
|
18
|
+
private config: Required<LBAuthConfig>;
|
|
19
|
+
|
|
20
|
+
constructor(config: LBAuthConfig = {}) {
|
|
21
|
+
this.config = {
|
|
22
|
+
baseUrl:
|
|
23
|
+
config.baseUrl ||
|
|
24
|
+
process.env.LB_BASE_URL ||
|
|
25
|
+
"https://prompt.lastbrain.io",
|
|
26
|
+
apiKey: config.apiKey || process.env.LB_API_KEY || "",
|
|
27
|
+
mode: config.mode || "auto",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Détecte le mode d'authentification à utiliser
|
|
33
|
+
*/
|
|
34
|
+
private getAuthMode(): "env-key" | "session" {
|
|
35
|
+
if (this.config.mode === "env-key") return "env-key";
|
|
36
|
+
if (this.config.mode === "session") return "session";
|
|
37
|
+
|
|
38
|
+
// Mode auto : préfère env-key si disponible
|
|
39
|
+
return this.config.apiKey ? "env-key" : "session";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Construit les headers d'authentification
|
|
44
|
+
*/
|
|
45
|
+
private async getAuthHeaders(
|
|
46
|
+
sessionToken?: string
|
|
47
|
+
): Promise<Record<string, string>> {
|
|
48
|
+
const mode = this.getAuthMode();
|
|
49
|
+
|
|
50
|
+
if (mode === "env-key" && this.config.apiKey) {
|
|
51
|
+
return {
|
|
52
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Mode session : utilise le token fourni ou tente de lire le cookie
|
|
57
|
+
if (sessionToken) {
|
|
58
|
+
return {
|
|
59
|
+
Authorization: `Bearer ${sessionToken}`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Si aucun token fourni, la requête échouera (attendu pour déclencher auth)
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Fait une requête à l'API LastBrain
|
|
69
|
+
*/
|
|
70
|
+
private async request<T>(
|
|
71
|
+
endpoint: string,
|
|
72
|
+
options: RequestInit & LBRequestOptions = {}
|
|
73
|
+
): Promise<T> {
|
|
74
|
+
const {
|
|
75
|
+
baseUrl,
|
|
76
|
+
headers: customHeaders,
|
|
77
|
+
timeout,
|
|
78
|
+
...fetchOptions
|
|
79
|
+
} = options;
|
|
80
|
+
|
|
81
|
+
const url = `${baseUrl || this.config.baseUrl}${endpoint}`;
|
|
82
|
+
const authHeaders = await this.getAuthHeaders();
|
|
83
|
+
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
const timeoutId = timeout
|
|
86
|
+
? setTimeout(() => controller.abort(), timeout)
|
|
87
|
+
: undefined;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const response = await fetch(url, {
|
|
91
|
+
...fetchOptions,
|
|
92
|
+
headers: {
|
|
93
|
+
"Content-Type": "application/json",
|
|
94
|
+
...authHeaders,
|
|
95
|
+
...customHeaders,
|
|
96
|
+
},
|
|
97
|
+
signal: controller.signal,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const error = await response.json().catch(() => ({}));
|
|
102
|
+
throw new Error(error.message || `HTTP ${response.status}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return await response.json();
|
|
106
|
+
} finally {
|
|
107
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Login utilisateur (email + password)
|
|
113
|
+
* Retourne un access_token temporaire pour récupérer les clés API
|
|
114
|
+
*/
|
|
115
|
+
async login(email: string, password: string): Promise<LBLoginResult> {
|
|
116
|
+
return this.request<LBLoginResult>("/auth/login", {
|
|
117
|
+
method: "POST",
|
|
118
|
+
body: JSON.stringify({ email, password }),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Récupère les clés API de l'utilisateur
|
|
124
|
+
*/
|
|
125
|
+
async getUserApiKeys(accessToken: string): Promise<LBApiKey[]> {
|
|
126
|
+
return this.request<LBApiKey[]>("/user/api-keys", {
|
|
127
|
+
headers: {
|
|
128
|
+
Authorization: `Bearer ${accessToken}`,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Crée une session (72h) avec une clé API sélectionnée
|
|
135
|
+
*/
|
|
136
|
+
async createSession(
|
|
137
|
+
accessToken: string,
|
|
138
|
+
apiKeyId: string
|
|
139
|
+
): Promise<LBSessionResult> {
|
|
140
|
+
return this.request<LBSessionResult>("/auth/session", {
|
|
141
|
+
method: "POST",
|
|
142
|
+
headers: {
|
|
143
|
+
Authorization: `Bearer ${accessToken}`,
|
|
144
|
+
},
|
|
145
|
+
body: JSON.stringify({ api_key_id: apiKeyId }),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Vérifie si une session est valide
|
|
151
|
+
*/
|
|
152
|
+
async verifySession(sessionToken: string): Promise<LBSession | null> {
|
|
153
|
+
try {
|
|
154
|
+
return await this.request<LBSession>("/auth/session/verify", {
|
|
155
|
+
headers: {
|
|
156
|
+
Authorization: `Bearer ${sessionToken}`,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
} catch {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Déconnexion (invalide la session)
|
|
166
|
+
*/
|
|
167
|
+
async logout(sessionToken: string): Promise<void> {
|
|
168
|
+
await this.request("/auth/session/logout", {
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers: {
|
|
171
|
+
Authorization: `Bearer ${sessionToken}`,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Fait un appel IA (génération de texte)
|
|
178
|
+
*/
|
|
179
|
+
async generateText(params: {
|
|
180
|
+
prompt: string;
|
|
181
|
+
model?: string;
|
|
182
|
+
sessionToken?: string;
|
|
183
|
+
[key: string]: any;
|
|
184
|
+
}): Promise<any> {
|
|
185
|
+
const { sessionToken, ...body } = params;
|
|
186
|
+
const headers = await this.getAuthHeaders(sessionToken);
|
|
187
|
+
|
|
188
|
+
return this.request("/ai/generate-text", {
|
|
189
|
+
method: "POST",
|
|
190
|
+
headers,
|
|
191
|
+
body: JSON.stringify(body),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Fait un appel IA (génération d'image)
|
|
197
|
+
*/
|
|
198
|
+
async generateImage(params: {
|
|
199
|
+
prompt: string;
|
|
200
|
+
model?: string;
|
|
201
|
+
sessionToken?: string;
|
|
202
|
+
[key: string]: any;
|
|
203
|
+
}): Promise<any> {
|
|
204
|
+
const { sessionToken, ...body } = params;
|
|
205
|
+
const headers = await this.getAuthHeaders(sessionToken);
|
|
206
|
+
|
|
207
|
+
return this.request("/ai/generate-image", {
|
|
208
|
+
method: "POST",
|
|
209
|
+
headers,
|
|
210
|
+
body: JSON.stringify(body),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Factory pour créer un client LastBrain
|
|
217
|
+
*/
|
|
218
|
+
export function createLBClient(config?: LBAuthConfig): LBClient {
|
|
219
|
+
return new LBClient(config);
|
|
220
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
|
+
export * from "./types/auth";
|
|
2
3
|
export * from "./errors/errorCodes";
|
|
3
4
|
export * from "./errors/normalizeError";
|
|
4
5
|
export * from "./client/createClient";
|
|
6
|
+
export * from "./client/lb-client";
|
|
5
7
|
|
|
6
8
|
// Route handlers
|
|
7
9
|
export * from "./route-handlers/nextjs/gateway";
|
|
@@ -11,3 +13,4 @@ export {
|
|
|
11
13
|
PUT as EnhancedPUT,
|
|
12
14
|
} from "./route-handlers/nextjs/enhanced-gateway";
|
|
13
15
|
export * as enhancedGateway from "./route-handlers/nextjs/enhanced-gateway";
|
|
16
|
+
export * from "./route-handlers/nextjs/lb-proxy";
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy handler pour Next.js App Router
|
|
3
|
+
* Permet de faire des appels IA LastBrain depuis le serveur
|
|
4
|
+
* avec support de LB_API_KEY ou session cookie
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Configuration du proxy LastBrain
|
|
11
|
+
*/
|
|
12
|
+
export interface LBProxyConfig {
|
|
13
|
+
/** URL de base de l'API LastBrain */
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
/** Clé API LastBrain (optionnel, peut utiliser env) */
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
/** Nom du cookie de session */
|
|
18
|
+
sessionCookieName?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Crée un handler de proxy unifié pour Next.js App Router
|
|
23
|
+
*
|
|
24
|
+
* Usage:
|
|
25
|
+
* ```typescript
|
|
26
|
+
* // app/api/lastbrain/[...path]/route.ts
|
|
27
|
+
* import { createLBProxyHandler } from "@lastbrain/ai-ui-core";
|
|
28
|
+
*
|
|
29
|
+
* export const { GET, POST, PUT, DELETE, PATCH } = createLBProxyHandler({
|
|
30
|
+
* baseUrl: process.env.LB_BASE_URL,
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function createLBProxyHandler(config: LBProxyConfig = {}) {
|
|
35
|
+
const {
|
|
36
|
+
baseUrl = process.env.LB_BASE_URL || "https://lastbrain.io",
|
|
37
|
+
apiKey = process.env.LB_API_KEY,
|
|
38
|
+
sessionCookieName = "lb_session",
|
|
39
|
+
} = config;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Handler unique pour toutes les méthodes HTTP et tous les paths
|
|
43
|
+
*/
|
|
44
|
+
async function handler(
|
|
45
|
+
request: NextRequest,
|
|
46
|
+
context?: { params: Promise<{ path?: string[] }> }
|
|
47
|
+
): Promise<NextResponse> {
|
|
48
|
+
try {
|
|
49
|
+
// Extraire le path depuis l'URL ou les params
|
|
50
|
+
let path = "";
|
|
51
|
+
if (context?.params) {
|
|
52
|
+
const resolvedParams = await context.params;
|
|
53
|
+
path = resolvedParams.path?.join("/") || "";
|
|
54
|
+
} else {
|
|
55
|
+
// Fallback: extraire depuis l'URL
|
|
56
|
+
const url = new URL(request.url);
|
|
57
|
+
const pathMatch = url.pathname.match(/\/api\/lastbrain\/(.*)/);
|
|
58
|
+
path = pathMatch ? pathMatch[1] : "";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Routes publiques qui ne nécessitent pas d'authentification
|
|
62
|
+
const publicPaths = [
|
|
63
|
+
"auth/login",
|
|
64
|
+
"auth/session/create",
|
|
65
|
+
"public/user/api-keys",
|
|
66
|
+
];
|
|
67
|
+
const isPublicPath = publicPaths.some((p) => path.startsWith(p));
|
|
68
|
+
|
|
69
|
+
// Construire l'URL complète vers LastBrain
|
|
70
|
+
const targetUrl = `${baseUrl}/api/ai/${path}`;
|
|
71
|
+
|
|
72
|
+
// Préparer les headers
|
|
73
|
+
const headers: Record<string, string> = {};
|
|
74
|
+
|
|
75
|
+
// Copier les headers pertinents de la requête originale
|
|
76
|
+
const contentType = request.headers.get("content-type");
|
|
77
|
+
if (contentType) {
|
|
78
|
+
headers["Content-Type"] = contentType;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const authorization = request.headers.get("authorization");
|
|
82
|
+
if (authorization) {
|
|
83
|
+
headers["Authorization"] = authorization;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Authentification : LB_API_KEY ou session cookie
|
|
87
|
+
if (!authorization) {
|
|
88
|
+
if (apiKey) {
|
|
89
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
90
|
+
} else {
|
|
91
|
+
// Lire le cookie de session
|
|
92
|
+
const sessionToken = request.cookies.get(sessionCookieName)?.value;
|
|
93
|
+
if (sessionToken) {
|
|
94
|
+
headers["Authorization"] = `Bearer ${sessionToken}`;
|
|
95
|
+
} else if (!isPublicPath) {
|
|
96
|
+
// Pas d'auth disponible et route protégée
|
|
97
|
+
return NextResponse.json(
|
|
98
|
+
{
|
|
99
|
+
error: "No authentication provided",
|
|
100
|
+
hint: "Set LB_API_KEY env variable or login via /api/lastbrain/auth/login",
|
|
101
|
+
},
|
|
102
|
+
{ status: 401 }
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Lire le body si c'est POST/PUT/PATCH
|
|
109
|
+
let body: string | undefined = undefined;
|
|
110
|
+
if (["POST", "PUT", "PATCH"].includes(request.method)) {
|
|
111
|
+
const text = await request.text();
|
|
112
|
+
if (text) {
|
|
113
|
+
body = text;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Faire la requête au backend LastBrain
|
|
118
|
+
const response = await fetch(targetUrl, {
|
|
119
|
+
method: request.method,
|
|
120
|
+
headers,
|
|
121
|
+
body,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Lire la réponse
|
|
125
|
+
const responseText = await response.text();
|
|
126
|
+
let data: any;
|
|
127
|
+
try {
|
|
128
|
+
data = responseText ? JSON.parse(responseText) : {};
|
|
129
|
+
} catch {
|
|
130
|
+
data = { error: "Invalid JSON response", raw: responseText };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Créer la réponse Next.js
|
|
134
|
+
const nextResponse = NextResponse.json(data, { status: response.status });
|
|
135
|
+
|
|
136
|
+
// Copier les cookies de la réponse (notamment lb_session)
|
|
137
|
+
const setCookie = response.headers.get("set-cookie");
|
|
138
|
+
if (setCookie) {
|
|
139
|
+
nextResponse.headers.set("set-cookie", setCookie);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return nextResponse;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error("[LB Proxy] Error:", error);
|
|
145
|
+
return NextResponse.json(
|
|
146
|
+
{ error: error instanceof Error ? error.message : "Proxy error" },
|
|
147
|
+
{ status: 500 }
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Retourner un objet avec les handlers pour chaque méthode HTTP
|
|
153
|
+
return {
|
|
154
|
+
GET: handler,
|
|
155
|
+
POST: handler,
|
|
156
|
+
PUT: handler,
|
|
157
|
+
DELETE: handler,
|
|
158
|
+
PATCH: handler,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Helper pour ajouter ou mettre à jour le cookie de session
|
|
164
|
+
*/
|
|
165
|
+
export function setLBSessionCookie(
|
|
166
|
+
response: NextResponse,
|
|
167
|
+
sessionToken: string,
|
|
168
|
+
expiresIn: number = 259200 // 72h en secondes
|
|
169
|
+
): NextResponse {
|
|
170
|
+
response.cookies.set({
|
|
171
|
+
name: "lb_session",
|
|
172
|
+
value: sessionToken,
|
|
173
|
+
httpOnly: true,
|
|
174
|
+
secure: process.env.NODE_ENV === "production",
|
|
175
|
+
sameSite: "lax",
|
|
176
|
+
path: "/",
|
|
177
|
+
maxAge: expiresIn,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return response;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Helper pour supprimer le cookie de session
|
|
185
|
+
*/
|
|
186
|
+
export function clearLBSessionCookie(response: NextResponse): NextResponse {
|
|
187
|
+
response.cookies.delete("lb_session");
|
|
188
|
+
return response;
|
|
189
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types pour le système d'authentification LastBrain
|
|
3
|
+
* Permet l'utilisation des services IA avec LB_API_KEY ou session utilisateur
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration du client LastBrain
|
|
8
|
+
*/
|
|
9
|
+
export interface LBAuthConfig {
|
|
10
|
+
/** URL de base de l'API LastBrain (ex: https://api.lastbrain.io) */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/** Clé API LastBrain (côté serveur uniquement) */
|
|
13
|
+
apiKey?: string;
|
|
14
|
+
/** Mode d'authentification */
|
|
15
|
+
mode?: "env-key" | "session" | "auto";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Session utilisateur LastBrain (stockée dans cookie HttpOnly)
|
|
20
|
+
*/
|
|
21
|
+
export interface LBSession {
|
|
22
|
+
/** Token de session opaque signé côté serveur */
|
|
23
|
+
sessionToken: string;
|
|
24
|
+
/** ID utilisateur associé */
|
|
25
|
+
userId: string;
|
|
26
|
+
/** ID de la clé API sélectionnée */
|
|
27
|
+
apiKeyId: string;
|
|
28
|
+
/** Timestamp d'expiration (72h après création) */
|
|
29
|
+
expiresAt: number;
|
|
30
|
+
/** Scopes/permissions accordés */
|
|
31
|
+
scopes?: string[];
|
|
32
|
+
/** Quotas restants (optionnel) */
|
|
33
|
+
quotas?: {
|
|
34
|
+
tokens?: number;
|
|
35
|
+
requests?: number;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Informations utilisateur LastBrain
|
|
41
|
+
*/
|
|
42
|
+
export interface LBUser {
|
|
43
|
+
id: string;
|
|
44
|
+
email: string;
|
|
45
|
+
name?: string;
|
|
46
|
+
avatar?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Clé API LastBrain de l'utilisateur
|
|
51
|
+
*/
|
|
52
|
+
export interface LBApiKey {
|
|
53
|
+
id: string;
|
|
54
|
+
name: string;
|
|
55
|
+
/** Préfixe visible de la clé (ex: "lb_live_abc...") */
|
|
56
|
+
keyPrefix: string;
|
|
57
|
+
scopes: string[];
|
|
58
|
+
isActive: boolean;
|
|
59
|
+
createdAt: string;
|
|
60
|
+
lastUsedAt?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Résultat de connexion
|
|
65
|
+
*/
|
|
66
|
+
export interface LBLoginResult {
|
|
67
|
+
/** Token d'accès temporaire pour récupérer les clés API */
|
|
68
|
+
accessToken: string;
|
|
69
|
+
/** Informations utilisateur */
|
|
70
|
+
user: LBUser;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Résultat de création de session
|
|
75
|
+
*/
|
|
76
|
+
export interface LBSessionResult {
|
|
77
|
+
/** Token de session opaque (72h) */
|
|
78
|
+
sessionToken: string;
|
|
79
|
+
/** Durée de validité en secondes (259200 = 72h) */
|
|
80
|
+
expiresIn: number;
|
|
81
|
+
/** Clé API sélectionnée */
|
|
82
|
+
apiKey: LBApiKey;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Statut d'authentification
|
|
87
|
+
*/
|
|
88
|
+
export type LBAuthStatus = "ready" | "needs_auth" | "loading" | "error";
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* État d'authentification complet
|
|
92
|
+
*/
|
|
93
|
+
export interface LBAuthState {
|
|
94
|
+
/** Statut actuel */
|
|
95
|
+
status: LBAuthStatus;
|
|
96
|
+
/** Utilisateur connecté (si session active) */
|
|
97
|
+
user?: LBUser;
|
|
98
|
+
/** Clé API sélectionnée (si session active) */
|
|
99
|
+
selectedKey?: LBApiKey;
|
|
100
|
+
/** Session active */
|
|
101
|
+
session?: LBSession;
|
|
102
|
+
/** Erreur éventuelle */
|
|
103
|
+
error?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Options pour les requêtes IA
|
|
108
|
+
*/
|
|
109
|
+
export interface LBRequestOptions {
|
|
110
|
+
/** URL de base (override config) */
|
|
111
|
+
baseUrl?: string;
|
|
112
|
+
/** Headers supplémentaires */
|
|
113
|
+
headers?: Record<string, string>;
|
|
114
|
+
/** Timeout en ms */
|
|
115
|
+
timeout?: number;
|
|
116
|
+
}
|