@lastbrain/module-ai 0.1.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.
Files changed (56) hide show
  1. package/dist/ai.build.config.d.ts +4 -0
  2. package/dist/ai.build.config.d.ts.map +1 -0
  3. package/dist/ai.build.config.js +86 -0
  4. package/dist/api/admin/user-token/[id].d.ts +29 -0
  5. package/dist/api/admin/user-token/[id].d.ts.map +1 -0
  6. package/dist/api/admin/user-token/[id].js +57 -0
  7. package/dist/api/admin/user-token.d.ts +20 -0
  8. package/dist/api/admin/user-token.d.ts.map +1 -0
  9. package/dist/api/admin/user-token.js +92 -0
  10. package/dist/api/admin/user_prompts.d.ts +17 -0
  11. package/dist/api/admin/user_prompts.d.ts.map +1 -0
  12. package/dist/api/admin/user_prompts.js +94 -0
  13. package/dist/api/admin/user_token_ledger.d.ts +17 -0
  14. package/dist/api/admin/user_token_ledger.d.ts.map +1 -0
  15. package/dist/api/admin/user_token_ledger.js +94 -0
  16. package/dist/api/auth/generate-image.d.ts +11 -0
  17. package/dist/api/auth/generate-image.d.ts.map +1 -0
  18. package/dist/api/auth/generate-image.js +104 -0
  19. package/dist/api/auth/generate-text.d.ts +11 -0
  20. package/dist/api/auth/generate-text.d.ts.map +1 -0
  21. package/dist/api/auth/generate-text.js +96 -0
  22. package/dist/api/auth/user_prompts.d.ts +17 -0
  23. package/dist/api/auth/user_prompts.d.ts.map +1 -0
  24. package/dist/api/auth/user_prompts.js +94 -0
  25. package/dist/api/auth/user_token_ledger.d.ts +17 -0
  26. package/dist/api/auth/user_token_ledger.d.ts.map +1 -0
  27. package/dist/api/auth/user_token_ledger.js +94 -0
  28. package/dist/components/Doc.d.ts +2 -0
  29. package/dist/components/Doc.d.ts.map +1 -0
  30. package/dist/components/Doc.js +5 -0
  31. package/dist/index.d.ts +10 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +12 -0
  34. package/dist/server.d.ts +61 -0
  35. package/dist/server.d.ts.map +1 -0
  36. package/dist/server.js +186 -0
  37. package/dist/web/admin/UserTokenIdPage.d.ts +6 -0
  38. package/dist/web/admin/UserTokenIdPage.d.ts.map +1 -0
  39. package/dist/web/admin/UserTokenIdPage.js +8 -0
  40. package/dist/web/admin/UserTokenPage.d.ts +2 -0
  41. package/dist/web/admin/UserTokenPage.d.ts.map +1 -0
  42. package/dist/web/admin/UserTokenPage.js +6 -0
  43. package/dist/web/auth/TokenPage.d.ts +2 -0
  44. package/dist/web/auth/TokenPage.d.ts.map +1 -0
  45. package/dist/web/auth/TokenPage.js +6 -0
  46. package/dist/web/components/ImageGenerative.d.ts +22 -0
  47. package/dist/web/components/ImageGenerative.d.ts.map +1 -0
  48. package/dist/web/components/ImageGenerative.js +87 -0
  49. package/dist/web/components/TextareaGenerative.d.ts +23 -0
  50. package/dist/web/components/TextareaGenerative.d.ts.map +1 -0
  51. package/dist/web/components/TextareaGenerative.js +60 -0
  52. package/package.json +72 -0
  53. package/supabase/migrations/20251121000000_ai_tokens.sql +189 -0
  54. package/supabase/migrations/20251121093113_module-ai_init.sql +122 -0
  55. package/supabase/migrations-down/20251121000000_ai_tokens.sql +23 -0
  56. package/supabase/migrations-down/20251121093113_module-ai_init.sql +11 -0
