@lastbrain/app 0.1.45 → 0.1.47

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.
@@ -29,12 +29,12 @@ const dependenciesToEnsure = {
29
29
 
30
30
  const gitignoreTemplate = path.join(
31
31
  projectRoot,
32
- "packages/app/src/templates/gitignore/.gitignore",
32
+ "packages/app/src/templates/gitignore/.gitignore"
33
33
  );
34
34
  const consumerGitignore = path.join(projectRoot, "apps/web/.gitignore");
35
35
  const envTemplate = path.join(
36
36
  projectRoot,
37
- "packages/app/src/templates/env.example/.env.example",
37
+ "packages/app/src/templates/env.example/.env.example"
38
38
  );
39
39
  const consumerEnvExample = path.join(projectRoot, "apps/web/.env.example");
40
40
  const consumerEnvLocal = path.join(projectRoot, "apps/web/.env.local");
@@ -75,7 +75,7 @@ function _copyDirectory(srcDir: string, destDir: string) {
75
75
  for (const entry of entries) {
76
76
  _copyDirectory(
77
77
  path.join(srcDir, entry.name),
78
- path.join(destDir, entry.name),
78
+ path.join(destDir, entry.name)
79
79
  );
80
80
  }
81
81
  } else if (stats.isFile()) {
@@ -133,7 +133,7 @@ function cleanupStaleGroupFiles() {
133
133
 
134
134
  function mergeScripts(
135
135
  base: Record<string, string>,
136
- additions: Record<string, string>,
136
+ additions: Record<string, string>
137
137
  ) {
138
138
  return { ...base, ...additions };
139
139
  }
@@ -224,7 +224,7 @@ function main() {
224
224
  console.log("✅ apps/web synced with @lastbrain/app");
225
225
  console.log(`Copied or updated files (${copiedFiles.length}):`);
226
226
  copiedFiles.forEach((file) =>
227
- console.log(` • ${path.relative(projectRoot, file)}`),
227
+ console.log(` • ${path.relative(projectRoot, file)}`)
228
228
  );
229
229
  console.log("Scripts ensured in apps/web/package.json:");
230
230
  changes.scripts.forEach((script) => console.log(` • ${script}`));
@@ -232,17 +232,17 @@ function main() {
232
232
  changes.dependencies.forEach((dep) => console.log(` • ${dep}`));
233
233
  if (gitignoreSynced) {
234
234
  console.log(
235
- `.gitignore ensured at ${path.relative(projectRoot, gitignoreSynced)}`,
235
+ `.gitignore ensured at ${path.relative(projectRoot, gitignoreSynced)}`
236
236
  );
237
237
  }
238
238
  if (envExampleSynced) {
239
239
  console.log(
240
- `.env.example ensured at ${path.relative(projectRoot, envExampleSynced)}`,
240
+ `.env.example ensured at ${path.relative(projectRoot, envExampleSynced)}`
241
241
  );
242
242
  }
243
243
  if (envLocalCreated) {
244
244
  console.log(
245
- `.env.local ensured at ${path.relative(projectRoot, envLocalCreated)}`,
245
+ `.env.local ensured at ${path.relative(projectRoot, envLocalCreated)}`
246
246
  );
247
247
  }
248
248
  }
@@ -19,6 +19,7 @@ interface InitAppOptions {
19
19
  useHeroUI: boolean;
20
20
  withAuth?: boolean;
21
21
  interactive?: boolean;
22
+ isMonorepoProject?: boolean;
22
23
  }
23
24
 
24
25
  export async function initApp(options: InitAppOptions) {
@@ -29,7 +30,7 @@ export async function initApp(options: InitAppOptions) {
29
30
  useHeroUI,
30
31
  interactive = true,
31
32
  } = options;
32
- let { withAuth = false } = options;
33
+ let { withAuth = false, isMonorepoProject = false } = options;
33
34
 
34
35
  console.log(chalk.blue("\n🚀 LastBrain App Init\n"));
35
36
  console.log(chalk.gray(`📁 Dossier cible: ${targetDir}`));
@@ -55,20 +56,56 @@ export async function initApp(options: InitAppOptions) {
55
56
  checked: false,
56
57
  })),
57
58
  },
59
+ {
60
+ type: "rawlist",
61
+ name: "projectType",
62
+ message: "Type de projet ?",
63
+ choices: [
64
+ {
65
+ name: "📦 Indépendant (créer une nouvelle BDD Supabase)",
66
+ value: "independent",
67
+ },
68
+ {
69
+ name: "🏢 Monorepo (utiliser la BDD existante)",
70
+ value: "monorepo",
71
+ },
72
+ ],
73
+ default: "independent",
74
+ },
58
75
  ]);
