@lastbrain/app 0.1.43 → 0.1.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/__tests__/module-registry.test.js +1 -1
  2. package/dist/layouts/AdminLayoutWithSidebar.d.ts +2 -1
  3. package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -1
  4. package/dist/layouts/AdminLayoutWithSidebar.js +10 -4
  5. package/dist/layouts/AuthLayoutWithSidebar.d.ts +2 -1
  6. package/dist/layouts/AuthLayoutWithSidebar.d.ts.map +1 -1
  7. package/dist/layouts/AuthLayoutWithSidebar.js +10 -4
  8. package/dist/layouts/PublicLayout.d.ts +2 -1
  9. package/dist/layouts/PublicLayout.d.ts.map +1 -1
  10. package/dist/layouts/PublicLayoutWithSidebar.d.ts +2 -1
  11. package/dist/layouts/PublicLayoutWithSidebar.d.ts.map +1 -1
  12. package/dist/layouts/PublicLayoutWithSidebar.js +10 -4
  13. package/dist/scripts/db-migrations-sync.js +67 -38
  14. package/dist/scripts/dev-sync.js +1 -0
  15. package/dist/scripts/init-app.d.ts.map +1 -1
  16. package/dist/scripts/init-app.js +41 -9
  17. package/dist/scripts/module-build.d.ts.map +1 -1
  18. package/dist/scripts/module-build.js +17 -5
  19. package/dist/scripts/module-create.d.ts.map +1 -1
  20. package/dist/scripts/module-create.js +19 -0
  21. package/dist/scripts/module-delete.d.ts.map +1 -1
  22. package/dist/scripts/module-delete.js +6 -2
  23. package/dist/scripts/module-list.d.ts.map +1 -1
  24. package/dist/scripts/module-list.js +1 -1
  25. package/dist/styles.css +1 -1
  26. package/dist/templates/DefaultDoc.d.ts.map +1 -1
  27. package/dist/templates/DefaultDoc.js +6 -13
  28. package/dist/templates/DocPage.d.ts.map +1 -1
  29. package/dist/templates/DocPage.js +3 -5
  30. package/dist/templates/SimpleDocPage.js +2 -2
  31. package/dist/templates/SimpleHomePage.js +1 -1
  32. package/package.json +9 -3
  33. package/src/__tests__/module-registry.test.ts +1 -1
  34. package/src/layouts/AdminLayoutWithSidebar.tsx +13 -6
  35. package/src/layouts/AuthLayoutWithSidebar.tsx +13 -6
  36. package/src/layouts/PublicLayout.tsx +2 -2
  37. package/src/layouts/PublicLayoutWithSidebar.tsx +13 -6
  38. package/src/scripts/db-migrations-sync.ts +86 -57
  39. package/src/scripts/dev-sync.ts +1 -0
  40. package/src/scripts/init-app.ts +47 -9
  41. package/src/scripts/module-build.ts +19 -5
  42. package/src/scripts/module-create.ts +25 -2
  43. package/src/scripts/module-delete.ts +8 -6
  44. package/src/scripts/module-list.ts +1 -4
  45. package/src/templates/DefaultDoc.tsx +346 -492
  46. package/src/templates/DocPage.tsx +37 -19
  47. package/src/templates/SimpleDocPage.tsx +2 -2
  48. package/src/templates/SimpleHomePage.tsx +3 -3
@@ -46,7 +46,7 @@ describe("Module Registry", () => {
46
46
  });
47
47
  it("should have valid package names", () => {
48
48
  AVAILABLE_MODULES.forEach((module) => {
49
- expect(module.package).toMatch(/^@lastbrain\/module-/);
49
+ expect(module.package).toMatch(/^@lastbrain(-labs)?\/module-/);
50
50
  });
51
51
  });
52
52
  });
@@ -1,6 +1,7 @@
1
+ import { type MenuConfig } from "@lastbrain/ui";
1
2
  interface AdminLayoutWithSidebarProps {
2
3
  children: React.ReactNode;
3
- menuConfig?: any;
4
+ menuConfig?: MenuConfig;
4
5
  className?: string;
5
6
  }
6
7
  export declare function AdminLayoutWithSidebar({ children, menuConfig, className, }: AdminLayoutWithSidebarProps): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"AdminLayoutWithSidebar.d.ts","sourceRoot":"","sources":["../../src/layouts/AdminLayoutWithSidebar.tsx"],"names":[],"mappings":"AAQA,UAAU,2BAA2B;IACnC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,UAAU,EACV,SAAc,GACf,EAAE,2BAA2B,2CA0F7B"}
