@lastbrain/app 0.1.40 → 0.1.42

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.
@@ -3,6 +3,7 @@ export interface UserNotification {
3
3
  id: string;
4
4
  owner_id: string;
5
5
  title: string;
6
+ message: string;
6
7
  body?: string;
7
8
  type: string;
8
9
  read: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"useNotifications.d.ts","sourceRoot":"","sources":["../../src/hooks/useNotifications.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI;;;;;iCAwExB,MAAM;;yCAiEN,MAAM;;;;;EAwDhC"}
1
+ {"version":3,"file":"useNotifications.d.ts","sourceRoot":"","sources":["../../src/hooks/useNotifications.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI;;;;;iCAwExB,MAAM;;yCAiEN,MAAM;;;;;EAwDhC"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  export function PublicLayout({ children }) {
4
- return _jsx("section", { className: "pt-16 px-2 md:px-0 ", children: children });
4
+ return _jsx("section", { className: "pt-16 ", children: children });
5
5
  }
@@ -1 +1 @@
1
- {"version":3,"file":"module-add.d.ts","sourceRoot":"","sources":["../../src/scripts/module-add.ts"],"names":[],"mappings":"AAiCA,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,iBA6VpE"}
1
+ {"version":3,"file":"module-add.d.ts","sourceRoot":"","sources":["../../src/scripts/module-add.ts"],"names":[],"mappings":"AAiCA,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,iBA+SpE"}
@@ -145,62 +145,32 @@ export async function addModule(moduleName, targetDir) {
145
145
  }
146
146
  else if (migrationAction === "push") {
147
147
  console.log(chalk.yellow("\n⬆️ Application des nouvelles migrations..."));
148
- try {
149
- // Essayer en capturant la sortie pour diagnostiquer les erreurs fréquentes
150
- execSync("supabase migration up --local", {
151
- cwd: targetDir,
152
- stdio: "pipe",
153
- });
154
- console.log(chalk.green("✓ Migrations appliquées"));
148
+ // Appliquer directement avec psql au lieu de supabase migration up
149
+ const migrationsDir = path.join(targetDir, "supabase", "migrations");
150
+ const migrationFiles = fs
151
+ .readdirSync(migrationsDir)
152
+ .filter((f) => f.endsWith(".sql"))
153
+ .filter((f) => copiedMigrationFiles.includes(f))
154
+ .sort();
155
+ if (migrationFiles.length === 0) {
156
+ console.log(chalk.yellow("⚠️ Aucune nouvelle migration à appliquer"));
155
157
  }
156
- catch (error) {
157
- const output = String(error?.stderr || error?.stdout || error?.message || "");
158
- const mismatch = output.includes("Remote migration versions not found in local migrations directory");
159
- if (mismatch) {
160
- console.warn(chalk.yellow("⚠️ Historique des migrations désynchronisé."));
161
- // Extraire TOUTES les dates de migration depuis le message d'erreur
162
- const repairMatch = output.match(/supabase migration repair --status reverted ([\s\S]+?)\n/);
163
- let migrationDates = [];
164
- if (repairMatch && repairMatch[1]) {
165
- // Extraire toutes les dates de la commande suggérée
166
- migrationDates = repairMatch[1].trim().split(/\s+/);
167
- }
168
- else {
169
- // Fallback: chercher toutes les dates dans le message
170
- const allDates = output.match(/20\d{6,14}/g);
171
- migrationDates = allDates ? [...new Set(allDates)] : [];
172
- }
173
- if (migrationDates.length === 0) {
174
- console.error(chalk.red("❌ Impossible d'extraire les dates de migration"));
175
- console.log(chalk.gray("\nFaites un reset complet:\n supabase db reset\n"));
176
- }
177
- else {
178
- // Réparation automatique de l'historique
179
- const datesStr = migrationDates.join(" ");
180
- console.log(chalk.yellow(`\n🔧 Réparation automatique de l'historique...`));
181
- console.log(chalk.gray(` Dates: ${datesStr}`));
182
- try {
183
- execSync(`supabase migration repair --status reverted ${datesStr} --local`, {
184
- cwd: targetDir,
185
- stdio: "inherit",
186
- });
187
- console.log(chalk.green("✓ Historique réparé"));
188
- console.log(chalk.yellow("\n⬆️ Application des nouvelles migrations..."));
189
- execSync("supabase migration up --local", {
190
- cwd: targetDir,
191
- stdio: "inherit",
192
- });
193
- console.log(chalk.green("✓ Migrations appliquées"));
194
- }
195
- catch (e) {
196
- console.error(chalk.red("❌ Échec de la réparation automatique"));
197
- console.log(chalk.gray(`\nVous pouvez essayer manuellement:\n supabase migration repair --status reverted ${datesStr} --local\n supabase migration up --local`));
198
- console.log(chalk.gray(`\nOu faire un reset complet:\n supabase db reset\n`));
199
- }
158
+ else {
159
+ try {
160
+ const dbUrl = "postgresql://postgres:postgres@127.0.0.1:54322/postgres";
161
+ for (const file of migrationFiles) {
162
+ const filePath = path.join(migrationsDir, file);
163
+ console.log(chalk.gray(` Exécution de ${file}...`));
164
+ execSync(`psql "${dbUrl}" -f "${filePath}"`, {
165
+ cwd: targetDir,
166
+ stdio: "pipe",
167
+ });
200
168
  }
169
+ console.log(chalk.green(`✓ ${migrationFiles.length} migration(s) appliquée(s)`));
201
170
  }
202
- else {
171
+ catch (error) {
203
172
  console.error(chalk.red("❌ Erreur lors de l'application des migrations"));
173
+ console.log(chalk.gray("\nVous pouvez essayer manuellement:\n supabase db reset\n"));
204
174
  }
205
175
  }
206
176
  }
@@ -1 +1 @@
1
- {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AA69BA,wBAAsB,cAAc,kBA6EnC"}
1
+ {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAw0CA,wBAAsB,cAAc,kBAyFnC"}
@@ -259,7 +259,7 @@ function generateMenuConfig(moduleConfigs) {
259
259
  const configDir = path.join(projectRoot, "config");
260
260
  ensureDirectory(configDir);
261
261
  const menuPath = path.join(configDir, "menu.ts");
262
- // Collecter les menus de tous les modules
262
+ // Collecter les menus de tous les modules avec leurs modules sources
263
263
  const publicMenus = [];
264
264
  const authMenus = [];
265
265
  const adminMenus = [];
@@ -267,16 +267,28 @@ function generateMenuConfig(moduleConfigs) {
267
267
  moduleConfigs.forEach((moduleConfig) => {
268
268
  if (moduleConfig.menu) {
269
269
  if (moduleConfig.menu.public) {
270
- publicMenus.push(...moduleConfig.menu.public);
270
+ publicMenus.push(...moduleConfig.menu.public.map((item) => ({
271
+ ...item,
272
+ moduleName: moduleConfig.moduleName,
273
+ })));
271
274
  }
272
275
  if (moduleConfig.menu.auth) {
273
- authMenus.push(...moduleConfig.menu.auth);
276
+ authMenus.push(...moduleConfig.menu.auth.map((item) => ({
277
+ ...item,
278
+ moduleName: moduleConfig.moduleName,
279
+ })));
274
280
  }
275
281
  if (moduleConfig.menu.admin) {
276
- adminMenus.push(...moduleConfig.menu.admin);
282
+ adminMenus.push(...moduleConfig.menu.admin.map((item) => ({
283
+ ...item,
284
+ moduleName: moduleConfig.moduleName,
285
+ })));
277
286
  }
278
287
  if (moduleConfig.menu.account) {
279
- accountMenus.push(...moduleConfig.menu.account);
288
+ accountMenus.push(...moduleConfig.menu.account.map((item) => ({
289
+ ...item,
290
+ moduleName: moduleConfig.moduleName,
291
+ })));
280
292
  }
281
293
  }
282
294
  });
@@ -288,9 +300,45 @@ function generateMenuConfig(moduleConfigs) {
288
300
  authMenus.sort(sortByOrder);
289
301
  adminMenus.sort(sortByOrder);
290
302
  accountMenus.sort(sortByOrder);
303
+ // Collecter les composants à importer dynamiquement
304
+ const componentImports = [];
305
+ const allMenus = [
306
+ ...publicMenus,
307
+ ...authMenus,
308
+ ...adminMenus,
309
+ ...accountMenus,
310
+ ];
311
+ allMenus.forEach((menu, index) => {
312
+ if (menu.componentExport && menu.moduleName) {
313
+ const componentName = `MenuComponent${index}`;
314
+ componentImports.push(`const ${componentName} = dynamic(() => import("${menu.moduleName}").then(mod => ({ default: mod.${menu.componentExport} })), { ssr: false });`);
315
+ // Ajouter une référence au composant
316
+ menu.__componentRef = componentName;
317
+ }
318
+ });
319
+ // Fonction pour préparer les menus avec les composants
320
+ const prepareMenusForExport = (menus) => {
321
+ return menus.map((menu) => {
322
+ const { moduleName, __componentRef, ...cleanMenu } = menu;
323
+ if (__componentRef) {
324
+ // Retourner une référence au composant au lieu de la sérialiser
325
+ return `{ ...${JSON.stringify(cleanMenu)}, component: ${__componentRef} }`;
326
+ }
327
+ return JSON.stringify(cleanMenu);
328
+ });
329
+ };
330
+ const publicMenusExport = prepareMenusForExport(publicMenus);
331
+ const authMenusExport = prepareMenusForExport(authMenus);
332
+ const adminMenusExport = prepareMenusForExport(adminMenus);
333
+ const accountMenusExport = prepareMenusForExport(accountMenus);
291
334
  // Générer le contenu du fichier
292
335
  const content = `// Auto-generated menu configuration
293
336
  // Generated from module build configs
337
+ "use client";
338
+
339
+ import dynamic from "next/dynamic";
340
+
341
+ ${componentImports.join("\n")}
294
342
 
295
343
  export interface MenuItem {
296
344
  title: string;
@@ -300,6 +348,11 @@ export interface MenuItem {
300
348
  order?: number;
301
349
  shortcut?: string;
302
350
  shortcutDisplay?: string;
351
+ type?: 'text' | 'icon' | 'textIcon';
352
+ position?: 'center' | 'end';
353
+ component?: React.ComponentType<any>;
354
+ componentExport?: string;
355
+ entryPoint?: string;
303
356
  }
304
357
 
305
358
  export interface MenuConfig {
@@ -310,10 +363,10 @@ export interface MenuConfig {
310
363
  }
311
364
 
312
365
  export const menuConfig: MenuConfig = {
313
- public: ${JSON.stringify(publicMenus, null, 2)},
314
- auth: ${JSON.stringify(authMenus, null, 2)},
315
- admin: ${JSON.stringify(adminMenus, null, 2)},
316
- account: ${JSON.stringify(accountMenus, null, 2)},
366
+ public: [${publicMenusExport.join(",\n ")}],
367
+ auth: [${authMenusExport.join(",\n ")}],
368
+ admin: [${adminMenusExport.join(",\n ")}],
369
+ account: [${accountMenusExport.join(",\n ")}],
317
370
  };
318
371
  `;
319
372
  fs.writeFileSync(menuPath, content);
@@ -809,6 +862,264 @@ async function generateUserTabsConfig(moduleConfigs) {
809
862
  console.error("❌ Error generating user tabs configuration:", error);
810
863
  }
811
864
  }
865
+ async function generateBucketsConfig(moduleConfigs) {
866
+ try {
867
+ // Extraire les configurations storage des modules
868
+ const allBuckets = moduleConfigs
869
+ .filter((config) => config.storage?.buckets && config.storage.buckets.length > 0)
870
+ .flatMap((config) => config.storage.buckets);
871
+ if (allBuckets.length === 0) {
872
+ console.log("⏭️ No storage buckets configuration found in modules");
873
+ return;
874
+ }
875
+ // Générer le contenu du fichier
876
+ const timestamp = new Date().toISOString();
877
+ const bucketsEntries = allBuckets
878
+ .map((bucket) => {
879
+ // Sérialiser customAccessControl si elle existe
880
+ const customAccessControl = bucket.customAccessControl
881
+ ? `\n customAccessControl: ${bucket.customAccessControl.toString()},`
882
+ : "";
883
+ return ` ${bucket.name}: {
884
+ name: "${bucket.name}",
885
+ isPublic: ${bucket.public},
886
+ description: "${bucket.description || `Storage bucket for ${bucket.name}`}",${bucket.allowedMimeTypes ? `\n allowedFileTypes: ${JSON.stringify(bucket.allowedMimeTypes)},` : ""}${bucket.maxFileSize ? `\n maxFileSize: ${bucket.maxFileSize}, // ${bucket.fileSizeLimit || `${Math.round(bucket.maxFileSize / 1024 / 1024)}MB`}` : ""}${customAccessControl}
887
+ }`;
888
+ })
889
+ .join(",\n");
890
+ const content = `/**
891
+ * Storage configuration for buckets and access control
892
+ *
893
+ * GENERATED FILE - DO NOT EDIT MANUALLY
894
+ * Generated at: ${timestamp}
895
+ * Generated from module build configs
896
+ */
897
+
898
+ export interface BucketConfig {
899
+ name: string;
900
+ isPublic: boolean;
901
+ description: string;
902
+ allowedFileTypes?: string[];
903
+ maxFileSize?: number; // in bytes
904
+ customAccessControl?: (userId: string, filePath: string) => boolean;
905
+ }
906
+
907
+ export const BUCKET_CONFIGS: Record<string, BucketConfig> = {
908
+ ${bucketsEntries}
909
+ };
910
+
911
+ /**
912
+ * Get bucket configuration
913
+ */
914
+ export function getBucketConfig(bucketName: string): BucketConfig | null {
915
+ return BUCKET_CONFIGS[bucketName] || null;
916
+ }
917
+
918
+ /**
919
+ * Check if bucket is public
920
+ */
921
+ export function isPublicBucket(bucketName: string): boolean {
922
+ const config = getBucketConfig(bucketName);
923
+ return config?.isPublic ?? false;
924
+ }
925
+
926
+ /**
927
+ * Check if user has access to a specific file
928
+ */
929
+ export function hasFileAccess(bucketName: string, userId: string, filePath: string): boolean {
930
+ const config = getBucketConfig(bucketName);
931
+ if (!config) return false;
932
+
933
+ // Public buckets are accessible to everyone
934
+ if (config.isPublic) return true;
935
+
936
+ // Private buckets require authentication
937
+ if (!userId) return false;
938
+
939
+ // Apply custom access control if defined
940
+ if (config.customAccessControl) {
941
+ return config.customAccessControl(userId, filePath);
942
+ }
943
+
944
+ return true;
945
+ }
946
+
947
+ /**
948
+ * Validate file type for bucket
949
+ */
950
+ export function isValidFileType(bucketName: string, contentType: string): boolean {
951
+ const config = getBucketConfig(bucketName);
952
+ if (!config || !config.allowedFileTypes) return true;
953
+
954
+ return config.allowedFileTypes.includes(contentType);
955
+ }
956
+
957
+ /**
958
+ * Check if file size is within bucket limits
959
+ */
960
+ export function isValidFileSize(bucketName: string, fileSize: number): boolean {
961
+ const config = getBucketConfig(bucketName);
962
+ if (!config || !config.maxFileSize) return true;
963
+
964
+ return fileSize <= config.maxFileSize;
965
+ }
966
+ `;
967
+ // Créer le fichier dans lib/
968
+ const outputPath = path.join(projectRoot, "lib", "bucket-config.ts");
969
+ const libDir = path.dirname(outputPath);
970
+ // Créer le dossier lib s'il n'existe pas
971
+ if (!fs.existsSync(libDir)) {
972
+ fs.mkdirSync(libDir, { recursive: true });
973
+ }
974
+ // Écrire le fichier TypeScript
975
+ fs.writeFileSync(outputPath, content);
976
+ if (isDebugMode) {
977
+ console.log(`✅ Generated storage buckets configuration: ${outputPath}`);
978
+ console.log(`📊 Storage buckets count: ${allBuckets.length}`);
979
+ // Afficher un résumé
980
+ allBuckets.forEach((bucket) => {
981
+ const access = bucket.public ? "public" : "private";
982
+ console.log(` - ${bucket.name} (${access})`);
983
+ });
984
+ }
985
+ }
986
+ catch (error) {
987
+ console.error("❌ Error generating buckets configuration:", error);
988
+ }
989
+ }
990
+ async function generateStorageProxyApi(moduleConfigs) {
991
+ try {
992
+ // Extraire les configurations storage des modules
993
+ const allBuckets = moduleConfigs
994
+ .filter((config) => config.storage?.buckets && config.storage.buckets.length > 0)
995
+ .flatMap((config) => config.storage.buckets);
996
+ if (allBuckets.length === 0) {
997
+ console.log("⏭️ No storage buckets found, skipping proxy API generation");
998
+ return;
999
+ }
1000
+ // Identifier les buckets publics et privés
1001
+ const publicBuckets = allBuckets.filter((b) => b.public);
1002
+ const privateBuckets = allBuckets.filter((b) => !b.public);
1003
+ // Générer les conditions pour les buckets publics
1004
+ const publicBucketConditions = publicBuckets
1005
+ .map((bucket) => `bucket === "${bucket.name}"`)
1006
+ .filter(Boolean);
1007
+ // Ajouter les conditions pour les chemins publics dans les buckets privés
1008
+ const publicPathConditions = [];
1009
+ if (publicBuckets.some((b) => b.name === "recipes")) {
1010
+ publicPathConditions.push('storagePath.startsWith("recipes/")');
1011
+ }
1012
+ const allPublicConditions = [
1013
+ ...publicBucketConditions,
1014
+ ...publicPathConditions,
1015
+ ];
1016
+ const publicCondition = allPublicConditions.length > 0
1017
+ ? allPublicConditions.join(" || ")
1018
+ : "false";
1019
+ const timestamp = new Date().toISOString();
1020
+ const content = `import { getSupabaseServerClient } from "@lastbrain/core/server";
1021
+ import { NextRequest, NextResponse } from "next/server";
1022
+
1023
+ /**
1024
+ * GET /api/storage/[bucket]/[...path]
1025
+ * Proxy pour servir les images avec authentication
1026
+ *
1027
+ * GENERATED FILE - DO NOT EDIT MANUALLY
1028
+ * Generated at: ${timestamp}
1029
+ * Generated from module storage configurations
1030
+ *
1031
+ * Buckets configurés:
1032
+ ${allBuckets.map((b) => ` * - ${b.name} (${b.public ? "public" : "private"})`).join("\n")}
1033
+ */
1034
+ export async function GET(
1035
+ request: NextRequest,
1036
+ context: { params: Promise<{ bucket: string; path: string[] }> },
1037
+ ) {
1038
+ try {
1039
+ const { bucket, path } = await context.params;
1040
+ const storagePath = path.join("/");
1041
+
1042
+ // Les images publiques sont accessibles sans auth
1043
+ if (${publicCondition}) {
1044
+ const supabase = await getSupabaseServerClient();
1045
+ const { data, error } = await supabase.storage
1046
+ .from(bucket)
1047
+ .createSignedUrl(storagePath, 3600); // 1 heure
1048
+
1049
+ if (error) {
1050
+ console.error(\`[storage] Error creating signed URL for public image:\`, error);
1051
+ return new NextResponse("Not found", { status: 404 });
1052
+ }
1053
+
1054
+ // Rediriger vers l'URL signée
1055
+ return NextResponse.redirect(data.signedUrl);
1056
+ }
1057
+
1058
+ // Les images privées nécessitent une authentification
1059
+ const supabase = await getSupabaseServerClient();
1060
+ const {
1061
+ data: { user },
1062
+ error: authError,
1063
+ } = await supabase.auth.getUser();
1064
+
1065
+ if (authError || !user) {
1066
+ return new NextResponse("Unauthorized", { status: 401 });
1067
+ }
1068
+
1069
+ // Cas spécial: si le chemin commence par /product/ ou /recipe/,
1070
+ // c'est une image avec format court qui nécessite le préfixe userId
1071
+ let actualStoragePath = storagePath;
1072
+
1073
+ if (storagePath.startsWith("product/") || storagePath.startsWith("recipe/")) {
1074
+ actualStoragePath = \`\${user.id}/\${storagePath}\`;
1075
+ }
1076
+
1077
+ // Vérifier que l'utilisateur a accès à cette image
1078
+ // Format: {userId}/recipe/{recipeId}/{filename} ou {userId}/product/{productId}/{filename}
1079
+ const pathParts = actualStoragePath.split("/");
1080
+ const pathUserId = pathParts[0];
1081
+
1082
+ if (pathUserId !== user.id) {
1083
+ return new NextResponse("Forbidden", { status: 403 });
1084
+ }
1085
+
1086
+ // Créer une URL signée pour l'image privée
1087
+ const { data, error } = await supabase.storage
1088
+ .from(bucket)
1089
+ .createSignedUrl(actualStoragePath, 3600); // 1 heure
1090
+
1091
+ if (error) {
1092
+ console.error(\`[storage] Error creating signed URL:\`, error);
1093
+ return new NextResponse("Not found", { status: 404 });
1094
+ }
1095
+
1096
+ // Rediriger vers l'URL signée
1097
+ return NextResponse.redirect(data.signedUrl);
1098
+ } catch (error) {
1099
+ console.error("[storage] Error:", error);
1100
+ return new NextResponse("Internal server error", { status: 500 });
1101
+ }
1102
+ }
1103
+ `;
1104
+ // Créer le fichier dans app/api/storage/[bucket]/[...path]/
1105
+ const outputPath = path.join(projectRoot, "app", "api", "storage", "[bucket]", "[...path]", "route.ts");
1106
+ const outputDir = path.dirname(outputPath);
1107
+ // Créer le dossier s'il n'existe pas
1108
+ if (!fs.existsSync(outputDir)) {
1109
+ fs.mkdirSync(outputDir, { recursive: true });
1110
+ }
1111
+ // Écrire le fichier TypeScript
1112
+ fs.writeFileSync(outputPath, content);
1113
+ if (isDebugMode) {
1114
+ console.log(`✅ Generated storage proxy API: ${outputPath}`);
1115
+ console.log(`📊 Public buckets: ${publicBuckets.map((b) => b.name).join(", ") || "none"}`);
1116
+ console.log(`📊 Private buckets: ${privateBuckets.map((b) => b.name).join(", ") || "none"}`);
1117
+ }
1118
+ }
1119
+ catch (error) {
1120
+ console.error("❌ Error generating storage proxy API:", error);
1121
+ }
1122
+ }
812
1123
  export async function runModuleBuild() {
813
1124
  ensureDirectory(appDirectory);
814
1125
  // Nettoyer les fichiers générés précédemment
@@ -863,6 +1174,16 @@ export async function runModuleBuild() {
863
1174
  console.log("📑 Generating user tabs configuration...");
864
1175
  }
865
1176
  await generateUserTabsConfig(moduleConfigs);
1177
+ // Générer la configuration des buckets storage
1178
+ if (isDebugMode) {
1179
+ console.log("🗄️ Generating storage buckets configuration...");
1180
+ }
1181
+ await generateBucketsConfig(moduleConfigs);
1182
+ // Générer le proxy storage API
1183
+ if (isDebugMode) {
1184
+ console.log("🔌 Generating storage proxy API...");
1185
+ }
1186
+ await generateStorageProxyApi(moduleConfigs);
866
1187
  // Message de succès final
867
1188
  if (!isDebugMode) {
868
1189
  console.log("\n✅ Module build completed successfully!");