@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
@@ -83,11 +83,12 @@ export async function initApp(options) {
83
83
  await createConfigFiles(targetDir, force, useHeroUI, projectName);
84
84
  // 5. Créer le système de proxy storage
85
85
  await createStorageProxy(targetDir, force);
86
- // 6. Créer .gitignore et .env.local.example
86
+ // 6. Créer .gitignore, vercel.json et .env.example
87
87
  await createGitIgnore(targetDir, force);
88
+ await createVercelJson(targetDir, projectName, force);
88
89
  await createEnvExample(targetDir, force);
89
- await createEnvLocal(targetDir, force, isMonorepoProject);
90
- await createEnvProd(targetDir, force);
90
+ // 6b. Créer les fichiers i18n/default pour les traductions personnalisées
91
+ await createI18nDefaults(targetDir, force, projectName);
91
92
  // 7. Créer la structure Supabase avec migrations (seulement pour projets indépendants)
92
93
  if (!isMonorepoProject) {
93
94
  console.log(chalk.blue("🗄️ Création de la structure Supabase locale...\n"));
@@ -192,7 +193,7 @@ export async function initApp(options) {
192
193
  if (!isMonorepoProject) {
193
194
  console.log(chalk.white(" pnpm db:init"));
194
195
  }
195
- console.log(chalk.white(" pnpm dev:local (ou pnpm dev:prod)\n"));
196
+ console.log(chalk.white(" pnpm dev:local (ou pnpm dev:dev / dev:prod)\n"));
196
197
  }
197
198
  }
198
199
  else {
@@ -202,11 +203,13 @@ export async function initApp(options) {
202
203
  console.log(chalk.white(" 3. pnpm build:modules (générer les routes des modules)"));
203
204
  console.log(chalk.white(" 4. pnpm db:migrations:sync (synchroniser les migrations)"));
204
205
  console.log(chalk.white(" 5. pnpm db:init (initialiser la base de données)"));
205
- console.log(chalk.white(" 6. pnpm dev:local (ou pnpm dev:prod)\n"));
206
+ console.log(chalk.white(" 6. Copier .env.example vers .env.local/.env.development/.env.production"));
207
+ console.log(chalk.white(" 7. pnpm dev:local (ou pnpm dev:dev / dev:prod)\n"));
206
208
  console.log(chalk.cyan("\n💡 Scripts disponibles:"));
207
- console.log(chalk.white(" • pnpm dev:local - Lance avec .env.local"));
208
- console.log(chalk.white(" • pnpm dev:prod - Lance avec .env.prod"));
209
- console.log(chalk.white(" • pnpm dev - Lance avec .env actuel\n"));
209
+ console.log(chalk.white(" • pnpm dev:local - Lance avec NODE_ENV=local (utilise .env.local)"));
210
+ console.log(chalk.white(" • pnpm dev:dev - Lance avec NODE_ENV=development (utilise .env.development)"));
211
+ console.log(chalk.white(" • pnpm dev:prod - Lance avec NODE_ENV=production (utilise .env.production)"));
212
+ console.log(chalk.white(" • pnpm dev - Lance avec Next.js (utilise .env par défaut)\n"));
210
213
  console.log(chalk.gray("Prérequis pour Supabase :"));
211
214
  console.log(chalk.white(" - Docker Desktop installé et lancé"));
212
215
  console.log(chalk.white(" - Supabase CLI : brew install supabase/tap/supabase\n"));
@@ -285,13 +288,20 @@ async function addDependencies(targetDir, useHeroUI, withAuth, selectedModules =
285
288
  for (const moduleName of selectedModules) {
286
289
  const moduleInfo = AVAILABLE_MODULES.find((m) => m.name === moduleName);
287
290
  if (moduleInfo && moduleInfo.package !== "@lastbrain/module-auth") {
288
- // Utiliser les vraies versions au lieu de "latest"
289
- if (moduleInfo.package === "@lastbrain/module-ai") {
290
- requiredDeps[moduleInfo.package] = versions.moduleAi;
291
+ // Dans un monorepo, utiliser workspace:*
292
+ const targetIsInMonorepo = fs.existsSync(path.join(targetDir, "../../../packages/core/package.json")) ||
293
+ fs.existsSync(path.join(targetDir, "../../packages/core/package.json"));
294
+ if (targetIsInMonorepo) {
295
+ requiredDeps[moduleInfo.package] = "workspace:*";
291
296
  }
292
297
  else {
293
- // Pour les futurs modules, utiliser "latest" en attendant
294
- requiredDeps[moduleInfo.package] = "latest";
298
+ // Hors monorepo, utiliser les versions publiées
299
+ if (moduleInfo.package === "@lastbrain/module-ai") {
300
+ requiredDeps[moduleInfo.package] = versions.moduleAi;
301
+ }
302
+ else {
303
+ requiredDeps[moduleInfo.package] = "latest";
304
+ }
295
305
  }
296
306
  }
297
307
  }
@@ -421,26 +431,30 @@ export default function RootLayout({ children }: PropsWithChildren<{}>) {
421
431
  await fs.writeFile(globalsPath, globalsContent);
422
432
  console.log(chalk.green("✓ styles/globals.css créé"));
423
433
  }
424
- // Créer la page d'accueil publique (racine)
425
- const homePagePath = path.join(appDir, "[lang]", "page.tsx");
434
+ // Créer la page d'accueil publique dans (public)
435
+ const langDir = path.join(appDir, "[lang]");
436
+ await fs.ensureDir(langDir);
437
+ const publicDir = path.join(langDir, "(public)");
438
+ await fs.ensureDir(publicDir);
439
+ const homePagePath = path.join(publicDir, "page.tsx");
426
440
  if (!fs.existsSync(homePagePath) || force) {
427
441
  const homePageContent = `// GENERATED BY LASTBRAIN APP-SHELL
442
+ "use client";
428
443
 
429
444
  import { SimpleHomePage } from "@lastbrain/app";
430
- import { Footer } from "@lastbrain/ui";
431
- import { footerConfig } from "../config/footer";
445
+
432
446
 
433
447
  export default function RootPage() {
434
448
  return (
435
449
  <>
436
450
  <SimpleHomePage showAuth={${withAuth}} />
437
- <Footer config={footerConfig} />
451
+
438
452
  </>
439
453
  );
440
454
  }
441
455
  `;
442
456
  await fs.writeFile(homePagePath, homePageContent);
443
- console.log(chalk.green("✓ app/page.tsx créé"));
457
+ console.log(chalk.green("✓ app/[lang]/(public)/page.tsx créé"));
444
458
  }
445
459
  // Créer la page not-found.tsx
446
460
  const notFoundPath = path.join(appDir, "not-found.tsx");
@@ -834,63 +848,47 @@ async function createConfigFiles(targetDir, force, useHeroUI, projectName) {
834
848
  const middlewarePath = path.join(targetDir, "middleware.ts");
835
849
  if (!fs.existsSync(middlewarePath) || force) {
836
850
  const middleware = `import { type NextRequest, NextResponse } from "next/server";
837
- import { createServerClient, type CookieOptions } from "@supabase/ssr";
838
-
839
- /**
840
- * Crée un client Supabase pour le middleware
841
- */
842
- function createMiddlewareClient(request: NextRequest) {
843
- let response = NextResponse.next({
844
- request: {
845
- headers: request.headers,
846
- },
847
- });
848
-
849
- const supabase = createServerClient(
850
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
851
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
852
- {
853
- cookies: {
854
- get(name: string) {
855
- return request.cookies.get(name)?.value;
856
- },
857
- set(name: string, value: string, options: CookieOptions) {
858
- request.cookies.set({
859
- name,
860
- value,
861
- ...options,
862
- });
863
- response = NextResponse.next({
864
- request: {
865
- headers: request.headers,
866
- },
867
- });
868
- response.cookies.set({
869
- name,
870
- value,
871
- ...options,
872
- });
873
- },
874
- remove(name: string, options: CookieOptions) {
875
- request.cookies.delete(name);
876
- response = NextResponse.next({
877
- request: {
878
- headers: request.headers,
879
- },
880
- });
881
- response.cookies.delete(name);
882
- },
883
- },
884
- }
885
- );
886
-
887
- return { supabase, response };
888
- }
851
+ import { createMiddlewareClient } from "@lastbrain/core/server";
852
+ import { logger } from "@lastbrain/core";
889
853
 
890
854
  export async function middleware(request: NextRequest) {
891
855
  const { pathname } = request.nextUrl;
892
856
  const isApi = pathname.startsWith("/api/");
893
857
 
858
+ // Liste des locales supportées
859
+ const supportedLocales = ["fr", "en", "es"];
860
+ const defaultLocale = "fr";
861
+
862
+ // Special case: root path — redirect to default locale for better SEO
863
+ if (pathname === "/") {
864
+ const redirectUrl = new URL(\`/\${defaultLocale}\`, request.url);
865
+ // preserve search params
866
+ redirectUrl.search = request.nextUrl.search;
867
+ return NextResponse.redirect(redirectUrl);
868
+ }
869
+
870
+ // Détecter la langue depuis l'URL (ex: /fr/... ou /es/...)
871
+ const langMatch = pathname.match(/^\\/([a-z]{2})(\\/|$)/);
872
+ const detectedLang = langMatch ? langMatch[1] : null;
873
+
874
+ // Utiliser la langue détectée ou la langue par défaut
875
+ const currentLang =
876
+ detectedLang && supportedLocales.includes(detectedLang)
877
+ ? detectedLang
878
+ : defaultLocale;
879
+
880
+ // Créer la réponse de base avec les headers de langue
881
+ const createResponseWithLang = (response: NextResponse) => {
882
+ response.headers.set("x-locale", currentLang);
883
+ response.headers.set("x-pathname", pathname);
884
+ return response;
885
+ };
886
+
887
+ // Extraire le chemin sans le préfixe de langue pour les vérifications
888
+ const pathnameWithoutLang = detectedLang
889
+ ? pathname.replace(\`/\${detectedLang}\`, "") || "/"
890
+ : pathname;
891
+
894
892
  // Pages publiques d'authentification (ne pas protéger)
895
893
  const publicAuthPages = [
896
894
  "/signin",
@@ -898,6 +896,8 @@ export async function middleware(request: NextRequest) {
898
896
  "/reset-password",
899
897
  "/forgot-password",
900
898
  "/callback",
899
+ "/confirm",
900
+ "/auth-code-error",
901
901
  ];
902
902
 
903
903
  if(process.env.MAINTENANCE_MODE === "true" && !pathname.startsWith("/maintenance")) {
@@ -911,7 +911,7 @@ export async function middleware(request: NextRequest) {
911
911
 
912
912
  // Ne pas protéger les pages publiques d'authentification
913
913
  if (isPublicAuthPage) {
914
- return NextResponse.next();
914
+ return createResponseWithLang(NextResponse.next());
915
915
  }
916
916
 
917
917
  // Protéger les routes /auth/* (espace membre)
@@ -1003,7 +1003,7 @@ export async function middleware(request: NextRequest) {
1003
1003
  }
1004
1004
  }
1005
1005
 
1006
- return NextResponse.next();
1006
+ return createResponseWithLang(NextResponse.next());
1007
1007
  }
1008
1008
 
1009
1009
  export const config = {
@@ -1374,6 +1374,21 @@ export default config;
1374
1374
  await fs.writeJson(tsconfigPath, tsconfig, { spaces: 2 });
1375
1375
  console.log(chalk.green("✓ tsconfig.json créé"));
1376
1376
  }
1377
+ // locale-helpers.ts - Re-export locale helpers from config
1378
+ const localeHelpersPath = path.join(targetDir, "locale-helpers.ts");
1379
+ if (!fs.existsSync(localeHelpersPath) || force) {
1380
+ const localeHelpers = `// Re-export locale helpers from config for easy import in packages
1381
+ export {
1382
+ langToLocale,
1383
+ getAlternateLocales,
1384
+ getLocaleMap,
1385
+ LOCALE_CONFIG,
1386
+ } from "./config/locales.generated";
1387
+ export type { LocaleConfig } from "./config/locales.generated";
1388
+ `;
1389
+ await fs.writeFile(localeHelpersPath, localeHelpers);
1390
+ console.log(chalk.green("✓ locale-helpers.ts créé"));
1391
+ }
1377
1392
  // config/menu.ts
1378
1393
  const configDir = path.join(targetDir, "config");
1379
1394
  await fs.ensureDir(configDir);
@@ -1470,8 +1485,6 @@ export const menuCustom: MenuCustom = {
1470
1485
  import type { FooterConfig } from "@lastbrain/ui";
1471
1486
 
1472
1487
  export const footerConfig: FooterConfig = {
1473
- companyName: "${projectName}",
1474
- companyDescription: "Application LastBrain",
1475
1488
  links: [],
1476
1489
  social: [],
1477
1490
  };
@@ -1598,6 +1611,83 @@ export default realtimeConfig;
1598
1611
  await fs.writeFile(realtimePath, realtimeContent);
1599
1612
  console.log(chalk.green("✓ config/realtime.ts placeholder créé"));
1600
1613
  }
1614
+ // Créer un placeholder pour locales.generated.ts qui sera généré par build:modules
1615
+ const localesGeneratedPath = path.join(targetDir, "config", "locales.generated.ts");
1616
+ if (!fs.existsSync(localesGeneratedPath) || force) {
1617
+ const localesContent = `// GENERATED FILE - DO NOT EDIT MANUALLY
1618
+ // Ce fichier sera automatiquement généré lors du build:modules
1619
+ // Exécutez "pnpm build:modules" pour créer la configuration des locales
1620
+
1621
+ export const LOCALE_MAP: Record<string, string> = {
1622
+ fr: "Français",
1623
+ en: "English",
1624
+ es: "Español",
1625
+ de: "Deutsch",
1626
+ it: "Italiano",
1627
+ pt: "Português",
1628
+ nl: "Nederlands",
1629
+ ru: "Русский",
1630
+ ja: "日本語",
1631
+ zh: "中文",
1632
+ ar: "العربية",
1633
+ hi: "हिन्दी",
1634
+ };
1635
+
1636
+ export interface LocaleConfig {
1637
+ code: string;
1638
+ name: string;
1639
+ flag: string;
1640
+ }
1641
+
1642
+ export const LOCALE_CONFIG: LocaleConfig[] = Object.entries(LOCALE_MAP).map(
1643
+ ([code, name]) => ({
1644
+ code,
1645
+ name,
1646
+ flag: code.toUpperCase(),
1647
+ })
1648
+ );
1649
+
1650
+ export function langToLocale(lang: string): string {
1651
+ return lang;
1652
+ }
1653
+
1654
+ export function getAlternateLocales(currentLang: string): string[] {
1655
+ return Object.keys(LOCALE_MAP).filter((lang) => lang !== currentLang);
1656
+ }
1657
+
1658
+ export function getLocaleMap(): Record<string, string> {
1659
+ return LOCALE_MAP;
1660
+ }
1661
+ `;
1662
+ await fs.writeFile(localesGeneratedPath, localesContent);
1663
+ console.log(chalk.green("✓ config/locales.generated.ts placeholder créé"));
1664
+ }
1665
+ // Créer un placeholder pour auth-dashboard.ts qui sera généré par build:modules
1666
+ const authDashboardPath = path.join(targetDir, "config", "auth-dashboard.ts");
1667
+ if (!fs.existsSync(authDashboardPath) || force) {
1668
+ const authDashboardContent = `// GENERATED FILE - DO NOT EDIT MANUALLY
1669
+ // Ce fichier sera automatiquement généré lors du build:modules
1670
+ // Exécutez "pnpm build:modules" pour créer la configuration auth dashboard
1671
+
1672
+ "use client";
1673
+
1674
+ import type React from "react";
1675
+
1676
+ export interface ModuleAuthDashboard {
1677
+ key: string;
1678
+ title: string;
1679
+ icon?: string;
1680
+ order?: number;
1681
+ component: React.ComponentType<Record<string, never>>;
1682
+ }
1683
+
1684
+ export const moduleAuthDashboards: ModuleAuthDashboard[] = [];
1685
+
1686
+ export default moduleAuthDashboards;
1687
+ `;
1688
+ await fs.writeFile(authDashboardPath, authDashboardContent);
1689
+ console.log(chalk.green("✓ config/auth-dashboard.ts placeholder créé"));
1690
+ }
1601
1691
  }
1602
1692
  async function createGitIgnore(targetDir, force) {
1603
1693
  const gitignorePath = path.join(targetDir, ".gitignore");
@@ -1667,14 +1757,34 @@ coverage/
1667
1757
  console.log(chalk.green("✓ .gitignore créé"));
1668
1758
  }
1669
1759
  }
1760
+ async function createVercelJson(targetDir, projectName, force) {
1761
+ const vercelJsonPath = path.join(targetDir, "vercel.json");
1762
+ if (!fs.existsSync(vercelJsonPath) || force) {
1763
+ console.log(chalk.yellow("\n📝 Création de vercel.json..."));
1764
+ const vercelConfig = {
1765
+ buildCommand: `cd ../.. && pnpm install && turbo run build --filter=${projectName}`,
1766
+ framework: "nextjs",
1767
+ functions: {
1768
+ "app/api/public/convert-pdf/route.ts": {
1769
+ maxDuration: 60,
1770
+ },
1771
+ "app/api/audits/convert-pdf/route.ts": {
1772
+ maxDuration: 60,
1773
+ },
1774
+ },
1775
+ };
1776
+ await fs.writeFile(vercelJsonPath, JSON.stringify(vercelConfig, null, 2), "utf-8");
1777
+ console.log(chalk.green("✓ vercel.json créé"));
1778
+ }
1779
+ }
1670
1780
  async function createEnvExample(targetDir, force) {
1671
- const envExamplePath = path.join(targetDir, ".env.local.example");
1781
+ const envExamplePath = path.join(targetDir, ".env.example");
1672
1782
  if (!fs.existsSync(envExamplePath) || force) {
1673
- console.log(chalk.yellow("\n🔐 Création de .env.local.example..."));
1783
+ console.log(chalk.yellow("\n🔐 Création de .env.example..."));
1674
1784
  const envContent = `# Supabase Configuration
1675
- # Exécutez 'pnpm db:init' pour initialiser Supabase local et générer le vrai .env.local
1785
+ # Copiez ce fichier vers .env.local, .env.development ou .env.production selon vos besoins
1676
1786
 
1677
- # Supabase Local (par défaut)
1787
+ # Supabase Local (pour développement)
1678
1788
  NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
1679
1789
  NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_LOCAL_ANON_KEY
1680
1790
  SUPABASE_SERVICE_ROLE_KEY=YOUR_LOCAL_SERVICE_ROLE_KEY
@@ -1683,64 +1793,13 @@ SUPABASE_SERVICE_ROLE_KEY=YOUR_LOCAL_SERVICE_ROLE_KEY
1683
1793
  # NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
1684
1794
  # NEXT_PUBLIC_SUPABASE_ANON_KEY=your_production_anon_key
1685
1795
  # SUPABASE_SERVICE_ROLE_KEY=your_production_service_role_key
1686
- `;
1687
- await fs.writeFile(envExamplePath, envContent);
1688
- console.log(chalk.green("✓ .env.local.example créé"));
1689
- }
1690
- }
1691
- async function createEnvLocal(targetDir, force, isMonorepoProject = false) {
1692
- const envLocalPath = path.join(targetDir, ".env.local");
1693
- if (!fs.existsSync(envLocalPath) || force) {
1694
- console.log(chalk.yellow("\n🔐 Création de .env.local..."));
1695
- let envContent;
1696
- if (isMonorepoProject) {
1697
- // Pour les projets monorepo, utiliser les variables du monorepo
1698
- envContent = `# Supabase Configuration (Monorepo - Centralisé)
1699
- # Les variables Supabase sont gérées au niveau du monorepo
1700
1796
 
1701
- # OpenAI Configuration (clé factice pour éviter les erreurs de build)
1702
- OPENAI_API_KEY=sk-fake-openai-key-for-development-replace-with-real-key
1703
-
1704
- # Note: Les variables Supabase sont fournies par le monorepo parent
1797
+ # AI Gateway API Key (recommandé) ou OpenAI direct
1798
+ AI_GATEWAY_API_KEY=your-ai-gateway-key
1799
+ # OPENAI_API_KEY=sk-proj-your-openai-key
1705
1800
  `;
1706
- }
1707
- else {
1708
- // Pour les projets indépendants
1709
- envContent = `# Supabase Configuration
1710
- # Valeurs par défaut pour le développement local
1711
-
1712
- # Supabase Local (par défaut)
1713
- NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
1714
- NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGc...
1715
- SUPABASE_SERVICE_ROLE_KEY=eyJhbGc...
1716
-
1717
- # OpenAI Configuration (clé factice pour éviter les erreurs de build)
1718
- OPENAI_API_KEY=sk-fake-openai-key-for-development-replace-with-real-key
1719
- `;
1720
- }
1721
- await fs.writeFile(envLocalPath, envContent);
1722
- console.log(chalk.green("✓ .env.local créé"));
1723
- }
1724
- }
1725
- async function createEnvProd(targetDir, force) {
1726
- const envProdPath = path.join(targetDir, ".env.prod");
1727
- if (!fs.existsSync(envProdPath) || force) {
1728
- console.log(chalk.yellow("\n🔐 Création de .env.prod..."));
1729
- const envContent = `# Production Environment Configuration
1730
- # Copy your production values here
1731
-
1732
- # Supabase Production
1733
- NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
1734
- NEXT_PUBLIC_SUPABASE_ANON_KEY=your-prod-anon-key
1735
- SUPABASE_SERVICE_ROLE_KEY=your-prod-service-role-key
1736
-
1737
- # OpenAI Production
1738
- OPENAI_API_KEY=sk-proj-your-prod-api-key
1739
-
1740
- # Note: Update these values with your actual production credentials
1741
- `;
1742
- await fs.writeFile(envProdPath, envContent);
1743
- console.log(chalk.green("✓ .env.prod créé"));
1801
+ await fs.writeFile(envExamplePath, envContent);
1802
+ console.log(chalk.green("✓ .env.example créé"));
1744
1803
  }
1745
1804
  }
1746
1805
  async function createSupabaseStructure(targetDir, force) {
@@ -1787,13 +1846,14 @@ async function addScriptsToPackageJson(targetDir) {
1787
1846
  ? "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"
1788
1847
  : "echo 'No prebuild needed'",
1789
1848
  dev: "next dev",
1790
- "dev:local": "env-cmd -f .env.local next dev",
1791
- "dev:prod": "env-cmd -f .env.prod next dev",
1849
+ "dev:dev": "NODE_ENV=development next dev --turbopack",
1850
+ "dev:local": "NODE_ENV=local next dev --turbopack",
1851
+ "dev:prod": "NODE_ENV=production next dev --turbopack",
1792
1852
  build: "next build",
1793
1853
  start: "next start",
1794
1854
  lint: "next lint",
1795
1855
  lastbrain: scriptsPrefix,
1796
- "build:modules": `${scriptsPrefix} module:build && prettier --write \\"**/*.{js,jsx,ts,tsx,json,md}\\"`,
1856
+ "build:modules": `${scriptsPrefix} module:build && prettier --write "**/*.{js,jsx,ts,tsx,json,md}"`,
1797
1857
  "db:migrations:sync": `${scriptsPrefix} db:migrations:sync`,
1798
1858
  "db:init": `${scriptsPrefix} db:init`,
1799
1859
  "readme:create": `${scriptsPrefix} readme:create`,
@@ -2173,3 +2233,148 @@ export async function GET(
2173
2233
  }
2174
2234
  console.log(chalk.green("✓ Système de proxy storage configuré"));
2175
2235
  }
2236
+ async function createI18nDefaults(targetDir, force, projectName) {
2237
+ console.log(chalk.blue("📝 Création des fichiers i18n/default...\n"));
2238
+ const i18nDefaultDir = path.join(targetDir, "i18n", "default");
2239
+ await fs.ensureDir(i18nDefaultDir);
2240
+ // Fichier FR
2241
+ const frPath = path.join(i18nDefaultDir, "fr.json");
2242
+ const frContent = {
2243
+ "app.name": projectName,
2244
+ "app.description": `Bienvenue dans ${projectName}`,
2245
+ "app.tagline": "Votre plateforme de gestion",
2246
+ "app.icon": "Sparkles",
2247
+ "app.twitter_creator": "@YourTwitter",
2248
+ "app.twitter_site": "@YourTwitter",
2249
+ "app.email.contact": "contact@example.com",
2250
+ "app.email.color.primary": "#3b82f6",
2251
+ "app.email.color.secondary": "#8b5cf6",
2252
+ // Navigation
2253
+ "nav.home": "Accueil",
2254
+ "nav.dashboard": "Tableau de bord",
2255
+ "nav.profile": "Profil",
2256
+ "nav.settings": "Paramètres",
2257
+ "nav.admin": "Administration",
2258
+ "nav.logout": "Déconnexion",
2259
+ "nav.login": "Connexion",
2260
+ "nav.signup": "S'inscrire",
2261
+ // Actions communes
2262
+ "action.save": "Enregistrer",
2263
+ "action.cancel": "Annuler",
2264
+ "action.delete": "Supprimer",
2265
+ "action.edit": "Modifier",
2266
+ "action.create": "Créer",
2267
+ "action.search": "Rechercher",
2268
+ "action.filter": "Filtrer",
2269
+ "action.export": "Exporter",
2270
+ "action.import": "Importer",
2271
+ "action.close": "Fermer",
2272
+ "action.confirm": "Confirmer",
2273
+ "action.back": "Retour",
2274
+ "action.next": "Suivant",
2275
+ "action.previous": "Précédent",
2276
+ "action.submit": "Soumettre",
2277
+ "action.reset": "Réinitialiser",
2278
+ // Messages communs
2279
+ "message.loading": "Chargement...",
2280
+ "message.saving": "Enregistrement...",
2281
+ "message.success": "Opération réussie",
2282
+ "message.error": "Une erreur est survenue",
2283
+ "message.no_data": "Aucune donnée disponible",
2284
+ "message.confirm_delete": "Êtes-vous sûr de vouloir supprimer cet élément ?",
2285
+ "message.unsaved_changes": "Vous avez des modifications non enregistrées",
2286
+ // Formulaires
2287
+ "form.required": "Ce champ est requis",
2288
+ "form.invalid_email": "Email invalide",
2289
+ "form.password_too_short": "Le mot de passe est trop court",
2290
+ "form.passwords_no_match": "Les mots de passe ne correspondent pas",
2291
+ // Contact
2292
+ "contact.page.title": "Contactez-nous",
2293
+ "contact.page.subtitle": "Nous sommes à votre écoute",
2294
+ "contact.form.name": "Nom",
2295
+ "contact.form.name_placeholder": "Votre nom",
2296
+ "contact.form.email": "Email",
2297
+ "contact.form.email_placeholder": "votre@email.fr",
2298
+ "contact.form.subject": "Sujet",
2299
+ "contact.form.message": "Message",
2300
+ "contact.form.message_placeholder": "Votre message...",
2301
+ "contact.form.submit": "Envoyer",
2302
+ "contact.success.title": "Message envoyé !",
2303
+ "contact.success.description": "Nous vous répondrons dans les plus brefs délais",
2304
+ };
2305
+ if (!fs.existsSync(frPath) || force) {
2306
+ await fs.writeJson(frPath, frContent, { spaces: 2 });
2307
+ console.log(chalk.green(`✓ i18n/default/fr.json créé`));
2308
+ }
2309
+ // Fichier EN
2310
+ const enPath = path.join(i18nDefaultDir, "en.json");
2311
+ const enContent = {
2312
+ "app.name": projectName,
2313
+ "app.description": `Welcome to ${projectName}`,
2314
+ "app.tagline": "Your management platform",
2315
+ "app.icon": "Sparkles",
2316
+ "app.twitter_creator": "@YourTwitter",
2317
+ "app.twitter_site": "@YourTwitter",
2318
+ "app.email.contact": "contact@example.com",
2319
+ "app.email.color.primary": "#3b82f6",
2320
+ "app.email.color.secondary": "#8b5cf6",
2321
+ // Navigation
2322
+ "nav.home": "Home",
2323
+ "nav.dashboard": "Dashboard",
2324
+ "nav.profile": "Profile",
2325
+ "nav.settings": "Settings",
2326
+ "nav.admin": "Administration",
2327
+ "nav.logout": "Logout",
2328
+ "nav.login": "Login",
2329
+ "nav.signup": "Sign up",
2330
+ // Common actions
2331
+ "action.save": "Save",
2332
+ "action.cancel": "Cancel",
2333
+ "action.delete": "Delete",
2334
+ "action.edit": "Edit",
2335
+ "action.create": "Create",
2336
+ "action.search": "Search",
2337
+ "action.filter": "Filter",
2338
+ "action.export": "Export",
2339
+ "action.import": "Import",
2340
+ "action.close": "Close",
2341
+ "action.confirm": "Confirm",
2342
+ "action.back": "Back",
2343
+ "action.next": "Next",
2344
+ "action.previous": "Previous",
2345
+ "action.submit": "Submit",
2346
+ "action.reset": "Reset",
2347
+ // Common messages
2348
+ "message.loading": "Loading...",
2349
+ "message.saving": "Saving...",
2350
+ "message.success": "Operation successful",
2351
+ "message.error": "An error occurred",
2352
+ "message.no_data": "No data available",
2353
+ "message.confirm_delete": "Are you sure you want to delete this item?",
2354
+ "message.unsaved_changes": "You have unsaved changes",
2355
+ // Forms
2356
+ "form.required": "This field is required",
2357
+ "form.invalid_email": "Invalid email",
2358
+ "form.password_too_short": "Password is too short",
2359
+ "form.passwords_no_match": "Passwords do not match",
2360
+ // Contact
2361
+ "contact.page.title": "Contact Us",
2362
+ "contact.page.subtitle": "We're here to help",
2363
+ "contact.form.name": "Name",
2364
+ "contact.form.name_placeholder": "Your name",
2365
+ "contact.form.email": "Email",
2366
+ "contact.form.email_placeholder": "your@email.com",
2367
+ "contact.form.subject": "Subject",
2368
+ "contact.form.message": "Message",
2369
+ "contact.form.message_placeholder": "Your message...",
2370
+ "contact.form.submit": "Send",
2371
+ "contact.success.title": "Message sent!",
2372
+ "contact.success.description": "We will get back to you as soon as possible",
2373
+ };
2374
+ if (!fs.existsSync(enPath) || force) {
2375
+ await fs.writeJson(enPath, enContent, { spaces: 2 });
2376
+ console.log(chalk.green(`✓ i18n/default/en.json créé`));
2377
+ }
2378
+ console.log(chalk.yellow("\n💡 Vous pouvez personnaliser ces fichiers pour votre application"));
2379
+ console.log(chalk.gray(" Chemin: i18n/default/{fr,en}.json\n"));
2380
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAkyDA,wBAAsB,cAAc,kBA8GnC"}
1
+ {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAwlFA,wBAAsB,cAAc,kBAoJnC"}