@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.
Files changed (74) 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.map +1 -1
  8. package/dist/components/LanguageSwitcher.js +89 -5
  9. package/dist/config/version.d.ts.map +1 -1
  10. package/dist/config/version.js +30 -19
  11. package/dist/i18n/useLink.d.ts.map +1 -1
  12. package/dist/i18n/useLink.js +15 -0
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +4 -0
  16. package/dist/layouts/AdminLayoutWithSidebar.d.ts +3 -1
  17. package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -1
  18. package/dist/layouts/AdminLayoutWithSidebar.js +2 -2
  19. package/dist/layouts/AppProviders.d.ts +7 -1
  20. package/dist/layouts/AppProviders.d.ts.map +1 -1
  21. package/dist/layouts/AppProviders.js +24 -3
  22. package/dist/layouts/AuthLayout.js +1 -1
  23. package/dist/layouts/PublicLayout.js +1 -1
  24. package/dist/layouts/RootLayout.d.ts.map +1 -1
  25. package/dist/scripts/init-app.d.ts.map +1 -1
  26. package/dist/scripts/init-app.js +301 -138
  27. package/dist/scripts/module-build.d.ts.map +1 -1
  28. package/dist/scripts/module-build.js +402 -67
  29. package/dist/scripts/module-create.d.ts.map +1 -1
  30. package/dist/scripts/module-create.js +227 -10
  31. package/dist/scripts/sitemap-flat-generator.d.ts +39 -0
  32. package/dist/scripts/sitemap-flat-generator.d.ts.map +1 -0
  33. package/dist/scripts/sitemap-flat-generator.js +231 -0
  34. package/dist/scripts/sitemap-manifest-generator.d.ts +59 -0
  35. package/dist/scripts/sitemap-manifest-generator.d.ts.map +1 -0
  36. package/dist/scripts/sitemap-manifest-generator.js +290 -0
  37. package/dist/sitemap/manifest.d.ts +8 -0
  38. package/dist/sitemap/manifest.d.ts.map +1 -0
  39. package/dist/sitemap/manifest.js +6 -0
  40. package/dist/styles.css +2 -2
  41. package/dist/templates/AuthGuidePage.js +2 -0
  42. package/dist/templates/DefaultDoc.d.ts.map +1 -1
  43. package/dist/templates/DefaultDoc.js +9 -5
  44. package/dist/templates/DocPage.d.ts.map +1 -1
  45. package/dist/templates/DocPage.js +40 -0
  46. package/dist/templates/MigrationsGuidePage.js +2 -0
  47. package/dist/templates/ModuleGuidePage.d.ts.map +1 -1
  48. package/dist/templates/ModuleGuidePage.js +4 -1
  49. package/dist/templates/SimpleHomePage.js +2 -0
  50. package/package.json +11 -4
  51. package/src/analytics/registry.ts +14 -0
  52. package/src/auth/useAuthSession.ts +91 -1
  53. package/src/cli.ts +19 -3
  54. package/src/components/LanguageSwitcher.tsx +113 -23
  55. package/src/config/version.ts +30 -19
  56. package/src/i18n/useLink.ts +15 -0
  57. package/src/index.ts +17 -0
  58. package/src/layouts/AdminLayoutWithSidebar.tsx +4 -0
  59. package/src/layouts/AppProviders.tsx +66 -8
  60. package/src/layouts/AuthLayout.tsx +1 -1
  61. package/src/layouts/PublicLayout.tsx +1 -1
  62. package/src/layouts/RootLayout.tsx +0 -1
  63. package/src/scripts/init-app.ts +360 -149
  64. package/src/scripts/module-build.ts +458 -72
  65. package/src/scripts/module-create.ts +260 -10
  66. package/src/scripts/sitemap-flat-generator.ts +313 -0
  67. package/src/scripts/sitemap-manifest-generator.ts +476 -0
  68. package/src/sitemap/manifest.ts +17 -0
  69. package/src/templates/AuthGuidePage.tsx +1 -1
  70. package/src/templates/DefaultDoc.tsx +397 -6
  71. package/src/templates/DocPage.tsx +40 -0
  72. package/src/templates/MigrationsGuidePage.tsx +1 -1
  73. package/src/templates/ModuleGuidePage.tsx +3 -2
  74. package/src/templates/SimpleHomePage.tsx +1 -1
@@ -124,11 +124,10 @@ 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);
132
131
 
133
132
  // 6b. Créer les fichiers i18n/default pour les traductions personnalisées
