@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.
Files changed (39) hide show
  1. package/dist/scripts/init-app.js +0 -2
  2. package/package.json +3 -2
  3. package/src/app-shell/(admin)/layout.tsx +13 -0
  4. package/src/app-shell/(auth)/layout.tsx +13 -0
  5. package/src/app-shell/(public)/page.tsx +11 -0
  6. package/src/app-shell/layout.tsx +5 -0
  7. package/src/app-shell/not-found.tsx +28 -0
  8. package/src/auth/authHelpers.ts +24 -0
  9. package/src/auth/useAuthSession.ts +54 -0
  10. package/src/cli.ts +96 -0
  11. package/src/index.ts +21 -0
  12. package/src/layouts/AdminLayout.tsx +7 -0
  13. package/src/layouts/AppProviders.tsx +61 -0
  14. package/src/layouts/AuthLayout.tsx +7 -0
  15. package/src/layouts/PublicLayout.tsx +7 -0
  16. package/src/layouts/RootLayout.tsx +27 -0
  17. package/src/modules/module-loader.ts +14 -0
  18. package/src/scripts/README.md +262 -0
  19. package/src/scripts/db-init.ts +338 -0
  20. package/src/scripts/db-migrations-sync.ts +86 -0
  21. package/src/scripts/dev-sync.ts +218 -0
  22. package/src/scripts/init-app.ts +1077 -0
  23. package/src/scripts/module-add.ts +242 -0
  24. package/src/scripts/module-build.ts +502 -0
  25. package/src/scripts/module-create.ts +809 -0
  26. package/src/scripts/module-list.ts +37 -0
  27. package/src/scripts/module-remove.ts +367 -0
  28. package/src/scripts/readme-build.ts +60 -0
  29. package/src/styles.css +3 -0
  30. package/src/templates/AuthGuidePage.tsx +68 -0
  31. package/src/templates/DefaultDoc.tsx +462 -0
  32. package/src/templates/DocPage.tsx +381 -0
  33. package/src/templates/DocsPageWithModules.tsx +22 -0
  34. package/src/templates/MigrationsGuidePage.tsx +61 -0
  35. package/src/templates/ModuleGuidePage.tsx +71 -0
  36. package/src/templates/SimpleDocPage.tsx +587 -0
  37. package/src/templates/SimpleHomePage.tsx +385 -0
  38. package/src/templates/env.example/.env.example +6 -0
  39. package/src/templates/migrations/20201010100000_app_base.sql +228 -0
@@ -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.8",
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,5 @@
1
+ "use client";
2
+
3
+ import { RootLayout } from "../layouts/RootLayout.js";
4
+
5
+ export default RootLayout;
@@ -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,7 @@
1
+ "use client";
2
+
3
+ import type { PropsWithChildren } from "react";
4
+
5
+ export function AdminLayout({ children }: PropsWithChildren<{}>) {
6
+ return <div className="pt-18 px-2 md:px-5">{children}</div>;
7
+ }
@@ -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,7 @@
1
+ "use client";
2
+
3
+ import type { PropsWithChildren } from "react";
4
+
5
+ export function AuthLayout({ children }: PropsWithChildren<{}>) {
6
+ return <div className="pt-18 px-2 md:px-5 ">{children}</div>;
7
+ }
@@ -0,0 +1,7 @@
1
+ "use client";
2
+
3
+ import type { PropsWithChildren } from "react";
4
+
5
+ export function PublicLayout({ children }: PropsWithChildren<{}>) {
6
+ return <section className=" px-4 ">{children}</section>;
7
+ }
@@ -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
+ }