@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
|
@@ -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: ${
|
|
314
|
-
auth: ${
|
|
315
|
-
admin: ${
|
|
316
|
-
account: ${
|
|
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,310 @@ 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
|
+
* Buckets configurés:
|
|
1031
|
+
${allBuckets.map((b) => ` * - ${b.name} (${b.public ? "public" : "private"})`).join("\n")}
|
|
1032
|
+
*/
|
|
1033
|
+
export async function GET(
|
|
1034
|
+
request: NextRequest,
|
|
1035
|
+
context: { params: Promise<{ bucket: string; path: string[] }> },
|
|
1036
|
+
) {
|
|
1037
|
+
try {
|
|
1038
|
+
const { bucket, path } = await context.params;
|
|
1039
|
+
const storagePath = path.join("/");
|
|
1040
|
+
|
|
1041
|
+
// Les images publiques sont accessibles sans auth
|
|
1042
|
+
if (${publicCondition}) {
|
|
1043
|
+
const supabase = await getSupabaseServerClient();
|
|
1044
|
+
const { data, error } = await supabase.storage
|
|
1045
|
+
.from(bucket)
|
|
1046
|
+
.createSignedUrl(storagePath, 3600); // 1 heure
|
|
1047
|
+
|
|
1048
|
+
if (error) {
|
|
1049
|
+
console.error(\`[storage] Error creating signed URL for public image:\`, error);
|
|
1050
|
+
return new NextResponse("Not found", { status: 404 });
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// Rediriger vers l'URL signée
|
|
1054
|
+
return NextResponse.redirect(data.signedUrl);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// Les images privées nécessitent une authentification
|
|
1058
|
+
const supabase = await getSupabaseServerClient();
|
|
1059
|
+
const {
|
|
1060
|
+
data: { user },
|
|
1061
|
+
error: authError,
|
|
1062
|
+
} = await supabase.auth.getUser();
|
|
1063
|
+
|
|
1064
|
+
if (authError || !user) {
|
|
1065
|
+
return new NextResponse("Unauthorized", { status: 401 });
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// Cas spécial: si le chemin commence par /product/ ou /recipe/,
|
|
1069
|
+
// c'est une image avec format court qui nécessite le préfixe userId
|
|
1070
|
+
let actualStoragePath = storagePath;
|
|
1071
|
+
|
|
1072
|
+
if (storagePath.startsWith("product/") || storagePath.startsWith("recipe/")) {
|
|
1073
|
+
actualStoragePath = \`\${user.id}/\${storagePath}\`;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// Vérifier que l'utilisateur a accès à cette image
|
|
1077
|
+
// Format: {userId}/recipe/{recipeId}/{filename} ou {userId}/product/{productId}/{filename}
|
|
1078
|
+
const pathParts = actualStoragePath.split("/");
|
|
1079
|
+
const pathUserId = pathParts[0];
|
|
1080
|
+
|
|
1081
|
+
if (pathUserId !== user.id) {
|
|
1082
|
+
return new NextResponse("Forbidden", { status: 403 });
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// Créer une URL signée pour l'image privée
|
|
1086
|
+
const { data, error } = await supabase.storage
|
|
1087
|
+
.from(bucket)
|
|
1088
|
+
.createSignedUrl(actualStoragePath, 3600); // 1 heure
|
|
1089
|
+
|
|
1090
|
+
if (error) {
|
|
1091
|
+
console.error(\`[storage] Error creating signed URL:\`, error);
|
|
1092
|
+
return new NextResponse("Not found", { status: 404 });
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// Rediriger vers l'URL signée
|
|
1096
|
+
return NextResponse.redirect(data.signedUrl);
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
console.error("[storage] Error:", error);
|
|
1099
|
+
return new NextResponse("Internal server error", { status: 500 });
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
`;
|
|
1103
|
+
// Créer le fichier dans app/api/storage/[bucket]/[...path]/
|
|
1104
|
+
const outputPath = path.join(projectRoot, "app", "api", "storage", "[bucket]", "[...path]", "route.ts");
|
|
1105
|
+
const outputDir = path.dirname(outputPath);
|
|
1106
|
+
// Créer le dossier s'il n'existe pas
|
|
1107
|
+
if (!fs.existsSync(outputDir)) {
|
|
1108
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1109
|
+
}
|
|
1110
|
+
// Écrire le fichier TypeScript
|
|
1111
|
+
fs.writeFileSync(outputPath, content);
|
|
1112
|
+
if (isDebugMode) {
|
|
1113
|
+
console.log(`✅ Generated storage proxy API: ${outputPath}`);
|
|
1114
|
+
console.log(`📊 Public buckets: ${publicBuckets.map((b) => b.name).join(", ") || "none"}`);
|
|
1115
|
+
console.log(`📊 Private buckets: ${privateBuckets.map((b) => b.name).join(", ") || "none"}`);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
catch (error) {
|
|
1119
|
+
console.error("❌ Error generating storage proxy API:", error);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
async function generateFooterConfig(moduleConfigs) {
|
|
1123
|
+
try {
|
|
1124
|
+
// Extraire tous les liens footer des modules
|
|
1125
|
+
const allFooterLinks = moduleConfigs
|
|
1126
|
+
.filter((config) => config.footer && config.footer.length > 0)
|
|
1127
|
+
.flatMap((config) => config.footer);
|
|
1128
|
+
if (allFooterLinks.length === 0) {
|
|
1129
|
+
console.log("⏭️ No footer links found, skipping footer config generation");
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
// Trier les liens par position et order
|
|
1133
|
+
allFooterLinks.sort((a, b) => {
|
|
1134
|
+
const posOrder = { left: 0, center: 1, right: 2 };
|
|
1135
|
+
const posA = posOrder[a.position || "left"] || 0;
|
|
1136
|
+
const posB = posOrder[b.position || "left"] || 0;
|
|
1137
|
+
if (posA !== posB)
|
|
1138
|
+
return posA - posB;
|
|
1139
|
+
return (a.order || 0) - (b.order || 0);
|
|
1140
|
+
});
|
|
1141
|
+
const timestamp = new Date().toISOString();
|
|
1142
|
+
const content = `// Auto-generated footer configuration
|
|
1143
|
+
// Generated from module build configs
|
|
1144
|
+
// Generated at: ${timestamp}
|
|
1145
|
+
"use client";
|
|
1146
|
+
|
|
1147
|
+
import type { FooterConfig } from "@lastbrain/ui";
|
|
1148
|
+
|
|
1149
|
+
export const footerConfig: FooterConfig = {
|
|
1150
|
+
companyName: "LastBrain",
|
|
1151
|
+
companyDescription: "Plateforme de développement rapide d'applications",
|
|
1152
|
+
links: ${JSON.stringify(allFooterLinks, null, 2)},
|
|
1153
|
+
social: [],
|
|
1154
|
+
};
|
|
1155
|
+
`;
|
|
1156
|
+
const configDir = path.join(projectRoot, "config");
|
|
1157
|
+
ensureDirectory(configDir);
|
|
1158
|
+
const footerPath = path.join(configDir, "footer.ts");
|
|
1159
|
+
fs.writeFileSync(footerPath, content);
|
|
1160
|
+
if (isDebugMode) {
|
|
1161
|
+
console.log(`✅ Generated footer config: ${footerPath}`);
|
|
1162
|
+
console.log(` - ${allFooterLinks.length} footer link(s)`);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
catch (error) {
|
|
1166
|
+
console.error("❌ Error generating footer config:", error);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
812
1169
|
export async function runModuleBuild() {
|
|
813
1170
|
ensureDirectory(appDirectory);
|
|
814
1171
|
// Nettoyer les fichiers générés précédemment
|
|
@@ -863,6 +1220,21 @@ export async function runModuleBuild() {
|
|
|
863
1220
|
console.log("📑 Generating user tabs configuration...");
|
|
864
1221
|
}
|
|
865
1222
|
await generateUserTabsConfig(moduleConfigs);
|
|
1223
|
+
// Générer la configuration des buckets storage
|
|
1224
|
+
if (isDebugMode) {
|
|
1225
|
+
console.log("🗄️ Generating storage buckets configuration...");
|
|
1226
|
+
}
|
|
1227
|
+
await generateBucketsConfig(moduleConfigs);
|
|
1228
|
+
// Générer le proxy storage API
|
|
1229
|
+
if (isDebugMode) {
|
|
1230
|
+
console.log("🔌 Generating storage proxy API...");
|
|
1231
|
+
}
|
|
1232
|
+
await generateStorageProxyApi(moduleConfigs);
|
|
1233
|
+
// Générer la configuration footer
|
|
1234
|
+
if (isDebugMode) {
|
|
1235
|
+
console.log("🦶 Generating footer configuration...");
|
|
1236
|
+
}
|
|
1237
|
+
await generateFooterConfig(moduleConfigs);
|
|
866
1238
|
// Message de succès final
|
|
867
1239
|
if (!isDebugMode) {
|
|
868
1240
|
console.log("\n✅ Module build completed successfully!");
|