@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.
- 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 +3 -1
- package/dist/components/LanguageSwitcher.d.ts.map +1 -1
- package/dist/components/LanguageSwitcher.js +134 -21
- package/dist/config/version.d.ts.map +1 -1
- package/dist/config/version.js +30 -19
- package/dist/i18n/server-lang.d.ts +1 -1
- package/dist/i18n/server-lang.d.ts.map +1 -1
- package/dist/i18n/types.d.ts +1 -1
- package/dist/i18n/types.d.ts.map +1 -1
- 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 +9 -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 +343 -138
- package/dist/scripts/module-build.d.ts.map +1 -1
- package/dist/scripts/module-build.js +784 -59
- 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 +31 -26
- 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 +183 -60
- package/src/config/version.ts +30 -19
- package/src/i18n/server-lang.ts +2 -1
- package/src/i18n/types.ts +2 -1
- 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 +74 -9
- 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 +418 -149
- package/src/scripts/module-build.ts +923 -63
- 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/dist/scripts/init-app.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
90
|
-
await
|
|
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.
|
|
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:
|
|
209
|
-
console.log(chalk.white(" • pnpm dev
|
|
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
|
-
//
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
//
|
|
294
|
-
|
|
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 (
|
|
425
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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.
|
|
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.
|
|
1783
|
+
console.log(chalk.yellow("\n🔐 Création de .env.example..."));
|
|
1674
1784
|
const envContent = `# Supabase Configuration
|
|
1675
|
-
#
|
|
1785
|
+
# Copiez ce fichier vers .env.local, .env.development ou .env.production selon vos besoins
|
|
1676
1786
|
|
|
1677
|
-
# Supabase Local (
|
|
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
|
-
#
|
|
1702
|
-
|
|
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
|
-
|
|
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:
|
|
1791
|
-
"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
|
|
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":"
|
|
1
|
+
{"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAwlFA,wBAAsB,cAAc,kBAoJnC"}
|