134
133
  await createI18nDefaults(targetDir, force, projectName);
@@ -280,7 +279,9 @@ export async function initApp(options: InitAppOptions) {
280
279
  if (!isMonorepoProject) {
281
280
  console.log(chalk.white(" pnpm db:init"));
282
281
  }
283
- 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
+ );
284
285
  }
285
286
  } else {
286
287
  console.log(chalk.cyan("\n📋 Prochaines étapes:"));
@@ -295,12 +296,36 @@ export async function initApp(options: InitAppOptions) {
295
296
  console.log(
296
297
  chalk.white(" 5. pnpm db:init (initialiser la base de données)")
297
298
  );
298
- 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
+ );
299
307
 
300
308
  console.log(chalk.cyan("\n💡 Scripts disponibles:"));
301
- console.log(chalk.white(" • pnpm dev:local - Lance avec .env.local"));
302
- console.log(chalk.white(" • pnpm dev:prod - Lance avec .env.prod"));
303
- 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
+ );
304
329
 
305
330
  console.log(chalk.gray("Prérequis pour Supabase :"));
306
331
  console.log(chalk.white(" - Docker Desktop installé et lancé"));
@@ -409,12 +434,22 @@ async function addDependencies(
409
434
  for (const moduleName of selectedModules) {
410
435
  const moduleInfo = AVAILABLE_MODULES.find((m) => m.name === moduleName);
411
436
  if (moduleInfo && moduleInfo.package !== "@lastbrain/module-auth") {
412
- // Utiliser les vraies versions au lieu de "latest"
413
- if (moduleInfo.package === "@lastbrain/module-ai") {
414
- 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:*";
415
446
  } else {
416
- // Pour les futurs modules, utiliser "latest" en attendant
417
- 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
+ }
418
453
  }
419
454
  }
420
455
  }
@@ -563,26 +598,30 @@ export default function RootLayout({ children }: PropsWithChildren<{}>) {
563
598
  console.log(chalk.green("✓ styles/globals.css créé"));
564
599
  }
565
600
 
566
- // Créer la page d'accueil publique (racine)
567
- 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");
568
607
  if (!fs.existsSync(homePagePath) || force) {
569
608
  const homePageContent = `// GENERATED BY LASTBRAIN APP-SHELL
609
+ "use client";
570
610
 
571
611
  import { SimpleHomePage } from "@lastbrain/app";
572
- import { Footer } from "@lastbrain/ui";
573
- import { footerConfig } from "../config/footer";
612
+
574
613
 
575
614
  export default function RootPage() {
576
615
  return (
577
616
  <>
578
617
  <SimpleHomePage showAuth={${withAuth}} />
579
- <Footer config={footerConfig} />
618
+
580
619
  </>
581
620
  );
582
621
  }
583
622
  `;
584
623
  await fs.writeFile(homePagePath, homePageContent);
585
- console.log(chalk.green("✓ app/page.tsx créé"));
624
+ console.log(chalk.green("✓ app/[lang]/(public)/page.tsx créé"));
586
625
  }
587
626
 
588
627
  // Créer la page not-found.tsx
