@lastbrain/app 0.1.8 → 0.1.9
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/scripts/init-app.js +0 -2
- package/package.json +3 -2
- package/src/app-shell/(admin)/layout.tsx +13 -0
- package/src/app-shell/(auth)/layout.tsx +13 -0
- package/src/app-shell/(public)/page.tsx +11 -0
- package/src/app-shell/layout.tsx +5 -0
- package/src/app-shell/not-found.tsx +28 -0
- package/src/auth/authHelpers.ts +24 -0
- package/src/auth/useAuthSession.ts +54 -0
- package/src/cli.ts +96 -0
- package/src/index.ts +21 -0
- package/src/layouts/AdminLayout.tsx +7 -0
- package/src/layouts/AppProviders.tsx +61 -0
- package/src/layouts/AuthLayout.tsx +7 -0
- package/src/layouts/PublicLayout.tsx +7 -0
- package/src/layouts/RootLayout.tsx +27 -0
- package/src/modules/module-loader.ts +14 -0
- package/src/scripts/README.md +262 -0
- package/src/scripts/db-init.ts +338 -0
- package/src/scripts/db-migrations-sync.ts +86 -0
- package/src/scripts/dev-sync.ts +218 -0
- package/src/scripts/init-app.ts +1077 -0
- package/src/scripts/module-add.ts +242 -0
- package/src/scripts/module-build.ts +502 -0
- package/src/scripts/module-create.ts +809 -0
- package/src/scripts/module-list.ts +37 -0
- package/src/scripts/module-remove.ts +367 -0
- package/src/scripts/readme-build.ts +60 -0
- package/src/styles.css +3 -0
- package/src/templates/AuthGuidePage.tsx +68 -0
- package/src/templates/DefaultDoc.tsx +462 -0
- package/src/templates/DocPage.tsx +381 -0
- package/src/templates/DocsPageWithModules.tsx +22 -0
- package/src/templates/MigrationsGuidePage.tsx +61 -0
- package/src/templates/ModuleGuidePage.tsx +71 -0
- package/src/templates/SimpleDocPage.tsx +587 -0
- package/src/templates/SimpleHomePage.tsx +385 -0
- package/src/templates/env.example/.env.example +6 -0
- package/src/templates/migrations/20201010100000_app_base.sql +228 -0
package/dist/scripts/init-app.js
CHANGED
|
@@ -270,8 +270,6 @@ async function createNextStructure(targetDir, force, useHeroUI, withAuth) {
|
|
|
270
270
|
"use client";
|
|
271
271
|
|
|
272
272
|
import "../styles/globals.css";
|
|
273
|
-
import "@lastbrain/ui/dist/styles.css";
|
|
274
|
-
import "@lastbrain/app/dist/styles.css";
|
|
275
273
|
import { HeroUIProvider } from "@heroui/system";
|
|
276
274
|
|
|
277
275
|
import { ThemeProvider } from "next-themes";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lastbrain/app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Framework modulaire Next.js avec CLI et système de modules",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"lastbrain": "./dist/cli.js"
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
|
-
"dist"
|
|
30
|
+
"dist",
|
|
31
|
+
"src"
|
|
31
32
|
],
|
|
32
33
|
"dependencies": {
|
|
33
34
|
"@lastbrain/core": "0.1.0",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { PropsWithChildren } from "react";
|
|
4
|
+
|
|
5
|
+
export default function AdminShellLayout({ children }: PropsWithChildren<{}>) {
|
|
6
|
+
return (
|
|
7
|
+
<div className="mx-auto max-w-5xl px-4 py-16">
|
|
8
|
+
<h1 className="text-3xl font-bold">Section Admin</h1>
|
|
9
|
+
<p className="text-slate-600">Gestion avancée des modules.</p>
|
|
10
|
+
{children}
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { PropsWithChildren } from "react";
|
|
4
|
+
|
|
5
|
+
export default function AuthLayout({ children }: PropsWithChildren<{}>) {
|
|
6
|
+
return (
|
|
7
|
+
<div className="mx-auto max-w-5xl px-4 py-16">
|
|
8
|
+
<h1 className="text-3xl font-bold">Section Auth</h1>
|
|
9
|
+
<p className="text-slate-600">Les pages authentifiées regroupées ici.</p>
|
|
10
|
+
{children}
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export default function PublicPage() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="mx-auto max-w-3xl space-y-4 px-4 py-16 text-center">
|
|
6
|
+
<h1 className="text-3xl font-bold">Bienvenue sur LastBrain</h1>
|
|
7
|
+
<p className="text-slate-600">Cette coquille publique présente la structure par défaut.</p>
|
|
8
|
+
<button className="rounded-full bg-slate-900 px-5 py-2 text-white">Connexion</button>
|
|
9
|
+
</div>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Button } from "@lastbrain/ui";
|
|
3
|
+
import { useRouter } from "next/navigation";
|
|
4
|
+
|
|
5
|
+
export default function NotFound() {
|
|
6
|
+
const router = useRouter();
|
|
7
|
+
return (
|
|
8
|
+
<div className="flex min-h-screen items-center justify-center bg-background">
|
|
9
|
+
<div className="mx-auto max-w-md text-center">
|
|
10
|
+
<h1 className="mb-4 text-6xl font-bold text-foreground">404</h1>
|
|
11
|
+
<h2 className="mb-4 text-2xl font-semibold text-foreground">
|
|
12
|
+
Page non trouvée
|
|
13
|
+
</h2>
|
|
14
|
+
<p className="mb-8 text-muted-foreground">
|
|
15
|
+
La page que vous recherchez n'existe pas ou a été déplacée.
|
|
16
|
+
</p>
|
|
17
|
+
<Button
|
|
18
|
+
onPress={() => {
|
|
19
|
+
router.back();
|
|
20
|
+
}}
|
|
21
|
+
className="inline-flex items-center justify-center rounded-md bg-primary px-6 py-3 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90"
|
|
22
|
+
>
|
|
23
|
+
Retour à l'accueil
|
|
24
|
+
</Button>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { supabaseBrowserClient } from "@lastbrain/core";
|
|
4
|
+
|
|
5
|
+
export async function signOut() {
|
|
6
|
+
const { error } = await supabaseBrowserClient.auth.signOut();
|
|
7
|
+
if (error) {
|
|
8
|
+
console.error("Error signing out:", error);
|
|
9
|
+
throw error;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function signIn(email: string, password: string) {
|
|
14
|
+
const { data, error } = await supabaseBrowserClient.auth.signInWithPassword({
|
|
15
|
+
email,
|
|
16
|
+
password,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (error) {
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return data;
|
|
24
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { supabaseBrowserClient } from "@lastbrain/core";
|
|
5
|
+
import type { User } from "@supabase/supabase-js";
|
|
6
|
+
|
|
7
|
+
export function useAuthSession() {
|
|
8
|
+
const [user, setUser] = useState<User | null>(null);
|
|
9
|
+
const [loading, setLoading] = useState(true);
|
|
10
|
+
const [isSuperAdmin, setIsSuperAdmin] = useState(false);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const checkSuperAdmin = async (userId: string) => {
|
|
14
|
+
try {
|
|
15
|
+
const { data, error } = await supabaseBrowserClient.rpc(
|
|
16
|
+
"is_superadmin",
|
|
17
|
+
{ user_id: userId }
|
|
18
|
+
);
|
|
19
|
+
if (!error && data) {
|
|
20
|
+
setIsSuperAdmin(data);
|
|
21
|
+
}
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error("Error checking superadmin status:", error);
|
|
24
|
+
setIsSuperAdmin(false);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Récupérer la session initiale
|
|
29
|
+
supabaseBrowserClient.auth.getSession().then(({ data: { session } }) => {
|
|
30
|
+
setUser(session?.user ?? null);
|
|
31
|
+
if (session?.user) {
|
|
32
|
+
checkSuperAdmin(session.user.id);
|
|
33
|
+
}
|
|
34
|
+
setLoading(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Écouter les changements d'auth
|
|
38
|
+
const {
|
|
39
|
+
data: { subscription },
|
|
40
|
+
} = supabaseBrowserClient.auth.onAuthStateChange((_event, session) => {
|
|
41
|
+
setUser(session?.user ?? null);
|
|
42
|
+
if (session?.user) {
|
|
43
|
+
checkSuperAdmin(session.user.id);
|
|
44
|
+
} else {
|
|
45
|
+
setIsSuperAdmin(false);
|
|
46
|
+
}
|
|
47
|
+
setLoading(false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return () => subscription.unsubscribe();
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
return { user, loading, isSuperAdmin };
|
|
54
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { initApp } from "./scripts/init-app.js";
|
|
6
|
+
import { addModule } from "./scripts/module-add.js";
|
|
7
|
+
import { removeModule } from "./scripts/module-remove.js";
|
|
8
|
+
import { listModules } from "./scripts/module-list.js";
|
|
9
|
+
import { createModule } from "./scripts/module-create.js";
|
|
10
|
+
|
|
11
|
+
const program = new Command();
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.name("lastbrain")
|
|
15
|
+
.description("CLI pour créer et gérer des applications LastBrain")
|
|
16
|
+
.version("0.1.0");
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.command("init [directory]")
|
|
20
|
+
.description("Initialise une nouvelle application Next.js LastBrain")
|
|
21
|
+
.option("-f, --force", "Écrase les fichiers existants")
|
|
22
|
+
.option("--no-heroui", "Ne pas utiliser HeroUI (Tailwind CSS uniquement)")
|
|
23
|
+
.option("--with-auth", "Inclure le module d'authentification")
|
|
24
|
+
.option(
|
|
25
|
+
"--no-interactive",
|
|
26
|
+
"Mode non-interactif (skip la sélection des modules)"
|
|
27
|
+
)
|
|
28
|
+
.action(async (directory: string | undefined, options) => {
|
|
29
|
+
try {
|
|
30
|
+
const targetDir = directory
|
|
31
|
+
? path.resolve(process.cwd(), directory)
|
|
32
|
+
: process.cwd();
|
|
33
|
+
|
|
34
|
+
await initApp({
|
|
35
|
+
force: options.force || false,
|
|
36
|
+
targetDir,
|
|
37
|
+
projectName: directory || path.basename(process.cwd()),
|
|
38
|
+
useHeroUI: options.heroui !== false, // Par défaut true, false si --no-heroui
|
|
39
|
+
withAuth: options.withAuth || false,
|
|
40
|
+
interactive: options.interactive !== false,
|
|
41
|
+
});
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error("❌ Erreur lors de l'initialisation:", error);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
program
|
|
49
|
+
.command("add-module <module>")
|
|
50
|
+
.description("Ajoute un module à l'application")
|
|
51
|
+
.action(async (moduleName: string) => {
|
|
52
|
+
try {
|
|
53
|
+
await addModule(moduleName, process.cwd());
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error("❌ Erreur lors de l'ajout du module:", error);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
program
|
|
61
|
+
.command("remove-module <module>")
|
|
62
|
+
.description("Supprime un module de l'application")
|
|
63
|
+
.action(async (moduleName: string) => {
|
|
64
|
+
try {
|
|
65
|
+
await removeModule(moduleName, process.cwd());
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error("❌ Erreur lors de la suppression du module:", error);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
program
|
|
73
|
+
.command("list-modules")
|
|
74
|
+
.description("Liste les modules disponibles et installés")
|
|
75
|
+
.action(async () => {
|
|
76
|
+
try {
|
|
77
|
+
await listModules(process.cwd());
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error("❌ Erreur lors de la liste des modules:", error);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
program
|
|
85
|
+
.command("create-module")
|
|
86
|
+
.description("Crée un nouveau module dans packages/")
|
|
87
|
+
.action(async () => {
|
|
88
|
+
try {
|
|
89
|
+
await createModule();
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error("❌ Erreur lors de la création du module:", error);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
program.parse();
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export {
|
|
2
|
+
AppProviders,
|
|
3
|
+
useAuth,
|
|
4
|
+
useModules,
|
|
5
|
+
useNotifications,
|
|
6
|
+
} from "./layouts/AppProviders.js";
|
|
7
|
+
export { useAuthSession } from "./auth/useAuthSession.js";
|
|
8
|
+
export { signOut, signIn } from "./auth/authHelpers.js";
|
|
9
|
+
export { RootLayout } from "./layouts/RootLayout.js";
|
|
10
|
+
export { PublicLayout } from "./layouts/PublicLayout.js";
|
|
11
|
+
export { AuthLayout } from "./layouts/AuthLayout.js";
|
|
12
|
+
export { AdminLayout } from "./layouts/AdminLayout.js";
|
|
13
|
+
export { getModuleConfigs } from "./modules/module-loader.js";
|
|
14
|
+
// Templates de pages (starter + docs)
|
|
15
|
+
|
|
16
|
+
export { SimpleHomePage } from "./templates/SimpleHomePage.js";
|
|
17
|
+
export { DocPage } from "./templates/DocPage.js";
|
|
18
|
+
export { SimpleDocPage } from "./templates/SimpleDocPage.js";
|
|
19
|
+
export { ModuleGuidePage } from "./templates/ModuleGuidePage.js";
|
|
20
|
+
export { AuthGuidePage } from "./templates/AuthGuidePage.js";
|
|
21
|
+
export { MigrationsGuidePage } from "./templates/MigrationsGuidePage.js";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { PropsWithChildren } from "react";
|
|
4
|
+
import { createContext, useContext, useMemo } from "react";
|
|
5
|
+
import { getModuleConfigs } from "../modules/module-loader.js";
|
|
6
|
+
import { Header, ToastProvider } from "@lastbrain/ui";
|
|
7
|
+
import { useAuthSession } from "../auth/useAuthSession.js";
|
|
8
|
+
import type { User } from "@supabase/supabase-js";
|
|
9
|
+
import { useRouter } from "next/navigation.js";
|
|
10
|
+
|
|
11
|
+
const ModuleContext = createContext(getModuleConfigs());
|
|
12
|
+
const NotificationContext = createContext({ messages: [] as string[] });
|
|
13
|
+
const AuthContext = createContext<{
|
|
14
|
+
user: User | null;
|
|
15
|
+
loading: boolean;
|
|
16
|
+
isSuperAdmin: boolean;
|
|
17
|
+
}>({
|
|
18
|
+
user: null,
|
|
19
|
+
loading: true,
|
|
20
|
+
isSuperAdmin: false,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export function useModules() {
|
|
24
|
+
return useContext(ModuleContext);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useNotifications() {
|
|
28
|
+
return useContext(NotificationContext);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function useAuth() {
|
|
32
|
+
return useContext(AuthContext);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function AppProviders({ children }: PropsWithChildren<{}>) {
|
|
36
|
+
const modules = useMemo(() => getModuleConfigs(), []);
|
|
37
|
+
const notifications = useMemo(() => ({ messages: [] as string[] }), []);
|
|
38
|
+
const { user, loading, isSuperAdmin } = useAuthSession();
|
|
39
|
+
// const router = useRouter();
|
|
40
|
+
const authValue = useMemo(
|
|
41
|
+
() => ({ user, loading, isSuperAdmin }),
|
|
42
|
+
[user, loading, isSuperAdmin]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// const handleLogout = async () => {
|
|
46
|
+
// const { signOut } = await import("../auth/authHelpers.js");
|
|
47
|
+
// await signOut();
|
|
48
|
+
// router.push("/auth/signin");
|
|
49
|
+
// };
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<AuthContext.Provider value={authValue}>
|
|
53
|
+
<ModuleContext.Provider value={modules}>
|
|
54
|
+
<NotificationContext.Provider value={notifications}>
|
|
55
|
+
{children}
|
|
56
|
+
<ToastProvider placement="top-right" toastOffset={75} />
|
|
57
|
+
</NotificationContext.Provider>
|
|
58
|
+
</ModuleContext.Provider>
|
|
59
|
+
</AuthContext.Provider>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { PropsWithChildren } from "react";
|
|
4
|
+
import { ThemeProvider } from "next-themes";
|
|
5
|
+
import { AppProviders } from "./AppProviders.js";
|
|
6
|
+
|
|
7
|
+
// Note: L'app Next.js doit importer son propre globals.css dans son layout
|
|
8
|
+
export function RootLayout({ children }: PropsWithChildren<{}>) {
|
|
9
|
+
return (
|
|
10
|
+
<html lang="fr" suppressHydrationWarning>
|
|
11
|
+
<body className="min-h-screen">
|
|
12
|
+
<ThemeProvider
|
|
13
|
+
attribute="class"
|
|
14
|
+
defaultTheme="light"
|
|
15
|
+
enableSystem={false}
|
|
16
|
+
storageKey="lastbrain-theme"
|
|
17
|
+
>
|
|
18
|
+
<AppProviders>
|
|
19
|
+
<div className=" min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-white">
|
|
20
|
+
{children}
|
|
21
|
+
</div>
|
|
22
|
+
</AppProviders>
|
|
23
|
+
</ThemeProvider>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ModuleBuildConfig } from "@lastbrain/core";
|
|
2
|
+
|
|
3
|
+
// Ce fichier est utilisé côté client par AppProviders
|
|
4
|
+
// Le chargement des modules se fait uniquement au build-time dans module-build.ts
|
|
5
|
+
// Côté client, on exporte un tableau vide car les modules sont déjà compilés
|
|
6
|
+
export const moduleConfigs: ModuleBuildConfig[] = [];
|
|
7
|
+
|
|
8
|
+
export function getModuleConfigs() {
|
|
9
|
+
return moduleConfigs;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function registerModuleConfig(config: ModuleBuildConfig) {
|
|
13
|
+
moduleConfigs.push(config);
|
|
14
|
+
}
|