@lastbrain/app 2.0.31 → 2.0.35
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/analytics/registry.d.ts +7 -0
- package/dist/analytics/registry.d.ts.map +1 -0
- package/dist/analytics/registry.js +11 -0
- package/dist/auth/useAuthSession.d.ts.map +1 -1
- package/dist/auth/useAuthSession.js +85 -1
- package/dist/cli.js +19 -3
- package/dist/components/LanguageSwitcher.d.ts.map +1 -1
- package/dist/components/LanguageSwitcher.js +89 -5
- package/dist/config/version.d.ts.map +1 -1
- package/dist/config/version.js +30 -19
- package/dist/i18n/useLink.d.ts.map +1 -1
- package/dist/i18n/useLink.js +15 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/layouts/AdminLayoutWithSidebar.d.ts +3 -1
- package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/AdminLayoutWithSidebar.js +2 -2
- package/dist/layouts/AppProviders.d.ts +7 -1
- package/dist/layouts/AppProviders.d.ts.map +1 -1
- package/dist/layouts/AppProviders.js +24 -3
- package/dist/layouts/AuthLayout.js +1 -1
- package/dist/layouts/PublicLayout.js +1 -1
- package/dist/layouts/RootLayout.d.ts.map +1 -1
- package/dist/scripts/init-app.d.ts.map +1 -1
- package/dist/scripts/init-app.js +301 -138
- package/dist/scripts/module-build.d.ts.map +1 -1
- package/dist/scripts/module-build.js +402 -67
- package/dist/scripts/module-create.d.ts.map +1 -1
- package/dist/scripts/module-create.js +227 -10
- package/dist/scripts/sitemap-flat-generator.d.ts +39 -0
- package/dist/scripts/sitemap-flat-generator.d.ts.map +1 -0
- package/dist/scripts/sitemap-flat-generator.js +231 -0
- package/dist/scripts/sitemap-manifest-generator.d.ts +59 -0
- package/dist/scripts/sitemap-manifest-generator.d.ts.map +1 -0
- package/dist/scripts/sitemap-manifest-generator.js +290 -0
- package/dist/sitemap/manifest.d.ts +8 -0
- package/dist/sitemap/manifest.d.ts.map +1 -0
- package/dist/sitemap/manifest.js +6 -0
- package/dist/styles.css +2 -2
- package/dist/templates/AuthGuidePage.js +2 -0
- package/dist/templates/DefaultDoc.d.ts.map +1 -1
- package/dist/templates/DefaultDoc.js +9 -5
- package/dist/templates/DocPage.d.ts.map +1 -1
- package/dist/templates/DocPage.js +40 -0
- package/dist/templates/MigrationsGuidePage.js +2 -0
- package/dist/templates/ModuleGuidePage.d.ts.map +1 -1
- package/dist/templates/ModuleGuidePage.js +4 -1
- package/dist/templates/SimpleHomePage.js +2 -0
- package/package.json +11 -4
- package/src/analytics/registry.ts +14 -0
- package/src/auth/useAuthSession.ts +91 -1
- package/src/cli.ts +19 -3
- package/src/components/LanguageSwitcher.tsx +113 -23
- package/src/config/version.ts +30 -19
- package/src/i18n/useLink.ts +15 -0
- package/src/index.ts +17 -0
- package/src/layouts/AdminLayoutWithSidebar.tsx +4 -0
- package/src/layouts/AppProviders.tsx +66 -8
- package/src/layouts/AuthLayout.tsx +1 -1
- package/src/layouts/PublicLayout.tsx +1 -1
- package/src/layouts/RootLayout.tsx +0 -1
- package/src/scripts/init-app.ts +360 -149
- package/src/scripts/module-build.ts +458 -72
- package/src/scripts/module-create.ts +260 -10
- package/src/scripts/sitemap-flat-generator.ts +313 -0
- package/src/scripts/sitemap-manifest-generator.ts +476 -0
- package/src/sitemap/manifest.ts +17 -0
- package/src/templates/AuthGuidePage.tsx +1 -1
- package/src/templates/DefaultDoc.tsx +397 -6
- package/src/templates/DocPage.tsx +40 -0
- package/src/templates/MigrationsGuidePage.tsx +1 -1
- package/src/templates/ModuleGuidePage.tsx +3 -2
- package/src/templates/SimpleHomePage.tsx +1 -1
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
+
import { generateManifestBasedSitemaps } from "./sitemap-manifest-generator.js";
|
|
5
|
+
import type { ModuleBuildConfig } from "../index.js";
|
|
4
6
|
|
|
5
7
|
import type {
|
|
6
8
|
ModuleApiConfig,
|
|
7
|
-
ModuleBuildConfig,
|
|
8
9
|
ModuleMenuItemConfig,
|
|
9
10
|
ModulePageConfig,
|
|
10
11
|
ModuleSection,
|
|
11
12
|
ModuleRealtimeConfig,
|
|
12
|
-
|
|
13
|
+
ModuleSitemapConfig,
|
|
14
|
+
} from "../index.js";
|
|
15
|
+
|
|
16
|
+
const LOCALE_MAP: Record<string, string> = {
|
|
17
|
+
fr: "fr_FR",
|
|
18
|
+
en: "en_US",
|
|
19
|
+
es: "es_ES",
|
|
20
|
+
};
|
|
13
21
|
|
|
14
22
|
// Utiliser PROJECT_ROOT si défini (pour pnpm --filter), sinon process.cwd()
|
|
15
23
|
const projectRoot = process.env.PROJECT_ROOT || process.cwd();
|
|
@@ -248,7 +256,7 @@ function buildPage(moduleConfig: ModuleBuildConfig, page: ModulePageConfig) {
|
|
|
248
256
|
page.componentExport === "UserPage";
|
|
249
257
|
|
|
250
258
|
// Détecter les pages légales qui utilisent cookies (privacy, terms, returns)
|
|
251
|
-
const
|
|
259
|
+
const _isLegalPage =
|
|
252
260
|
page.section === "public" &&
|
|
253
261
|
(page.path.includes("privacy") ||
|
|
254
262
|
page.path.includes("terms") ||
|
|
@@ -261,6 +269,7 @@ function buildPage(moduleConfig: ModuleBuildConfig, page: ModulePageConfig) {
|
|
|
261
269
|
// On importe directement depuis app/config au lieu de passer via props
|
|
262
270
|
content = `// GENERATED BY LASTBRAIN MODULE BUILD
|
|
263
271
|
import { UserDetailPage } from "${moduleConfig.moduleName}";
|
|
272
|
+
import { logger } from "@lastbrain/core";
|
|
264
273
|
|
|
265
274
|
interface UserPageProps { params: Promise<{ id: string }> }
|
|
266
275
|
|
|
@@ -270,7 +279,7 @@ async function getModuleUserTabs() {
|
|
|
270
279
|
const { moduleUserTabs } = await import("../../../../../../config/user-tabs");
|
|
271
280
|
return moduleUserTabs || [];
|
|
272
281
|
} catch (e) {
|
|
273
|
-
|
|
282
|
+
logger.warn("[user-detail-wrapper] erreur chargement user-tabs", e);
|
|
274
283
|
return [];
|
|
275
284
|
}
|
|
276
285
|
}
|
|
@@ -292,16 +301,6 @@ const ${page.componentExport} = dynamic(
|
|
|
292
301
|
{ ssr: false }
|
|
293
302
|
);
|
|
294
303
|
|
|
295
|
-
export default function ${wrapperName}${hasDynamicParams ? "(props: Record<string, unknown>)" : "()"} {
|
|
296
|
-
return <${page.componentExport} ${hasDynamicParams ? "{...props}" : ""} />;
|
|
297
|
-
}
|
|
298
|
-
`;
|
|
299
|
-
} else if (isLegalPage) {
|
|
300
|
-
content = `// GENERATED BY LASTBRAIN MODULE BUILD
|
|
301
|
-
import { ${page.componentExport} } from "${moduleConfig.moduleName}";
|
|
302
|
-
|
|
303
|
-
export const dynamic = 'force-dynamic';
|
|
304
|
-
|
|
305
304
|
export default function ${wrapperName}${hasDynamicParams ? "(props: Record<string, unknown>)" : "()"} {
|
|
306
305
|
return <${page.componentExport} ${hasDynamicParams ? "{...props}" : ""} />;
|
|
307
306
|
}
|
|
@@ -320,7 +319,7 @@ export default function ${wrapperName}${hasDynamicParams ? "(props: Record<strin
|
|
|
320
319
|
.map((seg) => seg.slice(1, -1)); // Enlève les []
|
|
321
320
|
|
|
322
321
|
// Détecter si le module path lui-même a des params dynamiques (pas seulement [lang])
|
|
323
|
-
const
|
|
322
|
+
const _moduleHasDynamicParams = segments.some(
|
|
324
323
|
(seg) => seg.startsWith("[") && seg.endsWith("]")
|
|
325
324
|
);
|
|
326
325
|
|
|
@@ -549,6 +548,7 @@ export interface MenuItem {
|
|
|
549
548
|
shortcut?: string;
|
|
550
549
|
shortcutDisplay?: string;
|
|
551
550
|
type?: 'text' | 'icon' | 'textIcon';
|
|
551
|
+
keyboardOnly?: boolean;
|
|
552
552
|
position?: 'center' | 'end';
|
|
553
553
|
component?: React.ComponentType<any>;
|
|
554
554
|
componentExport?: string;
|
|
@@ -686,7 +686,7 @@ function copyModuleMigrations(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
686
686
|
}
|
|
687
687
|
|
|
688
688
|
function generateDocsPage(moduleConfigs: ModuleBuildConfig[]) {
|
|
689
|
-
const docsDir = path.join(appDirectory, "docs");
|
|
689
|
+
const docsDir = path.join(appDirectory, "[lang]", "(public)", "docs");
|
|
690
690
|
ensureDirectory(docsDir);
|
|
691
691
|
|
|
692
692
|
const docsPagePath = path.join(docsDir, "page.tsx");
|
|
@@ -714,13 +714,13 @@ function generateDocsPage(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
714
714
|
|
|
715
715
|
allModules.forEach((moduleEntry) => {
|
|
716
716
|
const moduleName = moduleEntry.package;
|
|
717
|
-
// Extraire le nom du module sans le scope et sans "
|
|
717
|
+
// Extraire le nom du module sans le scope et sans "-pro"
|
|
718
718
|
// Ex: @lastbrain/module-auth -> auth
|
|
719
|
-
// Ex: @lastbrain-labs/module-
|
|
719
|
+
// Ex: @lastbrain-labs/module-metrics-pro -> metrics
|
|
720
720
|
const moduleId = moduleName
|
|
721
721
|
.replace("@lastbrain-labs/module-", "")
|
|
722
722
|
.replace("@lastbrain/module-", "")
|
|
723
|
-
.replace(/-pro$/, ""); // Retirer le
|
|
723
|
+
.replace(/-pro$/, ""); // Retirer le suffixe -pro
|
|
724
724
|
|
|
725
725
|
const docComponentName = `${toPascalCase(moduleId)}ModuleDoc`;
|
|
726
726
|
|
|
@@ -772,6 +772,45 @@ ${moduleConfigurations.join(",\n")}
|
|
|
772
772
|
if (isDebugMode) {
|
|
773
773
|
console.log(`📚 Generated docs page: ${docsPagePath}`);
|
|
774
774
|
}
|
|
775
|
+
|
|
776
|
+
// Generate docs layout with translation support
|
|
777
|
+
const docsLayoutPath = path.join(docsDir, "layout.tsx");
|
|
778
|
+
const docsLayoutContent = `// Auto-generated docs layout with translation support
|
|
779
|
+
import { loadTranslations } from "@lastbrain/app/i18n/server-lang";
|
|
780
|
+
import { ClientLayout } from "../../../../components/ClientLayout";
|
|
781
|
+
import { headers } from "next/headers";
|
|
782
|
+
|
|
783
|
+
// Extract language from request headers
|
|
784
|
+
async function getLanguageFromRequest(): Promise<string> {
|
|
785
|
+
const headersList = await headers();
|
|
786
|
+
const acceptLanguage = headersList.get("accept-language") || "";
|
|
787
|
+
// Detect if French is requested
|
|
788
|
+
if (acceptLanguage.toLowerCase().includes("fr")) {
|
|
789
|
+
return "fr";
|
|
790
|
+
}
|
|
791
|
+
return "en";
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
export default async function DocsLayout({
|
|
795
|
+
children,
|
|
796
|
+
}: {
|
|
797
|
+
children: React.ReactNode;
|
|
798
|
+
}) {
|
|
799
|
+
const lang = await getLanguageFromRequest();
|
|
800
|
+
const translations = await loadTranslations(lang);
|
|
801
|
+
|
|
802
|
+
return (
|
|
803
|
+
<ClientLayout lang={lang} translations={translations}>
|
|
804
|
+
<div className="min-h-screen pt-12">{children}</div>
|
|
805
|
+
</ClientLayout>
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
`;
|
|
809
|
+
|
|
810
|
+
fs.writeFileSync(docsLayoutPath, docsLayoutContent);
|
|
811
|
+
if (isDebugMode) {
|
|
812
|
+
console.log(`📚 Generated docs layout: ${docsLayoutPath}`);
|
|
813
|
+
}
|
|
775
814
|
}
|
|
776
815
|
|
|
777
816
|
function buildGroupedApi(
|
|
@@ -838,7 +877,8 @@ function cleanGeneratedFiles() {
|
|
|
838
877
|
"page.tsx", // Page racine seulement
|
|
839
878
|
"admin/page.tsx", // Page admin racine
|
|
840
879
|
"admin/layout.tsx", // Layout admin racine
|
|
841
|
-
"docs/page.tsx", // Page docs générée
|
|
880
|
+
"[lang]/(public)/docs/page.tsx", // Page docs générée
|
|
881
|
+
"[lang]/(public)/docs/layout.tsx", // Layout docs généré
|
|
842
882
|
// Middleware et autres fichiers core
|
|
843
883
|
"middleware.ts",
|
|
844
884
|
// Dossiers de lib et config
|
|
@@ -917,7 +957,14 @@ function cleanGeneratedFiles() {
|
|
|
917
957
|
};
|
|
918
958
|
|
|
919
959
|
// Nettoyer les dossiers de sections
|
|
920
|
-
const sectionsToClean = [
|
|
960
|
+
const sectionsToClean = [
|
|
961
|
+
"(public)",
|
|
962
|
+
"auth",
|
|
963
|
+
"admin",
|
|
964
|
+
"api",
|
|
965
|
+
"[lang]",
|
|
966
|
+
"sitemap",
|
|
967
|
+
];
|
|
921
968
|
sectionsToClean.forEach((section) => {
|
|
922
969
|
const sectionPath = path.join(appDirectory, section);
|
|
923
970
|
if (fs.existsSync(sectionPath)) {
|
|
@@ -993,7 +1040,7 @@ function cleanOldRoutesWithoutLang() {
|
|
|
993
1040
|
}
|
|
994
1041
|
});
|
|
995
1042
|
}
|
|
996
|
-
function
|
|
1043
|
+
function _generateAppAside() {
|
|
997
1044
|
const targetPath = path.join(appDirectory, "components", "AppAside.tsx");
|
|
998
1045
|
const templateContent = `"use client";
|
|
999
1046
|
|
|
@@ -1026,10 +1073,15 @@ export function AppAside({ className = "", isVisible = true }: AppAsideProps) {
|
|
|
1026
1073
|
}
|
|
1027
1074
|
}
|
|
1028
1075
|
|
|
1029
|
-
function generateLayouts() {
|
|
1030
|
-
//
|
|
1076
|
+
function generateLayouts(moduleConfigs: ModuleBuildConfig[]) {
|
|
1077
|
+
// Vérifier si le module billing-pro est actif
|
|
1078
|
+
const hasBillingModule = moduleConfigs.some(
|
|
1079
|
+
(config) => config.moduleName === "@lastbrain-labs/module-billing-pro"
|
|
1080
|
+
);
|
|
1081
|
+
|
|
1082
|
+
// Générer ClientLayout wrapper client (dans components/ racine)
|
|
1031
1083
|
const clientLayoutPath = path.join(
|
|
1032
|
-
|
|
1084
|
+
projectRoot,
|
|
1033
1085
|
"components",
|
|
1034
1086
|
"ClientLayout.tsx"
|
|
1035
1087
|
);
|
|
@@ -1085,21 +1137,33 @@ export function ClientLayout({
|
|
|
1085
1137
|
console.error(`❌ Error generating ClientLayout component: ${error}`);
|
|
1086
1138
|
}
|
|
1087
1139
|
|
|
1088
|
-
// Générer AppProviders wrapper client
|
|
1140
|
+
// Générer AppProviders wrapper client (dans components/ racine)
|
|
1089
1141
|
const appProvidersPath = path.join(
|
|
1090
|
-
|
|
1142
|
+
projectRoot,
|
|
1091
1143
|
"components",
|
|
1092
1144
|
"AppProviders.tsx"
|
|
1093
1145
|
);
|
|
1146
|
+
|
|
1147
|
+
// Import conditionnel de EntitlementsProvider
|
|
1148
|
+
const entitlementsImport = hasBillingModule
|
|
1149
|
+
? `import { EntitlementsProvider } from "@lastbrain-labs/module-billing-pro";`
|
|
1150
|
+
: "";
|
|
1151
|
+
|
|
1152
|
+
// Prop conditionnel pour EntitlementsProviderComponent
|
|
1153
|
+
const entitlementsProp = hasBillingModule
|
|
1154
|
+
? `EntitlementsProviderComponent={EntitlementsProvider}`
|
|
1155
|
+
: "";
|
|
1156
|
+
|
|
1094
1157
|
const appProvidersContent = `"use client";
|
|
1095
1158
|
|
|
1096
1159
|
import { AppProviders as BaseAppProviders } from "@lastbrain/app";
|
|
1097
|
-
|
|
1160
|
+
${entitlementsImport}
|
|
1161
|
+
import { realtimeConfig } from "../config/realtime";
|
|
1098
1162
|
import type { ReactNode } from "react";
|
|
1099
1163
|
import type { Language } from "@lastbrain/core";
|
|
1100
1164
|
|
|
1101
1165
|
export function AppProviders({
|
|
1102
|
-
children,
|
|
1166
|
+
children,
|
|
1103
1167
|
lang = "fr",
|
|
1104
1168
|
translations = {},
|
|
1105
1169
|
availableLanguages = ["fr", "en"],
|
|
@@ -1115,6 +1179,7 @@ export function AppProviders({
|
|
|
1115
1179
|
lang={lang}
|
|
1116
1180
|
translations={translations}
|
|
1117
1181
|
availableLanguages={availableLanguages}
|
|
1182
|
+
${entitlementsProp}
|
|
1118
1183
|
>
|
|
1119
1184
|
{children}
|
|
1120
1185
|
</BaseAppProviders>
|
|
@@ -1131,19 +1196,26 @@ export function AppProviders({
|
|
|
1131
1196
|
console.error(`❌ Error generating AppProviders component: ${error}`);
|
|
1132
1197
|
}
|
|
1133
1198
|
|
|
1134
|
-
// Générer AppHeader component
|
|
1135
|
-
const appHeaderPath = path.join(
|
|
1199
|
+
// Générer AppHeader component (dans components/ racine)
|
|
1200
|
+
const appHeaderPath = path.join(projectRoot, "components", "AppHeader.tsx");
|
|
1136
1201
|
const appHeaderContent = `"use client";
|
|
1137
1202
|
|
|
1138
1203
|
import { Header } from "@lastbrain/ui";
|
|
1139
|
-
import { LanguageSwitcher } from "@lastbrain/app";
|
|
1140
|
-
import { menuConfig } from "
|
|
1141
|
-
import {
|
|
1204
|
+
import { LanguageSwitcher, useAuth } from "@lastbrain/app";
|
|
1205
|
+
import { menuConfig } from "../config/menu";
|
|
1206
|
+
import { menuIgnored } from "../config/menu-ignored";
|
|
1207
|
+
import { LOCALE_CONFIG } from "../config/locales.generated";
|
|
1142
1208
|
|
|
1143
1209
|
export function AppHeader() {
|
|
1210
|
+
const { user, loading, isSuperAdmin } = useAuth();
|
|
1211
|
+
|
|
1144
1212
|
return (
|
|
1145
1213
|
<Header
|
|
1214
|
+
user={user}
|
|
1215
|
+
isSuperAdmin={isSuperAdmin}
|
|
1146
1216
|
menuConfig={menuConfig}
|
|
1217
|
+
accountMenu={menuConfig.account}
|
|
1218
|
+
menuIgnored={menuIgnored}
|
|
1147
1219
|
languageSwitcher={
|
|
1148
1220
|
<LanguageSwitcher
|
|
1149
1221
|
variant="minimal"
|
|
@@ -1212,7 +1284,7 @@ export default async function LangLayout({
|
|
|
1212
1284
|
|
|
1213
1285
|
return (
|
|
1214
1286
|
<ClientLayout lang={validLang} translations={translations} availableLanguages={[...LOCALE_CONFIG.languages]}>
|
|
1215
|
-
<div className="min-h-screen
|
|
1287
|
+
<div className="min-h-screen ">
|
|
1216
1288
|
{children}
|
|
1217
1289
|
</div>
|
|
1218
1290
|
</ClientLayout>
|
|
@@ -1243,6 +1315,7 @@ export default async function LangLayout({
|
|
|
1243
1315
|
|
|
1244
1316
|
import { AuthLayoutWithSidebar, langHref, useLanguage } from "@lastbrain/app";
|
|
1245
1317
|
import { menuConfig as fullMenuConfig } from "../../../config/menu";
|
|
1318
|
+
import { menuIgnored } from "../../../config/menu-ignored";
|
|
1246
1319
|
|
|
1247
1320
|
export default function SectionLayout({
|
|
1248
1321
|
children,
|
|
@@ -1263,7 +1336,7 @@ export default function SectionLayout({
|
|
|
1263
1336
|
};
|
|
1264
1337
|
|
|
1265
1338
|
return (
|
|
1266
|
-
<AuthLayoutWithSidebar menuConfig={menuConfig}>
|
|
1339
|
+
<AuthLayoutWithSidebar menuConfig={menuConfig} menuIgnored={menuIgnored}>
|
|
1267
1340
|
{children}
|
|
1268
1341
|
</AuthLayoutWithSidebar>
|
|
1269
1342
|
);
|
|
@@ -1279,6 +1352,39 @@ export default function SectionLayout({
|
|
|
1279
1352
|
console.error(`❌ Error generating auth layout: ${error}`);
|
|
1280
1353
|
}
|
|
1281
1354
|
|
|
1355
|
+
// Générer layout public avec footer
|
|
1356
|
+
const publicLayoutPath = path.join(
|
|
1357
|
+
appDirectory,
|
|
1358
|
+
"[lang]",
|
|
1359
|
+
"(public)",
|
|
1360
|
+
"layout.tsx"
|
|
1361
|
+
);
|
|
1362
|
+
const publicLayoutContent = `"use client";
|
|
1363
|
+
|
|
1364
|
+
import type React from "react";
|
|
1365
|
+
import { PublicLayout } from "@lastbrain/app";
|
|
1366
|
+
import { footerConfig } from "../../../config/footer";
|
|
1367
|
+
|
|
1368
|
+
export default function PublicSectionLayout({
|
|
1369
|
+
children,
|
|
1370
|
+
}: {
|
|
1371
|
+
children: React.ReactNode;
|
|
1372
|
+
}) {
|
|
1373
|
+
return <PublicLayout footerConfig={footerConfig}>
|
|
1374
|
+
{children}
|
|
1375
|
+
</PublicLayout>;
|
|
1376
|
+
}`;
|
|
1377
|
+
|
|
1378
|
+
try {
|
|
1379
|
+
writeScaffoldFile(
|
|
1380
|
+
publicLayoutPath,
|
|
1381
|
+
publicLayoutContent,
|
|
1382
|
+
"public layout with footer"
|
|
1383
|
+
);
|
|
1384
|
+
} catch (error) {
|
|
1385
|
+
console.error(`❌ Error generating public layout: ${error}`);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1282
1388
|
// Générer layout admin avec sidebar
|
|
1283
1389
|
const adminLayoutPath = path.join(
|
|
1284
1390
|
appDirectory,
|
|
@@ -1290,6 +1396,7 @@ export default function SectionLayout({
|
|
|
1290
1396
|
|
|
1291
1397
|
import { AdminLayoutWithSidebar, langHref, useLanguage } from "@lastbrain/app";
|
|
1292
1398
|
import { menuConfig as fullMenuConfig } from "../../../config/menu";
|
|
1399
|
+
import { menuIgnored } from "../../../config/menu-ignored";
|
|
1293
1400
|
|
|
1294
1401
|
export default function AdminLayout({
|
|
1295
1402
|
children,
|
|
@@ -1310,7 +1417,7 @@ export default function AdminLayout({
|
|
|
1310
1417
|
};
|
|
1311
1418
|
|
|
1312
1419
|
return (
|
|
1313
|
-
<AdminLayoutWithSidebar menuConfig={menuConfig}>
|
|
1420
|
+
<AdminLayoutWithSidebar menuConfig={menuConfig} menuIgnored={menuIgnored}>
|
|
1314
1421
|
{children}
|
|
1315
1422
|
</AdminLayoutWithSidebar>
|
|
1316
1423
|
);
|
|
@@ -1414,10 +1521,12 @@ async function generateUserTabsConfig(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
1414
1521
|
} else {
|
|
1415
1522
|
// Générer les imports statiques (Next/dynamic pour chaque composant)
|
|
1416
1523
|
const importsForApp = userTabsConfigs
|
|
1417
|
-
.map(
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1524
|
+
.map((tab) => {
|
|
1525
|
+
// UserTokenTab est un Client Component, utiliser ssr: false
|
|
1526
|
+
const ssrMode =
|
|
1527
|
+
tab.componentExport === "UserTokenTab" ? "false" : "true";
|
|
1528
|
+
return `const ${tab.componentExport} = dynamic(() => import("${tab.moduleName}").then(mod => ({ default: mod.${tab.componentExport} })), { ssr: ${ssrMode} });`;
|
|
1529
|
+
})
|
|
1421
1530
|
.join("\n");
|
|
1422
1531
|
|
|
1423
1532
|
// Générer le tableau des tabs
|
|
@@ -1815,7 +1924,6 @@ export async function GET(
|
|
|
1815
1924
|
.createSignedUrl(storagePath, 3600); // 1 heure
|
|
1816
1925
|
|
|
1817
1926
|
if (error) {
|
|
1818
|
-
console.error(\`[storage] Error creating signed URL for public image:\`, error);
|
|
1819
1927
|
return new NextResponse("Not found", { status: 404 });
|
|
1820
1928
|
}
|
|
1821
1929
|
|
|
@@ -1836,16 +1944,23 @@ export async function GET(
|
|
|
1836
1944
|
|
|
1837
1945
|
// Cas spécial: si le chemin commence par /product/ ou /recipe/,
|
|
1838
1946
|
// c'est une image avec format court qui nécessite le préfixe userId
|
|
1947
|
+
// Sinon le chemin contient déjà le userId
|
|
1839
1948
|
let actualStoragePath = storagePath;
|
|
1840
|
-
|
|
1949
|
+
const pathParts = storagePath.split("/");
|
|
1841
1950
|
|
|
1842
|
-
|
|
1951
|
+
// Vérifier si le premier segment est un UUID (userId déjà présent)
|
|
1952
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1953
|
+
const firstSegmentIsUuid = uuidRegex.test(pathParts[0]);
|
|
1843
1954
|
|
|
1955
|
+
if (!firstSegmentIsUuid) {
|
|
1956
|
+
// Le chemin ne contient pas le userId, on l'ajoute
|
|
1957
|
+
actualStoragePath = \`\${user.id}/\${storagePath}\`;
|
|
1958
|
+
}
|
|
1844
1959
|
|
|
1845
1960
|
// Vérifier que l'utilisateur a accès à cette image
|
|
1846
1961
|
// Format: {userId}/recipe/{recipeId}/{filename} ou {userId}/product/{productId}/{filename}
|
|
1847
|
-
const
|
|
1848
|
-
const pathUserId =
|
|
1962
|
+
const actualPathParts = actualStoragePath.split("/");
|
|
1963
|
+
const pathUserId = actualPathParts[0];
|
|
1849
1964
|
|
|
1850
1965
|
if (pathUserId !== user.id) {
|
|
1851
1966
|
return new NextResponse("Forbidden", { status: 403 });
|
|
@@ -1854,17 +1969,31 @@ export async function GET(
|
|
|
1854
1969
|
// Créer une URL signée pour l'image privée
|
|
1855
1970
|
const { data, error } = await supabase.storage
|
|
1856
1971
|
.from(bucket)
|
|
1857
|
-
.createSignedUrl(actualStoragePath,
|
|
1972
|
+
.createSignedUrl(actualStoragePath, 60); // 1 minute seulement
|
|
1858
1973
|
|
|
1859
1974
|
if (error) {
|
|
1860
|
-
console.error(\`[storage] Error creating signed URL:\`, error);
|
|
1861
1975
|
return new NextResponse("Not found", { status: 404 });
|
|
1862
1976
|
}
|
|
1863
1977
|
|
|
1864
|
-
//
|
|
1865
|
-
|
|
1978
|
+
// Télécharger l'image et la retourner avec des headers no-cache
|
|
1979
|
+
const imageResponse = await fetch(data.signedUrl);
|
|
1980
|
+
if (!imageResponse.ok) {
|
|
1981
|
+
return new NextResponse("Not found", { status: 404 });
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
const imageBuffer = await imageResponse.arrayBuffer();
|
|
1985
|
+
const contentType = imageResponse.headers.get("content-type") || "image/jpeg";
|
|
1986
|
+
|
|
1987
|
+
return new NextResponse(imageBuffer, {
|
|
1988
|
+
status: 200,
|
|
1989
|
+
headers: {
|
|
1990
|
+
"Content-Type": contentType,
|
|
1991
|
+
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
1992
|
+
"Pragma": "no-cache",
|
|
1993
|
+
"Expires": "0",
|
|
1994
|
+
},
|
|
1995
|
+
});
|
|
1866
1996
|
} catch (error) {
|
|
1867
|
-
console.error("[storage] Error:", error);
|
|
1868
1997
|
return new NextResponse("Internal server error", { status: 500 });
|
|
1869
1998
|
}
|
|
1870
1999
|
}
|
|
@@ -1956,6 +2085,255 @@ export const footerConfig: FooterConfig = {
|
|
|
1956
2085
|
}
|
|
1957
2086
|
}
|
|
1958
2087
|
|
|
2088
|
+
/**
|
|
2089
|
+
* Génère les routes sitemap à partir des configurations des modules
|
|
2090
|
+
*/
|
|
2091
|
+
async function generateSitemapRoutes(moduleConfigs: ModuleBuildConfig[]) {
|
|
2092
|
+
try {
|
|
2093
|
+
// Collecter tous les sitemaps de tous les modules
|
|
2094
|
+
const allSitemaps: Array<{
|
|
2095
|
+
moduleConfig: ModuleBuildConfig;
|
|
2096
|
+
sitemap: ModuleSitemapConfig;
|
|
2097
|
+
}> = [];
|
|
2098
|
+
|
|
2099
|
+
moduleConfigs.forEach((config) => {
|
|
2100
|
+
if (config.sitemaps && config.sitemaps.length > 0) {
|
|
2101
|
+
config.sitemaps.forEach((sitemap) => {
|
|
2102
|
+
allSitemaps.push({ moduleConfig: config, sitemap });
|
|
2103
|
+
});
|
|
2104
|
+
}
|
|
2105
|
+
});
|
|
2106
|
+
|
|
2107
|
+
if (allSitemaps.length === 0) {
|
|
2108
|
+
if (isDebugMode) {
|
|
2109
|
+
console.log("⏭️ No sitemaps found, skipping sitemap generation");
|
|
2110
|
+
}
|
|
2111
|
+
return;
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
// Détecter les sitemaps index des modules (sitemap.xml ou sitemap-index)
|
|
2115
|
+
const moduleSitemapIndexes: string[] = [];
|
|
2116
|
+
|
|
2117
|
+
// Renommer les /sitemap.xml en /sitemap-{moduleName}.xml pour éviter les conflits
|
|
2118
|
+
allSitemaps.forEach(({ moduleConfig, sitemap }) => {
|
|
2119
|
+
if (
|
|
2120
|
+
sitemap.path === "/sitemap.xml" ||
|
|
2121
|
+
sitemap.path.endsWith("-index.xml")
|
|
2122
|
+
) {
|
|
2123
|
+
// Extraire le nom court du module (ex: module-blog -> blog)
|
|
2124
|
+
const moduleShortName = moduleConfig.moduleName
|
|
2125
|
+
.replace("@lastbrain/", "")
|
|
2126
|
+
.replace("@lastbrain-labs/", "")
|
|
2127
|
+
.replace("module-", "")
|
|
2128
|
+
.replace("module-core-", "");
|
|
2129
|
+
|
|
2130
|
+
// Renommer le path
|
|
2131
|
+
sitemap.path = `/sitemap-${moduleShortName}.xml`;
|
|
2132
|
+
moduleSitemapIndexes.push(sitemap.path);
|
|
2133
|
+
|
|
2134
|
+
if (isDebugMode) {
|
|
2135
|
+
console.log(
|
|
2136
|
+
`🔄 Renamed sitemap from /sitemap.xml to ${sitemap.path} for ${moduleConfig.moduleName}`
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
});
|
|
2141
|
+
|
|
2142
|
+
// Générer chaque route sitemap
|
|
2143
|
+
for (const { moduleConfig, sitemap } of allSitemaps) {
|
|
2144
|
+
const sitemapPath = sitemap.path;
|
|
2145
|
+
|
|
2146
|
+
// Déterminer le chemin du fichier route.ts
|
|
2147
|
+
let routeDir: string;
|
|
2148
|
+
if (sitemapPath === "/sitemap.xml") {
|
|
2149
|
+
// Route principale sitemap.xml à la racine de app
|
|
2150
|
+
routeDir = path.join(appDirectory, "sitemap.xml");
|
|
2151
|
+
} else {
|
|
2152
|
+
// Sous-sitemaps (ex: /sitemap/recipes.xml -> app/sitemap/recipes.xml)
|
|
2153
|
+
const segments = sitemapPath
|
|
2154
|
+
.replace(/^\//, "")
|
|
2155
|
+
.replace(/\.xml$/, ".xml")
|
|
2156
|
+
.split("/");
|
|
2157
|
+
routeDir = path.join(appDirectory, ...segments);
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
const routeFilePath = path.join(routeDir, "route.ts");
|
|
2161
|
+
ensureDirectory(routeDir);
|
|
2162
|
+
|
|
2163
|
+
// Déterminer le type de sitemap et générer le code approprié
|
|
2164
|
+
const entryPoint = sitemap.entryPoint;
|
|
2165
|
+
let content: string;
|
|
2166
|
+
|
|
2167
|
+
if (entryPoint === "sitemap/index") {
|
|
2168
|
+
// Sitemap index principal
|
|
2169
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD - Sitemap Index Route
|
|
2170
|
+
// Module: ${moduleConfig.moduleName}
|
|
2171
|
+
// Path: ${sitemapPath}
|
|
2172
|
+
import { generateSitemapIndex } from "${moduleConfig.moduleName}/sitemap";
|
|
2173
|
+
|
|
2174
|
+
export async function GET(): Promise<Response> {
|
|
2175
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || (process.env.VERCEL_URL ? \`https://\${process.env.VERCEL_URL}\` : "https://example.com");
|
|
2176
|
+
|
|
2177
|
+
// Liste des sous-sitemaps
|
|
2178
|
+
const sitemapPaths = [
|
|
2179
|
+
"/sitemap/pages.xml",
|
|
2180
|
+
"/sitemap/recipes.xml",
|
|
2181
|
+
"/sitemap/chefs.xml",
|
|
2182
|
+
];
|
|
2183
|
+
|
|
2184
|
+
const xml = generateSitemapIndex(baseUrl, sitemapPaths);
|
|
2185
|
+
|
|
2186
|
+
return new Response(xml, {
|
|
2187
|
+
headers: {
|
|
2188
|
+
"Content-Type": "application/xml",
|
|
2189
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600",
|
|
2190
|
+
},
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
`;
|
|
2194
|
+
} else if (entryPoint === "sitemap/pages") {
|
|
2195
|
+
// Sitemap des pages statiques
|
|
2196
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD - Static Pages Sitemap Route
|
|
2197
|
+
// Module: ${moduleConfig.moduleName}
|
|
2198
|
+
// Path: ${sitemapPath}
|
|
2199
|
+
import { generateStaticPagesSitemap, generateSitemapXml } from "${moduleConfig.moduleName}/sitemap";
|
|
2200
|
+
|
|
2201
|
+
export async function GET(): Promise<Response> {
|
|
2202
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || (process.env.VERCEL_URL ? \`https://\${process.env.VERCEL_URL}\` : "https://example.com");
|
|
2203
|
+
|
|
2204
|
+
const sitemap = await generateStaticPagesSitemap(baseUrl);
|
|
2205
|
+
const xml = generateSitemapXml(sitemap);
|
|
2206
|
+
|
|
2207
|
+
return new Response(xml, {
|
|
2208
|
+
headers: {
|
|
2209
|
+
"Content-Type": "application/xml",
|
|
2210
|
+
"Cache-Control": "public, max-age=86400, s-maxage=86400",
|
|
2211
|
+
},
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
`;
|
|
2215
|
+
} else if (entryPoint === "sitemap/recipes") {
|
|
2216
|
+
// Sitemap des recettes
|
|
2217
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD - Recipes Sitemap Route
|
|
2218
|
+
// Module: ${moduleConfig.moduleName}
|
|
2219
|
+
// Path: ${sitemapPath}
|
|
2220
|
+
import { generateRecipesSitemap, generateSitemapXml } from "${moduleConfig.moduleName}/sitemap";
|
|
2221
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
2222
|
+
|
|
2223
|
+
export async function GET(): Promise<Response> {
|
|
2224
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || (process.env.VERCEL_URL ? \`https://\${process.env.VERCEL_URL}\` : "https://example.com");
|
|
2225
|
+
const supabase = await getSupabaseServiceClient();
|
|
2226
|
+
|
|
2227
|
+
const sitemap = await generateRecipesSitemap(baseUrl, supabase);
|
|
2228
|
+
const xml = generateSitemapXml(sitemap);
|
|
2229
|
+
|
|
2230
|
+
return new Response(xml, {
|
|
2231
|
+
headers: {
|
|
2232
|
+
"Content-Type": "application/xml",
|
|
2233
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600",
|
|
2234
|
+
},
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2237
|
+
`;
|
|
2238
|
+
} else if (entryPoint === "sitemap/chefs") {
|
|
2239
|
+
// Sitemap des chefs
|
|
2240
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD - Chefs Sitemap Route
|
|
2241
|
+
// Module: ${moduleConfig.moduleName}
|
|
2242
|
+
// Path: ${sitemapPath}
|
|
2243
|
+
import { generateChefsSitemap, generateSitemapXml } from "${moduleConfig.moduleName}/sitemap";
|
|
2244
|
+
import { getSupabaseServiceClient } from "@lastbrain/core/server";
|
|
2245
|
+
|
|
2246
|
+
export async function GET(): Promise<Response> {
|
|
2247
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || (process.env.VERCEL_URL ? \`https://\${process.env.VERCEL_URL}\` : "https://example.com");
|
|
2248
|
+
const supabase = await getSupabaseServiceClient();
|
|
2249
|
+
|
|
2250
|
+
const sitemap = await generateChefsSitemap(baseUrl, supabase);
|
|
2251
|
+
const xml = generateSitemapXml(sitemap);
|
|
2252
|
+
|
|
2253
|
+
return new Response(xml, {
|
|
2254
|
+
headers: {
|
|
2255
|
+
"Content-Type": "application/xml",
|
|
2256
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600",
|
|
2257
|
+
},
|
|
2258
|
+
});
|
|
2259
|
+
}
|
|
2260
|
+
`;
|
|
2261
|
+
} else {
|
|
2262
|
+
// Sitemap générique - export direct du handler
|
|
2263
|
+
content = `// GENERATED BY LASTBRAIN MODULE BUILD - Sitemap route
|
|
2264
|
+
// Module: ${moduleConfig.moduleName}
|
|
2265
|
+
// Path: ${sitemapPath}
|
|
2266
|
+
export { ${sitemap.handlerExport} } from "${moduleConfig.moduleName}/${sitemap.entryPoint}";
|
|
2267
|
+
`;
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
fs.writeFileSync(routeFilePath, content);
|
|
2271
|
+
|
|
2272
|
+
if (isDebugMode) {
|
|
2273
|
+
console.log(`🗺️ Generated sitemap route: ${routeFilePath}`);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
// Générer le sitemap.xml global qui agrège tous les sitemaps des modules
|
|
2278
|
+
if (moduleSitemapIndexes.length > 0) {
|
|
2279
|
+
const globalSitemapDir = path.join(appDirectory, "sitemap.xml");
|
|
2280
|
+
const globalSitemapFile = path.join(globalSitemapDir, "route.ts");
|
|
2281
|
+
ensureDirectory(globalSitemapDir);
|
|
2282
|
+
|
|
2283
|
+
const sitemapUrls = moduleSitemapIndexes
|
|
2284
|
+
.map((path) => {
|
|
2285
|
+
return ` {
|
|
2286
|
+
url: \`\${baseUrl}${path}\`,
|
|
2287
|
+
lastModified: new Date(),
|
|
2288
|
+
}`;
|
|
2289
|
+
})
|
|
2290
|
+
.join(",\n");
|
|
2291
|
+
|
|
2292
|
+
const globalSitemapContent = `// GENERATED BY LASTBRAIN MODULE BUILD - Global Sitemap Index
|
|
2293
|
+
// This file aggregates all module sitemaps
|
|
2294
|
+
// Module sitemaps: ${moduleSitemapIndexes.join(", ")}
|
|
2295
|
+
|
|
2296
|
+
export async function GET(): Promise<Response> {
|
|
2297
|
+
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || (process.env.VERCEL_URL ? \`https://\${process.env.VERCEL_URL}\` : "https://example.com");
|
|
2298
|
+
|
|
2299
|
+
const sitemapIndexXml = \`<?xml version="1.0" encoding="UTF-8"?>
|
|
2300
|
+
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
2301
|
+
${moduleSitemapIndexes
|
|
2302
|
+
.map(
|
|
2303
|
+
(path) => ` <sitemap>
|
|
2304
|
+
<loc>\${baseUrl}${path}</loc>
|
|
2305
|
+
<lastmod>\${new Date().toISOString()}</lastmod>
|
|
2306
|
+
</sitemap>`
|
|
2307
|
+
)
|
|
2308
|
+
.join("\n")}
|
|
2309
|
+
</sitemapindex>\`;
|
|
2310
|
+
|
|
2311
|
+
return new Response(sitemapIndexXml, {
|
|
2312
|
+
headers: {
|
|
2313
|
+
"Content-Type": "application/xml",
|
|
2314
|
+
"Cache-Control": "public, max-age=3600, s-maxage=3600",
|
|
2315
|
+
},
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
`;
|
|
2319
|
+
|
|
2320
|
+
fs.writeFileSync(globalSitemapFile, globalSitemapContent);
|
|
2321
|
+
|
|
2322
|
+
if (isDebugMode) {
|
|
2323
|
+
console.log(
|
|
2324
|
+
`🌍 Generated global sitemap.xml aggregating ${moduleSitemapIndexes.length} module sitemap(s)`
|
|
2325
|
+
);
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
if (isDebugMode) {
|
|
2330
|
+
console.log(`✅ Generated ${allSitemaps.length} sitemap route(s)`);
|
|
2331
|
+
}
|
|
2332
|
+
} catch (error) {
|
|
2333
|
+
console.error("❌ Error generating sitemap routes:", error);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
|
|
1959
2337
|
/**
|
|
1960
2338
|
* Génère les fichiers i18n en concaténant les traductions des modules
|
|
1961
2339
|
* et en incluant les traductions personnalisées de i18n/default
|
|
@@ -1964,22 +2342,6 @@ async function generateLocaleConfig() {
|
|
|
1964
2342
|
const appI18nDir = path.join(projectRoot, "i18n");
|
|
1965
2343
|
const appConfigDir = path.join(projectRoot, "config");
|
|
1966
2344
|
|
|
1967
|
-
// Mapping standard des codes de langue vers les locales
|
|
1968
|
-
const LOCALE_MAP: Record<string, string> = {
|
|
1969
|
-
fr: "fr_FR",
|
|
1970
|
-
en: "en_US",
|
|
1971
|
-
es: "es_ES",
|
|
1972
|
-
de: "de_DE",
|
|
1973
|
-
it: "it_IT",
|
|
1974
|
-
pt: "pt_PT",
|
|
1975
|
-
nl: "nl_NL",
|
|
1976
|
-
ru: "ru_RU",
|
|
1977
|
-
ja: "ja_JP",
|
|
1978
|
-
zh: "zh_CN",
|
|
1979
|
-
ar: "ar_SA",
|
|
1980
|
-
hi: "hi_IN",
|
|
1981
|
-
};
|
|
1982
|
-
|
|
1983
2345
|
try {
|
|
1984
2346
|
// Scanner les fichiers i18n disponibles
|
|
1985
2347
|
let languages: string[] = ["fr"]; // Fallback par défaut
|
|
@@ -2255,7 +2617,7 @@ function generateHomePage(moduleConfigs: ModuleBuildConfig[]) {
|
|
|
2255
2617
|
const content = `// GENERATED BY LASTBRAIN MODULE BUILD
|
|
2256
2618
|
// Homepage from ${homePageModule.moduleName}
|
|
2257
2619
|
import { ${importComponentName} as ModuleHomePage${homePageConfig.metadataExport ? `, ${metadataImportAlias}` : ""} } from "${importPath}";
|
|
2258
|
-
|
|
2620
|
+
|
|
2259
2621
|
import { LOCALE_CONFIG } from "../../../config/locales.generated";
|
|
2260
2622
|
${
|
|
2261
2623
|
homePageConfig.metadataExport
|
|
@@ -2264,8 +2626,12 @@ ${
|
|
|
2264
2626
|
}`
|
|
2265
2627
|
: ""
|
|
2266
2628
|
}
|
|
2267
|
-
export default function ${wrapperComponentName}(
|
|
2268
|
-
|
|
2629
|
+
export default function ${wrapperComponentName}({
|
|
2630
|
+
params,
|
|
2631
|
+
}: {
|
|
2632
|
+
params: Promise<{ lang: string }>;
|
|
2633
|
+
}) {
|
|
2634
|
+
return <ModuleHomePage params={params} localeConfig={LOCALE_CONFIG} />;
|
|
2269
2635
|
}
|
|
2270
2636
|
`;
|
|
2271
2637
|
|
|
@@ -2342,8 +2708,9 @@ export async function runModuleBuild() {
|
|
|
2342
2708
|
dumpNavigation();
|
|
2343
2709
|
generateMenuConfig(moduleConfigs);
|
|
2344
2710
|
generateDocsPage(moduleConfigs);
|
|
2345
|
-
|
|
2346
|
-
|
|
2711
|
+
// Note: AppAside, AppHeader, etc. sont maintenant générés dans components/ à la racine
|
|
2712
|
+
// et non plus dupliqués dans app/components/
|
|
2713
|
+
generateLayouts(moduleConfigs);
|
|
2347
2714
|
copyModuleMigrations(moduleConfigs);
|
|
2348
2715
|
|
|
2349
2716
|
// Générer la page d'accueil "/" si un module la définit
|
|
@@ -2388,6 +2755,25 @@ export async function runModuleBuild() {
|
|
|
2388
2755
|
}
|
|
2389
2756
|
await generateFooterConfig(moduleConfigs);
|
|
2390
2757
|
|
|
2758
|
+
// Générer les routes sitemap (ancien système)
|
|
2759
|
+
if (isDebugMode) {
|
|
2760
|
+
console.log("🗺️ Generating sitemap routes (legacy)...");
|
|
2761
|
+
}
|
|
2762
|
+
//await generateSitemapRoutes(moduleConfigs);
|
|
2763
|
+
|
|
2764
|
+
// Générer les sitemaps basés sur manifests (nouveau système)
|
|
2765
|
+
if (isDebugMode) {
|
|
2766
|
+
console.log("🗺️ Generating manifest-based sitemaps...");
|
|
2767
|
+
}
|
|
2768
|
+
const availableLanguages = Object.keys(LOCALE_MAP);
|
|
2769
|
+
await generateManifestBasedSitemaps(
|
|
2770
|
+
appDirectory,
|
|
2771
|
+
moduleConfigs,
|
|
2772
|
+
availableLanguages,
|
|
2773
|
+
projectRequire,
|
|
2774
|
+
isDebugMode
|
|
2775
|
+
);
|
|
2776
|
+
|
|
2391
2777
|
// Générer les fichiers i18n
|
|
2392
2778
|
if (isDebugMode) {
|
|
2393
2779
|
console.log("🌍 Building i18n files...");
|