1
+ {"version":3,"file":"AdminLayoutWithSidebar.d.ts","sourceRoot":"","sources":["../../src/layouts/AdminLayoutWithSidebar.tsx"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAK5E,UAAU,2BAA2B;IACnC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,UAAU,EACV,SAAc,GACf,EAAE,2BAA2B,2CAiG7B"}
@@ -44,13 +44,19 @@ export function AdminLayoutWithSidebar({ children, menuConfig, className = "", }
44
44
  }, []);
45
45
  // Détecter si on est dans la section admin pour le skeleton
46
46
  const isAdminSection = pathname.startsWith("/admin");
47
+ // Créer un menuConfig sûr avec des valeurs par défaut
48
+ const safeMenuConfig = {
49
+ public: menuConfig?.public || [],
50
+ auth: menuConfig?.auth || [],
51
+ admin: menuConfig?.admin || [],
52
+ };
47
53
  // Vérifier si menuConfig est vraiment disponible et valide
48
54
  const hasValidMenuConfig = menuConfig &&
49
55
  typeof menuConfig === "object" &&
50
- (menuConfig.admin?.length > 0 ||
51
- menuConfig.auth?.length > 0 ||
52
- menuConfig.public?.length > 0);
56
+ (safeMenuConfig.admin.length > 0 ||
57
+ safeMenuConfig.auth.length > 0 ||
58
+ safeMenuConfig.public.length > 0);
53
59
  // Afficher le skeleton pendant le chargement de la session ou si pas de menuConfig valide
54
60
  const shouldShowSkeleton = loading || !hasValidMenuConfig;
55
- return (_jsxs("div", { className: "flex min-h-screen", children: [shouldShowSkeleton ? (_jsx(AppAsideSkeleton, { className: className, isAdminSection: isAdminSection })) : (_jsx(AppAside, { menuConfig: menuConfig, isSuperAdmin: isSuperAdmin, isAuthenticated: !!user, className: className })), _jsx("div", { className: `flex-1 transition-all duration-300 ${!mounted ? "lg:ml-72" : isCollapsed ? "lg:ml-20" : "lg:ml-72"}`, children: _jsx(AdminLayout, { children: children }) })] }));
61
+ return (_jsxs("div", { className: "flex min-h-screen", children: [shouldShowSkeleton ? (_jsx(AppAsideSkeleton, { className: className, isAdminSection: isAdminSection })) : (_jsx(AppAside, { menuConfig: safeMenuConfig, isSuperAdmin: isSuperAdmin, isAuthenticated: !!user, className: className })), _jsx("div", { className: `flex-1 transition-all duration-300 ${!mounted ? "lg:ml-72" : isCollapsed ? "lg:ml-20" : "lg:ml-72"}`, children: _jsx(AdminLayout, { children: children }) })] }));
56
62
  }
@@ -1,6 +1,7 @@
1
+ import { type MenuConfig } from "@lastbrain/ui";
1
2
  interface AuthLayoutWithSidebarProps {
2
3
  children: React.ReactNode;
3
- menuConfig?: any;
4
+ menuConfig?: MenuConfig;
4
5
  className?: string;
5
6
  }
6
7
  export declare function AuthLayoutWithSidebar({ children, menuConfig, className, }: AuthLayoutWithSidebarProps): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"AuthLayoutWithSidebar.d.ts","sourceRoot":"","sources":["../../src/layouts/AuthLayoutWithSidebar.tsx"],"names":[],"mappings":"AAOA,UAAU,0BAA0B;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,UAAU,EACV,SAAc,GACf,EAAE,0BAA0B,2CAwF5B"}
1
+ {"version":3,"file":"AuthLayoutWithSidebar.d.ts","sourceRoot":"","sources":["../../src/layouts/AuthLayoutWithSidebar.tsx"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAI5E,UAAU,0BAA0B;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,UAAU,EACV,SAAc,GACf,EAAE,0BAA0B,2CA+F5B"}
@@ -42,13 +42,19 @@ export function AuthLayoutWithSidebar({ children, menuConfig, className = "", })
42
42
  }, []);
43
43
  // Pour la section auth, isAdminSection sera false
44
44
  const isAdminSection = false;
45
+ // Créer un menuConfig sûr avec des valeurs par défaut
46
+ const safeMenuConfig = {
47
+ public: menuConfig?.public || [],
48
+ auth: menuConfig?.auth || [],
49
+ admin: menuConfig?.admin || [],
50
+ };
45
51
  // Vérifier si menuConfig est vraiment disponible et valide
46
52
  const hasValidMenuConfig = menuConfig &&
47
53
  typeof menuConfig === "object" &&