59
76
 
60
77
  selectedModules.push(...answers.modules);
61
78
  withAuth = selectedModules.includes("auth");
79
+ isMonorepoProject = answers.projectType === "monorepo";
80
+
81
+ if (isMonorepoProject) {
82
+ console.log(
83
+ chalk.cyan("\n💡 Mode monorepo activé:"),
84
+ chalk.gray(
85
+ "Votre app utilisera la base de données centralisée du monorepo."
86
+ )
87
+ );
88
+ console.log(
89
+ chalk.gray(" Aucune configuration Supabase locale ne sera créée.\n")
90
+ );
91
+ } else {
92
+ console.log(
93
+ chalk.cyan("\n💡 Mode projet indépendant activé:"),
94
+ chalk.gray("Votre app aura sa propre instance Supabase locale.")
95
+ );
96
+ console.log(chalk.gray(" Configuration complète sera créée.\n"));
97
+ }
62
98
 
63
99
  console.log();
64
100
  } else if (!interactive) {
65
101
  // En mode non-interactif, installer auth et ai par défaut
66
102
  selectedModules.push("auth", "ai");
67
103
  withAuth = true;
104
+ isMonorepoProject = false;
68
105
  console.log(
69
106
  chalk.blue(
70
- "📦 Modules installés par défaut : Authentication, AI Generation\n",
71
- ),
107
+ "📦 Modules installés par défaut : Authentication, AI Generation\n"
108
+ )
72
109
  );
73
110
  }
74
111
 
