@lastbrain/app 0.1.37 → 0.1.40
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/cli.js +6 -1
- package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/AdminLayoutWithSidebar.js +36 -1
- package/dist/layouts/AuthLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/AuthLayoutWithSidebar.js +36 -1
- package/dist/layouts/PublicLayout.js +1 -1
- package/dist/layouts/PublicLayoutWithSidebar.d.ts.map +1 -1
- package/dist/layouts/PublicLayoutWithSidebar.js +36 -1
- package/dist/scripts/init-app.js +54 -12
- package/dist/scripts/module-add.d.ts.map +1 -1
- package/dist/scripts/module-add.js +54 -6
- package/dist/scripts/module-build.d.ts.map +1 -1
- package/dist/scripts/module-build.js +102 -39
- package/dist/scripts/module-list.d.ts.map +1 -1
- package/dist/scripts/module-list.js +38 -8
- package/dist/styles.css +1 -1
- package/dist/templates/DefaultDoc.d.ts.map +1 -1
- package/dist/templates/DefaultDoc.js +1 -1
- package/package.json +2 -2
- package/src/cli.ts +6 -1
- package/src/layouts/AdminLayoutWithSidebar.tsx +51 -1
- package/src/layouts/AuthLayoutWithSidebar.tsx +51 -1
- package/src/layouts/PublicLayout.tsx +1 -1
- package/src/layouts/PublicLayoutWithSidebar.tsx +51 -1
- package/src/scripts/init-app.ts +56 -12
- package/src/scripts/module-add.ts +86 -7
- package/src/scripts/module-build.ts +111 -41
- package/src/scripts/module-list.ts +45 -8
- package/src/templates/DefaultDoc.tsx +28 -0
|
@@ -243,5 +243,5 @@ export function DocUsageCustom() {
|
|
|
243
243
|
<p>Description...</p>
|
|
244
244
|
</div>
|
|
245
245
|
);
|
|
246
|
-
}` }) }), _jsx("h3", { className: "text-lg font-semibold mb-2", children: "Exporter la documentation" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: ["Dans", " ", _jsx("code", { className: "text-sm bg-slate-100 dark:bg-slate-800 px-2 py-1 rounded", children: "src/index.ts" }), " ", ":"] }), _jsx(Alert, { hideIcon: true, color: "primary", className: "p-4 mb-4", children: _jsx("pre", { className: "whitespace-pre-wrap", children: `export { MonModuleDoc } from "./components/Doc.js";` }) })] })] }), _jsxs(Card, { id: "section-links", className: "scroll-mt-32", children: [_jsx(CardHeader, { children: _jsxs("h2", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(Link, { size: 24 }), "Liens utiles & remerciements \uD83D\uDE4F"] }) }), _jsx(CardBody, { children: _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: [_jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://nextjs.org/docs", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Next.js" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Documentation officielle" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://nextjs.org/docs" })] }) }), _jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://supabase.com/docs", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Supabase" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Documentation officielle" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://supabase.com/docs" })] }) }), _jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://www.heroui.com/docs", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Heroui" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Composants UI" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://www.heroui.com/docs" })] }) }), _jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://tailwindcss.com/docs", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Tailwind CSS" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Framework CSS" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://tailwindcss.com/docs" })] }) }), _jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://lucide.dev/", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Lucide Icons" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Icones SVG" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://lucide.dev/" })] }) }), _jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://stripe.com/docs", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Stripe" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Syst\u00E8me de paiement" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://stripe.com/docs" })] }) })] }) })] }), _jsxs(Card, { id: "section-modules", className: "scroll-mt-32", children: [_jsx(CardHeader, { children: _jsxs("h2", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(FileText, { size: 24 }), "Modules"] }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "Les modules LastBrain permettent d'ajouter des fonctionnalit\u00E9s compl\u00E8tes \u00E0 votre application de mani\u00E8re modulaire." }), _jsx("h3", { className: "text-lg font-semibold mb-2", children: "Modules Disponibles" }), _jsx("h4", { className: "font-medium mb-2", children: "[module-ai](../packages/module-ai/README.md)" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "@lastbrain/module-ai" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Pages" }), ": 1 auth, 1 admin"] }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Tables" }), ": user_token_ledger, user_prompts"] }), _jsx("h4", { className: "font-medium mb-2", children: "Installation du module ai" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain add-module ai` }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: _jsx("a", { href: "../packages/module-ai/README.md", className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: "\uD83D\uDCD6 Documentation compl\u00E8te \u2192" }) }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "---" }), _jsx("h4", { className: "font-medium mb-2", children: "[module-auth](../packages/module-auth/README.md)" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "@lastbrain/module-auth" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Pages" }), ": 3 publique(s), 4 auth, 2 admin"] }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Tables" }), ": user_profil, user_address, user_notifications"] }), _jsx("h4", { className: "font-medium mb-2", children: "Installation du module auth" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain add-module auth` }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: _jsx("a", { href: "../packages/module-auth/README.md", className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: "\uD83D\uDCD6 Documentation compl\u00E8te \u2192" }) }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "---" }), _jsx("h4", { className: "font-medium mb-2", children: "[module-tasks](../packages/module-tasks/README.md)" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "@lastbrain/module-tasks" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Pages" }), ": 2 auth, 1 admin"] }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Tables" }), ": tasks, tasks_logs"] }), _jsx("h4", { className: "font-medium mb-2", children: "Installation du module tasks" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain add-module tasks` }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: _jsx("a", { href: "../packages/module-tasks/README.md", className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: "\uD83D\uDCD6 Documentation compl\u00E8te \u2192" }) }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "---" }), _jsx("h3", { className: "text-lg font-semibold mb-2", children: "Commandes" }), _jsx("h4", { className: "font-medium mb-2", children: "Installer un module" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain add-module nom-du-module` }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm build:modules` }), _jsx("h4", { className: "font-medium mb-2", children: "Cr\u00E9er un nouveau module" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain create-module nom-du-module` }), _jsx("h4", { className: "font-medium mb-2", children: "G\u00E9n\u00E9rer la documentation des modules" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm generate:module-docs` }), _jsx("h4", { className: "font-medium mb-2", children: "Supprimer un module" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain remove-module nom-du-module` }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm build:modules` }), _jsx("h3", { className: "text-lg font-semibold mb-2", children: "D\u00E9veloppement de modules" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: ["Pour cr\u00E9er un nouveau module, consultez la", " ", _jsx("a", { href: "./004_CREATE_MODULE.md", className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: "documentation de cr\u00E9ation de modules" }), "."] }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "La documentation de chaque module est g\u00E9n\u00E9r\u00E9e automatiquement depuis :" }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "text-green-600", children: "\u2705" }), _jsxs("span", { children: ["Le fichier", " ", _jsx("code", { className: "text-sm bg-slate-100 dark:bg-slate-800 px-2 py-1 rounded", children: "module-name.build.config.ts" }), " ", "pour les pages et APIs"] })] }), _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "text-green-600", children: "\u2705" }), _jsx("span", { children: "Les fichiers de migration SQL pour les tables" })] }), _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "text-green-600", children: "\u2705" }), _jsxs("span", { children: ["Le README.md est auto-g\u00E9n\u00E9r\u00E9 avec", " ", _jsx("code", { className: "text-sm bg-slate-100 dark:bg-slate-800 px-2 py-1 rounded", children: "pnpm generate:module-docs" })] })] })] })] })] })] }));
|
|
246
|
+
}` }) }), _jsx("h3", { className: "text-lg font-semibold mb-2", children: "Exporter la documentation" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: ["Dans", " ", _jsx("code", { className: "text-sm bg-slate-100 dark:bg-slate-800 px-2 py-1 rounded", children: "src/index.ts" }), " ", ":"] }), _jsx(Alert, { hideIcon: true, color: "primary", className: "p-4 mb-4", children: _jsx("pre", { className: "whitespace-pre-wrap", children: `export { MonModuleDoc } from "./components/Doc.js";` }) })] })] }), _jsxs(Card, { id: "section-links", className: "scroll-mt-32", children: [_jsx(CardHeader, { children: _jsxs("h2", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(Link, { size: 24 }), "Liens utiles & remerciements \uD83D\uDE4F"] }) }), _jsx(CardBody, { children: _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: [_jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://nextjs.org/docs", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Next.js" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Documentation officielle" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://nextjs.org/docs" })] }) }), _jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://supabase.com/docs", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Supabase" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Documentation officielle" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://supabase.com/docs" })] }) }), _jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://www.heroui.com/docs", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Heroui" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Composants UI" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://www.heroui.com/docs" })] }) }), _jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://tailwindcss.com/docs", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Tailwind CSS" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Framework CSS" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://tailwindcss.com/docs" })] }) }), _jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://lucide.dev/", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Lucide Icons" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Icones SVG" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://lucide.dev/" })] }) }), _jsx(Card, { className: "hover:shadow-lg transition-shadow", isPressable: true, as: "a", href: "https://stripe.com/docs", target: "_blank", rel: "noopener noreferrer", children: _jsxs(CardBody, { children: [_jsx("h3", { className: "text-lg font-semibold mb-2", children: "Stripe" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-3", children: "Syst\u00E8me de paiement" }), _jsx("p", { className: "text-xs text-blue-600 dark:text-blue-400 truncate", children: "https://stripe.com/docs" })] }) })] }) })] }), _jsxs(Card, { id: "section-modules", className: "scroll-mt-32", children: [_jsx(CardHeader, { children: _jsxs("h2", { className: "text-2xl font-semibold flex items-center gap-2", children: [_jsx(FileText, { size: 24 }), "Modules"] }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "Les modules LastBrain permettent d'ajouter des fonctionnalit\u00E9s compl\u00E8tes \u00E0 votre application de mani\u00E8re modulaire." }), _jsx("h3", { className: "text-lg font-semibold mb-2", children: "Modules Disponibles" }), _jsx("h4", { className: "font-medium mb-2", children: "[module-ai](../packages/module-ai/README.md)" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "@lastbrain/module-ai" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Pages" }), ": 1 auth, 1 admin"] }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Tables" }), ": user_token_ledger, user_prompts"] }), _jsx("h4", { className: "font-medium mb-2", children: "Installation du module ai" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain add-module ai` }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: _jsx("a", { href: "../packages/module-ai/README.md", className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: "\uD83D\uDCD6 Documentation compl\u00E8te \u2192" }) }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "---" }), _jsx("h4", { className: "font-medium mb-2", children: "[module-auth](../packages/module-auth/README.md)" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "@lastbrain/module-auth" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Pages" }), ": 3 publique(s), 4 auth, 2 admin"] }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Tables" }), ": user_profil, user_address, user_notifications"] }), _jsx("h4", { className: "font-medium mb-2", children: "Installation du module auth" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain add-module auth` }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: _jsx("a", { href: "../packages/module-auth/README.md", className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: "\uD83D\uDCD6 Documentation compl\u00E8te \u2192" }) }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "---" }), _jsx("h4", { className: "font-medium mb-2", children: "[module-recipes](../packages/module-recipes/README.md)" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "@lastbrain/module-recipes" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Pages" }), ": 4 auth"] }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Tables" }), ": recipes, recipe_favorites, recipe_comments, recipe_image_queue, recipe_embeddings"] }), _jsx("h4", { className: "font-medium mb-2", children: "Installation du module recipes" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain add-module recipes` }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: _jsx("a", { href: "../packages/module-recipes/README.md", className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: "\uD83D\uDCD6 Documentation compl\u00E8te \u2192" }) }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "---" }), _jsx("h4", { className: "font-medium mb-2", children: "[module-tasks](../packages/module-tasks/README.md)" }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "@lastbrain/module-tasks" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Pages" }), ": 2 auth, 1 admin"] }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: [_jsx("strong", { children: "Tables" }), ": tasks, tasks_logs"] }), _jsx("h4", { className: "font-medium mb-2", children: "Installation du module tasks" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain add-module tasks` }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: _jsx("a", { href: "../packages/module-tasks/README.md", className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: "\uD83D\uDCD6 Documentation compl\u00E8te \u2192" }) }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "---" }), _jsx("h3", { className: "text-lg font-semibold mb-2", children: "Commandes" }), _jsx("h4", { className: "font-medium mb-2", children: "Installer un module" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain add-module nom-du-module` }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm build:modules` }), _jsx("h4", { className: "font-medium mb-2", children: "Cr\u00E9er un nouveau module" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain create-module nom-du-module` }), _jsx("h4", { className: "font-medium mb-2", children: "G\u00E9n\u00E9rer la documentation des modules" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm generate:module-docs` }), _jsx("h4", { className: "font-medium mb-2", children: "Supprimer un module" }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm lastbrain remove-module nom-du-module` }), _jsx(Snippet, { symbol: "", hideSymbol: true, className: "text-sm mb-2", children: `pnpm build:modules` }), _jsx("h3", { className: "text-lg font-semibold mb-2", children: "D\u00E9veloppement de modules" }), _jsxs("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: ["Pour cr\u00E9er un nouveau module, consultez la", " ", _jsx("a", { href: "./004_CREATE_MODULE.md", className: "text-blue-600 hover:underline", target: "_blank", rel: "noopener noreferrer", children: "documentation de cr\u00E9ation de modules" }), "."] }), _jsx("p", { className: "text-sm text-slate-600 dark:text-slate-400 mb-2", children: "La documentation de chaque module est g\u00E9n\u00E9r\u00E9e automatiquement depuis :" }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "text-green-600", children: "\u2705" }), _jsxs("span", { children: ["Le fichier", " ", _jsx("code", { className: "text-sm bg-slate-100 dark:bg-slate-800 px-2 py-1 rounded", children: "module-name.build.config.ts" }), " ", "pour les pages et APIs"] })] }), _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "text-green-600", children: "\u2705" }), _jsx("span", { children: "Les fichiers de migration SQL pour les tables" })] }), _jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "text-green-600", children: "\u2705" }), _jsxs("span", { children: ["Le README.md est auto-g\u00E9n\u00E9r\u00E9 avec", " ", _jsx("code", { className: "text-sm bg-slate-100 dark:bg-slate-800 px-2 py-1 rounded", children: "pnpm generate:module-docs" })] })] })] })] })] })] }));
|
|
247
247
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lastbrain/app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"description": "Framework modulaire Next.js avec CLI et système de modules",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@tailwindcss/postcss": "^4.1.17",
|
|
52
52
|
"@types/fs-extra": "^11.0.4",
|
|
53
53
|
"@types/inquirer": "^9.0.9",
|
|
54
|
-
"@types/node": "^
|
|
54
|
+
"@types/node": "^24.10.1",
|
|
55
55
|
"inquirer": "^13.0.1",
|
|
56
56
|
"postcss": "^8.4.0",
|
|
57
57
|
"postcss-cli": "^11.0.0",
|
package/src/cli.ts
CHANGED
|
@@ -97,8 +97,13 @@ program
|
|
|
97
97
|
program
|
|
98
98
|
.command("module:build")
|
|
99
99
|
.description("Build les configurations de modules")
|
|
100
|
-
.
|
|
100
|
+
.option("--debug", "Affiche tous les logs détaillés")
|
|
101
|
+
.action(async (options) => {
|
|
101
102
|
try {
|
|
103
|
+
// Passer le flag debug via process.argv pour que module-build puisse le lire
|
|
104
|
+
if (options.debug && !process.argv.includes("--debug")) {
|
|
105
|
+
process.argv.push("--debug");
|
|
106
|
+
}
|
|
102
107
|
const { runModuleBuild } = await import("./scripts/module-build.js");
|
|
103
108
|
await runModuleBuild();
|
|
104
109
|
} catch (error) {
|
|
@@ -4,6 +4,7 @@ import { AdminLayout } from "./AdminLayout.js";
|
|
|
4
4
|
import { AppAside, AppAsideSkeleton } from "@lastbrain/ui";
|
|
5
5
|
import { useAuthSession } from "../auth/useAuthSession.js";
|
|
6
6
|
import { usePathname } from "next/navigation";
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
7
8
|
|
|
8
9
|
interface AdminLayoutWithSidebarProps {
|
|
9
10
|
children: React.ReactNode;
|
|
@@ -18,6 +19,53 @@ export function AdminLayoutWithSidebar({
|
|
|
18
19
|
}: AdminLayoutWithSidebarProps) {
|
|
19
20
|
const { isSuperAdmin, loading, user } = useAuthSession();
|
|
20
21
|
const pathname = usePathname();
|
|
22
|
+
const [isCollapsed, setIsCollapsed] = useState(() => {
|
|
23
|
+
if (typeof window !== "undefined") {
|
|
24
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
25
|
+
return savedState !== null ? JSON.parse(savedState) : false;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
});
|
|
29
|
+
const [mounted, setMounted] = useState(false);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
33
|
+
setMounted(true);
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (typeof window !== "undefined") {
|
|
38
|
+
// Écouter les changements du localStorage depuis le même onglet
|
|
39
|
+
const handleStorageChange = (_e: StorageEvent | CustomEvent) => {
|
|
40
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
41
|
+
if (savedState !== null) {
|
|
42
|
+
// Utiliser queueMicrotask pour déférer le setState
|
|
43
|
+
queueMicrotask(() => {
|
|
44
|
+
setIsCollapsed(JSON.parse(savedState));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Écouter l'événement storage (changements depuis d'autres onglets)
|
|
50
|
+
window.addEventListener("storage", handleStorageChange as EventListener);
|
|
51
|
+
// Écouter l'événement custom pour les changements du même onglet
|
|
52
|
+
window.addEventListener(
|
|
53
|
+
"localStorage-changed",
|
|
54
|
+
handleStorageChange as EventListener,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return () => {
|
|
58
|
+
window.removeEventListener(
|
|
59
|
+
"storage",
|
|
60
|
+
handleStorageChange as EventListener,
|
|
61
|
+
);
|
|
62
|
+
window.removeEventListener(
|
|
63
|
+
"localStorage-changed",
|
|
64
|
+
handleStorageChange as EventListener,
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}, []);
|
|
21
69
|
|
|
22
70
|
// Détecter si on est dans la section admin pour le skeleton
|
|
23
71
|
const isAdminSection = pathname.startsWith("/admin");
|
|
@@ -51,7 +99,9 @@ export function AdminLayoutWithSidebar({
|
|
|
51
99
|
)}
|
|
52
100
|
|
|
53
101
|
{/* Contenu principal avec marge pour la sidebar */}
|
|
54
|
-
<div
|
|
102
|
+
<div
|
|
103
|
+
className={`flex-1 transition-all duration-300 ${!mounted ? "lg:ml-72" : isCollapsed ? "lg:ml-20" : "lg:ml-72"}`}
|
|
104
|
+
>
|
|
55
105
|
<AdminLayout>{children}</AdminLayout>
|
|
56
106
|
</div>
|
|
57
107
|
</div>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { AuthLayout } from "./AuthLayout.js";
|
|
4
4
|
import { AppAside, AppAsideSkeleton } from "@lastbrain/ui";
|
|
5
5
|
import { useAuthSession } from "../auth/useAuthSession.js";
|
|
6
|
+
import { useEffect, useState } from "react";
|
|
6
7
|
|
|
7
8
|
interface AuthLayoutWithSidebarProps {
|
|
8
9
|
children: React.ReactNode;
|
|
@@ -16,6 +17,53 @@ export function AuthLayoutWithSidebar({
|
|
|
16
17
|
className = "",
|
|
17
18
|
}: AuthLayoutWithSidebarProps) {
|
|
18
19
|
const { isSuperAdmin, loading, user } = useAuthSession();
|
|
20
|
+
const [isCollapsed, setIsCollapsed] = useState(() => {
|
|
21
|
+
if (typeof window !== "undefined") {
|
|
22
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
23
|
+
return savedState !== null ? JSON.parse(savedState) : false;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
});
|
|
27
|
+
const [mounted, setMounted] = useState(false);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
31
|
+
setMounted(true);
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (typeof window !== "undefined") {
|
|
36
|
+
// Écouter les changements du localStorage depuis le même onglet
|
|
37
|
+
const handleStorageChange = (_e: StorageEvent | CustomEvent) => {
|
|
38
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
39
|
+
if (savedState !== null) {
|
|
40
|
+
// Utiliser queueMicrotask pour déférer le setState
|
|
41
|
+
queueMicrotask(() => {
|
|
42
|
+
setIsCollapsed(JSON.parse(savedState));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Écouter l'événement storage (changements depuis d'autres onglets)
|
|
48
|
+
window.addEventListener("storage", handleStorageChange as EventListener);
|
|
49
|
+
// Écouter l'événement custom pour les changements du même onglet
|
|
50
|
+
window.addEventListener(
|
|
51
|
+
"localStorage-changed",
|
|
52
|
+
handleStorageChange as EventListener,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
window.removeEventListener(
|
|
57
|
+
"storage",
|
|
58
|
+
handleStorageChange as EventListener,
|
|
59
|
+
);
|
|
60
|
+
window.removeEventListener(
|
|
61
|
+
"localStorage-changed",
|
|
62
|
+
handleStorageChange as EventListener,
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}, []);
|
|
19
67
|
|
|
20
68
|
// Pour la section auth, isAdminSection sera false
|
|
21
69
|
const isAdminSection = false;
|
|
@@ -48,7 +96,9 @@ export function AuthLayoutWithSidebar({
|
|
|
48
96
|
/>
|
|
49
97
|
)}
|
|
50
98
|
{/* Contenu principal avec marge pour la sidebar */}
|
|
51
|
-
<div
|
|
99
|
+
<div
|
|
100
|
+
className={`flex-1 transition-all duration-300 ${!mounted ? "lg:ml-72" : isCollapsed ? "lg:ml-20" : "lg:ml-72"}`}
|
|
101
|
+
>
|
|
52
102
|
<AuthLayout>{children}</AuthLayout>
|
|
53
103
|
</div>
|
|
54
104
|
</div>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { PublicLayout } from "./PublicLayout.js";
|
|
4
4
|
import { AppAside, AppAsideSkeleton } from "@lastbrain/ui";
|
|
5
5
|
import { useAuthSession } from "../auth/useAuthSession.js";
|
|
6
|
+
import { useEffect, useState } from "react";
|
|
6
7
|
|
|
7
8
|
interface PublicLayoutWithSidebarProps {
|
|
8
9
|
children: React.ReactNode;
|
|
@@ -16,6 +17,53 @@ export function PublicLayoutWithSidebar({
|
|
|
16
17
|
className = "",
|
|
17
18
|
}: PublicLayoutWithSidebarProps) {
|
|
18
19
|
const { user, loading } = useAuthSession();
|
|
20
|
+
const [isCollapsed, setIsCollapsed] = useState(() => {
|
|
21
|
+
if (typeof window !== "undefined") {
|
|
22
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
23
|
+
return savedState !== null ? JSON.parse(savedState) : false;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
});
|
|
27
|
+
const [mounted, setMounted] = useState(false);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
31
|
+
setMounted(true);
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (typeof window !== "undefined") {
|
|
36
|
+
// Écouter les changements du localStorage depuis le même onglet
|
|
37
|
+
const handleStorageChange = (_e: StorageEvent | CustomEvent) => {
|
|
38
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
39
|
+
if (savedState !== null) {
|
|
40
|
+
// Utiliser queueMicrotask pour déférer le setState
|
|
41
|
+
queueMicrotask(() => {
|
|
42
|
+
setIsCollapsed(JSON.parse(savedState));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Écouter l'événement storage (changements depuis d'autres onglets)
|
|
48
|
+
window.addEventListener("storage", handleStorageChange as EventListener);
|
|
49
|
+
// Écouter l'événement custom pour les changements du même onglet
|
|
50
|
+
window.addEventListener(
|
|
51
|
+
"localStorage-changed",
|
|
52
|
+
handleStorageChange as EventListener,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
window.removeEventListener(
|
|
57
|
+
"storage",
|
|
58
|
+
handleStorageChange as EventListener,
|
|
59
|
+
);
|
|
60
|
+
window.removeEventListener(
|
|
61
|
+
"localStorage-changed",
|
|
62
|
+
handleStorageChange as EventListener,
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}, []);
|
|
19
67
|
|
|
20
68
|
// Pour la section public, isAdminSection sera false
|
|
21
69
|
const isAdminSection = false;
|
|
@@ -48,7 +96,9 @@ export function PublicLayoutWithSidebar({
|
|
|
48
96
|
/>
|
|
49
97
|
)}
|
|
50
98
|
{/* Contenu principal avec marge pour la sidebar */}
|
|
51
|
-
<div
|
|
99
|
+
<div
|
|
100
|
+
className={`flex-1 transition-all duration-300 ${!mounted ? "lg:ml-72" : isCollapsed ? "lg:ml-20" : "lg:ml-72"}`}
|
|
101
|
+
>
|
|
52
102
|
<PublicLayout>{children}</PublicLayout>
|
|
53
103
|
</div>
|
|
54
104
|
</div>
|
package/src/scripts/init-app.ts
CHANGED
|
@@ -903,7 +903,7 @@ async function createConfigFiles(
|
|
|
903
903
|
) {
|
|
904
904
|
console.log(chalk.yellow("\n⚙️ Création des fichiers de configuration..."));
|
|
905
905
|
|
|
906
|
-
// middleware.ts - Protection des routes /auth/* et /admin/*
|
|
906
|
+
// middleware.ts - Protection des routes /auth/*, /admin/* et /api/admin/*
|
|
907
907
|
const middlewarePath = path.join(targetDir, "middleware.ts");
|
|
908
908
|
if (!fs.existsSync(middlewarePath) || force) {
|
|
909
909
|
const middleware = `import { type NextRequest, NextResponse } from "next/server";
|
|
@@ -911,6 +911,7 @@ import { createMiddlewareClient } from "@lastbrain/core/server";
|
|
|
911
911
|
|
|
912
912
|
export async function middleware(request: NextRequest) {
|
|
913
913
|
const { pathname } = request.nextUrl;
|
|
914
|
+
const isApi = pathname.startsWith("/api/");
|
|
914
915
|
|
|
915
916
|
// Protéger les routes /auth/* (espace membre)
|
|
916
917
|
if (pathname.startsWith("/auth")) {
|
|
@@ -920,33 +921,56 @@ export async function middleware(request: NextRequest) {
|
|
|
920
921
|
data: { session },
|
|
921
922
|
} = await supabase.auth.getSession();
|
|
922
923
|
|
|
923
|
-
// Pas de session → redirection vers /signin
|
|
924
|
+
// Pas de session → nettoyage des cookies + redirection vers /auth/signin
|
|
924
925
|
if (!session) {
|
|
925
|
-
const redirectUrl = new URL("/signin", request.url);
|
|
926
|
+
const redirectUrl = new URL("/auth/signin", request.url);
|
|
926
927
|
redirectUrl.searchParams.set("redirect", pathname);
|
|
927
|
-
|
|
928
|
+
const res = NextResponse.redirect(redirectUrl);
|
|
929
|
+
res.cookies.delete("sb-access-token");
|
|
930
|
+
res.cookies.delete("sb-refresh-token");
|
|
931
|
+
res.cookies.delete("sb:token");
|
|
932
|
+
res.cookies.delete("sb:refresh-token");
|
|
933
|
+
return res;
|
|
928
934
|
}
|
|
929
935
|
|
|
930
936
|
return response;
|
|
931
937
|
} catch (error) {
|
|
932
938
|
console.error("Middleware auth error:", error);
|
|
933
|
-
|
|
939
|
+
const res = NextResponse.redirect(new URL("/auth/signin", request.url));
|
|
940
|
+
res.cookies.delete("sb-access-token");
|
|
941
|
+
res.cookies.delete("sb-refresh-token");
|
|
942
|
+
res.cookies.delete("sb:token");
|
|
943
|
+
res.cookies.delete("sb:refresh-token");
|
|
944
|
+
return res;
|
|
934
945
|
}
|
|
935
946
|
}
|
|
936
947
|
|
|
937
|
-
// Protéger les routes /admin/* (superadmin uniquement)
|
|
938
|
-
if (pathname.startsWith("/admin")) {
|
|
948
|
+
// Protéger les routes /admin/* et /api/admin/* (superadmin uniquement)
|
|
949
|
+
if (pathname.startsWith("/admin") || pathname.startsWith("/api/admin")) {
|
|
939
950
|
try {
|
|
940
951
|
const { supabase, response } = createMiddlewareClient(request);
|
|
941
952
|
const {
|
|
942
953
|
data: { session },
|
|
943
954
|
} = await supabase.auth.getSession();
|
|
944
955
|
|
|
945
|
-
// Pas de session → redirection vers /signin
|
|
956
|
+
// Pas de session → 401 JSON pour API, sinon redirection vers /auth/signin avec nettoyage cookies
|
|
946
957
|
if (!session) {
|
|
947
|
-
|
|
958
|
+
if (isApi) {
|
|
959
|
+
const res = NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
|
960
|
+
res.cookies.delete("sb-access-token");
|
|
961
|
+
res.cookies.delete("sb-refresh-token");
|
|
962
|
+
res.cookies.delete("sb:token");
|
|
963
|
+
res.cookies.delete("sb:refresh-token");
|
|
964
|
+
return res;
|
|
965
|
+
}
|
|
966
|
+
const redirectUrl = new URL("/auth/signin", request.url);
|
|
948
967
|
redirectUrl.searchParams.set("redirect", pathname);
|
|
949
|
-
|
|
968
|
+
const res = NextResponse.redirect(redirectUrl);
|
|
969
|
+
res.cookies.delete("sb-access-token");
|
|
970
|
+
res.cookies.delete("sb-refresh-token");
|
|
971
|
+
res.cookies.delete("sb:token");
|
|
972
|
+
res.cookies.delete("sb:refresh-token");
|
|
973
|
+
return res;
|
|
950
974
|
}
|
|
951
975
|
|
|
952
976
|
// Vérifier si l'utilisateur est superadmin
|
|
@@ -957,13 +981,24 @@ export async function middleware(request: NextRequest) {
|
|
|
957
981
|
|
|
958
982
|
if (error || !isSuperAdmin) {
|
|
959
983
|
console.error("Access denied: not a superadmin", error);
|
|
984
|
+
if (isApi) {
|
|
985
|
+
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
|
986
|
+
}
|
|
960
987
|
return NextResponse.redirect(new URL("/", request.url));
|
|
961
988
|
}
|
|
962
989
|
|
|
963
990
|
return response;
|
|
964
991
|
} catch (error) {
|
|
965
992
|
console.error("Middleware admin error:", error);
|
|
966
|
-
|
|
993
|
+
if (isApi) {
|
|
994
|
+
return NextResponse.json({ error: "Erreur middleware" }, { status: 500 });
|
|
995
|
+
}
|
|
996
|
+
const res = NextResponse.redirect(new URL("/", request.url));
|
|
997
|
+
res.cookies.delete("sb-access-token");
|
|
998
|
+
res.cookies.delete("sb-refresh-token");
|
|
999
|
+
res.cookies.delete("sb:token");
|
|
1000
|
+
res.cookies.delete("sb:refresh-token");
|
|
1001
|
+
return res;
|
|
967
1002
|
}
|
|
968
1003
|
}
|
|
969
1004
|
|
|
@@ -985,7 +1020,9 @@ export const config = {
|
|
|
985
1020
|
`;
|
|
986
1021
|
await fs.writeFile(middlewarePath, middleware);
|
|
987
1022
|
console.log(
|
|
988
|
-
chalk.green(
|
|
1023
|
+
chalk.green(
|
|
1024
|
+
"✓ middleware.ts créé (protection /auth/*, /admin/* et /api/admin/*)",
|
|
1025
|
+
),
|
|
989
1026
|
);
|
|
990
1027
|
}
|
|
991
1028
|
|
|
@@ -1561,6 +1598,13 @@ export const BUCKET_CONFIGS: Record<string, BucketConfig> = {
|
|
|
1561
1598
|
return filePath.startsWith(\`\${userId}/\`);
|
|
1562
1599
|
},
|
|
1563
1600
|
},
|
|
1601
|
+
recipes: {
|
|
1602
|
+
name: "recipes",
|
|
1603
|
+
isPublic: true,
|
|
1604
|
+
description: "Public recipe images accessible by slug",
|
|
1605
|
+
allowedFileTypes: ["image/jpeg", "image/png", "image/webp", "image/gif"],
|
|
1606
|
+
maxFileSize: 50 * 1024 * 1024, // 50MB
|
|
1607
|
+
},
|
|
1564
1608
|
// Example for future buckets:
|
|
1565
1609
|
// public: {
|
|
1566
1610
|
// name: "public",
|
|
@@ -205,15 +205,94 @@ export async function addModule(moduleName: string, targetDir: string) {
|
|
|
205
205
|
chalk.yellow("\n⬆️ Application des nouvelles migrations..."),
|
|
206
206
|
);
|
|
207
207
|
try {
|
|
208
|
-
|
|
208
|
+
// Essayer en capturant la sortie pour diagnostiquer les erreurs fréquentes
|
|
209
|
+
execSync("supabase migration up --local", {
|
|
209
210
|
cwd: targetDir,
|
|
210
|
-
stdio: "
|
|
211
|
+
stdio: "pipe",
|
|
211
212
|
});
|
|
212
213
|
console.log(chalk.green("✓ Migrations appliquées"));
|
|
213
|
-
} catch {
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
} catch (error: any) {
|
|
215
|
+
const output = String(
|
|
216
|
+
error?.stderr || error?.stdout || error?.message || "",
|
|
217
|
+
);
|
|
218
|
+
const mismatch = output.includes(
|
|
219
|
+
"Remote migration versions not found in local migrations directory",
|
|
216
220
|
);
|
|
221
|
+
|
|
222
|
+
if (mismatch) {
|
|
223
|
+
console.warn(
|
|
224
|
+
chalk.yellow("⚠️ Historique des migrations désynchronisé."),
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// Extraire TOUTES les dates de migration depuis le message d'erreur
|
|
228
|
+
const repairMatch = output.match(
|
|
229
|
+
/supabase migration repair --status reverted ([\s\S]+?)\n/,
|
|
230
|
+
);
|
|
231
|
+
let migrationDates: string[] = [];
|
|
232
|
+
|
|
233
|
+
if (repairMatch && repairMatch[1]) {
|
|
234
|
+
// Extraire toutes les dates de la commande suggérée
|
|
235
|
+
migrationDates = repairMatch[1].trim().split(/\s+/);
|
|
236
|
+
} else {
|
|
237
|
+
// Fallback: chercher toutes les dates dans le message
|
|
238
|
+
const allDates = output.match(/20\d{6,14}/g);
|
|
239
|
+
migrationDates = allDates ? [...new Set(allDates)] : [];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (migrationDates.length === 0) {
|
|
243
|
+
console.error(
|
|
244
|
+
chalk.red("❌ Impossible d'extraire les dates de migration"),
|
|
245
|
+
);
|
|
246
|
+
console.log(
|
|
247
|
+
chalk.gray("\nFaites un reset complet:\n supabase db reset\n"),
|
|
248
|
+
);
|
|
249
|
+
} else {
|
|
250
|
+
// Réparation automatique de l'historique
|
|
251
|
+
const datesStr = migrationDates.join(" ");
|
|
252
|
+
console.log(
|
|
253
|
+
chalk.yellow(`\n🔧 Réparation automatique de l'historique...`),
|
|
254
|
+
);
|
|
255
|
+
console.log(chalk.gray(` Dates: ${datesStr}`));
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
execSync(
|
|
259
|
+
`supabase migration repair --status reverted ${datesStr} --local`,
|
|
260
|
+
{
|
|
261
|
+
cwd: targetDir,
|
|
262
|
+
stdio: "inherit",
|
|
263
|
+
},
|
|
264
|
+
);
|
|
265
|
+
console.log(chalk.green("✓ Historique réparé"));
|
|
266
|
+
|
|
267
|
+
console.log(
|
|
268
|
+
chalk.yellow("\n⬆️ Application des nouvelles migrations..."),
|
|
269
|
+
);
|
|
270
|
+
execSync("supabase migration up --local", {
|
|
271
|
+
cwd: targetDir,
|
|
272
|
+
stdio: "inherit",
|
|
273
|
+
});
|
|
274
|
+
console.log(chalk.green("✓ Migrations appliquées"));
|
|
275
|
+
} catch (e) {
|
|
276
|
+
console.error(
|
|
277
|
+
chalk.red("❌ Échec de la réparation automatique"),
|
|
278
|
+
);
|
|
279
|
+
console.log(
|
|
280
|
+
chalk.gray(
|
|
281
|
+
`\nVous pouvez essayer manuellement:\n supabase migration repair --status reverted ${datesStr} --local\n supabase migration up --local`,
|
|
282
|
+
),
|
|
283
|
+
);
|
|
284
|
+
console.log(
|
|
285
|
+
chalk.gray(
|
|
286
|
+
`\nOu faire un reset complet:\n supabase db reset\n`,
|
|
287
|
+
),
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
console.error(
|
|
293
|
+
chalk.red("❌ Erreur lors de l'application des migrations"),
|
|
294
|
+
);
|
|
295
|
+
}
|
|
217
296
|
}
|
|
218
297
|
} else {
|
|
219
298
|
console.log(
|
|
@@ -287,13 +366,13 @@ export async function addModule(moduleName: string, targetDir: string) {
|
|
|
287
366
|
// 7. Générer automatiquement les routes du module
|
|
288
367
|
console.log(chalk.yellow("🔧 Génération des routes du module..."));
|
|
289
368
|
try {
|
|
290
|
-
execSync("pnpm build:modules", { cwd: targetDir, stdio: "inherit" });
|
|
369
|
+
execSync("pnpm run build:modules", { cwd: targetDir, stdio: "inherit" });
|
|
291
370
|
console.log(chalk.green("✓ Routes du module générées"));
|
|
292
371
|
} catch {
|
|
293
372
|
console.error(chalk.red("❌ Erreur lors de la génération des routes"));
|
|
294
373
|
console.error(
|
|
295
374
|
chalk.gray(
|
|
296
|
-
"Vous pouvez les générer manuellement avec: pnpm build:modules",
|
|
375
|
+
"Vous pouvez les générer manuellement avec: cd apps/<votre-app> && pnpm run build:modules",
|
|
297
376
|
),
|
|
298
377
|
);
|
|
299
378
|
}
|