@@ -1034,74 +1073,47 @@ async function createConfigFiles(
1034
1073
  const middlewarePath = path.join(targetDir, "middleware.ts");
1035
1074
  if (!fs.existsSync(middlewarePath) || force) {
1036
1075
  const middleware = `import { type NextRequest, NextResponse } from "next/server";
1037
- import { createServerClient, type CookieOptions } from "@supabase/ssr";
1038
-
1039
- /**
1040
- * Crée un client Supabase pour le middleware
1041
- */
1042
- function createMiddlewareClient(request: NextRequest) {
1043
- let response = NextResponse.next({
1044
- request: {
1045
- headers: request.headers,
1046
- },
1047
- });
1048
-
1049
- const supabase = createServerClient(
1050
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
1051
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
1052
- {
1053
- cookies: {
1054
- get(name: string) {
1055
- return request.cookies.get(name)?.value;
1056
- },
1057
- set(name: string, value: string, options: CookieOptions) {
1058
- request.cookies.set({
1059
- name,
1060
- value,
1061
- ...options,
1062
- });
1063
- response = NextResponse.next({
1064
- request: {
1065
- headers: request.headers,
1066
- },
1067
- });
1068
- response.cookies.set({
1069
- name,
1070
- value,
1071
- ...options,
1072
- });
1073
- },
1074
- remove(name: string, options: CookieOptions) {
1075
- request.cookies.delete(name);
1076
- response = NextResponse.next({
1077
- request: {
1078
- headers: request.headers,
1079
- },
1080
- });
1081
- response.cookies.delete(name);
1082
- },
1083
- },
1084
- }
1085
- );
1086
-
1087
- return { supabase, response };
1088
- }
1076
+ import { createMiddlewareClient } from "@lastbrain/core/server";
1077
+ import { logger } from "@lastbrain/core";
1089
1078
 
1090
1079
  export async function middleware(request: NextRequest) {
1091
1080
  const { pathname } = request.nextUrl;
1092
1081
  const isApi = pathname.startsWith("/api/");
1093
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
+
1094
1095
  // Détecter la langue depuis l'URL (ex: /fr/... ou /es/...)
1095
1096
  const langMatch = pathname.match(/^\\/([a-z]{2})(\\/|$)/);
1096
- const detectedLang = langMatch ? langMatch[1] : "en";
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;
1097
1104
 
1098
1105
  // Créer la réponse de base avec les headers de langue
1099
1106
  const createResponseWithLang = (response: NextResponse) => {
1100
- response.headers.set("x-locale", detectedLang);
1107
+ response.headers.set("x-locale", currentLang);
1101
1108
  response.headers.set("x-pathname", pathname);
1102
1109
  return response;
1103
1110
  };
1104
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
+
1105
1117
  // Pages publiques d'authentification (ne pas protéger)
1106
1118
  const publicAuthPages = [
1107
1119
  "/signin",
@@ -1109,6 +1121,8 @@ export async function middleware(request: NextRequest) {
1109
1121
  "/reset-password",
1110
1122
  "/forgot-password",
1111
1123
  "/callback",
1124
+ "/confirm",
1125
+ "/auth-code-error",
1112
1126
  ];
1113
1127
 
1114
1128
  if(process.env.MAINTENANCE_MODE === "true" && !pathname.startsWith("/maintenance")) {
@@ -1602,6 +1616,22 @@ export default config;
1602
1616
  console.log(chalk.green("✓ tsconfig.json créé"));
1603
1617
  }
1604
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
+
1605
1635
  // config/menu.ts
1606
1636
  const configDir = path.join(targetDir, "config");
1607
1637
  await fs.ensureDir(configDir);
@@ -1832,6 +1862,89 @@ export default realtimeConfig;
1832
1862
  await fs.writeFile(realtimePath, realtimeContent);
1833
1863
  console.log(chalk.green("✓ config/realtime.ts placeholder créé"));
1834
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
+ }
1835
1948
  }
1836
1949
 
1837
1950
  async function createGitIgnore(targetDir: string, force: boolean) {
@@ -1908,16 +2021,48 @@ coverage/
1908
2021
  }
1909
2022
  }
1910
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
+
1911
2056
  async function createEnvExample(targetDir: string, force: boolean) {
1912
- const envExamplePath = path.join(targetDir, ".env.local.example");
2057
+ const envExamplePath = path.join(targetDir, ".env.example");
1913
2058
 
1914
2059
  if (!fs.existsSync(envExamplePath) || force) {
1915
- console.log(chalk.yellow("\n🔐 Création de .env.local.example..."));
2060
+ console.log(chalk.yellow("\n🔐 Création de .env.example..."));
1916
2061
 
1917
2062
  const envContent = `# Supabase Configuration
1918
- # 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
1919
2064
 
1920
- # Supabase Local (par défaut)
2065
+ # Supabase Local (pour développement)
1921
2066
  NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
1922
2067
  NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_LOCAL_ANON_KEY
1923
2068
  SUPABASE_SERVICE_ROLE_KEY=YOUR_LOCAL_SERVICE_ROLE_KEY
@@ -1926,75 +2071,13 @@ SUPABASE_SERVICE_ROLE_KEY=YOUR_LOCAL_SERVICE_ROLE_KEY
1926
2071
  # NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
1927
2072
  # NEXT_PUBLIC_SUPABASE_ANON_KEY=your_production_anon_key
1928
2073
  # SUPABASE_SERVICE_ROLE_KEY=your_production_service_role_key
1929
- `;
1930
- await fs.writeFile(envExamplePath, envContent);
1931
- console.log(chalk.green("✓ .env.local.example créé"));
1932
- }
1933
- }
1934
-
1935
- async function createEnvLocal(
1936
- targetDir: string,
1937
- force: boolean,
1938
- isMonorepoProject: boolean = false
1939
- ) {
1940
- const envLocalPath = path.join(targetDir, ".env.local");
1941
-
1942
- if (!fs.existsSync(envLocalPath) || force) {
1943
- console.log(chalk.yellow("\n🔐 Création de .env.local..."));
1944
-
1945
- let envContent: string;
1946
-
1947
- if (isMonorepoProject) {
1948
- // Pour les projets monorepo, utiliser les variables du monorepo
1949
- envContent = `# Supabase Configuration (Monorepo - Centralisé)
1950
- # Les variables Supabase sont gérées au niveau du monorepo
1951
-
1952
- # OpenAI Configuration (clé factice pour éviter les erreurs de build)
1953
- OPENAI_API_KEY=sk-fake-openai-key-for-development-replace-with-real-key
1954
-
1955
- # Note: Les variables Supabase sont fournies par le monorepo parent
1956
- `;
1957
- } else {
1958
- // Pour les projets indépendants
1959
- envContent = `# Supabase Configuration
1960
- # Valeurs par défaut pour le développement local
1961
2074
 
1962
- # Supabase Local (par défaut)
1963
- NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
1964
- NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGc...
1965
- SUPABASE_SERVICE_ROLE_KEY=eyJhbGc...
1966
-
1967
- # OpenAI Configuration (clé factice pour éviter les erreurs de build)
1968
- OPENAI_API_KEY=sk-fake-openai-key-for-development-replace-with-real-key
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
1969
2078
  `;
1970
- }
1971
-
1972
- await fs.writeFile(envLocalPath, envContent);
1973
- console.log(chalk.green("✓ .env.local créé"));
1974
- }
1975
- }
1976
-
1977
- async function createEnvProd(targetDir: string, force: boolean) {
1978
- const envProdPath = path.join(targetDir, ".env.prod");
1979
-
1980
- if (!fs.existsSync(envProdPath) || force) {
1981
- console.log(chalk.yellow("\n🔐 Création de .env.prod..."));
1982
-
1983
- const envContent = `# Production Environment Configuration
1984
- # Copy your production values here
1985
-
1986
- # Supabase Production
1987
- NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
1988
- NEXT_PUBLIC_SUPABASE_ANON_KEY=your-prod-anon-key
1989
- SUPABASE_SERVICE_ROLE_KEY=your-prod-service-role-key
1990
-
1991
- # OpenAI Production
1992
- OPENAI_API_KEY=sk-proj-your-prod-api-key
1993
-
1994
- # Note: Update these values with your actual production credentials
1995
- `;
1996
- await fs.writeFile(envProdPath, envContent);
1997
- console.log(chalk.green("✓ .env.prod créé"));
2079
+ await fs.writeFile(envExamplePath, envContent);
2080
+ console.log(chalk.green("✓ .env.example créé"));
1998
2081
  }
1999
2082
  }
2000
2083
 
@@ -2076,13 +2159,14 @@ async function addScriptsToPackageJson(targetDir: string) {
2076
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"
2077
2160
  : "echo 'No prebuild needed'",
2078
2161
  dev: "next dev",
2079
- "dev:local": "env-cmd -f .env.local next dev",
2080
- "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",
2081
2165
  build: "next build",
2082
2166
  start: "next start",
2083
2167
  lint: "next lint",
2084
2168
  lastbrain: scriptsPrefix,
2085
- "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}"`,
2086
2170
  "db:migrations:sync": `${scriptsPrefix} db:migrations:sync`,
2087
2171
  "db:init": `${scriptsPrefix} db:init`,
2088
2172
  "readme:create": `${scriptsPrefix} readme:create`,
@@ -2526,7 +2610,71 @@ async function createI18nDefaults(
2526
2610
  "app.name": projectName,
2527
2611
  "app.description": `Bienvenue dans ${projectName}`,
2528
2612
  "app.tagline": "Votre plateforme de gestion",
2529
- "app.icon": "Utensils",
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",
2530
2678
  };
2531
2679
 
2532
2680
  if (!fs.existsSync(frPath) || force) {
@@ -2540,7 +2688,70 @@ async function createI18nDefaults(
2540
2688
  "app.name": projectName,
2541
2689
  "app.description": `Welcome to ${projectName}`,
2542
2690
  "app.tagline": "Your management platform",
2543
- "app.icon": "Utensils",
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",
2544
2755
  };
2545
2756
 
2546
2757
  if (!fs.existsSync(enPath) || force) {