@@ -0,0 +1,4 @@
1
+ import type { ModuleBuildConfig } from "@lastbrain/core";
2
+ declare const buildConfig: ModuleBuildConfig;
3
+ export default buildConfig;
4
+ //# sourceMappingURL=ai.build.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.build.config.d.ts","sourceRoot":"","sources":["../src/ai.build.config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,QAAA,MAAM,WAAW,EAAE,iBAoFlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,86 @@
1
+ const buildConfig = {
2
+ moduleName: "@lastbrain/module-ai",
3
+ pages: [
4
+ {
5
+ section: "auth",
6
+ path: "/token",
7
+ componentExport: "TokenPage",
8
+ },
9
+ {
10
+ section: "admin",
11
+ path: "/user-token",
12
+ componentExport: "UserTokenPage",
13
+ },
14
+ {
15
+ section: "admin",
16
+ path: "/user-token/[id]",
17
+ componentExport: "UserTokenIdPage",
18
+ }
19
+ ],
20
+ apis: [
21
+ // Routes de génération IA
22
+ {
23
+ method: "POST",
24
+ path: "/api/auth/generate-text",
25
+ handlerExport: "POST",
26
+ entryPoint: "api/auth/generate-text",
27
+ authRequired: true,
28
+ },
29
+ {
30
+ method: "POST",
31
+ path: "/api/auth/generate-image",
32
+ handlerExport: "POST",
33
+ entryPoint: "api/auth/generate-image",
34
+ authRequired: true,
35
+ },
36
+ // Routes admin pour la gestion des tokens
37
+ {
38
+ method: "GET",
39
+ path: "/api/admin/user-token",
40
+ handlerExport: "GET",
41
+ entryPoint: "api/admin/user-token",
42
+ authRequired: true,
43
+ },
44
+ {
45
+ method: "POST",
46
+ path: "/api/admin/user-token",
47
+ handlerExport: "POST",
48
+ entryPoint: "api/admin/user-token",
49
+ authRequired: true,
50
+ },
51
+ {
52
+ method: "GET",
53
+ path: "/api/admin/user-token/[id]",
54
+ handlerExport: "GET",
55
+ entryPoint: "api/admin/user-token/[id]",
56
+ authRequired: true,
57
+ },
58
+ ],
59
+ migrations: {
60
+ enabled: true,
61
+ priority: 30,
62
+ path: "supabase/migrations",
63
+ files: ["20251121000000_ai_tokens.sql"],
64
+ },
65
+ menu: {
66
+ auth: [
67
+ {
68
+ title: "Mes Tokens",
69
+ description: "Historique et balance des tokens IA",
70
+ icon: "Coins",
71
+ path: "/auth/token",
72
+ order: 100,
73
+ }
74
+ ],
75
+ admin: [
76
+ {
77
+ title: "Gestion Tokens",
78
+ description: "Gestion des tokens utilisateurs",
79
+ icon: "DatabaseZap",
80
+ path: "/admin/user-token",
81
+ order: 100,
82
+ }
83
+ ]
84
+ },
85
+ };
86
+ export default buildConfig;
@@ -0,0 +1,29 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ export declare function GET(request: NextRequest, props: {
3
+ params: Promise<{
4
+ id: string;
5
+ }>;
6
+ }): Promise<NextResponse<{
7
+ user: {
8
+ id: string;
9
+ email: string | undefined;
10
+ createdAt: string;
11
+ };
12
+ balance: number;
13
+ stats: {
14
+ balance: number;
15
+ totalPurchased: number;
16
+ totalGifted: number;
17
+ totalUsed: number;
18
+ totalAdjusted: number;
19
+ };
20
+ history: import("../../../server").TokenLedgerEntry[];
21
+ pagination: {
22
+ limit: number;
23
+ offset: number;
24
+ hasMore: boolean;
25
+ };
26
+ }> | NextResponse<{
27
+ error: any;
28
+ }>>;
29
+ //# sourceMappingURL=%5Bid%5D.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"[id].d.ts","sourceRoot":"","sources":["../../../../src/api/admin/user-token/[id].ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKxD,wBAAsB,GAAG,CACvB,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;IA6E3C"}
@@ -0,0 +1,57 @@
1
+ import { NextResponse } from "next/server";
2
+ import { getSupabaseServerClient } from "@lastbrain/core/server";
3
+ import { getTokenBalance, getTokenHistory, getTokenStats } from "../../../server";
4
+ // GET /api/admin/user-token/[id] - Détails d'un utilisateur spécifique
5
+ export async function GET(request, props) {
6
+ const params = await props.params;
7
+ const { id } = params;
8
+ try {
9
+ const supabase = await getSupabaseServerClient();
10
+ // Vérifier l'authentification et les permissions admin
11
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
12
+ if (authError || !user) {
13
+ return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
14
+ }
15
+ // Vérifier si l'utilisateur est superadmin
16
+ const { data: isSuperAdmin } = await supabase.rpc("is_superadmin", {
17
+ user_id: user.id,
18
+ });
19
+ if (!isSuperAdmin) {
20
+ return NextResponse.json({ error: "Accès refusé. Droits administrateur requis." }, { status: 403 });
21
+ }
22
+ const targetUserId = id;
23
+ // Récupérer les informations de l'utilisateur
24
+ const { data: targetUser, error: userError } = await supabase.auth.admin.getUserById(targetUserId);
25
+ if (userError || !targetUser) {
26
+ return NextResponse.json({ error: "Utilisateur non trouvé" }, { status: 404 });
27
+ }
28
+ // Récupérer le solde
29
+ const balance = await getTokenBalance(targetUserId);
30
+ // Récupérer les statistiques
31
+ const stats = await getTokenStats(targetUserId);
32
+ // Récupérer l'historique (limit depuis query params)
33
+ const searchParams = request.nextUrl.searchParams;
34
+ const limit = parseInt(searchParams.get("limit") || "50");
35
+ const offset = parseInt(searchParams.get("offset") || "0");
36
+ const history = await getTokenHistory(targetUserId, limit, offset);
37
+ return NextResponse.json({
38
+ user: {
39
+ id: targetUser.user.id,
40
+ email: targetUser.user.email,
41
+ createdAt: targetUser.user.created_at,
42
+ },
43
+ balance,
44
+ stats,
45
+ history,
46
+ pagination: {
47
+ limit,
48
+ offset,
49
+ hasMore: history.length === limit,
50
+ },
51
+ });
52
+ }
53
+ catch (error) {
54
+ console.error("[GET /api/admin/user-token/[id]] Error:", error);
55
+ return NextResponse.json({ error: error.message || "Erreur serveur" }, { status: 500 });
56
+ }
57
+ }
@@ -0,0 +1,20 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ export declare function GET(request: NextRequest): Promise<NextResponse<{
3
+ users: {
4
+ id: any;
5
+ email: string;
6
+ balance: any;
7
+ createdAt: string | undefined;
8
+ }[];
9
+ total: number;
10
+ }> | NextResponse<{
11
+ error: any;
12
+ }>>;
13
+ export declare function POST(request: NextRequest): Promise<NextResponse<{
14
+ success: boolean;
15
+ balance: number;
16
+ message: string;
17
+ }> | NextResponse<{
18
+ error: any;
19
+ }>>;
20
+ //# sourceMappingURL=user-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-token.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKxD,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;IA8D7C;AAGD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;IAsE9C"}
@@ -0,0 +1,92 @@
1
+ import { NextResponse } from "next/server";
2
+ import { getSupabaseServerClient } from "@lastbrain/core/server";
3
+ import { addTokens } from "../../server";
4
+ // GET /api/admin/user-token - Liste tous les utilisateurs avec leur balance
5
+ export async function GET(request) {
6
+ try {
7
+ const supabase = await getSupabaseServerClient();
8
+ // Vérifier l'authentification et les permissions admin
9
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
10
+ if (authError || !user) {
11
+ return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
12
+ }
13
+ // Vérifier si l'utilisateur est superadmin
14
+ const { data: isSuperAdmin } = await supabase.rpc("is_superadmin", {
15
+ user_id: user.id,
16
+ });
17
+ if (!isSuperAdmin) {
18
+ return NextResponse.json({ error: "Accès refusé. Droits administrateur requis." }, { status: 403 });
19
+ }
20
+ // Récupérer tous les utilisateurs avec leur balance
21
+ const { data: balances, error: balanceError } = await supabase
22
+ .from("user_token_balance_v")
23
+ .select("owner_id, balance")
24
+ .order("balance", { ascending: false });
25
+ if (balanceError)
26
+ throw balanceError;
27
+ // Récupérer les informations des utilisateurs
28
+ const userIds = balances?.map((b) => b.owner_id) || [];
29
+ const { data: users, error: usersError } = await supabase.auth.admin.listUsers();
30
+ if (usersError)
31
+ throw usersError;
32
+ // Combiner les données
33
+ const usersWithBalance = balances?.map((balance) => {
34
+ const userInfo = users.users.find((u) => u.id === balance.owner_id);
35
+ return {
36
+ id: balance.owner_id,
37
+ email: userInfo?.email || "Unknown",
38
+ balance: balance.balance,
39
+ createdAt: userInfo?.created_at,
40
+ };
41
+ });
42
+ return NextResponse.json({
43
+ users: usersWithBalance,
44
+ total: usersWithBalance?.length || 0,
45
+ });
46
+ }
47
+ catch (error) {
48
+ console.error("[GET /api/admin/user-token] Error:", error);
49
+ return NextResponse.json({ error: error.message || "Erreur serveur" }, { status: 500 });
50
+ }
51
+ }
52
+ // POST /api/admin/user-token - Ajouter/retirer des tokens à un utilisateur
53
+ export async function POST(request) {
54
+ try {
55
+ const supabase = await getSupabaseServerClient();
56
+ // Vérifier l'authentification et les permissions admin
57
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
58
+ if (authError || !user) {
59
+ return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
60
+ }
61
+ // Vérifier si l'utilisateur est superadmin
62
+ const { data: isSuperAdmin } = await supabase.rpc("is_superadmin", {
63
+ user_id: user.id,
64
+ });
65
+ if (!isSuperAdmin) {
66
+ return NextResponse.json({ error: "Accès refusé. Droits administrateur requis." }, { status: 403 });
67
+ }
68
+ const body = await request.json();
69
+ const { userId, amount, type, reason } = body;
70
+ if (!userId || !amount) {
71
+ return NextResponse.json({ error: "userId et amount sont requis" }, { status: 400 });
72
+ }
73
+ if (amount === 0) {
74
+ return NextResponse.json({ error: "Le montant ne peut pas être zéro" }, { status: 400 });
75
+ }
76
+ // Déterminer le type d'opération
77
+ const operationType = type || (amount > 0 ? "adjust" : "adjust");
78
+ const result = await addTokens(userId, amount, operationType, { reason: reason || "Ajustement manuel par admin", admin_id: user.id }, user.id);
79
+ if (!result.success) {
80
+ return NextResponse.json({ error: result.error }, { status: 500 });
81
+ }
82
+ return NextResponse.json({
83
+ success: true,
84
+ balance: result.balance,
85
+ message: `${amount > 0 ? "Ajouté" : "Retiré"} ${Math.abs(amount)} tokens`,
86
+ });
87
+ }
88
+ catch (error) {
89
+ console.error("[POST /api/admin/user-token] Error:", error);
90
+ return NextResponse.json({ error: error.message || "Erreur serveur" }, { status: 500 });
91
+ }
92
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * GET - Liste tous les enregistrements de user_prompts
3
+ */
4
+ export declare function GET(request: Request): Promise<Response>;
5
+ /**
6
+ * POST - Crée un nouvel enregistrement dans user_prompts
7
+ */
8
+ export declare function POST(request: Request): Promise<Response>;
9
+ /**
10
+ * PUT - Met à jour un enregistrement dans user_prompts
11
+ */
12
+ export declare function PUT(request: Request): Promise<Response>;
13
+ /**
14
+ * DELETE - Supprime un enregistrement de user_prompts
15
+ */
16
+ export declare function DELETE(request: Request): Promise<Response>;
17
+ //# sourceMappingURL=user_prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user_prompts.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user_prompts.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBAkBzC;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAsB1C;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBA4BzC;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,qBA0B5C"}
@@ -0,0 +1,94 @@
1
+ import { getSupabaseServerClient } from "@lastbrain/core/server";
2
+ const jsonResponse = (payload, status = 200) => {
3
+ return new Response(JSON.stringify(payload), {
4
+ headers: {
5
+ "content-type": "application/json"
6
+ },
7
+ status
8
+ });
9
+ };
10
+ /**
11
+ * GET - Liste tous les enregistrements de user_prompts
12
+ */
13
+ export async function GET(request) {
14
+ const supabase = await getSupabaseServerClient();
15
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
16
+ if (authError || !user) {
17
+ return jsonResponse({ error: "Non authentifié" }, 401);
18
+ }
19
+ const { data, error } = await supabase
20
+ .from("user_prompts")
21
+ .select("*");
22
+ if (error) {
23
+ return jsonResponse({ error: error.message }, 400);
24
+ }
25
+ return jsonResponse({ data });
26
+ }
27
+ /**
28
+ * POST - Crée un nouvel enregistrement dans user_prompts
29
+ */
30
+ export async function POST(request) {
31
+ const supabase = await getSupabaseServerClient();
32
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
33
+ if (authError || !user) {
34
+ return jsonResponse({ error: "Non authentifié" }, 401);
35
+ }
36
+ const body = await request.json();
37
+ const { data, error } = await supabase
38
+ .from("user_prompts")
39
+ .insert(body)
40
+ .select()
41
+ .single();
42
+ if (error) {
43
+ return jsonResponse({ error: error.message }, 400);
44
+ }
45
+ return jsonResponse({ data }, 201);
46
+ }
47
+ /**
48
+ * PUT - Met à jour un enregistrement dans user_prompts
49
+ */
50
+ export async function PUT(request) {
51
+ const supabase = await getSupabaseServerClient();
52
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
53
+ if (authError || !user) {
54
+ return jsonResponse({ error: "Non authentifié" }, 401);
55
+ }
56
+ const body = await request.json();
57
+ const { id, ...updateData } = body;
58
+ if (!id) {
59
+ return jsonResponse({ error: "ID requis pour la mise à jour" }, 400);
60
+ }
61
+ const { data, error } = await supabase
62
+ .from("user_prompts")
63
+ .update(updateData)
64
+ .eq("id", id)
65
+ .select()
66
+ .single();
67
+ if (error) {
68
+ return jsonResponse({ error: error.message }, 400);
69
+ }
70
+ return jsonResponse({ data });
71
+ }
72
+ /**
73
+ * DELETE - Supprime un enregistrement de user_prompts
74
+ */
75
+ export async function DELETE(request) {
76
+ const supabase = await getSupabaseServerClient();
77
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
78
+ if (authError || !user) {
79
+ return jsonResponse({ error: "Non authentifié" }, 401);
80
+ }
81
+ const { searchParams } = new URL(request.url);
82
+ const id = searchParams.get("id");
83
+ if (!id) {
84
+ return jsonResponse({ error: "ID requis pour la suppression" }, 400);
85
+ }
86
+ const { error } = await supabase
87
+ .from("user_prompts")
88
+ .delete()
89
+ .eq("id", id);
90
+ if (error) {
91
+ return jsonResponse({ error: error.message }, 400);
92
+ }
93
+ return jsonResponse({ success: true });
94
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * GET - Liste tous les enregistrements de user_token_ledger
3
+ */
4
+ export declare function GET(request: Request): Promise<Response>;
5
+ /**
6
+ * POST - Crée un nouvel enregistrement dans user_token_ledger
7
+ */
8
+ export declare function POST(request: Request): Promise<Response>;
9
+ /**
10
+ * PUT - Met à jour un enregistrement dans user_token_ledger
11
+ */
12
+ export declare function PUT(request: Request): Promise<Response>;
13
+ /**
14
+ * DELETE - Supprime un enregistrement de user_token_ledger
15
+ */
16
+ export declare function DELETE(request: Request): Promise<Response>;
17
+ //# sourceMappingURL=user_token_ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user_token_ledger.d.ts","sourceRoot":"","sources":["../../../src/api/admin/user_token_ledger.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBAkBzC;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,OAAO,qBAsB1C;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,OAAO,qBA4BzC;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,qBA0B5C"}
@@ -0,0 +1,94 @@
1
+ import { getSupabaseServerClient } from "@lastbrain/core/server";
2
+ const jsonResponse = (payload, status = 200) => {
3
+ return new Response(JSON.stringify(payload), {
4
+ headers: {
5
+ "content-type": "application/json"
6
+ },
7
+ status
8
+ });
9
+ };
10
+ /**
11
+ * GET - Liste tous les enregistrements de user_token_ledger
12
+ */
13
+ export async function GET(request) {
14
+ const supabase = await getSupabaseServerClient();
15
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
16
+ if (authError || !user) {
17
+ return jsonResponse({ error: "Non authentifié" }, 401);
18
+ }
19
+ const { data, error } = await supabase
20
+ .from("user_token_ledger")
21
+ .select("*");
22
+ if (error) {
23
+ return jsonResponse({ error: error.message }, 400);
24
+ }
25
+ return jsonResponse({ data });
26
+ }
27
+ /**
28
+ * POST - Crée un nouvel enregistrement dans user_token_ledger
29
+ */
30
+ export async function POST(request) {
31
+ const supabase = await getSupabaseServerClient();
32
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
33
+ if (authError || !user) {
34
+ return jsonResponse({ error: "Non authentifié" }, 401);
35
+ }
36
+ const body = await request.json();
37
+ const { data, error } = await supabase
38
+ .from("user_token_ledger")
39
+ .insert(body)
40
+ .select()
41
+ .single();
42
+ if (error) {
43
+ return jsonResponse({ error: error.message }, 400);
44
+ }
45
+ return jsonResponse({ data }, 201);
46
+ }
47
+ /**
48
+ * PUT - Met à jour un enregistrement dans user_token_ledger
49
+ */
50
+ export async function PUT(request) {
51
+ const supabase = await getSupabaseServerClient();
52
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
53
+ if (authError || !user) {
54
+ return jsonResponse({ error: "Non authentifié" }, 401);
55
+ }
56
+ const body = await request.json();
57
+ const { id, ...updateData } = body;
58
+ if (!id) {
59
+ return jsonResponse({ error: "ID requis pour la mise à jour" }, 400);
60
+ }
61
+ const { data, error } = await supabase
62
+ .from("user_token_ledger")
63
+ .update(updateData)
64
+ .eq("id", id)
65
+ .select()
66
+ .single();
67
+ if (error) {
68
+ return jsonResponse({ error: error.message }, 400);
69
+ }
70
+ return jsonResponse({ data });
71
+ }
72
+ /**
73
+ * DELETE - Supprime un enregistrement de user_token_ledger
74
+ */
75
+ export async function DELETE(request) {
76
+ const supabase = await getSupabaseServerClient();
77
+ const { data: { user }, error: authError } = await supabase.auth.getUser();
78
+ if (authError || !user) {
79
+ return jsonResponse({ error: "Non authentifié" }, 401);
80
+ }
81
+ const { searchParams } = new URL(request.url);
82
+ const id = searchParams.get("id");
83
+ if (!id) {
84
+ return jsonResponse({ error: "ID requis pour la suppression" }, 400);
85
+ }
86
+ const { error } = await supabase
87
+ .from("user_token_ledger")
88
+ .delete()
89
+ .eq("id", id);
90
+ if (error) {
91
+ return jsonResponse({ error: error.message }, 400);
92
+ }
93
+ return jsonResponse({ success: true });
94
+ }
@@ -0,0 +1,11 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ export declare function POST(request: NextRequest): Promise<NextResponse<{
3
+ imageUrl: string;
4
+ tokensUsed: number;
5
+ tokensRemaining: number;
6
+ model: "dall-e-3" | "dall-e-2";
7
+ cost: number;
8
+ }> | NextResponse<{
9
+ error: any;
10
+ }>>;
11
+ //# sourceMappingURL=generate-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-image.d.ts","sourceRoot":"","sources":["../../../src/api/auth/generate-image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoCxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;IA2H9C"}
@@ -0,0 +1,104 @@
1
+ import { NextResponse } from "next/server";
2
+ import { getSupabaseServerClient } from "@lastbrain/core/server";
3
+ import { useTokens, getTokenBalance } from "../../server";
4
+ import OpenAI from "openai";
5
+ const openai = new OpenAI({
6
+ apiKey: process.env.OPENAI_API_KEY,
7
+ });
8
+ // Coûts des images selon le modèle et la taille
9
+ const IMAGE_COSTS = {
10
+ "dall-e-2-256x256": 0.016,
11
+ "dall-e-2-512x512": 0.018,
12
+ "dall-e-2-1024x1024": 0.02,
13
+ "dall-e-3-1024x1024-standard": 0.04,
14
+ "dall-e-3-1024x1024-hd": 0.08,
15
+ "dall-e-3-1024x1792-standard": 0.08,
16
+ "dall-e-3-1024x1792-hd": 0.12,
17
+ "dall-e-3-1792x1024-standard": 0.08,
18
+ "dall-e-3-1792x1024-hd": 0.12,
19
+ };
20
+ // Équivalent en tokens (arbitraire pour notre système de comptage)
21
+ const TOKENS_PER_IMAGE = {
22
+ "dall-e-2": 1000,
23
+ "dall-e-3-standard": 2000,
24
+ "dall-e-3-hd": 4000,
25
+ };
26
+ export async function POST(request) {
27
+ try {
28
+ const supabase = await getSupabaseServerClient();
29
+ // Vérifier l'authentification
30
+ const { data: { user }, error: authError, } = await supabase.auth.getUser();
31
+ if (authError || !user) {
32
+ return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
33
+ }
34
+ const body = await request.json();
35
+ const { prompt, model = "dall-e-3", size = "1024x1024", quality = "standard", } = body;
36
+ if (!prompt) {
37
+ return NextResponse.json({ error: "Le prompt est requis" }, { status: 400 });
38
+ }
39
+ // Calculer le coût et les tokens
40
+ const costKey = model === "dall-e-3"
41
+ ? `${model}-${size}-${quality}`
42
+ : `${model}-${size}`;
43
+ const cost = IMAGE_COSTS[costKey] || 0.04;
44
+ const tokenKey = model === "dall-e-3" ? `${model}-${quality}` : model;
45
+ const tokensRequired = TOKENS_PER_IMAGE[tokenKey] || 1000;
46
+ // Vérifier le solde de tokens
47
+ const currentBalance = await getTokenBalance(user.id);
48
+ if (currentBalance < tokensRequired) {
49
+ return NextResponse.json({
50
+ error: `Solde insuffisant. Disponible: ${currentBalance}, requis: ${tokensRequired}`,
51
+ }, { status: 402 });
52
+ }
53
+ // Paramètres pour DALL-E 3
54
+ const imageParams = {
55
+ model,
56
+ prompt,
57
+ n: 1,
58
+ response_format: "url",
59
+ };
60
+ // DALL-E 3 a des paramètres différents de DALL-E 2
61
+ if (model === "dall-e-3") {
62
+ imageParams.size = size;
63
+ imageParams.quality = quality;
64
+ }
65
+ else {
66
+ // DALL-E 2 ne supporte que certaines tailles
67
+ const validSizes = ["256x256", "512x512", "1024x1024"];
68
+ imageParams.size = validSizes.includes(size) ? size : "1024x1024";
69
+ }
70
+ // Appeler OpenAI
71
+ const response = await openai.images.generate(imageParams);
72
+ const imageUrl = response.data?.[0]?.url;
73
+ if (!imageUrl) {
74
+ throw new Error("Aucune image générée");
75
+ }
76
+ // Déduire les tokens utilisés
77
+ const tokenResult = await useTokens(user.id, tokensRequired, model, prompt, {
78
+ size,
79
+ quality,
80
+ cost,
81
+ imageUrl,
82
+ });
83
+ if (!tokenResult.success) {
84
+ return NextResponse.json({ error: tokenResult.error || "Erreur lors de la déduction des tokens" }, { status: 500 });
85
+ }
86
+ return NextResponse.json({
87
+ imageUrl,
88
+ tokensUsed: tokensRequired,
89
+ tokensRemaining: tokenResult.balance,
90
+ model,
91
+ cost,
92
+ });
93
+ }
94
+ catch (error) {
95
+ console.error("Erreur de génération d'image:", error);
96
+ if (error.code === 'insufficient_quota') {
97
+ return NextResponse.json({ error: "Quota OpenAI dépassé. Contactez l'administrateur." }, { status: 503 });
98
+ }
99
+ if (error.code === 'content_policy_violation') {
100
+ return NextResponse.json({ error: "Le prompt viole la politique de contenu d'OpenAI." }, { status: 400 });
101
+ }
102
+ return NextResponse.json({ error: error.message || "Erreur lors de la génération de l'image" }, { status: 500 });
103
+ }
104
+ }
@@ -0,0 +1,11 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ export declare function POST(request: NextRequest): Promise<NextResponse<{
3
+ text: string;
4
+ tokensUsed: number;
5
+ tokensRemaining: number;
6
+ model: string;
7
+ cost: number;
8
+ }> | NextResponse<{
9
+ error: any;
10
+ }>>;
11
+ //# sourceMappingURL=generate-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-text.d.ts","sourceRoot":"","sources":["../../../src/api/auth/generate-text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiBxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;;;IA+H9C"}