@lastbrain/app 2.0.24 → 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 +3 -1
- package/dist/components/LanguageSwitcher.d.ts.map +1 -1
- package/dist/components/LanguageSwitcher.js +134 -21
- package/dist/config/version.d.ts.map +1 -1
- package/dist/config/version.js +30 -19
- package/dist/i18n/server-lang.d.ts +1 -1
- package/dist/i18n/server-lang.d.ts.map +1 -1
- package/dist/i18n/types.d.ts +1 -1
- package/dist/i18n/types.d.ts.map +1 -1
- 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 +9 -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 +343 -138
- package/dist/scripts/module-build.d.ts.map +1 -1
- package/dist/scripts/module-build.js +784 -59
- 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 +31 -26
- 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 +183 -60
- package/src/config/version.ts +30 -19
- package/src/i18n/server-lang.ts +2 -1
- package/src/i18n/types.ts +2 -1
- 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 +74 -9
- 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 +418 -149
- package/src/scripts/module-build.ts +923 -63
- 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
package/src/scripts/init-app.ts
CHANGED
|
@@ -124,11 +124,13 @@ export async function initApp(options: InitAppOptions) {
|
|
|
124
124
|
// 5. Créer le système de proxy storage
|
|
125
125
|
await createStorageProxy(targetDir, force);
|
|
126
126
|
|
|
127
|
-
// 6. Créer .gitignore et .env.
|
|
127
|
+
// 6. Créer .gitignore, vercel.json et .env.example
|
|
128
128
|
await createGitIgnore(targetDir, force);
|
|
129
|
+
await createVercelJson(targetDir, projectName, force);
|
|
129
130
|
await createEnvExample(targetDir, force);
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
|
|
132
|
+
// 6b. Créer les fichiers i18n/default pour les traductions personnalisées
|
|
133
|
+
await createI18nDefaults(targetDir, force, projectName);
|
|
132
134
|
|
|
133
135
|
// 7. Créer la structure Supabase avec migrations (seulement pour projets indépendants)
|
|
134
136
|
if (!isMonorepoProject) {
|
|
@@ -277,7 +279,9 @@ export async function initApp(options: InitAppOptions) {
|
|
|
277
279
|
if (!isMonorepoProject) {
|
|
278
280
|
console.log(chalk.white(" pnpm db:init"));
|
|
279
281
|
}
|
|
280
|
-
console.log(
|
|
282
|
+
console.log(
|
|
283
|
+
chalk.white(" pnpm dev:local (ou pnpm dev:dev / dev:prod)\n")
|
|
284
|
+
);
|
|
281
285
|
}
|
|
282
286
|
} else {
|
|
283
287
|
console.log(chalk.cyan("\n📋 Prochaines étapes:"));
|
|
@@ -292,12 +296,36 @@ export async function initApp(options: InitAppOptions) {
|
|
|
292
296
|
console.log(
|
|
293
297
|
chalk.white(" 5. pnpm db:init (initialiser la base de données)")
|
|
294
298
|
);
|
|
295
|
-
console.log(
|
|
299
|
+
console.log(
|
|
300
|
+
chalk.white(
|
|
301
|
+
" 6. Copier .env.example vers .env.local/.env.development/.env.production"
|
|
302
|
+
)
|
|
303
|
+
);
|
|
304
|
+
console.log(
|
|
305
|
+
chalk.white(" 7. pnpm dev:local (ou pnpm dev:dev / dev:prod)\n")
|
|
306
|
+
);
|
|
296
307
|
|
|
297
308
|
console.log(chalk.cyan("\n💡 Scripts disponibles:"));
|
|
298
|
-
console.log(
|
|
299
|
-
|
|
300
|
-
|
|
309
|
+
console.log(
|
|
310
|
+
chalk.white(
|
|
311
|
+
" • pnpm dev:local - Lance avec NODE_ENV=local (utilise .env.local)"
|
|
312
|
+
)
|
|
313
|
+
);
|
|
314
|
+
console.log(
|
|
315
|
+
chalk.white(
|
|
316
|
+
" • pnpm dev:dev - Lance avec NODE_ENV=development (utilise .env.development)"
|
|
317
|
+
)
|
|
318
|
+
);
|
|
319
|
+
console.log(
|
|
320
|
+
chalk.white(
|
|
321
|
+
" • pnpm dev:prod - Lance avec NODE_ENV=production (utilise .env.production)"
|
|
322
|
+
)
|
|
323
|
+
);
|
|
324
|
+
console.log(
|
|
325
|
+
chalk.white(
|
|
326
|
+
" • pnpm dev - Lance avec Next.js (utilise .env par défaut)\n"
|
|
327
|
+
)
|
|
328
|
+
);
|
|
301
329
|
|
|
302
330
|
console.log(chalk.gray("Prérequis pour Supabase :"));
|
|
303
331
|
console.log(chalk.white(" - Docker Desktop installé et lancé"));
|
|
@@ -406,12 +434,22 @@ async function addDependencies(
|
|
|
406
434
|
for (const moduleName of selectedModules) {
|
|
407
435
|
const moduleInfo = AVAILABLE_MODULES.find((m) => m.name === moduleName);
|
|
408
436
|
if (moduleInfo && moduleInfo.package !== "@lastbrain/module-auth") {
|
|
409
|
-
//
|
|
410
|
-
|
|
411
|
-
|
|
437
|
+
// Dans un monorepo, utiliser workspace:*
|
|
438
|
+
const targetIsInMonorepo =
|
|
439
|
+
fs.existsSync(
|
|
440
|
+
path.join(targetDir, "../../../packages/core/package.json")
|
|
441
|
+
) ||
|
|
442
|
+
fs.existsSync(path.join(targetDir, "../../packages/core/package.json"));
|
|
443
|
+
|
|
444
|
+
if (targetIsInMonorepo) {
|
|
445
|
+
requiredDeps[moduleInfo.package] = "workspace:*";
|
|
412
446
|
} else {
|
|
413
|
-
//
|
|
414
|
-
|
|
447
|
+
// Hors monorepo, utiliser les versions publiées
|
|
448
|
+
if (moduleInfo.package === "@lastbrain/module-ai") {
|
|
449
|
+
requiredDeps[moduleInfo.package] = versions.moduleAi;
|
|
450
|
+
} else {
|
|
451
|
+
requiredDeps[moduleInfo.package] = "latest";
|
|
452
|
+
}
|
|
415
453
|
}
|
|
416
454
|
}
|
|
417
455
|
}
|
|
@@ -560,26 +598,30 @@ export default function RootLayout({ children }: PropsWithChildren<{}>) {
|
|
|
560
598
|
console.log(chalk.green("✓ styles/globals.css créé"));
|
|
561
599
|
}
|
|
562
600
|
|
|
563
|
-
// Créer la page d'accueil publique (
|
|
564
|
-
const
|
|
601
|
+
// Créer la page d'accueil publique dans (public)
|
|
602
|
+
const langDir = path.join(appDir, "[lang]");
|
|
603
|
+
await fs.ensureDir(langDir);
|
|
604
|
+
const publicDir = path.join(langDir, "(public)");
|
|
605
|
+
await fs.ensureDir(publicDir);
|
|
606
|
+
const homePagePath = path.join(publicDir, "page.tsx");
|
|
565
607
|
if (!fs.existsSync(homePagePath) || force) {
|
|
566
608
|
const homePageContent = `// GENERATED BY LASTBRAIN APP-SHELL
|
|
609
|
+
"use client";
|
|
567
610
|
|
|
568
611
|
import { SimpleHomePage } from "@lastbrain/app";
|
|
569
|
-
|
|
570
|
-
import { footerConfig } from "../config/footer";
|
|
612
|
+
|
|
571
613
|
|
|
572
614
|
export default function RootPage() {
|
|
573
615
|
return (
|
|
574
616
|
<>
|
|
575
617
|
<SimpleHomePage showAuth={${withAuth}} />
|
|
576
|
-
|
|
618
|
+
|
|
577
619
|
</>
|
|
578
620
|
);
|
|
579
621
|
}
|
|
580
622
|
`;
|
|
581
623
|
await fs.writeFile(homePagePath, homePageContent);
|
|
582
|
-
console.log(chalk.green("✓ app/page.tsx créé"));
|
|
624
|
+
console.log(chalk.green("✓ app/[lang]/(public)/page.tsx créé"));
|
|
583
625
|
}
|
|
584
626
|
|
|
585
627
|
// Créer la page not-found.tsx
|
|
@@ -1031,63 +1073,47 @@ async function createConfigFiles(
|
|
|
1031
1073
|
const middlewarePath = path.join(targetDir, "middleware.ts");
|
|
1032
1074
|
if (!fs.existsSync(middlewarePath) || force) {
|
|
1033
1075
|
const middleware = `import { type NextRequest, NextResponse } from "next/server";
|
|
1034
|
-
import {
|
|
1035
|
-
|
|
1036
|
-
/**
|
|
1037
|
-
* Crée un client Supabase pour le middleware
|
|
1038
|
-
*/
|
|
1039
|
-
function createMiddlewareClient(request: NextRequest) {
|
|
1040
|
-
let response = NextResponse.next({
|
|
1041
|
-
request: {
|
|
1042
|
-
headers: request.headers,
|
|
1043
|
-
},
|
|
1044
|
-
});
|
|
1045
|
-
|
|
1046
|
-
const supabase = createServerClient(
|
|
1047
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
1048
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
1049
|
-
{
|
|
1050
|
-
cookies: {
|
|
1051
|
-
get(name: string) {
|
|
1052
|
-
return request.cookies.get(name)?.value;
|
|
1053
|
-
},
|
|
1054
|
-
set(name: string, value: string, options: CookieOptions) {
|
|
1055
|
-
request.cookies.set({
|
|
1056
|
-
name,
|
|
1057
|
-
value,
|
|
1058
|
-
...options,
|
|
1059
|
-
});
|
|
1060
|
-
response = NextResponse.next({
|
|
1061
|
-
request: {
|
|
1062
|
-
headers: request.headers,
|
|
1063
|
-
},
|
|
1064
|
-
});
|
|
1065
|
-
response.cookies.set({
|
|
1066
|
-
name,
|
|
1067
|
-
value,
|
|
1068
|
-
...options,
|
|
1069
|
-
});
|
|
1070
|
-
},
|
|
1071
|
-
remove(name: string, options: CookieOptions) {
|
|
1072
|
-
request.cookies.delete(name);
|
|
1073
|
-
response = NextResponse.next({
|
|
1074
|
-
request: {
|
|
1075
|
-
headers: request.headers,
|
|
1076
|
-
},
|
|
1077
|
-
});
|
|
1078
|
-
response.cookies.delete(name);
|
|
1079
|
-
},
|
|
1080
|
-
},
|
|
1081
|
-
}
|
|
1082
|
-
);
|
|
1083
|
-
|
|
1084
|
-
return { supabase, response };
|
|
1085
|
-
}
|
|
1076
|
+
import { createMiddlewareClient } from "@lastbrain/core/server";
|
|
1077
|
+
import { logger } from "@lastbrain/core";
|
|
1086
1078
|
|
|
1087
1079
|
export async function middleware(request: NextRequest) {
|
|
1088
1080
|
const { pathname } = request.nextUrl;
|
|
1089
1081
|
const isApi = pathname.startsWith("/api/");
|
|
1090
1082
|
|
|
1083
|
+
// Liste des locales supportées
|
|
1084
|
+
const supportedLocales = ["fr", "en", "es"];
|
|
1085
|
+
const defaultLocale = "fr";
|
|
1086
|
+
|
|
1087
|
+
// Special case: root path — redirect to default locale for better SEO
|
|
1088
|
+
if (pathname === "/") {
|
|
1089
|
+
const redirectUrl = new URL(\`/\${defaultLocale}\`, request.url);
|
|
1090
|
+
// preserve search params
|
|
1091
|
+
redirectUrl.search = request.nextUrl.search;
|
|
1092
|
+
return NextResponse.redirect(redirectUrl);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// Détecter la langue depuis l'URL (ex: /fr/... ou /es/...)
|
|
1096
|
+
const langMatch = pathname.match(/^\\/([a-z]{2})(\\/|$)/);
|
|
1097
|
+
const detectedLang = langMatch ? langMatch[1] : null;
|
|
1098
|
+
|
|
1099
|
+
// Utiliser la langue détectée ou la langue par défaut
|
|
1100
|
+
const currentLang =
|
|
1101
|
+
detectedLang && supportedLocales.includes(detectedLang)
|
|
1102
|
+
? detectedLang
|
|
1103
|
+
: defaultLocale;
|
|
1104
|
+
|
|
1105
|
+
// Créer la réponse de base avec les headers de langue
|
|
1106
|
+
const createResponseWithLang = (response: NextResponse) => {
|
|
1107
|
+
response.headers.set("x-locale", currentLang);
|
|
1108
|
+
response.headers.set("x-pathname", pathname);
|
|
1109
|
+
return response;
|
|
1110
|
+
};
|
|
1111
|
+
|
|
1112
|
+
// Extraire le chemin sans le préfixe de langue pour les vérifications
|
|
1113
|
+
const pathnameWithoutLang = detectedLang
|
|
1114
|
+
? pathname.replace(\`/\${detectedLang}\`, "") || "/"
|
|
1115
|
+
: pathname;
|
|
1116
|
+
|
|
1091
1117
|
// Pages publiques d'authentification (ne pas protéger)
|
|
1092
1118
|
const publicAuthPages = [
|
|
1093
1119
|
"/signin",
|
|
@@ -1095,6 +1121,8 @@ export async function middleware(request: NextRequest) {
|
|
|
1095
1121
|
"/reset-password",
|
|
1096
1122
|
"/forgot-password",
|
|
1097
1123
|
"/callback",
|
|
1124
|
+
"/confirm",
|
|
1125
|
+
"/auth-code-error",
|
|
1098
1126
|
];
|
|
1099
1127
|
|
|
1100
1128
|
if(process.env.MAINTENANCE_MODE === "true" && !pathname.startsWith("/maintenance")) {
|
|
@@ -1108,7 +1136,7 @@ export async function middleware(request: NextRequest) {
|
|
|
1108
1136
|
|
|
1109
1137
|
// Ne pas protéger les pages publiques d'authentification
|
|
1110
1138
|
if (isPublicAuthPage) {
|
|
1111
|
-
return NextResponse.next();
|
|
1139
|
+
return createResponseWithLang(NextResponse.next());
|
|
1112
1140
|
}
|
|
1113
1141
|
|
|
1114
1142
|
// Protéger les routes /auth/* (espace membre)
|
|
@@ -1200,7 +1228,7 @@ export async function middleware(request: NextRequest) {
|
|
|
1200
1228
|
}
|
|
1201
1229
|
}
|
|
1202
1230
|
|
|
1203
|
-
return NextResponse.next();
|
|
1231
|
+
return createResponseWithLang(NextResponse.next());
|
|
1204
1232
|
}
|
|
1205
1233
|
|
|
1206
1234
|
export const config = {
|
|
@@ -1588,6 +1616,22 @@ export default config;
|
|
|
1588
1616
|
console.log(chalk.green("✓ tsconfig.json créé"));
|
|
1589
1617
|
}
|
|
1590
1618
|
|
|
1619
|
+
// locale-helpers.ts - Re-export locale helpers from config
|
|
1620
|
+
const localeHelpersPath = path.join(targetDir, "locale-helpers.ts");
|
|
1621
|
+
if (!fs.existsSync(localeHelpersPath) || force) {
|
|
1622
|
+
const localeHelpers = `// Re-export locale helpers from config for easy import in packages
|
|
1623
|
+
export {
|
|
1624
|
+
langToLocale,
|
|
1625
|
+
getAlternateLocales,
|
|
1626
|
+
getLocaleMap,
|
|
1627
|
+
LOCALE_CONFIG,
|
|
1628
|
+
} from "./config/locales.generated";
|
|
1629
|
+
export type { LocaleConfig } from "./config/locales.generated";
|
|
1630
|
+
`;
|
|
1631
|
+
await fs.writeFile(localeHelpersPath, localeHelpers);
|
|
1632
|
+
console.log(chalk.green("✓ locale-helpers.ts créé"));
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1591
1635
|
// config/menu.ts
|
|
1592
1636
|
const configDir = path.join(targetDir, "config");
|
|
1593
1637
|
await fs.ensureDir(configDir);
|
|
@@ -1687,8 +1731,6 @@ export const menuCustom: MenuCustom = {
|
|
|
1687
1731
|
import type { FooterConfig } from "@lastbrain/ui";
|
|
1688
1732
|
|
|
1689
1733
|
export const footerConfig: FooterConfig = {
|
|
1690
|
-
companyName: "${projectName}",
|
|
1691
|
-
companyDescription: "Application LastBrain",
|
|
1692
1734
|
links: [],
|
|
1693
1735
|
social: [],
|
|
1694
1736
|
};
|
|
@@ -1820,6 +1862,89 @@ export default realtimeConfig;
|
|
|
1820
1862
|
await fs.writeFile(realtimePath, realtimeContent);
|
|
1821
1863
|
console.log(chalk.green("✓ config/realtime.ts placeholder créé"));
|
|
1822
1864
|
}
|
|
1865
|
+
|
|
1866
|
+
// Créer un placeholder pour locales.generated.ts qui sera généré par build:modules
|
|
1867
|
+
const localesGeneratedPath = path.join(
|
|
1868
|
+
targetDir,
|
|
1869
|
+
"config",
|
|
1870
|
+
"locales.generated.ts"
|
|
1871
|
+
);
|
|
1872
|
+
if (!fs.existsSync(localesGeneratedPath) || force) {
|
|
1873
|
+
const localesContent = `// GENERATED FILE - DO NOT EDIT MANUALLY
|
|
1874
|
+
// Ce fichier sera automatiquement généré lors du build:modules
|
|
1875
|
+
// Exécutez "pnpm build:modules" pour créer la configuration des locales
|
|
1876
|
+
|
|
1877
|
+
export const LOCALE_MAP: Record<string, string> = {
|
|
1878
|
+
fr: "Français",
|
|
1879
|
+
en: "English",
|
|
1880
|
+
es: "Español",
|
|
1881
|
+
de: "Deutsch",
|
|
1882
|
+
it: "Italiano",
|
|
1883
|
+
pt: "Português",
|
|
1884
|
+
nl: "Nederlands",
|
|
1885
|
+
ru: "Русский",
|
|
1886
|
+
ja: "日本語",
|
|
1887
|
+
zh: "中文",
|
|
1888
|
+
ar: "العربية",
|
|
1889
|
+
hi: "हिन्दी",
|
|
1890
|
+
};
|
|
1891
|
+
|
|
1892
|
+
export interface LocaleConfig {
|
|
1893
|
+
code: string;
|
|
1894
|
+
name: string;
|
|
1895
|
+
flag: string;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
export const LOCALE_CONFIG: LocaleConfig[] = Object.entries(LOCALE_MAP).map(
|
|
1899
|
+
([code, name]) => ({
|
|
1900
|
+
code,
|
|
1901
|
+
name,
|
|
1902
|
+
flag: code.toUpperCase(),
|
|
1903
|
+
})
|
|
1904
|
+
);
|
|
1905
|
+
|
|
1906
|
+
export function langToLocale(lang: string): string {
|
|
1907
|
+
return lang;
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
export function getAlternateLocales(currentLang: string): string[] {
|
|
1911
|
+
return Object.keys(LOCALE_MAP).filter((lang) => lang !== currentLang);
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
export function getLocaleMap(): Record<string, string> {
|
|
1915
|
+
return LOCALE_MAP;
|
|
1916
|
+
}
|
|
1917
|
+
`;
|
|
1918
|
+
await fs.writeFile(localesGeneratedPath, localesContent);
|
|
1919
|
+
console.log(chalk.green("✓ config/locales.generated.ts placeholder créé"));
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
// Créer un placeholder pour auth-dashboard.ts qui sera généré par build:modules
|
|
1923
|
+
const authDashboardPath = path.join(targetDir, "config", "auth-dashboard.ts");
|
|
1924
|
+
if (!fs.existsSync(authDashboardPath) || force) {
|
|
1925
|
+
const authDashboardContent = `// GENERATED FILE - DO NOT EDIT MANUALLY
|
|
1926
|
+
// Ce fichier sera automatiquement généré lors du build:modules
|
|
1927
|
+
// Exécutez "pnpm build:modules" pour créer la configuration auth dashboard
|
|
1928
|
+
|
|
1929
|
+
"use client";
|
|
1930
|
+
|
|
1931
|
+
import type React from "react";
|
|
1932
|
+
|
|
1933
|
+
export interface ModuleAuthDashboard {
|
|
1934
|
+
key: string;
|
|
1935
|
+
title: string;
|
|
1936
|
+
icon?: string;
|
|
1937
|
+
order?: number;
|
|
1938
|
+
component: React.ComponentType<Record<string, never>>;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
export const moduleAuthDashboards: ModuleAuthDashboard[] = [];
|
|
1942
|
+
|
|
1943
|
+
export default moduleAuthDashboards;
|
|
1944
|
+
`;
|
|
1945
|
+
await fs.writeFile(authDashboardPath, authDashboardContent);
|
|
1946
|
+
console.log(chalk.green("✓ config/auth-dashboard.ts placeholder créé"));
|
|
1947
|
+
}
|
|
1823
1948
|
}
|
|
1824
1949
|
|
|
1825
1950
|
async function createGitIgnore(targetDir: string, force: boolean) {
|
|
@@ -1896,16 +2021,48 @@ coverage/
|
|
|
1896
2021
|
}
|
|
1897
2022
|
}
|
|
1898
2023
|
|
|
2024
|
+
async function createVercelJson(
|
|
2025
|
+
targetDir: string,
|
|
2026
|
+
projectName: string,
|
|
2027
|
+
force: boolean
|
|
2028
|
+
) {
|
|
2029
|
+
const vercelJsonPath = path.join(targetDir, "vercel.json");
|
|
2030
|
+
|
|
2031
|
+
if (!fs.existsSync(vercelJsonPath) || force) {
|
|
2032
|
+
console.log(chalk.yellow("\n📝 Création de vercel.json..."));
|
|
2033
|
+
|
|
2034
|
+
const vercelConfig = {
|
|
2035
|
+
buildCommand: `cd ../.. && pnpm install && turbo run build --filter=${projectName}`,
|
|
2036
|
+
framework: "nextjs",
|
|
2037
|
+
functions: {
|
|
2038
|
+
"app/api/public/convert-pdf/route.ts": {
|
|
2039
|
+
maxDuration: 60,
|
|
2040
|
+
},
|
|
2041
|
+
"app/api/audits/convert-pdf/route.ts": {
|
|
2042
|
+
maxDuration: 60,
|
|
2043
|
+
},
|
|
2044
|
+
},
|
|
2045
|
+
};
|
|
2046
|
+
|
|
2047
|
+
await fs.writeFile(
|
|
2048
|
+
vercelJsonPath,
|
|
2049
|
+
JSON.stringify(vercelConfig, null, 2),
|
|
2050
|
+
"utf-8"
|
|
2051
|
+
);
|
|
2052
|
+
console.log(chalk.green("✓ vercel.json créé"));
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
|
|
1899
2056
|
async function createEnvExample(targetDir: string, force: boolean) {
|
|
1900
|
-
const envExamplePath = path.join(targetDir, ".env.
|
|
2057
|
+
const envExamplePath = path.join(targetDir, ".env.example");
|
|
1901
2058
|
|
|
1902
2059
|
if (!fs.existsSync(envExamplePath) || force) {
|
|
1903
|
-
console.log(chalk.yellow("\n🔐 Création de .env.
|
|
2060
|
+
console.log(chalk.yellow("\n🔐 Création de .env.example..."));
|
|
1904
2061
|
|
|
1905
2062
|
const envContent = `# Supabase Configuration
|
|
1906
|
-
#
|
|
2063
|
+
# Copiez ce fichier vers .env.local, .env.development ou .env.production selon vos besoins
|
|
1907
2064
|
|
|
1908
|
-
# Supabase Local (
|
|
2065
|
+
# Supabase Local (pour développement)
|
|
1909
2066
|
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
|
|
1910
2067
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_LOCAL_ANON_KEY
|
|
1911
2068
|
SUPABASE_SERVICE_ROLE_KEY=YOUR_LOCAL_SERVICE_ROLE_KEY
|
|
@@ -1914,75 +2071,13 @@ SUPABASE_SERVICE_ROLE_KEY=YOUR_LOCAL_SERVICE_ROLE_KEY
|
|
|
1914
2071
|
# NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
|
|
1915
2072
|
# NEXT_PUBLIC_SUPABASE_ANON_KEY=your_production_anon_key
|
|
1916
2073
|
# SUPABASE_SERVICE_ROLE_KEY=your_production_service_role_key
|
|
1917
|
-
`;
|
|
1918
|
-
await fs.writeFile(envExamplePath, envContent);
|
|
1919
|
-
console.log(chalk.green("✓ .env.local.example créé"));
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
2074
|
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
isMonorepoProject: boolean = false
|
|
1927
|
-
) {
|
|
1928
|
-
const envLocalPath = path.join(targetDir, ".env.local");
|
|
1929
|
-
|
|
1930
|
-
if (!fs.existsSync(envLocalPath) || force) {
|
|
1931
|
-
console.log(chalk.yellow("\n🔐 Création de .env.local..."));
|
|
1932
|
-
|
|
1933
|
-
let envContent: string;
|
|
1934
|
-
|
|
1935
|
-
if (isMonorepoProject) {
|
|
1936
|
-
// Pour les projets monorepo, utiliser les variables du monorepo
|
|
1937
|
-
envContent = `# Supabase Configuration (Monorepo - Centralisé)
|
|
1938
|
-
# Les variables Supabase sont gérées au niveau du monorepo
|
|
1939
|
-
|
|
1940
|
-
# OpenAI Configuration (clé factice pour éviter les erreurs de build)
|
|
1941
|
-
OPENAI_API_KEY=sk-fake-openai-key-for-development-replace-with-real-key
|
|
1942
|
-
|
|
1943
|
-
# Note: Les variables Supabase sont fournies par le monorepo parent
|
|
2075
|
+
# AI Gateway API Key (recommandé) ou OpenAI direct
|
|
2076
|
+
AI_GATEWAY_API_KEY=your-ai-gateway-key
|
|
2077
|
+
# OPENAI_API_KEY=sk-proj-your-openai-key
|
|
1944
2078
|
`;
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
envContent = `# Supabase Configuration
|
|
1948
|
-
# Valeurs par défaut pour le développement local
|
|
1949
|
-
|
|
1950
|
-
# Supabase Local (par défaut)
|
|
1951
|
-
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
|
|
1952
|
-
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGc...
|
|
1953
|
-
SUPABASE_SERVICE_ROLE_KEY=eyJhbGc...
|
|
1954
|
-
|
|
1955
|
-
# OpenAI Configuration (clé factice pour éviter les erreurs de build)
|
|
1956
|
-
OPENAI_API_KEY=sk-fake-openai-key-for-development-replace-with-real-key
|
|
1957
|
-
`;
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
|
-
await fs.writeFile(envLocalPath, envContent);
|
|
1961
|
-
console.log(chalk.green("✓ .env.local créé"));
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
|
|
1965
|
-
async function createEnvProd(targetDir: string, force: boolean) {
|
|
1966
|
-
const envProdPath = path.join(targetDir, ".env.prod");
|
|
1967
|
-
|
|
1968
|
-
if (!fs.existsSync(envProdPath) || force) {
|
|
1969
|
-
console.log(chalk.yellow("\n🔐 Création de .env.prod..."));
|
|
1970
|
-
|
|
1971
|
-
const envContent = `# Production Environment Configuration
|
|
1972
|
-
# Copy your production values here
|
|
1973
|
-
|
|
1974
|
-
# Supabase Production
|
|
1975
|
-
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
|
|
1976
|
-
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-prod-anon-key
|
|
1977
|
-
SUPABASE_SERVICE_ROLE_KEY=your-prod-service-role-key
|
|
1978
|
-
|
|
1979
|
-
# OpenAI Production
|
|
1980
|
-
OPENAI_API_KEY=sk-proj-your-prod-api-key
|
|
1981
|
-
|
|
1982
|
-
# Note: Update these values with your actual production credentials
|
|
1983
|
-
`;
|
|
1984
|
-
await fs.writeFile(envProdPath, envContent);
|
|
1985
|
-
console.log(chalk.green("✓ .env.prod créé"));
|
|
2079
|
+
await fs.writeFile(envExamplePath, envContent);
|
|
2080
|
+
console.log(chalk.green("✓ .env.example créé"));
|
|
1986
2081
|
}
|
|
1987
2082
|
}
|
|
1988
2083
|
|
|
@@ -2064,13 +2159,14 @@ async function addScriptsToPackageJson(targetDir: string) {
|
|
|
2064
2159
|
? "pnpm --filter @lastbrain/core build && pnpm --filter @lastbrain/ui build && pnpm --filter @lastbrain/app build && pnpm --filter @lastbrain/module-auth build && pnpm --filter @lastbrain/module-ai build"
|
|
2065
2160
|
: "echo 'No prebuild needed'",
|
|
2066
2161
|
dev: "next dev",
|
|
2067
|
-
"dev:
|
|
2068
|
-
"dev:
|
|
2162
|
+
"dev:dev": "NODE_ENV=development next dev --turbopack",
|
|
2163
|
+
"dev:local": "NODE_ENV=local next dev --turbopack",
|
|
2164
|
+
"dev:prod": "NODE_ENV=production next dev --turbopack",
|
|
2069
2165
|
build: "next build",
|
|
2070
2166
|
start: "next start",
|
|
2071
2167
|
lint: "next lint",
|
|
2072
2168
|
lastbrain: scriptsPrefix,
|
|
2073
|
-
"build:modules": `${scriptsPrefix} module:build && prettier --write
|
|
2169
|
+
"build:modules": `${scriptsPrefix} module:build && prettier --write "**/*.{js,jsx,ts,tsx,json,md}"`,
|
|
2074
2170
|
"db:migrations:sync": `${scriptsPrefix} db:migrations:sync`,
|
|
2075
2171
|
"db:init": `${scriptsPrefix} db:init`,
|
|
2076
2172
|
"readme:create": `${scriptsPrefix} readme:create`,
|
|
@@ -2497,3 +2593,176 @@ export async function GET(
|
|
|
2497
2593
|
|
|
2498
2594
|
console.log(chalk.green("✓ Système de proxy storage configuré"));
|
|
2499
2595
|
}
|
|
2596
|
+
|
|
2597
|
+
async function createI18nDefaults(
|
|
2598
|
+
targetDir: string,
|
|
2599
|
+
force: boolean,
|
|
2600
|
+
projectName: string
|
|
2601
|
+
) {
|
|
2602
|
+
console.log(chalk.blue("📝 Création des fichiers i18n/default...\n"));
|
|
2603
|
+
|
|
2604
|
+
const i18nDefaultDir = path.join(targetDir, "i18n", "default");
|
|
2605
|
+
await fs.ensureDir(i18nDefaultDir);
|
|
2606
|
+
|
|
2607
|
+
// Fichier FR
|
|
2608
|
+
const frPath = path.join(i18nDefaultDir, "fr.json");
|
|
2609
|
+
const frContent = {
|
|
2610
|
+
"app.name": projectName,
|
|
2611
|
+
"app.description": `Bienvenue dans ${projectName}`,
|
|
2612
|
+
"app.tagline": "Votre plateforme de gestion",
|
|
2613
|
+
"app.icon": "Sparkles",
|
|
2614
|
+
"app.twitter_creator": "@YourTwitter",
|
|
2615
|
+
"app.twitter_site": "@YourTwitter",
|
|
2616
|
+
"app.email.contact": "contact@example.com",
|
|
2617
|
+
"app.email.color.primary": "#3b82f6",
|
|
2618
|
+
"app.email.color.secondary": "#8b5cf6",
|
|
2619
|
+
|
|
2620
|
+
// Navigation
|
|
2621
|
+
"nav.home": "Accueil",
|
|
2622
|
+
"nav.dashboard": "Tableau de bord",
|
|
2623
|
+
"nav.profile": "Profil",
|
|
2624
|
+
"nav.settings": "Paramètres",
|
|
2625
|
+
"nav.admin": "Administration",
|
|
2626
|
+
"nav.logout": "Déconnexion",
|
|
2627
|
+
"nav.login": "Connexion",
|
|
2628
|
+
"nav.signup": "S'inscrire",
|
|
2629
|
+
|
|
2630
|
+
// Actions communes
|
|
2631
|
+
"action.save": "Enregistrer",
|
|
2632
|
+
"action.cancel": "Annuler",
|
|
2633
|
+
"action.delete": "Supprimer",
|
|
2634
|
+
"action.edit": "Modifier",
|
|
2635
|
+
"action.create": "Créer",
|
|
2636
|
+
"action.search": "Rechercher",
|
|
2637
|
+
"action.filter": "Filtrer",
|
|
2638
|
+
"action.export": "Exporter",
|
|
2639
|
+
"action.import": "Importer",
|
|
2640
|
+
"action.close": "Fermer",
|
|
2641
|
+
"action.confirm": "Confirmer",
|
|
2642
|
+
"action.back": "Retour",
|
|
2643
|
+
"action.next": "Suivant",
|
|
2644
|
+
"action.previous": "Précédent",
|
|
2645
|
+
"action.submit": "Soumettre",
|
|
2646
|
+
"action.reset": "Réinitialiser",
|
|
2647
|
+
|
|
2648
|
+
// Messages communs
|
|
2649
|
+
"message.loading": "Chargement...",
|
|
2650
|
+
"message.saving": "Enregistrement...",
|
|
2651
|
+
"message.success": "Opération réussie",
|
|
2652
|
+
"message.error": "Une erreur est survenue",
|
|
2653
|
+
"message.no_data": "Aucune donnée disponible",
|
|
2654
|
+
"message.confirm_delete":
|
|
2655
|
+
"Êtes-vous sûr de vouloir supprimer cet élément ?",
|
|
2656
|
+
"message.unsaved_changes": "Vous avez des modifications non enregistrées",
|
|
2657
|
+
|
|
2658
|
+
// Formulaires
|
|
2659
|
+
"form.required": "Ce champ est requis",
|
|
2660
|
+
"form.invalid_email": "Email invalide",
|
|
2661
|
+
"form.password_too_short": "Le mot de passe est trop court",
|
|
2662
|
+
"form.passwords_no_match": "Les mots de passe ne correspondent pas",
|
|
2663
|
+
|
|
2664
|
+
// Contact
|
|
2665
|
+
"contact.page.title": "Contactez-nous",
|
|
2666
|
+
"contact.page.subtitle": "Nous sommes à votre écoute",
|
|
2667
|
+
"contact.form.name": "Nom",
|
|
2668
|
+
"contact.form.name_placeholder": "Votre nom",
|
|
2669
|
+
"contact.form.email": "Email",
|
|
2670
|
+
"contact.form.email_placeholder": "votre@email.fr",
|
|
2671
|
+
"contact.form.subject": "Sujet",
|
|
2672
|
+
"contact.form.message": "Message",
|
|
2673
|
+
"contact.form.message_placeholder": "Votre message...",
|
|
2674
|
+
"contact.form.submit": "Envoyer",
|
|
2675
|
+
"contact.success.title": "Message envoyé !",
|
|
2676
|
+
"contact.success.description":
|
|
2677
|
+
"Nous vous répondrons dans les plus brefs délais",
|
|
2678
|
+
};
|
|
2679
|
+
|
|
2680
|
+
if (!fs.existsSync(frPath) || force) {
|
|
2681
|
+
await fs.writeJson(frPath, frContent, { spaces: 2 });
|
|
2682
|
+
console.log(chalk.green(`✓ i18n/default/fr.json créé`));
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
// Fichier EN
|
|
2686
|
+
const enPath = path.join(i18nDefaultDir, "en.json");
|
|
2687
|
+
const enContent = {
|
|
2688
|
+
"app.name": projectName,
|
|
2689
|
+
"app.description": `Welcome to ${projectName}`,
|
|
2690
|
+
"app.tagline": "Your management platform",
|
|
2691
|
+
"app.icon": "Sparkles",
|
|
2692
|
+
"app.twitter_creator": "@YourTwitter",
|
|
2693
|
+
"app.twitter_site": "@YourTwitter",
|
|
2694
|
+
"app.email.contact": "contact@example.com",
|
|
2695
|
+
"app.email.color.primary": "#3b82f6",
|
|
2696
|
+
"app.email.color.secondary": "#8b5cf6",
|
|
2697
|
+
|
|
2698
|
+
// Navigation
|
|
2699
|
+
"nav.home": "Home",
|
|
2700
|
+
"nav.dashboard": "Dashboard",
|
|
2701
|
+
"nav.profile": "Profile",
|
|
2702
|
+
"nav.settings": "Settings",
|
|
2703
|
+
"nav.admin": "Administration",
|
|
2704
|
+
"nav.logout": "Logout",
|
|
2705
|
+
"nav.login": "Login",
|
|
2706
|
+
"nav.signup": "Sign up",
|
|
2707
|
+
|
|
2708
|
+
// Common actions
|
|
2709
|
+
"action.save": "Save",
|
|
2710
|
+
"action.cancel": "Cancel",
|
|
2711
|
+
"action.delete": "Delete",
|
|
2712
|
+
"action.edit": "Edit",
|
|
2713
|
+
"action.create": "Create",
|
|
2714
|
+
"action.search": "Search",
|
|
2715
|
+
"action.filter": "Filter",
|
|
2716
|
+
"action.export": "Export",
|
|
2717
|
+
"action.import": "Import",
|
|
2718
|
+
"action.close": "Close",
|
|
2719
|
+
"action.confirm": "Confirm",
|
|
2720
|
+
"action.back": "Back",
|
|
2721
|
+
"action.next": "Next",
|
|
2722
|
+
"action.previous": "Previous",
|
|
2723
|
+
"action.submit": "Submit",
|
|
2724
|
+
"action.reset": "Reset",
|
|
2725
|
+
|
|
2726
|
+
// Common messages
|
|
2727
|
+
"message.loading": "Loading...",
|
|
2728
|
+
"message.saving": "Saving...",
|
|
2729
|
+
"message.success": "Operation successful",
|
|
2730
|
+
"message.error": "An error occurred",
|
|
2731
|
+
"message.no_data": "No data available",
|
|
2732
|
+
"message.confirm_delete": "Are you sure you want to delete this item?",
|
|
2733
|
+
"message.unsaved_changes": "You have unsaved changes",
|
|
2734
|
+
|
|
2735
|
+
// Forms
|
|
2736
|
+
"form.required": "This field is required",
|
|
2737
|
+
"form.invalid_email": "Invalid email",
|
|
2738
|
+
"form.password_too_short": "Password is too short",
|
|
2739
|
+
"form.passwords_no_match": "Passwords do not match",
|
|
2740
|
+
|
|
2741
|
+
// Contact
|
|
2742
|
+
"contact.page.title": "Contact Us",
|
|
2743
|
+
"contact.page.subtitle": "We're here to help",
|
|
2744
|
+
"contact.form.name": "Name",
|
|
2745
|
+
"contact.form.name_placeholder": "Your name",
|
|
2746
|
+
"contact.form.email": "Email",
|
|
2747
|
+
"contact.form.email_placeholder": "your@email.com",
|
|
2748
|
+
"contact.form.subject": "Subject",
|
|
2749
|
+
"contact.form.message": "Message",
|
|
2750
|
+
"contact.form.message_placeholder": "Your message...",
|
|
2751
|
+
"contact.form.submit": "Send",
|
|
2752
|
+
"contact.success.title": "Message sent!",
|
|
2753
|
+
"contact.success.description":
|
|
2754
|
+
"We will get back to you as soon as possible",
|
|
2755
|
+
};
|
|
2756
|
+
|
|
2757
|
+
if (!fs.existsSync(enPath) || force) {
|
|
2758
|
+
await fs.writeJson(enPath, enContent, { spaces: 2 });
|
|
2759
|
+
console.log(chalk.green(`✓ i18n/default/en.json créé`));
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
console.log(
|
|
2763
|
+
chalk.yellow(
|
|
2764
|
+
"\n💡 Vous pouvez personnaliser ces fichiers pour votre application"
|
|
2765
|
+
)
|
|
2766
|
+
);
|
|
2767
|
+
console.log(chalk.gray(" Chemin: i18n/default/{fr,en}.json\n"));
|
|
2768
|
+
}
|