48
- (menuConfig.admin?.length > 0 ||
49
- menuConfig.auth?.length > 0 ||
50
- menuConfig.public?.length > 0);
54
+ (safeMenuConfig.admin.length > 0 ||
55
+ safeMenuConfig.auth.length > 0 ||
56
+ safeMenuConfig.public.length > 0);
51
57
  // Afficher le skeleton pendant le chargement de la session ou si pas de menuConfig valide
52
58
  const shouldShowSkeleton = loading || !hasValidMenuConfig;
53
- return (_jsxs("div", { className: "flex min-h-screen", children: [shouldShowSkeleton ? (_jsx(AppAsideSkeleton, { className: className, isAdminSection: isAdminSection })) : (_jsx(AppAside, { menuConfig: menuConfig, isSuperAdmin: isSuperAdmin, isAuthenticated: !!user, className: className })), _jsx("div", { className: `flex-1 transition-all duration-300 ${!mounted ? "lg:ml-72" : isCollapsed ? "lg:ml-20" : "lg:ml-72"}`, children: _jsx(AuthLayout, { children: children }) })] }));
59
+ return (_jsxs("div", { className: "flex min-h-screen", children: [shouldShowSkeleton ? (_jsx(AppAsideSkeleton, { className: className, isAdminSection: isAdminSection })) : (_jsx(AppAside, { menuConfig: safeMenuConfig, isSuperAdmin: isSuperAdmin, isAuthenticated: !!user, className: className })), _jsx("div", { className: `flex-1 transition-all duration-300 ${!mounted ? "lg:ml-72" : isCollapsed ? "lg:ml-20" : "lg:ml-72"}`, children: _jsx(AuthLayout, { children: children }) })] }));
54
60
  }
@@ -1,6 +1,7 @@
1
+ import { type FooterConfig } from "@lastbrain/ui";
1
2
  interface PublicLayoutProps {
2
3
  children: React.ReactNode;
3
- footerConfig?: any;
4
+ footerConfig?: FooterConfig;
4
5
  }
5
6
  export declare function PublicLayout({ children, footerConfig }: PublicLayoutProps): import("react/jsx-runtime").JSX.Element;
6
7
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"PublicLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/PublicLayout.tsx"],"names":[],"mappings":"AAIA,UAAU,iBAAiB;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,iBAAiB,2CAOzE"}
1
+ {"version":3,"file":"PublicLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/PublicLayout.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAE1D,UAAU,iBAAiB;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,iBAAiB,2CAOzE"}
@@ -1,6 +1,7 @@
1
+ import { type MenuConfig } from "@lastbrain/ui";
1
2
  interface PublicLayoutWithSidebarProps {
2
3
  children: React.ReactNode;
3
- menuConfig?: any;
4
+ menuConfig?: MenuConfig;
4
5
  className?: string;
5
6
  }
6
7
  export declare function PublicLayoutWithSidebar({ children, menuConfig, className, }: PublicLayoutWithSidebarProps): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"PublicLayoutWithSidebar.d.ts","sourceRoot":"","sources":["../../src/layouts/PublicLayoutWithSidebar.tsx"],"names":[],"mappings":"AAOA,UAAU,4BAA4B;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,UAAU,EACV,SAAc,GACf,EAAE,4BAA4B,2CAwF9B"}
