@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.
Files changed (81) hide show
  1. package/dist/analytics/registry.d.ts +7 -0
  2. package/dist/analytics/registry.d.ts.map +1 -0
  3. package/dist/analytics/registry.js +11 -0
  4. package/dist/auth/useAuthSession.d.ts.map +1 -1
  5. package/dist/auth/useAuthSession.js +85 -1
  6. package/dist/cli.js +19 -3
  7. package/dist/components/LanguageSwitcher.d.ts +3 -1
  8. package/dist/components/LanguageSwitcher.d.ts.map +1 -1
  9. package/dist/components/LanguageSwitcher.js +134 -21
  10. package/dist/config/version.d.ts.map +1 -1
  11. package/dist/config/version.js +30 -19
  12. package/dist/i18n/server-lang.d.ts +1 -1
  13. package/dist/i18n/server-lang.d.ts.map +1 -1
  14. package/dist/i18n/types.d.ts +1 -1
  15. package/dist/i18n/types.d.ts.map +1 -1
  16. package/dist/i18n/useLink.d.ts.map +1 -1
  17. package/dist/i18n/useLink.js +15 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +4 -0
  21. package/dist/layouts/AdminLayoutWithSidebar.d.ts +3 -1
  22. package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -1
  23. package/dist/layouts/AdminLayoutWithSidebar.js +2 -2
  24. package/dist/layouts/AppProviders.d.ts +9 -1
  25. package/dist/layouts/AppProviders.d.ts.map +1 -1
  26. package/dist/layouts/AppProviders.js +24 -3
  27. package/dist/layouts/AuthLayout.js +1 -1
  28. package/dist/layouts/PublicLayout.js +1 -1
  29. package/dist/layouts/RootLayout.d.ts.map +1 -1
  30. package/dist/scripts/init-app.d.ts.map +1 -1
  31. package/dist/scripts/init-app.js +343 -138
  32. package/dist/scripts/module-build.d.ts.map +1 -1
  33. package/dist/scripts/module-build.js +784 -59
  34. package/dist/scripts/module-create.d.ts.map +1 -1
  35. package/dist/scripts/module-create.js +227 -10
  36. package/dist/scripts/sitemap-flat-generator.d.ts +39 -0
  37. package/dist/scripts/sitemap-flat-generator.d.ts.map +1 -0
  38. package/dist/scripts/sitemap-flat-generator.js +231 -0
  39. package/dist/scripts/sitemap-manifest-generator.d.ts +59 -0
  40. package/dist/scripts/sitemap-manifest-generator.d.ts.map +1 -0
  41. package/dist/scripts/sitemap-manifest-generator.js +290 -0
  42. package/dist/sitemap/manifest.d.ts +8 -0
  43. package/dist/sitemap/manifest.d.ts.map +1 -0
  44. package/dist/sitemap/manifest.js +6 -0
  45. package/dist/styles.css +2 -2
  46. package/dist/templates/AuthGuidePage.js +2 -0
  47. package/dist/templates/DefaultDoc.d.ts.map +1 -1
  48. package/dist/templates/DefaultDoc.js +9 -5
  49. package/dist/templates/DocPage.d.ts.map +1 -1
  50. package/dist/templates/DocPage.js +40 -0
  51. package/dist/templates/MigrationsGuidePage.js +2 -0
  52. package/dist/templates/ModuleGuidePage.d.ts.map +1 -1
  53. package/dist/templates/ModuleGuidePage.js +4 -1
  54. package/dist/templates/SimpleHomePage.js +2 -0
  55. package/package.json +31 -26
  56. package/src/analytics/registry.ts +14 -0
  57. package/src/auth/useAuthSession.ts +91 -1
  58. package/src/cli.ts +19 -3
  59. package/src/components/LanguageSwitcher.tsx +183 -60
  60. package/src/config/version.ts +30 -19
  61. package/src/i18n/server-lang.ts +2 -1
  62. package/src/i18n/types.ts +2 -1
  63. package/src/i18n/useLink.ts +15 -0
  64. package/src/index.ts +17 -0
  65. package/src/layouts/AdminLayoutWithSidebar.tsx +4 -0
  66. package/src/layouts/AppProviders.tsx +74 -9
  67. package/src/layouts/AuthLayout.tsx +1 -1
  68. package/src/layouts/PublicLayout.tsx +1 -1
  69. package/src/layouts/RootLayout.tsx +0 -1
  70. package/src/scripts/init-app.ts +418 -149
  71. package/src/scripts/module-build.ts +923 -63
  72. package/src/scripts/module-create.ts +260 -10
  73. package/src/scripts/sitemap-flat-generator.ts +313 -0
  74. package/src/scripts/sitemap-manifest-generator.ts +476 -0
  75. package/src/sitemap/manifest.ts +17 -0
  76. package/src/templates/AuthGuidePage.tsx +1 -1
  77. package/src/templates/DefaultDoc.tsx +397 -6
  78. package/src/templates/DocPage.tsx +40 -0
  79. package/src/templates/MigrationsGuidePage.tsx +1 -1
  80. package/src/templates/ModuleGuidePage.tsx +3 -2
  81. package/src/templates/SimpleHomePage.tsx +1 -1
@@ -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.local.example
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
- await createEnvLocal(targetDir, force, isMonorepoProject);
131
- await createEnvProd(targetDir, force);
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(chalk.white(" pnpm dev:local (ou pnpm dev:prod)\n"));
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(chalk.white(" 6. pnpm dev:local (ou pnpm dev:prod)\n"));
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(chalk.white(" • pnpm dev:local - Lance avec .env.local"));
299
- console.log(chalk.white(" • pnpm dev:prod - Lance avec .env.prod"));
300
- console.log(chalk.white(" • pnpm dev - Lance avec .env actuel\n"));
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
- // Utiliser les vraies versions au lieu de "latest"
410
- if (moduleInfo.package === "@lastbrain/module-ai") {
411
- requiredDeps[moduleInfo.package] = versions.moduleAi;
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
- // Pour les futurs modules, utiliser "latest" en attendant
414
- requiredDeps[moduleInfo.package] = "latest";
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 (racine)
564
- const homePagePath = path.join(appDir, "[lang]", "page.tsx");
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
- import { Footer } from "@lastbrain/ui";
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
- <Footer config={footerConfig} />
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 { createServerClient, type CookieOptions } from "@supabase/ssr";
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.local.example");
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.local.example..."));
2060
+ console.log(chalk.yellow("\n🔐 Création de .env.example..."));
1904
2061
 
1905
2062
  const envContent = `# Supabase Configuration
1906
- # Exécutez 'pnpm db:init' pour initialiser Supabase local et générer le vrai .env.local
2063
+ # Copiez ce fichier vers .env.local, .env.development ou .env.production selon vos besoins
1907
2064
 
1908
- # Supabase Local (par défaut)
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
- async function createEnvLocal(
1924
- targetDir: string,
1925
- force: boolean,
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
- } else {
1946
- // Pour les projets indépendants
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:local": "env-cmd -f .env.local next dev",
2068
- "dev:prod": "env-cmd -f .env.prod next 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 \\"**/*.{js,jsx,ts,tsx,json,md}\\"`,
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
+ }