@lastbrain/app 0.1.40 → 0.1.43
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/hooks/useNotifications.d.ts +1 -0
- package/dist/hooks/useNotifications.d.ts.map +1 -1
- package/dist/layouts/AdminLayout.d.ts.map +1 -1
- package/dist/layouts/AdminLayout.js +0 -1
- package/dist/layouts/AuthLayout.d.ts.map +1 -1
- package/dist/layouts/AuthLayout.js +0 -1
- package/dist/layouts/PublicLayout.d.ts +5 -2
- package/dist/layouts/PublicLayout.d.ts.map +1 -1
- package/dist/layouts/PublicLayout.js +4 -3
- package/dist/scripts/init-app.js +131 -50
- package/dist/scripts/module-add.d.ts.map +1 -1
- package/dist/scripts/module-add.js +22 -52
- package/dist/scripts/module-build.d.ts.map +1 -1
- package/dist/scripts/module-build.js +381 -9
- package/dist/styles.css +1 -1
- package/dist/templates/DefaultDoc.d.ts.map +1 -1
- package/dist/templates/DefaultDoc.js +2 -2
- package/dist/templates/DocPage.js +1 -1
- package/package.json +1 -1
- package/src/hooks/useNotifications.ts +1 -0
- package/src/layouts/AdminLayout.tsx +0 -2
- package/src/layouts/AuthLayout.tsx +0 -2
- package/src/layouts/PublicLayout.tsx +14 -2
- package/src/scripts/init-app.ts +144 -49
- package/src/scripts/module-add.ts +36 -82
- package/src/scripts/module-build.ts +447 -13
- package/src/templates/DefaultDoc.tsx +362 -12
- package/src/templates/DocPage.tsx +3 -3
|
@@ -321,25 +321,45 @@ function generateMenuConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
321
321
|
|
|
322
322
|
const menuPath = path.join(configDir, "menu.ts");
|
|
323
323
|
|
|
324
|
-
// Collecter les menus de tous les modules
|
|
325
|
-
const publicMenus: ModuleMenuItemConfig[] = [];
|
|
326
|
-
const authMenus: ModuleMenuItemConfig[] = [];
|
|
327
|
-
const adminMenus: ModuleMenuItemConfig[] = [];
|
|
328
|
-
const accountMenus: ModuleMenuItemConfig[] = [];
|
|
324
|
+
// Collecter les menus de tous les modules avec leurs modules sources
|
|
325
|
+
const publicMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
|
|
326
|
+
const authMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
|
|
327
|
+
const adminMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
|
|
328
|
+
const accountMenus: (ModuleMenuItemConfig & { moduleName?: string })[] = [];
|
|
329
329
|
|
|
330
330
|
moduleConfigs.forEach((moduleConfig: ModuleBuildConfig) => {
|
|
331
331
|
if (moduleConfig.menu) {
|
|
332
332
|
if (moduleConfig.menu.public) {
|
|
333
|
-
publicMenus.push(
|
|
333
|
+
publicMenus.push(
|
|
334
|
+
...moduleConfig.menu.public.map((item) => ({
|
|
335
|
+
...item,
|
|
336
|
+
moduleName: moduleConfig.moduleName,
|
|
337
|
+
})),
|
|
338
|
+
);
|
|
334
339
|
}
|
|
335
340
|
if (moduleConfig.menu.auth) {
|
|
336
|
-
authMenus.push(
|
|
341
|
+
authMenus.push(
|
|
342
|
+
...moduleConfig.menu.auth.map((item) => ({
|
|
343
|
+
...item,
|
|
344
|
+
moduleName: moduleConfig.moduleName,
|
|
345
|
+
})),
|
|
346
|
+
);
|
|
337
347
|
}
|
|
338
348
|
if (moduleConfig.menu.admin) {
|
|
339
|
-
adminMenus.push(
|
|
349
|
+
adminMenus.push(
|
|
350
|
+
...moduleConfig.menu.admin.map((item) => ({
|
|
351
|
+
...item,
|
|
352
|
+
moduleName: moduleConfig.moduleName,
|
|
353
|
+
})),
|
|
354
|
+
);
|
|
340
355
|
}
|
|
341
356
|
if (moduleConfig.menu.account) {
|
|
342
|
-
accountMenus.push(
|
|
357
|
+
accountMenus.push(
|
|
358
|
+
...moduleConfig.menu.account.map((item) => ({
|
|
359
|
+
...item,
|
|
360
|
+
moduleName: moduleConfig.moduleName,
|
|
361
|
+
})),
|
|
362
|
+
);
|
|
343
363
|
}
|
|
344
364
|
}
|
|
345
365
|
});
|
|
@@ -354,9 +374,51 @@ function generateMenuConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
354
374
|
adminMenus.sort(sortByOrder);
|
|
355
375
|
accountMenus.sort(sortByOrder);
|
|
356
376
|
|
|
377
|
+
// Collecter les composants à importer dynamiquement
|
|
378
|
+
const componentImports: string[] = [];
|
|
379
|
+
const allMenus = [
|
|
380
|
+
...publicMenus,
|
|
381
|
+
...authMenus,
|
|
382
|
+
...adminMenus,
|
|
383
|
+
...accountMenus,
|
|
384
|
+
];
|
|
385
|
+
|
|
386
|
+
allMenus.forEach((menu, index) => {
|
|
387
|
+
if (menu.componentExport && menu.moduleName) {
|
|
388
|
+
const componentName = `MenuComponent${index}`;
|
|
389
|
+
componentImports.push(
|
|
390
|
+
`const ${componentName} = dynamic(() => import("${menu.moduleName}").then(mod => ({ default: mod.${menu.componentExport} })), { ssr: false });`,
|
|
391
|
+
);
|
|
392
|
+
// Ajouter une référence au composant
|
|
393
|
+
(menu as any).__componentRef = componentName;
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Fonction pour préparer les menus avec les composants
|
|
398
|
+
const prepareMenusForExport = (menus: any[]) => {
|
|
399
|
+
return menus.map((menu) => {
|
|
400
|
+
const { moduleName, __componentRef, ...cleanMenu } = menu;
|
|
401
|
+
if (__componentRef) {
|
|
402
|
+
// Retourner une référence au composant au lieu de la sérialiser
|
|
403
|
+
return `{ ...${JSON.stringify(cleanMenu)}, component: ${__componentRef} }`;
|
|
404
|
+
}
|
|
405
|
+
return JSON.stringify(cleanMenu);
|
|
406
|
+
});
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const publicMenusExport = prepareMenusForExport(publicMenus);
|
|
410
|
+
const authMenusExport = prepareMenusForExport(authMenus);
|
|
411
|
+
const adminMenusExport = prepareMenusForExport(adminMenus);
|
|
412
|
+
const accountMenusExport = prepareMenusForExport(accountMenus);
|
|
413
|
+
|
|
357
414
|
// Générer le contenu du fichier
|
|
358
415
|
const content = `// Auto-generated menu configuration
|
|
359
416
|
// Generated from module build configs
|
|
417
|
+
"use client";
|
|
418
|
+
|
|
419
|
+
import dynamic from "next/dynamic";
|
|
420
|
+
|
|
421
|
+
${componentImports.join("\n")}
|
|
360
422
|
|
|
361
423
|
export interface MenuItem {
|
|
362
424
|
title: string;
|
|
@@ -366,6 +428,11 @@ export interface MenuItem {
|
|
|
366
428
|
order?: number;
|
|
367
429
|
shortcut?: string;
|
|
368
430
|
shortcutDisplay?: string;
|
|
431
|
+
type?: 'text' | 'icon' | 'textIcon';
|
|
432
|
+
position?: 'center' | 'end';
|
|
433
|
+
component?: React.ComponentType<any>;
|
|
434
|
+
componentExport?: string;
|
|
435
|
+
entryPoint?: string;
|
|
369
436
|
}
|
|
370
437
|
|
|
371
438
|
export interface MenuConfig {
|
|
@@ -376,10 +443,10 @@ export interface MenuConfig {
|
|
|
376
443
|
}
|
|
377
444
|
|
|
378
445
|
export const menuConfig: MenuConfig = {
|
|
379
|
-
public: ${
|
|
380
|
-
auth: ${
|
|
381
|
-
admin: ${
|
|
382
|
-
account: ${
|
|
446
|
+
public: [${publicMenusExport.join(",\n ")}],
|
|
447
|
+
auth: [${authMenusExport.join(",\n ")}],
|
|
448
|
+
admin: [${adminMenusExport.join(",\n ")}],
|
|
449
|
+
account: [${accountMenusExport.join(",\n ")}],
|
|
383
450
|
};
|
|
384
451
|
`;
|
|
385
452
|
|
|
@@ -987,6 +1054,355 @@ async function generateUserTabsConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
987
1054
|
}
|
|
988
1055
|
}
|
|
989
1056
|
|
|
1057
|
+
async function generateBucketsConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
1058
|
+
try {
|
|
1059
|
+
// Extraire les configurations storage des modules
|
|
1060
|
+
const allBuckets = moduleConfigs
|
|
1061
|
+
.filter(
|
|
1062
|
+
(config) =>
|
|
1063
|
+
config.storage?.buckets && config.storage.buckets.length > 0,
|
|
1064
|
+
)
|
|
1065
|
+
.flatMap((config) => config.storage!.buckets);
|
|
1066
|
+
|
|
1067
|
+
if (allBuckets.length === 0) {
|
|
1068
|
+
console.log("⏭️ No storage buckets configuration found in modules");
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// Générer le contenu du fichier
|
|
1073
|
+
const timestamp = new Date().toISOString();
|
|
1074
|
+
const bucketsEntries = allBuckets
|
|
1075
|
+
.map((bucket) => {
|
|
1076
|
+
// Sérialiser customAccessControl si elle existe
|
|
1077
|
+
const customAccessControl = bucket.customAccessControl
|
|
1078
|
+
? `\n customAccessControl: ${bucket.customAccessControl.toString()},`
|
|
1079
|
+
: "";
|
|
1080
|
+
|
|
1081
|
+
return ` ${bucket.name}: {
|
|
1082
|
+
name: "${bucket.name}",
|
|
1083
|
+
isPublic: ${bucket.public},
|
|
1084
|
+
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}
|
|
1085
|
+
}`;
|
|
1086
|
+
})
|
|
1087
|
+
.join(",\n");
|
|
1088
|
+
|
|
1089
|
+
const content = `/**
|
|
1090
|
+
* Storage configuration for buckets and access control
|
|
1091
|
+
*
|
|
1092
|
+
* GENERATED FILE - DO NOT EDIT MANUALLY
|
|
1093
|
+
* Generated at: ${timestamp}
|
|
1094
|
+
* Generated from module build configs
|
|
1095
|
+
*/
|
|
1096
|
+
|
|
1097
|
+
export interface BucketConfig {
|
|
1098
|
+
name: string;
|
|
1099
|
+
isPublic: boolean;
|
|
1100
|
+
description: string;
|
|
1101
|
+
allowedFileTypes?: string[];
|
|
1102
|
+
maxFileSize?: number; // in bytes
|
|
1103
|
+
customAccessControl?: (userId: string, filePath: string) => boolean;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
export const BUCKET_CONFIGS: Record<string, BucketConfig> = {
|
|
1107
|
+
${bucketsEntries}
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
/**
|
|
1111
|
+
* Get bucket configuration
|
|
1112
|
+
*/
|
|
1113
|
+
export function getBucketConfig(bucketName: string): BucketConfig | null {
|
|
1114
|
+
return BUCKET_CONFIGS[bucketName] || null;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Check if bucket is public
|
|
1119
|
+
*/
|
|
1120
|
+
export function isPublicBucket(bucketName: string): boolean {
|
|
1121
|
+
const config = getBucketConfig(bucketName);
|
|
1122
|
+
return config?.isPublic ?? false;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/**
|
|
1126
|
+
* Check if user has access to a specific file
|
|
1127
|
+
*/
|
|
1128
|
+
export function hasFileAccess(bucketName: string, userId: string, filePath: string): boolean {
|
|
1129
|
+
const config = getBucketConfig(bucketName);
|
|
1130
|
+
if (!config) return false;
|
|
1131
|
+
|
|
1132
|
+
// Public buckets are accessible to everyone
|
|
1133
|
+
if (config.isPublic) return true;
|
|
1134
|
+
|
|
1135
|
+
// Private buckets require authentication
|
|
1136
|
+
if (!userId) return false;
|
|
1137
|
+
|
|
1138
|
+
// Apply custom access control if defined
|
|
1139
|
+
if (config.customAccessControl) {
|
|
1140
|
+
return config.customAccessControl(userId, filePath);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
return true;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/**
|
|
1147
|
+
* Validate file type for bucket
|
|
1148
|
+
*/
|
|
1149
|
+
export function isValidFileType(bucketName: string, contentType: string): boolean {
|
|
1150
|
+
const config = getBucketConfig(bucketName);
|
|
1151
|
+
if (!config || !config.allowedFileTypes) return true;
|
|
1152
|
+
|
|
1153
|
+
return config.allowedFileTypes.includes(contentType);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* Check if file size is within bucket limits
|
|
1158
|
+
*/
|
|
1159
|
+
export function isValidFileSize(bucketName: string, fileSize: number): boolean {
|
|
1160
|
+
const config = getBucketConfig(bucketName);
|
|
1161
|
+
if (!config || !config.maxFileSize) return true;
|
|
1162
|
+
|
|
1163
|
+
return fileSize <= config.maxFileSize;
|
|
1164
|
+
}
|
|
1165
|
+
`;
|
|
1166
|
+
|
|
1167
|
+
// Créer le fichier dans lib/
|
|
1168
|
+
const outputPath = path.join(projectRoot, "lib", "bucket-config.ts");
|
|
1169
|
+
const libDir = path.dirname(outputPath);
|
|
1170
|
+
|
|
1171
|
+
// Créer le dossier lib s'il n'existe pas
|
|
1172
|
+
if (!fs.existsSync(libDir)) {
|
|
1173
|
+
fs.mkdirSync(libDir, { recursive: true });
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// Écrire le fichier TypeScript
|
|
1177
|
+
fs.writeFileSync(outputPath, content);
|
|
1178
|
+
|
|
1179
|
+
if (isDebugMode) {
|
|
1180
|
+
console.log(`✅ Generated storage buckets configuration: ${outputPath}`);
|
|
1181
|
+
console.log(`📊 Storage buckets count: ${allBuckets.length}`);
|
|
1182
|
+
|
|
1183
|
+
// Afficher un résumé
|
|
1184
|
+
allBuckets.forEach((bucket) => {
|
|
1185
|
+
const access = bucket.public ? "public" : "private";
|
|
1186
|
+
console.log(` - ${bucket.name} (${access})`);
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
console.error("❌ Error generating buckets configuration:", error);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
async function generateStorageProxyApi(moduleConfigs: ModuleBuildConfig[]) {
|
|
1195
|
+
try {
|
|
1196
|
+
// Extraire les configurations storage des modules
|
|
1197
|
+
const allBuckets = moduleConfigs
|
|
1198
|
+
.filter(
|
|
1199
|
+
(config) =>
|
|
1200
|
+
config.storage?.buckets && config.storage.buckets.length > 0,
|
|
1201
|
+
)
|
|
1202
|
+
.flatMap((config) => config.storage!.buckets);
|
|
1203
|
+
|
|
1204
|
+
if (allBuckets.length === 0) {
|
|
1205
|
+
console.log("⏭️ No storage buckets found, skipping proxy API generation");
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// Identifier les buckets publics et privés
|
|
1210
|
+
const publicBuckets = allBuckets.filter((b) => b.public);
|
|
1211
|
+
const privateBuckets = allBuckets.filter((b) => !b.public);
|
|
1212
|
+
|
|
1213
|
+
// Générer les conditions pour les buckets publics
|
|
1214
|
+
const publicBucketConditions = publicBuckets
|
|
1215
|
+
.map((bucket) => `bucket === "${bucket.name}"`)
|
|
1216
|
+
.filter(Boolean);
|
|
1217
|
+
|
|
1218
|
+
// Ajouter les conditions pour les chemins publics dans les buckets privés
|
|
1219
|
+
const publicPathConditions: string[] = [];
|
|
1220
|
+
if (publicBuckets.some((b) => b.name === "recipes")) {
|
|
1221
|
+
publicPathConditions.push('storagePath.startsWith("recipes/")');
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
const allPublicConditions = [
|
|
1225
|
+
...publicBucketConditions,
|
|
1226
|
+
...publicPathConditions,
|
|
1227
|
+
];
|
|
1228
|
+
const publicCondition =
|
|
1229
|
+
allPublicConditions.length > 0
|
|
1230
|
+
? allPublicConditions.join(" || ")
|
|
1231
|
+
: "false";
|
|
1232
|
+
|
|
1233
|
+
const timestamp = new Date().toISOString();
|
|
1234
|
+
const content = `import { getSupabaseServerClient } from "@lastbrain/core/server";
|
|
1235
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
1236
|
+
|
|
1237
|
+
/**
|
|
1238
|
+
* GET /api/storage/[bucket]/[...path]
|
|
1239
|
+
* Proxy pour servir les images avec authentication
|
|
1240
|
+
*
|
|
1241
|
+
* GENERATED FILE - DO NOT EDIT MANUALLY
|
|
1242
|
+
* Generated at: ${timestamp}
|
|
1243
|
+
* Generated from module storage configurations
|
|
1244
|
+
* Buckets configurés:
|
|
1245
|
+
${allBuckets.map((b) => ` * - ${b.name} (${b.public ? "public" : "private"})`).join("\n")}
|
|
1246
|
+
*/
|
|
1247
|
+
export async function GET(
|
|
1248
|
+
request: NextRequest,
|
|
1249
|
+
context: { params: Promise<{ bucket: string; path: string[] }> },
|
|
1250
|
+
) {
|
|
1251
|
+
try {
|
|
1252
|
+
const { bucket, path } = await context.params;
|
|
1253
|
+
const storagePath = path.join("/");
|
|
1254
|
+
|
|
1255
|
+
// Les images publiques sont accessibles sans auth
|
|
1256
|
+
if (${publicCondition}) {
|
|
1257
|
+
const supabase = await getSupabaseServerClient();
|
|
1258
|
+
const { data, error } = await supabase.storage
|
|
1259
|
+
.from(bucket)
|
|
1260
|
+
.createSignedUrl(storagePath, 3600); // 1 heure
|
|
1261
|
+
|
|
1262
|
+
if (error) {
|
|
1263
|
+
console.error(\`[storage] Error creating signed URL for public image:\`, error);
|
|
1264
|
+
return new NextResponse("Not found", { status: 404 });
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// Rediriger vers l'URL signée
|
|
1268
|
+
return NextResponse.redirect(data.signedUrl);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// Les images privées nécessitent une authentification
|
|
1272
|
+
const supabase = await getSupabaseServerClient();
|
|
1273
|
+
const {
|
|
1274
|
+
data: { user },
|
|
1275
|
+
error: authError,
|
|
1276
|
+
} = await supabase.auth.getUser();
|
|
1277
|
+
|
|
1278
|
+
if (authError || !user) {
|
|
1279
|
+
return new NextResponse("Unauthorized", { status: 401 });
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// Cas spécial: si le chemin commence par /product/ ou /recipe/,
|
|
1283
|
+
// c'est une image avec format court qui nécessite le préfixe userId
|
|
1284
|
+
let actualStoragePath = storagePath;
|
|
1285
|
+
|
|
1286
|
+
if (storagePath.startsWith("product/") || storagePath.startsWith("recipe/")) {
|
|
1287
|
+
actualStoragePath = \`\${user.id}/\${storagePath}\`;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// Vérifier que l'utilisateur a accès à cette image
|
|
1291
|
+
// Format: {userId}/recipe/{recipeId}/{filename} ou {userId}/product/{productId}/{filename}
|
|
1292
|
+
const pathParts = actualStoragePath.split("/");
|
|
1293
|
+
const pathUserId = pathParts[0];
|
|
1294
|
+
|
|
1295
|
+
if (pathUserId !== user.id) {
|
|
1296
|
+
return new NextResponse("Forbidden", { status: 403 });
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// Créer une URL signée pour l'image privée
|
|
1300
|
+
const { data, error } = await supabase.storage
|
|
1301
|
+
.from(bucket)
|
|
1302
|
+
.createSignedUrl(actualStoragePath, 3600); // 1 heure
|
|
1303
|
+
|
|
1304
|
+
if (error) {
|
|
1305
|
+
console.error(\`[storage] Error creating signed URL:\`, error);
|
|
1306
|
+
return new NextResponse("Not found", { status: 404 });
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// Rediriger vers l'URL signée
|
|
1310
|
+
return NextResponse.redirect(data.signedUrl);
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
console.error("[storage] Error:", error);
|
|
1313
|
+
return new NextResponse("Internal server error", { status: 500 });
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
`;
|
|
1317
|
+
|
|
1318
|
+
// Créer le fichier dans app/api/storage/[bucket]/[...path]/
|
|
1319
|
+
const outputPath = path.join(
|
|
1320
|
+
projectRoot,
|
|
1321
|
+
"app",
|
|
1322
|
+
"api",
|
|
1323
|
+
"storage",
|
|
1324
|
+
"[bucket]",
|
|
1325
|
+
"[...path]",
|
|
1326
|
+
"route.ts",
|
|
1327
|
+
);
|
|
1328
|
+
const outputDir = path.dirname(outputPath);
|
|
1329
|
+
|
|
1330
|
+
// Créer le dossier s'il n'existe pas
|
|
1331
|
+
if (!fs.existsSync(outputDir)) {
|
|
1332
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// Écrire le fichier TypeScript
|
|
1336
|
+
fs.writeFileSync(outputPath, content);
|
|
1337
|
+
|
|
1338
|
+
if (isDebugMode) {
|
|
1339
|
+
console.log(`✅ Generated storage proxy API: ${outputPath}`);
|
|
1340
|
+
console.log(
|
|
1341
|
+
`📊 Public buckets: ${publicBuckets.map((b) => b.name).join(", ") || "none"}`,
|
|
1342
|
+
);
|
|
1343
|
+
console.log(
|
|
1344
|
+
`📊 Private buckets: ${privateBuckets.map((b) => b.name).join(", ") || "none"}`,
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
} catch (error) {
|
|
1348
|
+
console.error("❌ Error generating storage proxy API:", error);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
async function generateFooterConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
1353
|
+
try {
|
|
1354
|
+
// Extraire tous les liens footer des modules
|
|
1355
|
+
const allFooterLinks = moduleConfigs
|
|
1356
|
+
.filter((config) => config.footer && config.footer.length > 0)
|
|
1357
|
+
.flatMap((config) => config.footer!);
|
|
1358
|
+
|
|
1359
|
+
if (allFooterLinks.length === 0) {
|
|
1360
|
+
console.log(
|
|
1361
|
+
"⏭️ No footer links found, skipping footer config generation",
|
|
1362
|
+
);
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// Trier les liens par position et order
|
|
1367
|
+
allFooterLinks.sort((a, b) => {
|
|
1368
|
+
const posOrder = { left: 0, center: 1, right: 2 };
|
|
1369
|
+
const posA = posOrder[a.position || "left"] || 0;
|
|
1370
|
+
const posB = posOrder[b.position || "left"] || 0;
|
|
1371
|
+
if (posA !== posB) return posA - posB;
|
|
1372
|
+
return (a.order || 0) - (b.order || 0);
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
const timestamp = new Date().toISOString();
|
|
1376
|
+
const content = `// Auto-generated footer configuration
|
|
1377
|
+
// Generated from module build configs
|
|
1378
|
+
// Generated at: ${timestamp}
|
|
1379
|
+
"use client";
|
|
1380
|
+
|
|
1381
|
+
import type { FooterConfig } from "@lastbrain/ui";
|
|
1382
|
+
|
|
1383
|
+
export const footerConfig: FooterConfig = {
|
|
1384
|
+
companyName: "LastBrain",
|
|
1385
|
+
companyDescription: "Plateforme de développement rapide d'applications",
|
|
1386
|
+
links: ${JSON.stringify(allFooterLinks, null, 2)},
|
|
1387
|
+
social: [],
|
|
1388
|
+
};
|
|
1389
|
+
`;
|
|
1390
|
+
|
|
1391
|
+
const configDir = path.join(projectRoot, "config");
|
|
1392
|
+
ensureDirectory(configDir);
|
|
1393
|
+
|
|
1394
|
+
const footerPath = path.join(configDir, "footer.ts");
|
|
1395
|
+
fs.writeFileSync(footerPath, content);
|
|
1396
|
+
|
|
1397
|
+
if (isDebugMode) {
|
|
1398
|
+
console.log(`✅ Generated footer config: ${footerPath}`);
|
|
1399
|
+
console.log(` - ${allFooterLinks.length} footer link(s)`);
|
|
1400
|
+
}
|
|
1401
|
+
} catch (error) {
|
|
1402
|
+
console.error("❌ Error generating footer config:", error);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
990
1406
|
export async function runModuleBuild() {
|
|
991
1407
|
ensureDirectory(appDirectory);
|
|
992
1408
|
|
|
@@ -1056,6 +1472,24 @@ export async function runModuleBuild() {
|
|
|
1056
1472
|
}
|
|
1057
1473
|
await generateUserTabsConfig(moduleConfigs);
|
|
1058
1474
|
|
|
1475
|
+
// Générer la configuration des buckets storage
|
|
1476
|
+
if (isDebugMode) {
|
|
1477
|
+
console.log("🗄️ Generating storage buckets configuration...");
|
|
1478
|
+
}
|
|
1479
|
+
await generateBucketsConfig(moduleConfigs);
|
|
1480
|
+
|
|
1481
|
+
// Générer le proxy storage API
|
|
1482
|
+
if (isDebugMode) {
|
|
1483
|
+
console.log("🔌 Generating storage proxy API...");
|
|
1484
|
+
}
|
|
1485
|
+
await generateStorageProxyApi(moduleConfigs);
|
|
1486
|
+
|
|
1487
|
+
// Générer la configuration footer
|
|
1488
|
+
if (isDebugMode) {
|
|
1489
|
+
console.log("🦶 Generating footer configuration...");
|
|
1490
|
+
}
|
|
1491
|
+
await generateFooterConfig(moduleConfigs);
|
|
1492
|
+
|
|
1059
1493
|
// Message de succès final
|
|
1060
1494
|
if (!isDebugMode) {
|
|
1061
1495
|
console.log("\n✅ Module build completed successfully!");
|