1
+ {"version":3,"file":"PublicLayoutWithSidebar.d.ts","sourceRoot":"","sources":["../../src/layouts/PublicLayoutWithSidebar.tsx"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAI5E,UAAU,4BAA4B;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,UAAU,EACV,SAAc,GACf,EAAE,4BAA4B,2CA+F9B"}
@@ -42,13 +42,19 @@ export function PublicLayoutWithSidebar({ children, menuConfig, className = "",
42
42
  }, []);
43
43
  // Pour la section public, isAdminSection sera false
44
44
  const isAdminSection = false;
45
+ // Créer un menuConfig sûr avec des valeurs par défaut
46
+ const safeMenuConfig = {
47
+ public: menuConfig?.public || [],
48
+ auth: menuConfig?.auth || [],
49
+ admin: menuConfig?.admin || [],
50
+ };
45
51
  // Vérifier si menuConfig est vraiment disponible et valide
46
52
  const hasValidMenuConfig = menuConfig &&
47
53
  typeof menuConfig === "object" &&
48
- (menuConfig.admin?.length > 0 ||
49
- menuConfig.auth?.length > 0 ||
50
- menuConfig.public?.length > 0);
54
+ (safeMenuConfig.admin.length > 0 ||
55
+ safeMenuConfig.auth.length > 0 ||
56
+ safeMenuConfig.public.length > 0);
51
57
  // Afficher le skeleton pendant le chargement de la session ou si pas de menuConfig valide
52
58
  const shouldShowSkeleton = loading || !hasValidMenuConfig;
53
- return (_jsxs("div", { className: "flex min-h-screen", children: [shouldShowSkeleton ? (_jsx(AppAsideSkeleton, { className: className, isAdminSection: isAdminSection })) : (_jsx(AppAside, { menuConfig: menuConfig, isSuperAdmin: false, isAuthenticated: !!user, className: className })), _jsx("div", { className: `flex-1 transition-all duration-300 ${!mounted ? "lg:ml-72" : isCollapsed ? "lg:ml-20" : "lg:ml-72"}`, children: _jsx(PublicLayout, { children: children }) })] }));
59
+ return (_jsxs("div", { className: "flex min-h-screen", children: [shouldShowSkeleton ? (_jsx(AppAsideSkeleton, { className: className, isAdminSection: isAdminSection })) : (_jsx(AppAside, { menuConfig: safeMenuConfig, isSuperAdmin: false, isAuthenticated: !!user, className: className })), _jsx("div", { className: `flex-1 transition-all duration-300 ${!mounted ? "lg:ml-72" : isCollapsed ? "lg:ml-20" : "lg:ml-72"}`, children: _jsx(PublicLayout, { children: children }) })] }));
54
60
  }
@@ -1,55 +1,84 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { getModuleConfigs } from "../modules/module-loader.js";
4
3
  // Utiliser PROJECT_ROOT si défini (pour pnpm --filter), sinon process.cwd()
5
4
  const projectRoot = process.env.PROJECT_ROOT || process.cwd();
6
5
  const migrationsDir = path.join(projectRoot, "supabase/migrations");
7
6
  function ensureDirectory(dir) {
8
7
  fs.mkdirSync(dir, { recursive: true });
9
8
  }
10
- var _CopyStrategy;
11
- (function (_CopyStrategy) {
12
- _CopyStrategy[_CopyStrategy["overwrite"] = 0] = "overwrite";
13
- _CopyStrategy[_CopyStrategy["skip"] = 1] = "skip";
14
- })(_CopyStrategy || (_CopyStrategy = {}));
15
- function getModulePackageDir(moduleName) {
16
- return path.join(projectRoot, "node_modules", ...moduleName.split("/"));
17
- }
18
- function copyMigration(moduleConfig, migration, file, index) {
19
- const moduleDir = getModulePackageDir(moduleConfig.moduleName);
20
- const migrationsPath = migration.path ?? "supabase/migrations";
21
- const sourceDir = path.join(moduleDir, migrationsPath);
22
- const sourceFile = path.join(sourceDir, file);
23
- if (!fs.existsSync(sourceFile)) {
24
- console.warn(`⚠️ Missing migration file ${sourceFile}`);
25
- return;
9
+ /**
10
+ * Copie les migrations d'un module vers supabase/migrations
11
+ */
12
+ function copyModuleMigrations(moduleName, moduleDir) {
13
+ const sourceMigrationsDir = path.join(moduleDir, "supabase", "migrations");
14
+ if (!fs.existsSync(sourceMigrationsDir)) {
15
+ return 0;
16
+ }
17
+ const files = fs
18
+ .readdirSync(sourceMigrationsDir)
19
+ .filter((file) => file.endsWith(".sql"))
20
+ .sort();
21
+ if (files.length === 0) {
22
+ return 0;
26
23
  }
27
24
  ensureDirectory(migrationsDir);
28
- const moduleSlug = moduleConfig.moduleName.replace("@lastbrain/", "module-");
29
- const prefix = String((migration.priority ?? 0) * 10 + index + 1).padStart(3, "0");
30
- const fileName = `${prefix}_${moduleSlug}_${file}`;
31
- const targetFile = path.join(migrationsDir, fileName);
32
- fs.copyFileSync(sourceFile, targetFile);
33
- console.log(`📜 Copied migration ${fileName}`);
25
+ let copiedCount = 0;
26
+ files.forEach((file) => {
27
+ const sourceFile = path.join(sourceMigrationsDir, file);
28
+ const targetFile = path.join(migrationsDir, file);
29
+ // Ne copier que si le fichier n'existe pas déjà
30
+ if (!fs.existsSync(targetFile)) {
31
+ fs.copyFileSync(sourceFile, targetFile);
32
+ console.log(`📜 Copied migration: ${file} from ${moduleName}`);
33
+ copiedCount++;
34
+ }
35
+ else {
36
+ console.log(`⏭️ Migration already exists: ${file}`);
37
+ }
38
+ });
39
+ return copiedCount;
34
40
  }
41
+ /**
42
+ * Synchronise les migrations de tous les modules actifs
43
+ */
35
44
  function syncModuleMigrations() {
36
- const moduleConfigs = getModuleConfigs();
37
- moduleConfigs.forEach((moduleConfig) => {
38
- const migrationBlock = moduleConfig.migrations;
39
- if (!migrationBlock || !migrationBlock.enabled) {
40
- return;
45
+ // Charger modules.json
46
+ const modulesJsonPath = path.join(projectRoot, ".lastbrain", "modules.json");
47
+ if (!fs.existsSync(modulesJsonPath)) {
48
+ console.error("❌ modules.json not found");
49
+ return;
50
+ }
51
+ const modulesData = JSON.parse(fs.readFileSync(modulesJsonPath, "utf-8"));
52
+ const modules = modulesData.modules || [];
53
+ // Filtrer les modules actifs
54
+ const activeModules = modules.filter((m) => m.active !== false);
55
+ console.log(`🔄 Syncing migrations from ${activeModules.length} active module(s)...\n`);
56
+ let totalCopied = 0;
57
+ activeModules.forEach((module) => {
58
+ const moduleName = module.package;
59
+ // Essayer plusieurs chemins possibles pour trouver le module
60
+ const possiblePaths = [
61
+ // Dans node_modules local de l'app
62
+ path.join(projectRoot, "node_modules", moduleName),
63
+ // Dans node_modules à la racine du workspace
64
+ path.join(projectRoot, "..", "..", "node_modules", moduleName),
65
+ // Directement dans packages/ (pour monorepo)
66
+ path.join(projectRoot, "..", "..", "packages", moduleName.replace("@lastbrain/", "").replace("@lastbrain-labs/", "")),
67
+ ];
68
+ let moduleDir = null;
69
+ for (const possiblePath of possiblePaths) {
70
+ if (fs.existsSync(possiblePath)) {
71
+ moduleDir = possiblePath;
72
+ break;
73
+ }
41
74
  }
42
- const moduleMigrationPath = path.join(getModulePackageDir(moduleConfig.moduleName), migrationBlock.path ?? "supabase/migrations");
43
- let files = migrationBlock.files;
44
- if (!files?.length && fs.existsSync(moduleMigrationPath)) {
45
- files = fs
46
- .readdirSync(moduleMigrationPath)
47
- .filter((file) => file.endsWith(".sql"))
48
- .sort();
75
+ if (!moduleDir) {
76
+ console.warn(`⚠️ Module not found: ${moduleName}`);
77
+ return;
49
78
  }
50
- files?.forEach((file, index) => {
51
- copyMigration(moduleConfig, migrationBlock, file, index);
52
- });
79
+ const copied = copyModuleMigrations(moduleName, moduleDir);
80
+ totalCopied += copied;
53
81
  });
82
+ console.log(`\n✅ Migration sync completed: ${totalCopied} file(s) copied`);
54
83
  }
55
84
  syncModuleMigrations();
@@ -20,6 +20,7 @@ const dependenciesToEnsure = {
20
20
  "@lastbrain/app": "workspace:*",
21
21
  "@lastbrain/core": "workspace:*",
22
22
  "@lastbrain/ui": "workspace:*",
23
+ "@lastbrain/module-auth": "workspace:*",
23
24
  };
24
25
  const gitignoreTemplate = path.join(projectRoot, "packages/app/src/templates/gitignore/.gitignore");
25
26
  const consumerGitignore = path.join(projectRoot, "apps/web/.gitignore");
@@ -1 +1 @@
1
- {"version":3,"file":"init-app.d.ts","sourceRoot":"","sources":["../../src/scripts/init-app.ts"],"names":[],"mappings":"AAcA,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,iBAyMpD"}
1
+ {"version":3,"file":"init-app.d.ts","sourceRoot":"","sources":["../../src/scripts/init-app.ts"],"names":[],"mappings":"AAcA,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,iBA0NpD"}
@@ -90,6 +90,17 @@ export async function initApp(options) {
90
90
  console.log(chalk.yellow("🔧 Génération des routes des modules...\n"));
91
91
  execSync("pnpm build:modules", { cwd: targetDir, stdio: "inherit" });
92
92
  console.log(chalk.green("\n✓ Routes des modules générées\n"));
93
+ console.log(chalk.yellow("📜 Synchronisation des migrations des modules...\n"));
94
+ try {
95
+ execSync("pnpm db:migrations:sync", {
96
+ cwd: targetDir,
97
+ stdio: "inherit",
98
+ });
99
+ console.log(chalk.green("\n✓ Migrations synchronisées\n"));
100
+ }
101
+ catch (error) {
102
+ console.log(chalk.yellow("\n⚠️ Erreur de synchronisation des migrations\n"));
103
+ }
93
104
  console.log(chalk.yellow("🗄️ Initialisation de la base de données...\n"));
94
105
  try {
95
106
  execSync("pnpm db:init", { cwd: targetDir, stdio: "inherit" });
@@ -132,6 +143,7 @@ export async function initApp(options) {
132
143
  console.log(chalk.white(` cd ${relativePath}`));
133
144
  console.log(chalk.white(" pnpm install"));
134
145
  console.log(chalk.white(" pnpm build:modules"));
146
+ console.log(chalk.white(" pnpm db:migrations:sync"));
135
147
  console.log(chalk.white(" pnpm db:init"));
136
148
  console.log(chalk.white(" pnpm dev\n"));
137
149
  }
@@ -141,10 +153,9 @@ export async function initApp(options) {
141
153
  console.log(chalk.white(" 1. cd " + relativePath));
142
154
  console.log(chalk.white(" 2. pnpm install (installer les dépendances)"));
143
155
  console.log(chalk.white(" 3. pnpm build:modules (générer les routes des modules)"));
144
- console.log(chalk.white(" 4. pnpm db:init (initialiser la base de données)"));
145
- console.log(chalk.white(" 5. pnpm dev (lancer le serveur)"));
146
- console.log(chalk.white(" 4. pnpm db:init (initialiser Supabase)"));
147
- console.log(chalk.white(" 5. pnpm dev (démarrer le serveur)\n"));
156
+ console.log(chalk.white(" 4. pnpm db:migrations:sync (synchroniser les migrations)"));
157
+ console.log(chalk.white(" 5. pnpm db:init (initialiser la base de données)"));
158
+ console.log(chalk.white(" 6. pnpm dev (lancer le serveur)\n"));
148
159
  console.log(chalk.gray("Prérequis pour Supabase :"));
149
160
  console.log(chalk.white(" - Docker Desktop installé et lancé"));
150
161
  console.log(chalk.white(" - Supabase CLI : brew install supabase/tap/supabase\n"));
@@ -795,6 +806,24 @@ export async function middleware(request: NextRequest) {
795
806
  const { pathname } = request.nextUrl;
796
807
  const isApi = pathname.startsWith("/api/");
797
808
 
809
+ // Pages publiques d'authentification (ne pas protéger)
810
+ const publicAuthPages = [
811
+ "/signin",
812
+ "/signup",
813
+ "/reset-password",
814
+ "/forgot-password",
815
+ "/callback",
816
+ ];
817
+
818
+ const isPublicAuthPage = publicAuthPages.some((page) =>
819
+ pathname.startsWith(page)
820
+ );
821
+
822
+ // Ne pas protéger les pages publiques d'authentification
823
+ if (isPublicAuthPage) {
824
+ return NextResponse.next();
825
+ }
826
+
798
827
  // Protéger les routes /auth/* (espace membre)
799
828
  if (pathname.startsWith("/auth")) {
800
829
  try {
@@ -803,9 +832,9 @@ export async function middleware(request: NextRequest) {
803
832
  data: { session },
804
833
  } = await supabase.auth.getSession();
805
834
 
806
- // Pas de session → nettoyage des cookies + redirection vers /auth/signin
835
+ // Pas de session → nettoyage des cookies + redirection vers /signin
807
836
  if (!session) {
808
- const redirectUrl = new URL("/auth/signin", request.url);
837
+ const redirectUrl = new URL("/signin", request.url);
809
838
  redirectUrl.searchParams.set("redirect", pathname);
810
839
  const res = NextResponse.redirect(redirectUrl);
811
840
  res.cookies.delete("sb-access-token");
@@ -818,7 +847,7 @@ export async function middleware(request: NextRequest) {
818
847
  return response;
819
848
  } catch (error) {
820
849
  console.error("Middleware auth error:", error);
821
- const res = NextResponse.redirect(new URL("/auth/signin", request.url));
850
+ const res = NextResponse.redirect(new URL("/signin", request.url));
822
851
  res.cookies.delete("sb-access-token");
823
852
  res.cookies.delete("sb-refresh-token");
824
853
  res.cookies.delete("sb:token");
@@ -835,7 +864,7 @@ export async function middleware(request: NextRequest) {
835
864
  data: { session },
836
865
  } = await supabase.auth.getSession();
837
866
 
838
- // Pas de session → 401 JSON pour API, sinon redirection vers /auth/signin avec nettoyage cookies
867
+ // Pas de session → 401 JSON pour API, sinon redirection vers /signin avec nettoyage cookies
839
868
  if (!session) {
840
869
  if (isApi) {
841
870
  const res = NextResponse.json({ error: "Non authentifié" }, { status: 401 });
@@ -845,7 +874,7 @@ export async function middleware(request: NextRequest) {
845
874
  res.cookies.delete("sb:refresh-token");
846
875
  return res;
847
876
  }
848
- const redirectUrl = new URL("/auth/signin", request.url);
877
+ const redirectUrl = new URL("/signin", request.url);
849
878
  redirectUrl.searchParams.set("redirect", pathname);
850
879
  const res = NextResponse.redirect(redirectUrl);
851
880
  res.cookies.delete("sb-access-token");
@@ -909,6 +938,9 @@ export const config = {
909
938
  const nextConfig = `/** @type {import('next').NextConfig} */
910
939
  const nextConfig = {
911
940
  reactStrictMode: true,
941
+ devIndicators: {
942
+ position: 'bottom-right',
943
+ },
912
944
  };
913
945
 
914
946
  export default nextConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AA63CA,wBAAsB,cAAc,kBA+FnC"}
1
+ {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AA24CA,wBAAsB,cAAc,kBA+FnC"}
@@ -29,7 +29,10 @@ async function loadModuleConfigs() {
29
29
  }
30
30
  const packageName = module.package;
31
31
  try {
32
- const moduleSuffix = packageName.replace("@lastbrain/module-", "");
32
+ // Extraire le suffix du module (la partie après @lastbrain/module- ou @lastbrain-labs/module-)
33
+ const moduleSuffix = packageName
34
+ .replace("@lastbrain/module-", "")
35
+ .replace("@lastbrain-labs/module-", "");
33
36
  const possibleConfigNames = [
34
37
  `${moduleSuffix}.build.config`,
35
38
  "build.config",
@@ -119,9 +122,12 @@ function toPascalCase(value) {
119
122
  .join("");
120
123
  }
121
124
  function buildPage(moduleConfig, page) {
122
- // Extraire le préfixe du module (ex: @lastbrain/module-auth -> auth)
125
+ // Extraire le préfixe du module (ex: @lastbrain/module-auth -> auth, @lastbrain-labs/module-recipes-pro -> recipes)
126
+ // Enlever -pro pour les routes auth et admin afin d'avoir des chemins propres
123
127
  const modulePrefix = moduleConfig.moduleName
124
128
  .replace(/^@lastbrain\/module-/, "")
129
+ .replace(/^@lastbrain-labs\/module-/, "")
130
+ .replace(/-pro$/, "") // Enlever le suffixe -pro
125
131
  .toLowerCase();
126
132
  if (isDebugMode) {
127
133
  console.log(`🔄 Building page for module ${modulePrefix}: ${page.path}`);
@@ -468,7 +474,13 @@ function generateDocsPage(moduleConfigs) {
468
474
  const moduleConfigurations = [];
469
475
  allModules.forEach((moduleEntry) => {
470
476
  const moduleName = moduleEntry.package;
471
- const moduleId = moduleName.replace("@lastbrain/module-", "");
477
+ // Extraire le nom du module sans le scope et sans "module-"
478
+ // Ex: @lastbrain/module-auth -> auth
479
+ // Ex: @lastbrain-labs/module-recipes-pro -> recipes
480
+ const moduleId = moduleName
481
+ .replace("@lastbrain-labs/module-", "")
482
+ .replace("@lastbrain/module-", "")
483
+ .replace(/-pro$/, ""); // Retirer le suffix -pro pour avoir le nom de base
472
484
  const docComponentName = `${toPascalCase(moduleId)}ModuleDoc`;
473
485
  // Trouver la config du module pour obtenir la description
474
486
  const moduleConfig = moduleConfigs.find((mc) => mc.moduleName === moduleName);
@@ -480,8 +492,8 @@ function generateDocsPage(moduleConfigs) {
480
492
  docImports.push(`import { ${docComponentName} } from "${moduleName}";`);
481
493
  }
482
494
  const config = {
483
- id: moduleId,
484
- name: `Module ${moduleId.charAt(0).toUpperCase() + moduleId.slice(1)}`,
495
+ id: moduleName, // Utiliser le nom complet du package comme ID
496
+ name: `Module ${toPascalCase(moduleId)}`,
485
497
  description: description,
486
498
  component: docComponentName,
487
499
  active: moduleEntry.active,
@@ -1 +1 @@
1
- {"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AA+vCD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAiB1C;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,iBAoLhB;AAED;;GAEG;AACH,wBAAsB,YAAY,kBAwKjC"}
1
+ {"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAwwCD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAiB1C;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,iBAoLhB;AAED;;GAEG;AACH,wBAAsB,YAAY,kBAsLjC"}
@@ -911,6 +911,11 @@ async function generateModuleReadme(config, moduleDir) {
911
911
  const moduleNameClean = config.slug.replace("module-", "");
912
912
  let md = `# 📦 Module ${moduleNameClean}\n\n`;
913
913
  md += `> ${config.moduleName}\n\n`;
914
+ // Description section
915
+ if (config.description) {
916
+ md += `## 📝 Description\n\n`;
917
+ md += `${config.description}\n\n`;
918
+ }
914
919
  // Information section
915
920
  md += `## 📋 Informations\n\n`;
916
921
  md += `- **Nom du package**: \`${config.moduleName}\`\n`;
@@ -1286,6 +1291,18 @@ export async function createModule() {
1286
1291
  },
1287
1292
  filter: (input) => input.trim().toLowerCase(),
1288
1293
  },
1294
+ {
1295
+ type: "input",
1296
+ name: "description",
1297
+ message: "Description du module (une ligne):",
1298
+ default: "Module LastBrain",
1299
+ validate: (input) => {
1300
+ if (!input || input.trim() === "") {
1301
+ return "La description est requise";
1302
+ }
1303
+ return true;
1304
+ },
1305
+ },
1289
1306
  {
1290
1307
  type: "input",
1291
1308
  name: "pagesPublic",
@@ -1314,6 +1331,7 @@ export async function createModule() {
1314
1331
  // Construire la configuration du module
1315
1332
  const slug = `module-${answers.slug}`;
1316
1333
  const moduleName = `@lastbrain/${slug}`;
1334
+ const description = answers.description;
1317
1335
  const pages = [];
1318
1336
  // Pages publiques
1319
1337
  const publicPages = parsePagesList(answers.pagesPublic);
@@ -1388,6 +1406,7 @@ export async function createModule() {
1388
1406
  moduleName,
1389
1407
  pages,
1390
1408
  tables,
1409
+ description,
1391
1410
  };
1392
1411
  // Trouver le répertoire racine du workspace (chercher pnpm-workspace.yaml)
1393
1412
  let rootDir;
@@ -1 +1 @@
1
- {"version":3,"file":"module-delete.d.ts","sourceRoot":"","sources":["../../src/scripts/module-delete.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,wBAAsB,YAAY,kBAgLjC"}
1
+ {"version":3,"file":"module-delete.d.ts","sourceRoot":"","sources":["../../src/scripts/module-delete.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,wBAAsB,YAAY,kBAkLjC"}
@@ -22,7 +22,7 @@ export async function deleteModule() {
22
22
  }
23
23
  const answers = await inquirer.prompt([
24
24
  {
25
- type: "list",
25
+ type: "select",
26
26
  name: "moduleName",
27
27
  message: "Quel module voulez-vous supprimer du monorepo ?",
28
28
  choices: AVAILABLE_MODULES.map((m) => ({
@@ -49,7 +49,11 @@ export async function deleteModule() {
49
49
  }
50
50
  console.log(chalk.blue(`\n🗑️ Suppression du module ${moduleMeta.package}...\n`));
51
51
  // 1. Supprimer le répertoire du module
52
- const moduleDir = path.join(rootDir, "packages", moduleMeta.package.replace("@lastbrain/", ""));
52
+ // Extraire le nom du package correctement (gérer @lastbrain et @lastbrain-labs)
53
+ const packageName = moduleMeta.package
54
+ .replace("@lastbrain-labs/", "")
55
+ .replace("@lastbrain/", "");
56
+ const moduleDir = path.join(rootDir, "packages", packageName);
53
57
  if (fs.existsSync(moduleDir)) {
54
58
  console.log(chalk.yellow(`📁 Suppression du répertoire: ${moduleDir}`));
55
59
  await fs.remove(moduleDir);
@@ -1 +1 @@
1
- {"version":3,"file":"module-list.d.ts","sourceRoot":"","sources":["../../src/scripts/module-list.ts"],"names":[],"mappings":"AAQA,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,iBAwElD"}
1
+ {"version":3,"file":"module-list.d.ts","sourceRoot":"","sources":["../../src/scripts/module-list.ts"],"names":[],"mappings":"AAKA,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,iBAwElD"}
@@ -1,7 +1,7 @@
1
1
  import fs from "fs-extra";
2
2
  import path from "path";
3
3
  import chalk from "chalk";
4
- import { AVAILABLE_MODULES, } from "@lastbrain/core/config/modules";
4
+ import { AVAILABLE_MODULES } from "@lastbrain/core/config/modules";
5
5
  export async function listModules(targetDir) {
6
6
  console.log(chalk.blue("\n📦 Modules disponibles:\n"));
7
7
  // Lire la config des modules installés