@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
package/dist/cli.js
CHANGED
|
@@ -89,8 +89,13 @@ program
|
|
|
89
89
|
program
|
|
90
90
|
.command("module:build")
|
|
91
91
|
.description("Build les configurations de modules")
|
|
92
|
-
.
|
|
92
|
+
.option("--debug", "Affiche tous les logs détaillés")
|
|
93
|
+
.action(async (options) => {
|
|
93
94
|
try {
|
|
95
|
+
// Passer le flag debug via process.argv pour que module-build puisse le lire
|
|
96
|
+
if (options.debug && !process.argv.includes("--debug")) {
|
|
97
|
+
process.argv.push("--debug");
|
|
98
|
+
}
|
|
94
99
|
const { runModuleBuild } = await import("./scripts/module-build.js");
|
|
95
100
|
await runModuleBuild();
|
|
96
101
|
}
|
|
@@ -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":"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"}
|
|
@@ -4,9 +4,44 @@ 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
|
export function AdminLayoutWithSidebar({ children, menuConfig, className = "", }) {
|
|
8
9
|
const { isSuperAdmin, loading, user } = useAuthSession();
|
|
9
10
|
const pathname = usePathname();
|
|
11
|
+
const [isCollapsed, setIsCollapsed] = useState(() => {
|
|
12
|
+
if (typeof window !== "undefined") {
|
|
13
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
14
|
+
return savedState !== null ? JSON.parse(savedState) : false;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
});
|
|
18
|
+
const [mounted, setMounted] = useState(false);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
21
|
+
setMounted(true);
|
|
22
|
+
}, []);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (typeof window !== "undefined") {
|
|
25
|
+
// Écouter les changements du localStorage depuis le même onglet
|
|
26
|
+
const handleStorageChange = (_e) => {
|
|
27
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
28
|
+
if (savedState !== null) {
|
|
29
|
+
// Utiliser queueMicrotask pour déférer le setState
|
|
30
|
+
queueMicrotask(() => {
|
|
31
|
+
setIsCollapsed(JSON.parse(savedState));
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
// Écouter l'événement storage (changements depuis d'autres onglets)
|
|
36
|
+
window.addEventListener("storage", handleStorageChange);
|
|
37
|
+
// Écouter l'événement custom pour les changements du même onglet
|
|
38
|
+
window.addEventListener("localStorage-changed", handleStorageChange);
|
|
39
|
+
return () => {
|
|
40
|
+
window.removeEventListener("storage", handleStorageChange);
|
|
41
|
+
window.removeEventListener("localStorage-changed", handleStorageChange);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}, []);
|
|
10
45
|
// Détecter si on est dans la section admin pour le skeleton
|
|
11
46
|
const isAdminSection = pathname.startsWith("/admin");
|
|
12
47
|
// Vérifier si menuConfig est vraiment disponible et valide
|
|
@@ -17,5 +52,5 @@ export function AdminLayoutWithSidebar({ children, menuConfig, className = "", }
|
|
|
17
52
|
menuConfig.public?.length > 0);
|
|
18
53
|
// Afficher le skeleton pendant le chargement de la session ou si pas de menuConfig valide
|
|
19
54
|
const shouldShowSkeleton = loading || !hasValidMenuConfig;
|
|
20
|
-
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:
|
|
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 }) })] }));
|
|
21
56
|
}
|
|
@@ -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":"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"}
|
|
@@ -3,8 +3,43 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
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
|
export function AuthLayoutWithSidebar({ children, menuConfig, className = "", }) {
|
|
7
8
|
const { isSuperAdmin, loading, user } = useAuthSession();
|
|
9
|
+
const [isCollapsed, setIsCollapsed] = useState(() => {
|
|
10
|
+
if (typeof window !== "undefined") {
|
|
11
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
12
|
+
return savedState !== null ? JSON.parse(savedState) : false;
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
});
|
|
16
|
+
const [mounted, setMounted] = useState(false);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
19
|
+
setMounted(true);
|
|
20
|
+
}, []);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (typeof window !== "undefined") {
|
|
23
|
+
// Écouter les changements du localStorage depuis le même onglet
|
|
24
|
+
const handleStorageChange = (_e) => {
|
|
25
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
26
|
+
if (savedState !== null) {
|
|
27
|
+
// Utiliser queueMicrotask pour déférer le setState
|
|
28
|
+
queueMicrotask(() => {
|
|
29
|
+
setIsCollapsed(JSON.parse(savedState));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
// Écouter l'événement storage (changements depuis d'autres onglets)
|
|
34
|
+
window.addEventListener("storage", handleStorageChange);
|
|
35
|
+
// Écouter l'événement custom pour les changements du même onglet
|
|
36
|
+
window.addEventListener("localStorage-changed", handleStorageChange);
|
|
37
|
+
return () => {
|
|
38
|
+
window.removeEventListener("storage", handleStorageChange);
|
|
39
|
+
window.removeEventListener("localStorage-changed", handleStorageChange);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}, []);
|
|
8
43
|
// Pour la section auth, isAdminSection sera false
|
|
9
44
|
const isAdminSection = false;
|
|
10
45
|
// Vérifier si menuConfig est vraiment disponible et valide
|
|
@@ -15,5 +50,5 @@ export function AuthLayoutWithSidebar({ children, menuConfig, className = "", })
|
|
|
15
50
|
menuConfig.public?.length > 0);
|
|
16
51
|
// Afficher le skeleton pendant le chargement de la session ou si pas de menuConfig valide
|
|
17
52
|
const shouldShowSkeleton = loading || !hasValidMenuConfig;
|
|
18
|
-
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:
|
|
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 }) })] }));
|
|
19
54
|
}
|
|
@@ -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":"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"}
|
|
@@ -3,8 +3,43 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
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
|
export function PublicLayoutWithSidebar({ children, menuConfig, className = "", }) {
|
|
7
8
|
const { user, loading } = useAuthSession();
|
|
9
|
+
const [isCollapsed, setIsCollapsed] = useState(() => {
|
|
10
|
+
if (typeof window !== "undefined") {
|
|
11
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
12
|
+
return savedState !== null ? JSON.parse(savedState) : false;
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
});
|
|
16
|
+
const [mounted, setMounted] = useState(false);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
19
|
+
setMounted(true);
|
|
20
|
+
}, []);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (typeof window !== "undefined") {
|
|
23
|
+
// Écouter les changements du localStorage depuis le même onglet
|
|
24
|
+
const handleStorageChange = (_e) => {
|
|
25
|
+
const savedState = localStorage.getItem("aside-collapsed");
|
|
26
|
+
if (savedState !== null) {
|
|
27
|
+
// Utiliser queueMicrotask pour déférer le setState
|
|
28
|
+
queueMicrotask(() => {
|
|
29
|
+
setIsCollapsed(JSON.parse(savedState));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
// Écouter l'événement storage (changements depuis d'autres onglets)
|
|
34
|
+
window.addEventListener("storage", handleStorageChange);
|
|
35
|
+
// Écouter l'événement custom pour les changements du même onglet
|
|
36
|
+
window.addEventListener("localStorage-changed", handleStorageChange);
|
|
37
|
+
return () => {
|
|
38
|
+
window.removeEventListener("storage", handleStorageChange);
|
|
39
|
+
window.removeEventListener("localStorage-changed", handleStorageChange);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}, []);
|
|
8
43
|
// Pour la section public, isAdminSection sera false
|
|
9
44
|
const isAdminSection = false;
|
|
10
45
|
// Vérifier si menuConfig est vraiment disponible et valide
|
|
@@ -15,5 +50,5 @@ export function PublicLayoutWithSidebar({ children, menuConfig, className = "",
|
|
|
15
50
|
menuConfig.public?.length > 0);
|
|
16
51
|
// Afficher le skeleton pendant le chargement de la session ou si pas de menuConfig valide
|
|
17
52
|
const shouldShowSkeleton = loading || !hasValidMenuConfig;
|
|
18
|
-
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:
|
|
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 }) })] }));
|
|
19
54
|
}
|
package/dist/scripts/init-app.js
CHANGED
|
@@ -723,7 +723,7 @@ export function AppProviders({ children }: { children: ReactNode }) {
|
|
|
723
723
|
}
|
|
724
724
|
async function createConfigFiles(targetDir, force, useHeroUI) {
|
|
725
725
|
console.log(chalk.yellow("\n⚙️ Création des fichiers de configuration..."));
|
|
726
|
-
// middleware.ts - Protection des routes /auth/* et /admin/*
|
|
726
|
+
// middleware.ts - Protection des routes /auth/*, /admin/* et /api/admin/*
|
|
727
727
|
const middlewarePath = path.join(targetDir, "middleware.ts");
|
|
728
728
|
if (!fs.existsSync(middlewarePath) || force) {
|
|
729
729
|
const middleware = `import { type NextRequest, NextResponse } from "next/server";
|
|
@@ -731,6 +731,7 @@ import { createMiddlewareClient } from "@lastbrain/core/server";
|
|
|
731
731
|
|
|
732
732
|
export async function middleware(request: NextRequest) {
|
|
733
733
|
const { pathname } = request.nextUrl;
|
|
734
|
+
const isApi = pathname.startsWith("/api/");
|
|
734
735
|
|
|
735
736
|
// Protéger les routes /auth/* (espace membre)
|
|
736
737
|
if (pathname.startsWith("/auth")) {
|
|
@@ -740,33 +741,56 @@ export async function middleware(request: NextRequest) {
|
|
|
740
741
|
data: { session },
|
|
741
742
|
} = await supabase.auth.getSession();
|
|
742
743
|
|
|
743
|
-
// Pas de session → redirection vers /signin
|
|
744
|
+
// Pas de session → nettoyage des cookies + redirection vers /auth/signin
|
|
744
745
|
if (!session) {
|
|
745
|
-
const redirectUrl = new URL("/signin", request.url);
|
|
746
|
+
const redirectUrl = new URL("/auth/signin", request.url);
|
|
746
747
|
redirectUrl.searchParams.set("redirect", pathname);
|
|
747
|
-
|
|
748
|
+
const res = NextResponse.redirect(redirectUrl);
|
|
749
|
+
res.cookies.delete("sb-access-token");
|
|
750
|
+
res.cookies.delete("sb-refresh-token");
|
|
751
|
+
res.cookies.delete("sb:token");
|
|
752
|
+
res.cookies.delete("sb:refresh-token");
|
|
753
|
+
return res;
|
|
748
754
|
}
|
|
749
755
|
|
|
750
756
|
return response;
|
|
751
757
|
} catch (error) {
|
|
752
758
|
console.error("Middleware auth error:", error);
|
|
753
|
-
|
|
759
|
+
const res = NextResponse.redirect(new URL("/auth/signin", request.url));
|
|
760
|
+
res.cookies.delete("sb-access-token");
|
|
761
|
+
res.cookies.delete("sb-refresh-token");
|
|
762
|
+
res.cookies.delete("sb:token");
|
|
763
|
+
res.cookies.delete("sb:refresh-token");
|
|
764
|
+
return res;
|
|
754
765
|
}
|
|
755
766
|
}
|
|
756
767
|
|
|
757
|
-
// Protéger les routes /admin/* (superadmin uniquement)
|
|
758
|
-
if (pathname.startsWith("/admin")) {
|
|
768
|
+
// Protéger les routes /admin/* et /api/admin/* (superadmin uniquement)
|
|
769
|
+
if (pathname.startsWith("/admin") || pathname.startsWith("/api/admin")) {
|
|
759
770
|
try {
|
|
760
771
|
const { supabase, response } = createMiddlewareClient(request);
|
|
761
772
|
const {
|
|
762
773
|
data: { session },
|
|
763
774
|
} = await supabase.auth.getSession();
|
|
764
775
|
|
|
765
|
-
// Pas de session → redirection vers /signin
|
|
776
|
+
// Pas de session → 401 JSON pour API, sinon redirection vers /auth/signin avec nettoyage cookies
|
|
766
777
|
if (!session) {
|
|
767
|
-
|
|
778
|
+
if (isApi) {
|
|
779
|
+
const res = NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
|
780
|
+
res.cookies.delete("sb-access-token");
|
|
781
|
+
res.cookies.delete("sb-refresh-token");
|
|
782
|
+
res.cookies.delete("sb:token");
|
|
783
|
+
res.cookies.delete("sb:refresh-token");
|
|
784
|
+
return res;
|
|
785
|
+
}
|
|
786
|
+
const redirectUrl = new URL("/auth/signin", request.url);
|
|
768
787
|
redirectUrl.searchParams.set("redirect", pathname);
|
|
769
|
-
|
|
788
|
+
const res = NextResponse.redirect(redirectUrl);
|
|
789
|
+
res.cookies.delete("sb-access-token");
|
|
790
|
+
res.cookies.delete("sb-refresh-token");
|
|
791
|
+
res.cookies.delete("sb:token");
|
|
792
|
+
res.cookies.delete("sb:refresh-token");
|
|
793
|
+
return res;
|
|
770
794
|
}
|
|
771
795
|
|
|
772
796
|
// Vérifier si l'utilisateur est superadmin
|
|
@@ -777,13 +801,24 @@ export async function middleware(request: NextRequest) {
|
|
|
777
801
|
|
|
778
802
|
if (error || !isSuperAdmin) {
|
|
779
803
|
console.error("Access denied: not a superadmin", error);
|
|
804
|
+
if (isApi) {
|
|
805
|
+
return NextResponse.json({ error: "Accès refusé" }, { status: 403 });
|
|
806
|
+
}
|
|
780
807
|
return NextResponse.redirect(new URL("/", request.url));
|
|
781
808
|
}
|
|
782
809
|
|
|
783
810
|
return response;
|
|
784
811
|
} catch (error) {
|
|
785
812
|
console.error("Middleware admin error:", error);
|
|
786
|
-
|
|
813
|
+
if (isApi) {
|
|
814
|
+
return NextResponse.json({ error: "Erreur middleware" }, { status: 500 });
|
|
815
|
+
}
|
|
816
|
+
const res = NextResponse.redirect(new URL("/", request.url));
|
|
817
|
+
res.cookies.delete("sb-access-token");
|
|
818
|
+
res.cookies.delete("sb-refresh-token");
|
|
819
|
+
res.cookies.delete("sb:token");
|
|
820
|
+
res.cookies.delete("sb:refresh-token");
|
|
821
|
+
return res;
|
|
787
822
|
}
|
|
788
823
|
}
|
|
789
824
|
|
|
@@ -804,7 +839,7 @@ export const config = {
|
|
|
804
839
|
};
|
|
805
840
|
`;
|
|
806
841
|
await fs.writeFile(middlewarePath, middleware);
|
|
807
|
-
console.log(chalk.green("✓ middleware.ts créé (protection /auth/* et /admin/*)"));
|
|
842
|
+
console.log(chalk.green("✓ middleware.ts créé (protection /auth/*, /admin/* et /api/admin/*)"));
|
|
808
843
|
}
|
|
809
844
|
// next.config.mjs
|
|
810
845
|
const nextConfigPath = path.join(targetDir, "next.config.mjs");
|
|
@@ -1290,6 +1325,13 @@ export const BUCKET_CONFIGS: Record<string, BucketConfig> = {
|
|
|
1290
1325
|
return filePath.startsWith(\`\${userId}/\`);
|
|
1291
1326
|
},
|
|
1292
1327
|
},
|
|
1328
|
+
recipes: {
|
|
1329
|
+
name: "recipes",
|
|
1330
|
+
isPublic: true,
|
|
1331
|
+
description: "Public recipe images accessible by slug",
|
|
1332
|
+
allowedFileTypes: ["image/jpeg", "image/png", "image/webp", "image/gif"],
|
|
1333
|
+
maxFileSize: 50 * 1024 * 1024, // 50MB
|
|
1334
|
+
},
|
|
1293
1335
|
// Example for future buckets:
|
|
1294
1336
|
// public: {
|
|
1295
1337
|
// name: "public",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-add.d.ts","sourceRoot":"","sources":["../../src/scripts/module-add.ts"],"names":[],"mappings":"AAiCA,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"module-add.d.ts","sourceRoot":"","sources":["../../src/scripts/module-add.ts"],"names":[],"mappings":"AAiCA,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,iBA6VpE"}
|
|
@@ -146,14 +146,62 @@ export async function addModule(moduleName, targetDir) {
|
|
|
146
146
|
else if (migrationAction === "push") {
|
|
147
147
|
console.log(chalk.yellow("\n⬆️ Application des nouvelles migrations..."));
|
|
148
148
|
try {
|
|
149
|
-
|
|
149
|
+
// Essayer en capturant la sortie pour diagnostiquer les erreurs fréquentes
|
|
150
|
+
execSync("supabase migration up --local", {
|
|
150
151
|
cwd: targetDir,
|
|
151
|
-
stdio: "
|
|
152
|
+
stdio: "pipe",
|
|
152
153
|
});
|
|
153
154
|
console.log(chalk.green("✓ Migrations appliquées"));
|
|
154
155
|
}
|
|
155
|
-
catch {
|
|
156
|
-
|
|
156
|
+
catch (error) {
|
|
157
|
+
const output = String(error?.stderr || error?.stdout || error?.message || "");
|
|
158
|
+
const mismatch = output.includes("Remote migration versions not found in local migrations directory");
|
|
159
|
+
if (mismatch) {
|
|
160
|
+
console.warn(chalk.yellow("⚠️ Historique des migrations désynchronisé."));
|
|
161
|
+
// Extraire TOUTES les dates de migration depuis le message d'erreur
|
|
162
|
+
const repairMatch = output.match(/supabase migration repair --status reverted ([\s\S]+?)\n/);
|
|
163
|
+
let migrationDates = [];
|
|
164
|
+
if (repairMatch && repairMatch[1]) {
|
|
165
|
+
// Extraire toutes les dates de la commande suggérée
|
|
166
|
+
migrationDates = repairMatch[1].trim().split(/\s+/);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// Fallback: chercher toutes les dates dans le message
|
|
170
|
+
const allDates = output.match(/20\d{6,14}/g);
|
|
171
|
+
migrationDates = allDates ? [...new Set(allDates)] : [];
|
|
172
|
+
}
|
|
173
|
+
if (migrationDates.length === 0) {
|
|
174
|
+
console.error(chalk.red("❌ Impossible d'extraire les dates de migration"));
|
|
175
|
+
console.log(chalk.gray("\nFaites un reset complet:\n supabase db reset\n"));
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
// Réparation automatique de l'historique
|
|
179
|
+
const datesStr = migrationDates.join(" ");
|
|
180
|
+
console.log(chalk.yellow(`\n🔧 Réparation automatique de l'historique...`));
|
|
181
|
+
console.log(chalk.gray(` Dates: ${datesStr}`));
|
|
182
|
+
try {
|
|
183
|
+
execSync(`supabase migration repair --status reverted ${datesStr} --local`, {
|
|
184
|
+
cwd: targetDir,
|
|
185
|
+
stdio: "inherit",
|
|
186
|
+
});
|
|
187
|
+
console.log(chalk.green("✓ Historique réparé"));
|
|
188
|
+
console.log(chalk.yellow("\n⬆️ Application des nouvelles migrations..."));
|
|
189
|
+
execSync("supabase migration up --local", {
|
|
190
|
+
cwd: targetDir,
|
|
191
|
+
stdio: "inherit",
|
|
192
|
+
});
|
|
193
|
+
console.log(chalk.green("✓ Migrations appliquées"));
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
console.error(chalk.red("❌ Échec de la réparation automatique"));
|
|
197
|
+
console.log(chalk.gray(`\nVous pouvez essayer manuellement:\n supabase migration repair --status reverted ${datesStr} --local\n supabase migration up --local`));
|
|
198
|
+
console.log(chalk.gray(`\nOu faire un reset complet:\n supabase db reset\n`));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
console.error(chalk.red("❌ Erreur lors de l'application des migrations"));
|
|
204
|
+
}
|
|
157
205
|
}
|
|
158
206
|
}
|
|
159
207
|
else {
|
|
@@ -203,12 +251,12 @@ export async function addModule(moduleName, targetDir) {
|
|
|
203
251
|
// 7. Générer automatiquement les routes du module
|
|
204
252
|
console.log(chalk.yellow("🔧 Génération des routes du module..."));
|
|
205
253
|
try {
|
|
206
|
-
execSync("pnpm build:modules", { cwd: targetDir, stdio: "inherit" });
|
|
254
|
+
execSync("pnpm run build:modules", { cwd: targetDir, stdio: "inherit" });
|
|
207
255
|
console.log(chalk.green("✓ Routes du module générées"));
|
|
208
256
|
}
|
|
209
257
|
catch {
|
|
210
258
|
console.error(chalk.red("❌ Erreur lors de la génération des routes"));
|
|
211
|
-
console.error(chalk.gray("Vous pouvez les générer manuellement avec: pnpm build:modules"));
|
|
259
|
+
console.error(chalk.gray("Vous pouvez les générer manuellement avec: cd apps/<votre-app> && pnpm run build:modules"));
|
|
212
260
|
}
|
|
213
261
|
console.log(chalk.gray("\nLe serveur de développement redémarrera automatiquement.\n"));
|
|
214
262
|
}
|
|
@@ -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":"AA69BA,wBAAsB,cAAc,kBA6EnC"}
|