@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.
- package/dist/analytics/registry.d.ts +7 -0
- package/dist/analytics/registry.d.ts.map +1 -0
- package/dist/analytics/registry.js +11 -0
- package/dist/auth/useAuthSession.d.ts.map +1 -1
- package/dist/auth/useAuthSession.js +85 -1
- package/dist/cli.js +19 -3
- package/dist/components/LanguageSwitcher.d.ts.map +1 -1
- package/dist/components/LanguageSwitcher.js +89 -5
- package/dist/config/version.d.ts.map +1 -1
- package/dist/config/version.js +30 -19
- package/dist/i18n/useLink.d.ts.map +1 -1
- package/dist/i18n/useLink.js +15 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/layouts/AdminLayoutWithSidebar.d.ts +3 -1
- package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/AdminLayoutWithSidebar.js +2 -2
- package/dist/layouts/AppProviders.d.ts +7 -1
- package/dist/layouts/AppProviders.d.ts.map +1 -1
- package/dist/layouts/AppProviders.js +24 -3
- package/dist/layouts/AuthLayout.js +1 -1
- package/dist/layouts/PublicLayout.js +1 -1
- package/dist/layouts/RootLayout.d.ts.map +1 -1
- package/dist/scripts/init-app.d.ts.map +1 -1
- package/dist/scripts/init-app.js +301 -138
- package/dist/scripts/module-build.d.ts.map +1 -1
- package/dist/scripts/module-build.js +402 -67
- package/dist/scripts/module-create.d.ts.map +1 -1
- package/dist/scripts/module-create.js +227 -10
- package/dist/scripts/sitemap-flat-generator.d.ts +39 -0
- package/dist/scripts/sitemap-flat-generator.d.ts.map +1 -0
- package/dist/scripts/sitemap-flat-generator.js +231 -0
- package/dist/scripts/sitemap-manifest-generator.d.ts +59 -0
- package/dist/scripts/sitemap-manifest-generator.d.ts.map +1 -0
- package/dist/scripts/sitemap-manifest-generator.js +290 -0
- package/dist/sitemap/manifest.d.ts +8 -0
- package/dist/sitemap/manifest.d.ts.map +1 -0
- package/dist/sitemap/manifest.js +6 -0
- package/dist/styles.css +2 -2
- package/dist/templates/AuthGuidePage.js +2 -0
- package/dist/templates/DefaultDoc.d.ts.map +1 -1
- package/dist/templates/DefaultDoc.js +9 -5
- package/dist/templates/DocPage.d.ts.map +1 -1
- package/dist/templates/DocPage.js +40 -0
- package/dist/templates/MigrationsGuidePage.js +2 -0
- package/dist/templates/ModuleGuidePage.d.ts.map +1 -1
- package/dist/templates/ModuleGuidePage.js +4 -1
- package/dist/templates/SimpleHomePage.js +2 -0
- package/package.json +11 -4
- package/src/analytics/registry.ts +14 -0
- package/src/auth/useAuthSession.ts +91 -1
- package/src/cli.ts +19 -3
- package/src/components/LanguageSwitcher.tsx +113 -23
- package/src/config/version.ts +30 -19
- package/src/i18n/useLink.ts +15 -0
- package/src/index.ts +17 -0
- package/src/layouts/AdminLayoutWithSidebar.tsx +4 -0
- package/src/layouts/AppProviders.tsx +66 -8
- package/src/layouts/AuthLayout.tsx +1 -1
- package/src/layouts/PublicLayout.tsx +1 -1
- package/src/layouts/RootLayout.tsx +0 -1
- package/src/scripts/init-app.ts +360 -149
- package/src/scripts/module-build.ts +458 -72
- package/src/scripts/module-create.ts +260 -10
- package/src/scripts/sitemap-flat-generator.ts +313 -0
- package/src/scripts/sitemap-manifest-generator.ts +476 -0
- package/src/sitemap/manifest.ts +17 -0
- package/src/templates/AuthGuidePage.tsx +1 -1
- package/src/templates/DefaultDoc.tsx +397 -6
- package/src/templates/DocPage.tsx +40 -0
- package/src/templates/MigrationsGuidePage.tsx +1 -1
- package/src/templates/ModuleGuidePage.tsx +3 -2
- package/src/templates/SimpleHomePage.tsx +1 -1
package/src/scripts/init-app.ts
CHANGED
|
@@ -124,11 +124,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.
|
|
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(
|
|
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(
|
|
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(
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
//
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
//
|
|
417
|
-
|
|
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 (
|
|
567
|
-
const
|
|
601
|
+
// Créer la page d'accueil publique dans (public)
|
|
602
|
+
const langDir = path.join(appDir, "[lang]");
|
|
603
|
+
await fs.ensureDir(langDir);
|
|
604
|
+
const publicDir = path.join(langDir, "(public)");
|
|
605
|
+
await fs.ensureDir(publicDir);
|
|
606
|
+
const homePagePath = path.join(publicDir, "page.tsx");
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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] :
|
|
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",
|
|
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.
|
|
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.
|
|
2060
|
+
console.log(chalk.yellow("\n🔐 Création de .env.example..."));
|
|
1916
2061
|
|
|
1917
2062
|
const envContent = `# Supabase Configuration
|
|
1918
|
-
#
|
|
2063
|
+
# Copiez ce fichier vers .env.local, .env.development ou .env.production selon vos besoins
|
|
1919
2064
|
|
|
1920
|
-
# Supabase Local (
|
|
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
|
-
#
|
|
1963
|
-
|
|
1964
|
-
|
|
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:
|
|
2080
|
-
"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
|
|
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": "
|
|
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": "
|
|
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) {
|