@@ -93,10 +130,22 @@ export async function initApp(options: InitAppOptions) {
93
130
  // 6. Créer .gitignore et .env.local.example
94
131
  await createGitIgnore(targetDir, force);
95
132
  await createEnvExample(targetDir, force);
96
- await createEnvLocal(targetDir, force);
133
+ await createEnvLocal(targetDir, force, isMonorepoProject);
134
+ await createEnvProd(targetDir, force);
97
135
 
98
- // 7. Créer la structure Supabase avec migrations
99
- await createSupabaseStructure(targetDir, force);
136
+ // 7. Créer la structure Supabase avec migrations (seulement pour projets indépendants)
137
+ if (!isMonorepoProject) {
138
+ console.log(
139
+ chalk.blue("🗄️ Création de la structure Supabase locale...\n")
140
+ );
141
+ await createSupabaseStructure(targetDir, force);
142
+ } else {
143
+ console.log(
144
+ chalk.yellow(
145
+ "⏭️ Projet monorepo détecté - pas de configuration Supabase locale requise\n"
146
+ )
147
+ );
148
+ }
100
149
 
101
150
  // 8. Ajouter les scripts NPM
102
151
  await addScriptsToPackageJson(targetDir);
@@ -111,7 +160,7 @@ export async function initApp(options: InitAppOptions) {
111
160
  }
112
161
 
113
162
  console.log(
114
- chalk.green("\n✅ Application LastBrain initialisée avec succès!\n"),
163
+ chalk.green("\n✅ Application LastBrain initialisée avec succès!\n")
115
164
  );
116
165
 
117
166
  const relativePath = path.relative(process.cwd(), targetDir);
@@ -139,7 +188,7 @@ export async function initApp(options: InitAppOptions) {
139
188
  console.log(chalk.green("\n✓ Routes des modules générées\n"));
140
189
 
141
190
  console.log(
142
- chalk.yellow("📜 Synchronisation des migrations des modules...\n"),
191
+ chalk.yellow("📜 Synchronisation des migrations des modules...\n")
143
192
  );
144
193
  try {
145
194
  execSync("pnpm db:migrations:sync", {
@@ -149,7 +198,7 @@ export async function initApp(options: InitAppOptions) {
149
198
  console.log(chalk.green("\n✓ Migrations synchronisées\n"));
150
199
  } catch (error) {
151
200
  console.log(
152
- chalk.yellow("\n⚠️ Erreur de synchronisation des migrations\n"),
201
+ chalk.yellow("\n⚠️ Erreur de synchronisation des migrations\n")
153
202
  );
154
203
  }
155
204
 
@@ -160,8 +209,8 @@ export async function initApp(options: InitAppOptions) {
160
209
  } catch {
161
210
  console.log(
162
211
  chalk.yellow(
163
- "\n⚠️ Erreur d'initialisation de la DB (normal si Supabase pas configuré)\n",
164
- ),
212
+ "\n⚠️ Erreur d'initialisation de la DB (normal si Supabase pas configuré)\n"
213
+ )
165
214
  );
166
215
  }
167
216
 
@@ -170,19 +219,19 @@ export async function initApp(options: InitAppOptions) {
170
219
  const url = `http://127.0.0.1:${port}`;
171
220
 
172
221
  console.log(
173
- chalk.yellow("🚀 Lancement du serveur de développement...\n"),
222
+ chalk.yellow("🚀 Lancement du serveur de développement...\n")
174
223
  );
175
224
  console.log(chalk.cyan(`📱 Ouvrez votre navigateur sur : ${url}\n`));
176
225
 
177
226
  console.log(chalk.blue("\n📋 Prochaines étapes après le démarrage :"));
178
227
  console.log(
179
- chalk.white(" 1. Cliquez sur 'Get Started' sur la page d'accueil"),
228
+ chalk.white(" 1. Cliquez sur 'Get Started' sur la page d'accueil")
180
229
  );
181
230
  console.log(chalk.white(" 2. Lancez Docker Desktop"));
182
231
  console.log(
183
232
  chalk.white(
184
- " 3. Installez Supabase CLI si nécessaire : brew install supabase/tap/supabase",
185
- ),
233
+ " 3. Installez Supabase CLI si nécessaire : brew install supabase/tap/supabase"
234
+ )
186
235
  );
187
236
  console.log(chalk.white(" 4. Exécutez : pnpm db:init"));
188
237
  console.log(chalk.white(" 5. Rechargez la page\n"));
@@ -212,27 +261,32 @@ export async function initApp(options: InitAppOptions) {
212
261
  console.log(chalk.white(" pnpm build:modules"));
213
262
  console.log(chalk.white(" pnpm db:migrations:sync"));
214
263
  console.log(chalk.white(" pnpm db:init"));
215
- console.log(chalk.white(" pnpm dev\n"));
264
+ console.log(chalk.white(" pnpm dev:local (ou pnpm dev:prod)\n"));
216
265
  }
217
266
  } else {
218
267
  console.log(chalk.cyan("\n📋 Prochaines étapes:"));
219
268
  console.log(chalk.white(" 1. cd " + relativePath));
220
269
  console.log(chalk.white(" 2. pnpm install (installer les dépendances)"));
221
270
  console.log(
222
- chalk.white(" 3. pnpm build:modules (générer les routes des modules)"),
271
+ chalk.white(" 3. pnpm build:modules (générer les routes des modules)")
223
272
  );
224
273
  console.log(
225
- chalk.white(" 4. pnpm db:migrations:sync (synchroniser les migrations)"),
274
+ chalk.white(" 4. pnpm db:migrations:sync (synchroniser les migrations)")
226
275
  );
227
276
  console.log(
228
- chalk.white(" 5. pnpm db:init (initialiser la base de données)"),
277
+ chalk.white(" 5. pnpm db:init (initialiser la base de données)")
229
278
  );
230
- console.log(chalk.white(" 6. pnpm dev (lancer le serveur)\n"));
279
+ console.log(chalk.white(" 6. pnpm dev:local (ou pnpm dev:prod)\n"));
280
+
281
+ console.log(chalk.cyan("\n💡 Scripts disponibles:"));
282
+ console.log(chalk.white(" • pnpm dev:local - Lance avec .env.local"));
283
+ console.log(chalk.white(" • pnpm dev:prod - Lance avec .env.prod"));
284
+ console.log(chalk.white(" • pnpm dev - Lance avec .env actuel\n"));
231
285
 
232
286
  console.log(chalk.gray("Prérequis pour Supabase :"));
233
287
  console.log(chalk.white(" - Docker Desktop installé et lancé"));
234
288
  console.log(
235
- chalk.white(" - Supabase CLI : brew install supabase/tap/supabase\n"),
289
+ chalk.white(" - Supabase CLI : brew install supabase/tap/supabase\n")
236
290
  );
237
291
 
238
292
  // Afficher la commande cd pour faciliter la copie
@@ -276,7 +330,7 @@ function getLastBrainVersions(targetDir: string): {
276
330
  } {
277
331
  const targetIsInMonorepo =
278
332
  fs.existsSync(
279
- path.join(targetDir, "../../../packages/core/package.json"),
333
+ path.join(targetDir, "../../../packages/core/package.json")
280
334
  ) ||
281
335
  fs.existsSync(path.join(targetDir, "../../packages/core/package.json"));
282
336
 
@@ -304,11 +358,11 @@ function getLastBrainVersions(targetDir: string): {
304
358
  const monorepoRoot = path.join(__dirname, "../../../../");
305
359
  const moduleAuthPkgPath = path.join(
306
360
  monorepoRoot,
307
- "packages/module-auth/package.json",
361
+ "packages/module-auth/package.json"
308
362
  );
309
363
  const moduleAiPkgPath = path.join(
310
364
  monorepoRoot,
311
- "packages/module-ai/package.json",
365
+ "packages/module-ai/package.json"
312
366
  );
313
367
  const corePkgPath = path.join(monorepoRoot, "packages/core/package.json");
314
368
  const uiPkgPath = path.join(monorepoRoot, "packages/ui/package.json");
@@ -321,7 +375,7 @@ function getLastBrainVersions(targetDir: string): {
321
375
  // Lire les vraies versions depuis les package.json du monorepo
322
376
  if (fs.existsSync(moduleAuthPkgPath)) {
323
377
  const moduleAuthPkg = JSON.parse(
324
- fs.readFileSync(moduleAuthPkgPath, "utf-8"),
378
+ fs.readFileSync(moduleAuthPkgPath, "utf-8")
325
379
  );
326
380
  moduleAuthVersion = `^${moduleAuthPkg.version}`;
327
381
  } else {
@@ -331,7 +385,7 @@ function getLastBrainVersions(targetDir: string): {
331
385
 
332
386
  if (fs.existsSync(moduleAiPkgPath)) {
333
387
  const moduleAiPkg = JSON.parse(
334
- fs.readFileSync(moduleAiPkgPath, "utf-8"),
388
+ fs.readFileSync(moduleAiPkgPath, "utf-8")
335
389
  );
336
390
  moduleAiVersion = `^${moduleAiPkg.version}`;
337
391
  } else {
@@ -360,8 +414,8 @@ function getLastBrainVersions(targetDir: string): {
360
414
  } catch (error) {
361
415
  console.warn(
362
416
  chalk.yellow(
363
- `⚠️ Impossible de lire les versions locales (${error}), utilisation de 'latest'`,
364
- ),
417
+ `⚠️ Impossible de lire les versions locales (${error}), utilisation de 'latest'`
418
+ )
365
419
  );
366
420
  }
367
421
 
@@ -379,7 +433,7 @@ async function addDependencies(
379
433
  targetDir: string,
380
434
  useHeroUI: boolean,
381
435
  withAuth: boolean,
382
- selectedModules: string[] = [],
436
+ selectedModules: string[] = []
383
437
  ) {
384
438
  const pkgPath = path.join(targetDir, "package.json");
385
439
  const pkg = await fs.readJson(pkgPath);
@@ -466,6 +520,7 @@ async function addDependencies(
466
520
  requiredDeps["lucide-react"] = "^0.554.0";
467
521
  requiredDeps["framer-motion"] = "^11.18.2";
468
522
  requiredDeps["clsx"] = "^2.1.1";
523
+ requiredDeps["env-cmd"] = "^11.0.0";
469
524
  }
470
525
 
471
526
  // DevDependencies
@@ -490,7 +545,7 @@ async function createNextStructure(
490
545
  targetDir: string,
491
546
  force: boolean,
492
547
  useHeroUI: boolean,
493
- withAuth: boolean,
548
+ withAuth: boolean
494
549
  ) {
495
550
  console.log(chalk.yellow("\n📁 Création de la structure Next.js..."));
496
551
 
@@ -550,9 +605,7 @@ export default function RootLayout({ children }: PropsWithChildren<{}>) {
550
605
  console.log(chalk.green("✓ app/layout.tsx créé"));
551
606
  } else {
552
607
  console.log(
553
- chalk.gray(
554
- " app/layout.tsx existe déjà (utilisez --force pour écraser)",
555
- ),
608
+ chalk.gray(" app/layout.tsx existe déjà (utilisez --force pour écraser)")
556
609
  );
557
610
  }
558
611
 
@@ -646,7 +699,7 @@ export default function NotFound() {
646
699
  async function createClientLayout(
647
700
  targetDir: string,
648
701
  force: boolean,
649
- useHeroUI: boolean,
702
+ useHeroUI: boolean
650
703
  ) {
651
704
  const componentsDir = path.join(targetDir, "components");
652
705
  await fs.ensureDir(componentsDir);
@@ -723,8 +776,8 @@ export function ClientLayout({ children }: { children: ReactNode }) {
723
776
  } else {
724
777
  console.log(
725
778
  chalk.gray(
726
- " components/ClientLayout.tsx existe déjà (utilisez --force pour écraser)",
727
- ),
779
+ " components/ClientLayout.tsx existe déjà (utilisez --force pour écraser)"
780
+ )
728
781
  );
729
782
  }
730
783
  }
@@ -733,7 +786,7 @@ async function createRoute(
733
786
  appDir: string,
734
787
  routeName: string,
735
788
  layoutType: string,
736
- force: boolean,
789
+ force: boolean
737
790
  ) {
738
791
  const routeDir = path.join(appDir, routeName);
739
792
  await fs.ensureDir(routeDir);
@@ -780,9 +833,8 @@ export default function AuthLayout({
780
833
  const layoutComponent =
781
834
  layoutType.charAt(0).toUpperCase() + layoutType.slice(1) + "Layout";
782
835
 
783
- if (routeName === "docs") {
784
- // Layout docs avec footer
785
- layoutContent = `import { ${layoutComponent} } from "@lastbrain/app";
836
+ // Layout docs avec footer
837
+ layoutContent = `import { ${layoutComponent} } from "@lastbrain/app";
786
838
  import { footerConfig } from "../../config/footer";
787
839
 
788
840
  export default function DocsLayout({
@@ -792,11 +844,6 @@ export default function DocsLayout({
792
844
  }) {
793
845
  return <${layoutComponent} footerConfig={footerConfig}>{children}</${layoutComponent}>;
794
846
  }`;
795
- } else {
796
- layoutContent = `import { ${layoutComponent} } from "@lastbrain/app";
797
-
798
- export default ${layoutComponent};`;
799
- }
800
847
  }
801
848
 
802
849
  await fs.writeFile(layoutPath, layoutContent);
@@ -893,6 +940,7 @@ export function AppHeader() {
893
940
  user={user}
894
941
  onLogout={handleLogout}
895
942
  menuConfig={menuConfig}
943
+ accountMenu={menuConfig.account}
896
944
  brandName="LastBrain App"
897
945
  brandHref="/"
898
946
  isSuperAdmin={isSuperAdmin}
@@ -911,8 +959,8 @@ export function AppHeader() {
911
959
  } else {
912
960
  console.log(
913
961
  chalk.gray(
914
- " components/AppHeader.tsx existe déjà (utilisez --force pour écraser)",
915
- ),
962
+ " components/AppHeader.tsx existe déjà (utilisez --force pour écraser)"
963
+ )
916
964
  );
917
965
  }
918
966
  }
@@ -951,8 +999,8 @@ export function AppAside({ className = "", isVisible = true }: AppAsideProps) {
951
999
  } else {
952
1000
  console.log(
953
1001
  chalk.gray(
954
- " components/AppAside.tsx existe déjà (utilisez --force pour écraser)",
955
- ),
1002
+ " components/AppAside.tsx existe déjà (utilisez --force pour écraser)"
1003
+ )
956
1004
  );
957
1005
  }
958
1006
  }
@@ -981,8 +1029,8 @@ export function AppProviders({ children }: { children: ReactNode }) {
981
1029
  } else {
982
1030
  console.log(
983
1031
  chalk.gray(
984
- " components/AppProviders.tsx existe déjà (utilisez --force pour écraser)",
985
- ),
1032
+ " components/AppProviders.tsx existe déjà (utilisez --force pour écraser)"
1033
+ )
986
1034
  );
987
1035
  }
988
1036
  }
@@ -991,7 +1039,7 @@ async function createConfigFiles(
991
1039
  targetDir: string,
992
1040
  force: boolean,
993
1041
  useHeroUI: boolean,
994
- projectName?: string,
1042
+ projectName?: string
995
1043
  ) {
996
1044
  console.log(chalk.yellow("\n⚙️ Création des fichiers de configuration..."));
997
1045
 
@@ -1014,6 +1062,11 @@ export async function middleware(request: NextRequest) {
1014
1062
  "/callback",
1015
1063
  ];
1016
1064
 
1065
+ if(process.env.MAINTENANCE_MODE === "true" && !pathname.startsWith("/maintenance")) {
1066
+ const redirectUrl = new URL("/maintenance", request.url);
1067
+ return NextResponse.redirect(redirectUrl);
1068
+ }
1069
+
1017
1070
  const isPublicAuthPage = publicAuthPages.some((page) =>
1018
1071
  pathname.startsWith(page)
1019
1072
  );
@@ -1131,8 +1184,8 @@ export const config = {
1131
1184
  await fs.writeFile(middlewarePath, middleware);
1132
1185
  console.log(
1133
1186
  chalk.green(
1134
- "✓ middleware.ts créé (protection /auth/*, /admin/* et /api/admin/*)",
1135
- ),
1187
+ "✓ middleware.ts créé (protection /auth/*, /admin/* et /api/admin/*)"
1188
+ )
1136
1189
  );
1137
1190
  }
1138
1191
 
@@ -1418,7 +1471,7 @@ async function createGitIgnore(targetDir: string, force: boolean) {
1418
1471
  // Copier le fichier .gitignore depuis le template
1419
1472
  const templateGitignorePath = path.join(
1420
1473
  __dirname,
1421
- "../templates/gitignore/.gitignore",
1474
+ "../templates/gitignore/.gitignore"
1422
1475
  );
1423
1476
 
1424
1477
  if (fs.existsSync(templateGitignorePath)) {
@@ -1475,16 +1528,7 @@ tmp/
1475
1528
  coverage/
1476
1529
  *.lcov
1477
1530
 
1478
- # Generated by module-build
1479
- app/navigation.generated.ts
1480
- app/routes.generated.ts
1481
- app/menu.generated.ts
1482
1531
 
1483
- # Generated app-shell overrides
1484
- app/(public)/
1485
- app/(auth)/
1486
- app/(admin)/
1487
- app/layout.tsx
1488
1532
  `;
1489
1533
  await fs.writeFile(gitignorePath, gitignoreContent);
1490
1534
  }
@@ -1516,13 +1560,31 @@ SUPABASE_SERVICE_ROLE_KEY=YOUR_LOCAL_SERVICE_ROLE_KEY
1516
1560
  }
1517
1561
  }
1518
1562
 
1519
- async function createEnvLocal(targetDir: string, force: boolean) {
1563
+ async function createEnvLocal(
1564
+ targetDir: string,
1565
+ force: boolean,
1566
+ isMonorepoProject: boolean = false
1567
+ ) {
1520
1568
  const envLocalPath = path.join(targetDir, ".env.local");
1521
1569
 
1522
1570
  if (!fs.existsSync(envLocalPath) || force) {
1523
1571
  console.log(chalk.yellow("\n🔐 Création de .env.local..."));
1524
1572
 
1525
- const envContent = `# Supabase Configuration
1573
+ let envContent: string;
1574
+
1575
+ if (isMonorepoProject) {
1576
+ // Pour les projets monorepo, utiliser les variables du monorepo
1577
+ envContent = `# Supabase Configuration (Monorepo - Centralisé)
1578
+ # Les variables Supabase sont gérées au niveau du monorepo
1579
+
1580
+ # OpenAI Configuration (clé factice pour éviter les erreurs de build)
1581
+ OPENAI_API_KEY=sk-fake-openai-key-for-development-replace-with-real-key
1582
+
1583
+ # Note: Les variables Supabase sont fournies par le monorepo parent
1584
+ `;
1585
+ } else {
1586
+ // Pour les projets indépendants
1587
+ envContent = `# Supabase Configuration
1526
1588
  # Valeurs par défaut pour le développement local
1527
1589
 
1528
1590
  # Supabase Local (par défaut)
@@ -1533,11 +1595,37 @@ SUPABASE_SERVICE_ROLE_KEY=eyJhbGc...
1533
1595
  # OpenAI Configuration (clé factice pour éviter les erreurs de build)
1534
1596
  OPENAI_API_KEY=sk-fake-openai-key-for-development-replace-with-real-key
1535
1597
  `;
1598
+ }
1599
+
1536
1600
  await fs.writeFile(envLocalPath, envContent);
1537
1601
  console.log(chalk.green("✓ .env.local créé"));
1538
1602
  }
1539
1603
  }
1540
1604
 
1605
+ async function createEnvProd(targetDir: string, force: boolean) {
1606
+ const envProdPath = path.join(targetDir, ".env.prod");
1607
+
1608
+ if (!fs.existsSync(envProdPath) || force) {
1609
+ console.log(chalk.yellow("\n🔐 Création de .env.prod..."));
1610
+
1611
+ const envContent = `# Production Environment Configuration
1612
+ # Copy your production values here
1613
+
1614
+ # Supabase Production
1615
+ NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
1616
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=your-prod-anon-key
1617
+ SUPABASE_SERVICE_ROLE_KEY=your-prod-service-role-key
1618
+
1619
+ # OpenAI Production
1620
+ OPENAI_API_KEY=sk-proj-your-prod-api-key
1621
+
1622
+ # Note: Update these values with your actual production credentials
1623
+ `;
1624
+ await fs.writeFile(envProdPath, envContent);
1625
+ console.log(chalk.green("✓ .env.prod créé"));
1626
+ }
1627
+ }
1628
+
1541
1629
  async function createSupabaseStructure(targetDir: string, force: boolean) {
1542
1630
  console.log(chalk.yellow("\n🗄️ Création de la structure Supabase..."));
1543
1631
 
@@ -1548,11 +1636,11 @@ async function createSupabaseStructure(targetDir: string, force: boolean) {
1548
1636
  // Copier le fichier de migration initial depuis le template
1549
1637
  const templateMigrationPath = path.join(
1550
1638
  __dirname,
1551
- "../templates/migrations/20201010100000_app_base.sql",
1639
+ "../templates/migrations/20201010100000_app_base.sql"
1552
1640
  );
1553
1641
  const migrationDestPath = path.join(
1554
1642
  migrationsDir,
1555
- "20201010100000_app_base.sql",
1643
+ "20201010100000_app_base.sql"
1556
1644
  );
1557
1645
 
1558
1646
  if (!fs.existsSync(migrationDestPath) || force) {
@@ -1560,35 +1648,35 @@ async function createSupabaseStructure(targetDir: string, force: boolean) {
1560
1648
  if (fs.existsSync(templateMigrationPath)) {
1561
1649
  await fs.copy(templateMigrationPath, migrationDestPath);
1562
1650
  console.log(
1563
- chalk.green("✓ supabase/migrations/20201010100000_app_base.sql créé"),
1651
+ chalk.green("✓ supabase/migrations/20201010100000_app_base.sql créé")
1564
1652
  );
1565
1653
  } else {
1566
1654
  console.log(
1567
1655
  chalk.yellow(
1568
- "⚠ Template de migration introuvable, création d'un fichier vide",
1569
- ),
1656
+ "⚠ Template de migration introuvable, création d'un fichier vide"
1657
+ )
1570
1658
  );
1571
1659
  await fs.writeFile(
1572
1660
  migrationDestPath,
1573
- "-- Initial migration\n-- Add your database schema here\n",
1661
+ "-- Initial migration\n-- Add your database schema here\n"
1574
1662
  );
1575
1663
  console.log(
1576
1664
  chalk.green(
1577
- "✓ supabase/migrations/20201010100000_app_base.sql créé (vide)",
1578
- ),
1665
+ "✓ supabase/migrations/20201010100000_app_base.sql créé (vide)"
1666
+ )
1579
1667
  );
1580
1668
  }
1581
1669
  } catch (error) {
1582
1670
  console.error(
1583
1671
  chalk.red("✗ Erreur lors de la création de la migration:"),
1584
- error,
1672
+ error
1585
1673
  );
1586
1674
  }
1587
1675
  } else {
1588
1676
  console.log(
1589
1677
  chalk.gray(
1590
- " supabase/migrations/20201010100000_app_base.sql existe déjà",
1591
- ),
1678
+ " supabase/migrations/20201010100000_app_base.sql existe déjà"
1679
+ )
1592
1680
  );
1593
1681
  }
1594
1682
  }
@@ -1602,7 +1690,7 @@ async function addScriptsToPackageJson(targetDir: string) {
1602
1690
  // Détecter si le projet cible est dans un workspace
1603
1691
  const targetIsInMonorepo =
1604
1692
  fs.existsSync(
1605
- path.join(targetDir, "../../../packages/core/package.json"),
1693
+ path.join(targetDir, "../../../packages/core/package.json")
1606
1694
  ) ||
1607
1695
  fs.existsSync(path.join(targetDir, "../../packages/core/package.json"));
1608
1696
 
@@ -1618,6 +1706,8 @@ async function addScriptsToPackageJson(targetDir: string) {
1618
1706
  ? "pnpm --filter @lastbrain/core build && pnpm --filter @lastbrain/ui build && pnpm --filter @lastbrain/module-auth build && pnpm --filter @lastbrain/module-ai build"
1619
1707
  : "echo 'No prebuild needed outside monorepo'",
1620
1708
  dev: "next dev",
1709
+ "dev:local": "env-cmd -f .env.local next dev",
1710
+ "dev:prod": "env-cmd -f .env.prod next dev",
1621
1711
  build: "next build",
1622
1712
  start: "next start",
1623
1713
  lint: "next lint",
@@ -1639,7 +1729,7 @@ async function addScriptsToPackageJson(targetDir: string) {
1639
1729
  async function saveModulesConfig(
1640
1730
  targetDir: string,
1641
1731
  selectedModules: string[],
1642
- withAuth: boolean,
1732
+ withAuth: boolean
1643
1733
  ) {
1644
1734
  const modulesConfigPath = path.join(targetDir, ".lastbrain", "modules.json");
1645
1735
  await fs.ensureDir(path.dirname(modulesConfigPath));
@@ -1660,7 +1750,7 @@ async function saveModulesConfig(
1660
1750
  const modulePath = path.join(
1661
1751
  targetDir,
1662
1752
  "node_modules",
1663
- ...availableModule.package.split("/"),
1753
+ ...availableModule.package.split("/")
1664
1754
  );
1665
1755
  const migrationsDir = path.join(modulePath, "supabase", "migrations");
1666
1756
 
@@ -1918,7 +2008,7 @@ export function isPrivateBucket(bucket: string): boolean {
1918
2008
  "api",
1919
2009
  "storage",
1920
2010
  "[bucket]",
1921
- "[...path]",
2011
+ "[...path]"
1922
2012
  );
1923
2013
  await fs.ensureDir(apiStorageDir);
1924
2014
 
@@ -2045,7 +2135,7 @@ export async function GET(
2045
2135
 
2046
2136
  await fs.writeFile(routePath, routeContent);
2047
2137
  console.log(
2048
- chalk.green("✓ app/api/storage/[bucket]/[...path]/route.ts créé"),
2138
+ chalk.green("✓ app/api/storage/[bucket]/[...path]/route.ts créé")
2049
2139
  );
2050
2140
  }
2051
2141