@lastbrain/app 0.1.43 → 0.1.44
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/__tests__/module-registry.test.js +1 -1
- package/dist/layouts/AdminLayoutWithSidebar.d.ts +2 -1
- package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/AdminLayoutWithSidebar.js +10 -4
- package/dist/layouts/AuthLayoutWithSidebar.d.ts +2 -1
- package/dist/layouts/AuthLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/AuthLayoutWithSidebar.js +10 -4
- package/dist/layouts/PublicLayout.d.ts +2 -1
- package/dist/layouts/PublicLayout.d.ts.map +1 -1
- package/dist/layouts/PublicLayoutWithSidebar.d.ts +2 -1
- package/dist/layouts/PublicLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/PublicLayoutWithSidebar.js +10 -4
- package/dist/scripts/db-migrations-sync.js +67 -38
- package/dist/scripts/dev-sync.js +1 -0
- package/dist/scripts/init-app.d.ts.map +1 -1
- package/dist/scripts/init-app.js +41 -9
- package/dist/scripts/module-build.d.ts.map +1 -1
- package/dist/scripts/module-build.js +15 -5
- package/dist/scripts/module-create.d.ts.map +1 -1
- package/dist/scripts/module-create.js +19 -0
- package/dist/scripts/module-delete.d.ts.map +1 -1
- package/dist/scripts/module-delete.js +6 -2
- package/dist/scripts/module-list.d.ts.map +1 -1
- package/dist/scripts/module-list.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/templates/DefaultDoc.d.ts.map +1 -1
- package/dist/templates/DefaultDoc.js +6 -13
- package/dist/templates/DocPage.d.ts.map +1 -1
- package/dist/templates/DocPage.js +3 -5
- package/dist/templates/SimpleDocPage.js +2 -2
- package/dist/templates/SimpleHomePage.js +1 -1
- package/package.json +7 -2
- package/src/__tests__/module-registry.test.ts +1 -1
- package/src/layouts/AdminLayoutWithSidebar.tsx +13 -6
- package/src/layouts/AuthLayoutWithSidebar.tsx +13 -6
- package/src/layouts/PublicLayout.tsx +2 -2
- package/src/layouts/PublicLayoutWithSidebar.tsx +13 -6
- package/src/scripts/db-migrations-sync.ts +86 -57
- package/src/scripts/dev-sync.ts +1 -0
- package/src/scripts/init-app.ts +47 -9
- package/src/scripts/module-build.ts +17 -5
- package/src/scripts/module-create.ts +25 -2
- package/src/scripts/module-delete.ts +8 -6
- package/src/scripts/module-list.ts +1 -4
- package/src/templates/DefaultDoc.tsx +346 -492
- package/src/templates/DocPage.tsx +37 -19
- package/src/templates/SimpleDocPage.tsx +2 -2
- 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
|
|
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?:
|
|
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":"
|
|
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
|
-
(
|
|
51
|
-
|
|
52
|
-
|
|
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:
|
|
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?:
|
|
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":"
|
|
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
|
-
(
|
|
49
|
-
|
|
50
|
-
|
|
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:
|
|
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?:
|
|
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":"
|
|
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?:
|
|
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":"
|
|
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
|
-
(
|
|
49
|
-
|
|
50
|
-
|
|
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:
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
51
|
-
|
|
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();
|
package/dist/scripts/dev-sync.js
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/scripts/init-app.js
CHANGED
|
@@ -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:
|
|
145
|
-
console.log(chalk.white(" 5. pnpm
|
|
146
|
-
console.log(chalk.white("
|
|
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 /
|
|
835
|
+
// Pas de session → nettoyage des cookies + redirection vers /signin
|
|
807
836
|
if (!session) {
|
|
808
|
-
const redirectUrl = new 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("/
|
|
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 /
|
|
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("/
|
|
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":"
|
|
1
|
+
{"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAy4CA,wBAAsB,cAAc,kBA+FnC"}
|
|
@@ -29,7 +29,10 @@ async function loadModuleConfigs() {
|
|
|
29
29
|
}
|
|
30
30
|
const packageName = module.package;
|
|
31
31
|
try {
|
|
32
|
-
|
|
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,10 @@ 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-pro)
|
|
123
126
|
const modulePrefix = moduleConfig.moduleName
|
|
124
127
|
.replace(/^@lastbrain\/module-/, "")
|
|
128
|
+
.replace(/^@lastbrain-labs\/module-/, "")
|
|
125
129
|
.toLowerCase();
|
|
126
130
|
if (isDebugMode) {
|
|
127
131
|
console.log(`🔄 Building page for module ${modulePrefix}: ${page.path}`);
|
|
@@ -468,7 +472,13 @@ function generateDocsPage(moduleConfigs) {
|
|
|
468
472
|
const moduleConfigurations = [];
|
|
469
473
|
allModules.forEach((moduleEntry) => {
|
|
470
474
|
const moduleName = moduleEntry.package;
|
|
471
|
-
|
|
475
|
+
// Extraire le nom du module sans le scope et sans "module-"
|
|
476
|
+
// Ex: @lastbrain/module-auth -> auth
|
|
477
|
+
// Ex: @lastbrain-labs/module-recipes-pro -> recipes
|
|
478
|
+
const moduleId = moduleName
|
|
479
|
+
.replace("@lastbrain-labs/module-", "")
|
|
480
|
+
.replace("@lastbrain/module-", "")
|
|
481
|
+
.replace(/-pro$/, ""); // Retirer le suffix -pro pour avoir le nom de base
|
|
472
482
|
const docComponentName = `${toPascalCase(moduleId)}ModuleDoc`;
|
|
473
483
|
// Trouver la config du module pour obtenir la description
|
|
474
484
|
const moduleConfig = moduleConfigs.find((mc) => mc.moduleName === moduleName);
|
|
@@ -480,8 +490,8 @@ function generateDocsPage(moduleConfigs) {
|
|
|
480
490
|
docImports.push(`import { ${docComponentName} } from "${moduleName}";`);
|
|
481
491
|
}
|
|
482
492
|
const config = {
|
|
483
|
-
id:
|
|
484
|
-
name: `Module ${
|
|
493
|
+
id: moduleName, // Utiliser le nom complet du package comme ID
|
|
494
|
+
name: `Module ${toPascalCase(moduleId)}`,
|
|
485
495
|
description: description,
|
|
486
496
|
component: docComponentName,
|
|
487
497
|
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;
|
|
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,
|
|
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: "
|
|
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
|
-
|
|
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":"
|
|
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
|
|
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
|