@lastbrain/app 0.1.0

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 (159) hide show
  1. package/README.md +256 -0
  2. package/dist/app-shell/(admin)/dashboard/page.d.ts +2 -0
  3. package/dist/app-shell/(admin)/dashboard/page.d.ts.map +1 -0
  4. package/dist/app-shell/(admin)/dashboard/page.js +5 -0
  5. package/dist/app-shell/(admin)/layout.d.ts +3 -0
  6. package/dist/app-shell/(admin)/layout.d.ts.map +1 -0
  7. package/dist/app-shell/(admin)/layout.js +5 -0
  8. package/dist/app-shell/(admin)/page.d.ts +2 -0
  9. package/dist/app-shell/(admin)/page.d.ts.map +1 -0
  10. package/dist/app-shell/(admin)/page.js +5 -0
  11. package/dist/app-shell/(auth)/dashboard/page.d.ts +2 -0
  12. package/dist/app-shell/(auth)/dashboard/page.d.ts.map +1 -0
  13. package/dist/app-shell/(auth)/dashboard/page.js +5 -0
  14. package/dist/app-shell/(auth)/layout.d.ts +3 -0
  15. package/dist/app-shell/(auth)/layout.d.ts.map +1 -0
  16. package/dist/app-shell/(auth)/layout.js +5 -0
  17. package/dist/app-shell/(auth)/page.d.ts +2 -0
  18. package/dist/app-shell/(auth)/page.d.ts.map +1 -0
  19. package/dist/app-shell/(auth)/page.js +5 -0
  20. package/dist/app-shell/(public)/page.d.ts +2 -0
  21. package/dist/app-shell/(public)/page.d.ts.map +1 -0
  22. package/dist/app-shell/(public)/page.js +5 -0
  23. package/dist/app-shell/layout.d.ts +3 -0
  24. package/dist/app-shell/layout.d.ts.map +1 -0
  25. package/dist/app-shell/layout.js +3 -0
  26. package/dist/app-shell/not-found.d.ts +2 -0
  27. package/dist/app-shell/not-found.d.ts.map +1 -0
  28. package/dist/app-shell/not-found.js +10 -0
  29. package/dist/auth/authHelpers.d.ts +7 -0
  30. package/dist/auth/authHelpers.d.ts.map +1 -0
  31. package/dist/auth/authHelpers.js +19 -0
  32. package/dist/auth/useAuthSession.d.ts +7 -0
  33. package/dist/auth/useAuthSession.d.ts.map +1 -0
  34. package/dist/auth/useAuthSession.js +43 -0
  35. package/dist/cli.d.ts +3 -0
  36. package/dist/cli.d.ts.map +1 -0
  37. package/dist/cli.js +88 -0
  38. package/dist/components/TableStructure.d.ts +8 -0
  39. package/dist/components/TableStructure.d.ts.map +1 -0
  40. package/dist/components/TableStructure.js +37 -0
  41. package/dist/index.d.ts +15 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +15 -0
  44. package/dist/layouts/AdminLayout.d.ts +3 -0
  45. package/dist/layouts/AdminLayout.d.ts.map +1 -0
  46. package/dist/layouts/AdminLayout.js +5 -0
  47. package/dist/layouts/AppProviders.d.ts +13 -0
  48. package/dist/layouts/AppProviders.d.ts.map +1 -0
  49. package/dist/layouts/AppProviders.js +35 -0
  50. package/dist/layouts/AuthLayout.d.ts +3 -0
  51. package/dist/layouts/AuthLayout.d.ts.map +1 -0
  52. package/dist/layouts/AuthLayout.js +5 -0
  53. package/dist/layouts/PublicLayout.d.ts +3 -0
  54. package/dist/layouts/PublicLayout.d.ts.map +1 -0
  55. package/dist/layouts/PublicLayout.js +5 -0
  56. package/dist/layouts/RootLayout.d.ts +3 -0
  57. package/dist/layouts/RootLayout.d.ts.map +1 -0
  58. package/dist/layouts/RootLayout.js +8 -0
  59. package/dist/module-build.d.ts +2 -0
  60. package/dist/module-build.d.ts.map +1 -0
  61. package/dist/module-build.js +50 -0
  62. package/dist/modules/index.d.ts +3 -0
  63. package/dist/modules/index.d.ts.map +1 -0
  64. package/dist/modules/index.js +2 -0
  65. package/dist/modules/module-loader.d.ts +5 -0
  66. package/dist/modules/module-loader.d.ts.map +1 -0
  67. package/dist/modules/module-loader.js +10 -0
  68. package/dist/scripts/db-init.d.ts +2 -0
  69. package/dist/scripts/db-init.d.ts.map +1 -0
  70. package/dist/scripts/db-init.js +281 -0
  71. package/dist/scripts/db-migrations-sync.d.ts +2 -0
  72. package/dist/scripts/db-migrations-sync.d.ts.map +1 -0
  73. package/dist/scripts/db-migrations-sync.js +55 -0
  74. package/dist/scripts/dev-sync.d.ts +2 -0
  75. package/dist/scripts/dev-sync.d.ts.map +1 -0
  76. package/dist/scripts/dev-sync.js +182 -0
  77. package/dist/scripts/init-app.d.ts +11 -0
  78. package/dist/scripts/init-app.d.ts.map +1 -0
  79. package/dist/scripts/init-app.js +846 -0
  80. package/dist/scripts/module-add.d.ts +13 -0
  81. package/dist/scripts/module-add.d.ts.map +1 -0
  82. package/dist/scripts/module-add.js +178 -0
  83. package/dist/scripts/module-build.d.ts +2 -0
  84. package/dist/scripts/module-build.d.ts.map +1 -0
  85. package/dist/scripts/module-build.js +413 -0
  86. package/dist/scripts/module-create.d.ts +5 -0
  87. package/dist/scripts/module-create.d.ts.map +1 -0
  88. package/dist/scripts/module-create.js +694 -0
  89. package/dist/scripts/module-list.d.ts +2 -0
  90. package/dist/scripts/module-list.d.ts.map +1 -0
  91. package/dist/scripts/module-list.js +31 -0
  92. package/dist/scripts/module-remove.d.ts +2 -0
  93. package/dist/scripts/module-remove.d.ts.map +1 -0
  94. package/dist/scripts/module-remove.js +282 -0
  95. package/dist/scripts/readme-build.d.ts +2 -0
  96. package/dist/scripts/readme-build.d.ts.map +1 -0
  97. package/dist/scripts/readme-build.js +39 -0
  98. package/dist/templates/AuthGuidePage.d.ts +2 -0
  99. package/dist/templates/AuthGuidePage.d.ts.map +1 -0
  100. package/dist/templates/AuthGuidePage.js +9 -0
  101. package/dist/templates/DefaultDoc.d.ts +2 -0
  102. package/dist/templates/DefaultDoc.d.ts.map +1 -0
  103. package/dist/templates/DefaultDoc.js +29 -0
  104. package/dist/templates/DocPage.d.ts +17 -0
  105. package/dist/templates/DocPage.d.ts.map +1 -0
  106. package/dist/templates/DocPage.js +165 -0
  107. package/dist/templates/DocsPageWithModules.d.ts +2 -0
  108. package/dist/templates/DocsPageWithModules.d.ts.map +1 -0
  109. package/dist/templates/DocsPageWithModules.js +8 -0
  110. package/dist/templates/HomePage.d.ts +6 -0
  111. package/dist/templates/HomePage.d.ts.map +1 -0
  112. package/dist/templates/HomePage.js +6 -0
  113. package/dist/templates/MigrationsGuidePage.d.ts +2 -0
  114. package/dist/templates/MigrationsGuidePage.d.ts.map +1 -0
  115. package/dist/templates/MigrationsGuidePage.js +11 -0
  116. package/dist/templates/ModuleGuidePage.d.ts +2 -0
  117. package/dist/templates/ModuleGuidePage.d.ts.map +1 -0
  118. package/dist/templates/ModuleGuidePage.js +14 -0
  119. package/dist/templates/SimpleDocPage.d.ts +2 -0
  120. package/dist/templates/SimpleDocPage.d.ts.map +1 -0
  121. package/dist/templates/SimpleDocPage.js +28 -0
  122. package/dist/templates/SimpleHomePage.d.ts +6 -0
  123. package/dist/templates/SimpleHomePage.d.ts.map +1 -0
  124. package/dist/templates/SimpleHomePage.js +7 -0
  125. package/dist/templates/env.example/.env.example +6 -0
  126. package/dist/templates/migrations/20201010100000_app_base.sql +228 -0
  127. package/dist/templates/migrations/20201010100000_init.sql +123 -0
  128. package/package.json +75 -0
  129. package/src/app-shell/(admin)/layout.tsx +13 -0
  130. package/src/app-shell/(auth)/layout.tsx +13 -0
  131. package/src/app-shell/(public)/page.tsx +11 -0
  132. package/src/app-shell/layout.tsx +5 -0
  133. package/src/app-shell/not-found.tsx +28 -0
  134. package/src/layouts/AdminLayout.tsx +7 -0
  135. package/src/layouts/AppProviders.tsx +61 -0
  136. package/src/layouts/AuthLayout.tsx +7 -0
  137. package/src/layouts/PublicLayout.tsx +7 -0
  138. package/src/layouts/RootLayout.tsx +27 -0
  139. package/src/scripts/README.md +262 -0
  140. package/src/scripts/db-init.ts +338 -0
  141. package/src/scripts/db-migrations-sync.ts +86 -0
  142. package/src/scripts/dev-sync.ts +218 -0
  143. package/src/scripts/init-app.ts +1011 -0
  144. package/src/scripts/module-add.ts +242 -0
  145. package/src/scripts/module-build.ts +502 -0
  146. package/src/scripts/module-create.ts +809 -0
  147. package/src/scripts/module-list.ts +37 -0
  148. package/src/scripts/module-remove.ts +367 -0
  149. package/src/scripts/readme-build.ts +60 -0
  150. package/src/templates/AuthGuidePage.tsx +68 -0
  151. package/src/templates/DefaultDoc.tsx +462 -0
  152. package/src/templates/DocPage.tsx +381 -0
  153. package/src/templates/DocsPageWithModules.tsx +22 -0
  154. package/src/templates/MigrationsGuidePage.tsx +61 -0
  155. package/src/templates/ModuleGuidePage.tsx +71 -0
  156. package/src/templates/SimpleDocPage.tsx +587 -0
  157. package/src/templates/SimpleHomePage.tsx +385 -0
  158. package/src/templates/env.example/.env.example +6 -0
  159. package/src/templates/migrations/20201010100000_app_base.sql +228 -0
@@ -0,0 +1,228 @@
1
+
2
+ -- ===========================================================================
3
+ -- Helper: is_superadmin (if not already present)
4
+ -- ===========================================================================
5
+ CREATE OR REPLACE FUNCTION public.is_superadmin(user_id uuid)
6
+ RETURNS boolean
7
+ LANGUAGE sql
8
+ STABLE
9
+ SECURITY DEFINER
10
+ SET search_path = public, auth
11
+ AS $function$
12
+ SELECT COALESCE((
13
+ SELECT
14
+ COALESCE((raw_app_meta_data ->> 'is_super_admin')::boolean, FALSE)
15
+ OR COALESCE((raw_user_meta_data ->> 'is_super_admin')::boolean, FALSE)
16
+ OR COALESCE((raw_app_meta_data -> 'roles') ? 'admin', FALSE)
17
+ OR COALESCE((raw_user_meta_data -> 'roles') ? 'admin', FALSE)
18
+ FROM auth.users
19
+ WHERE id = user_id
20
+ ), FALSE);
21
+ $function$;
22
+
23
+ REVOKE ALL ON FUNCTION public.is_superadmin(uuid) FROM PUBLIC;
24
+ GRANT EXECUTE ON FUNCTION public.is_superadmin(uuid) TO authenticated, service_role, anon;
25
+
26
+
27
+
28
+ -- ===========================================================================
29
+ -- Storage Buckets (for avatars and file storage)
30
+ -- ===========================================================================
31
+ INSERT INTO storage.buckets (id, name, public)
32
+ VALUES ('avatar', 'avatar', TRUE)
33
+ ON CONFLICT (id) DO NOTHING;
34
+
35
+ INSERT INTO storage.buckets (id, name, public)
36
+ VALUES ('app', 'app', FALSE)
37
+ ON CONFLICT (id) DO NOTHING;
38
+
39
+ -- ===========================================================================
40
+ -- Avatar bucket policies
41
+ -- ===========================================================================
42
+ DROP POLICY IF EXISTS avatar_public_read ON storage.objects;
43
+ CREATE POLICY avatar_public_read ON storage.objects
44
+ FOR SELECT USING (bucket_id = 'avatar');
45
+
46
+ DROP POLICY IF EXISTS avatar_insert_owner ON storage.objects;
47
+ CREATE POLICY avatar_insert_owner ON storage.objects
48
+ FOR INSERT WITH CHECK (
49
+ bucket_id = 'avatar' AND (
50
+ owner = auth.uid() OR public.is_superadmin(auth.uid())
51
+ )
52
+ );
53
+
54
+ DROP POLICY IF EXISTS avatar_manage_owner ON storage.objects;
55
+ CREATE POLICY avatar_manage_owner ON storage.objects
56
+ FOR UPDATE USING (
57
+ bucket_id = 'avatar' AND (
58
+ owner = auth.uid() OR public.is_superadmin(auth.uid())
59
+ )
60
+ )
61
+ WITH CHECK (
62
+ bucket_id = 'avatar' AND (
63
+ owner = auth.uid() OR public.is_superadmin(auth.uid())
64
+ )
65
+ );
66
+
67
+ DROP POLICY IF EXISTS avatar_delete_owner ON storage.objects;
68
+ CREATE POLICY avatar_delete_owner ON storage.objects
69
+ FOR DELETE USING (
70
+ bucket_id = 'avatar' AND (
71
+ owner = auth.uid() OR public.is_superadmin(auth.uid())
72
+ )
73
+ );
74
+
75
+ -- ===========================================================================
76
+ -- App bucket policies (per-user folders app/{uuid})
77
+ -- ===========================================================================
78
+ DROP POLICY IF EXISTS app_user_select ON storage.objects;
79
+ CREATE POLICY app_user_select ON storage.objects
80
+ FOR SELECT USING (
81
+ bucket_id = 'app' AND (
82
+ owner = auth.uid()
83
+ OR split_part(name, '/', 1) = auth.uid()::text
84
+ OR public.is_superadmin(auth.uid())
85
+ )
86
+ );
87
+
88
+ DROP POLICY IF EXISTS app_user_insert ON storage.objects;
89
+ CREATE POLICY app_user_insert ON storage.objects
90
+ FOR INSERT WITH CHECK (
91
+ bucket_id = 'app' AND (
92
+ split_part(name, '/', 1) = auth.uid()::text
93
+ OR owner = auth.uid()
94
+ OR public.is_superadmin(auth.uid())
95
+ )
96
+ );
97
+
98
+ DROP POLICY IF EXISTS app_user_manage ON storage.objects;
99
+ CREATE POLICY app_user_manage ON storage.objects
100
+ FOR UPDATE USING (
101
+ bucket_id = 'app' AND (
102
+ owner = auth.uid()
103
+ OR split_part(name, '/', 1) = auth.uid()::text
104
+ OR public.is_superadmin(auth.uid())
105
+ )
106
+ )
107
+ WITH CHECK (
108
+ bucket_id = 'app' AND (
109
+ owner = auth.uid()
110
+ OR split_part(name, '/', 1) = auth.uid()::text
111
+ OR public.is_superadmin(auth.uid())
112
+ )
113
+ );
114
+
115
+ DROP POLICY IF EXISTS app_user_delete ON storage.objects;
116
+ CREATE POLICY app_user_delete ON storage.objects
117
+ FOR DELETE USING (
118
+ bucket_id = 'app' AND (
119
+ owner = auth.uid()
120
+ OR split_part(name, '/', 1) = auth.uid()::text
121
+ OR public.is_superadmin(auth.uid())
122
+ )
123
+ );
124
+
125
+ -- ===========================================================================
126
+ -- RPC Function: Get table structure
127
+ -- ===========================================================================
128
+ CREATE OR REPLACE FUNCTION public.get_table_structure(p_table_name text)
129
+ RETURNS TABLE (
130
+ column_name text,
131
+ data_type text,
132
+ is_nullable text,
133
+ column_default text,
134
+ is_primary_key boolean,
135
+ is_foreign_key boolean,
136
+ foreign_table text,
137
+ foreign_column text,
138
+ description text
139
+ )
140
+ LANGUAGE plpgsql
141
+ SECURITY DEFINER
142
+ SET search_path = public, pg_catalog
143
+ AS $$
144
+ BEGIN
145
+ RETURN QUERY
146
+ SELECT
147
+ c.column_name::text,
148
+ CASE
149
+ WHEN c.data_type = 'USER-DEFINED' THEN c.udt_name::text
150
+ WHEN c.data_type = 'ARRAY' THEN c.udt_name::text || '[]'
151
+ ELSE c.data_type::text
152
+ END as data_type,
153
+ c.is_nullable::text,
154
+ c.column_default::text,
155
+ COALESCE(
156
+ EXISTS(
157
+ SELECT 1 FROM information_schema.table_constraints tc
158
+ JOIN information_schema.key_column_usage kcu
159
+ ON tc.constraint_name = kcu.constraint_name
160
+ AND tc.table_schema = kcu.table_schema
161
+ WHERE tc.constraint_type = 'PRIMARY KEY'
162
+ AND tc.table_schema = 'public'
163
+ AND tc.table_name = p_table_name
164
+ AND kcu.column_name = c.column_name
165
+ ), false
166
+ ) as is_primary_key,
167
+ COALESCE(
168
+ EXISTS(
169
+ SELECT 1 FROM information_schema.table_constraints tc
170
+ JOIN information_schema.key_column_usage kcu
171
+ ON tc.constraint_name = kcu.constraint_name
172
+ AND tc.table_schema = kcu.table_schema
173
+ WHERE tc.constraint_type = 'FOREIGN KEY'
174
+ AND tc.table_schema = 'public'
175
+ AND tc.table_name = p_table_name
176
+ AND kcu.column_name = c.column_name
177
+ ), false
178
+ ) as is_foreign_key,
179
+ (
180
+ SELECT ccu.table_name::text
181
+ FROM information_schema.table_constraints tc
182
+ JOIN information_schema.key_column_usage kcu
183
+ ON tc.constraint_name = kcu.constraint_name
184
+ AND tc.table_schema = kcu.table_schema
185
+ JOIN information_schema.constraint_column_usage ccu
186
+ ON ccu.constraint_name = tc.constraint_name
187
+ AND ccu.table_schema = tc.table_schema
188
+ WHERE tc.constraint_type = 'FOREIGN KEY'
189
+ AND tc.table_schema = 'public'
190
+ AND tc.table_name = p_table_name
191
+ AND kcu.column_name = c.column_name
192
+ LIMIT 1
193
+ ) as foreign_table,
194
+ (
195
+ SELECT ccu.column_name::text
196
+ FROM information_schema.table_constraints tc
197
+ JOIN information_schema.key_column_usage kcu
198
+ ON tc.constraint_name = kcu.constraint_name
199
+ AND tc.table_schema = kcu.table_schema
200
+ JOIN information_schema.constraint_column_usage ccu
201
+ ON ccu.constraint_name = tc.constraint_name
202
+ AND ccu.table_schema = tc.table_schema
203
+ WHERE tc.constraint_type = 'FOREIGN KEY'
204
+ AND tc.table_schema = 'public'
205
+ AND tc.table_name = p_table_name
206
+ AND kcu.column_name = c.column_name
207
+ LIMIT 1
208
+ ) as foreign_column,
209
+ pgd.description::text
210
+ FROM information_schema.columns c
211
+ LEFT JOIN pg_catalog.pg_statio_all_tables st
212
+ ON c.table_schema = st.schemaname
213
+ AND c.table_name = st.relname
214
+ LEFT JOIN pg_catalog.pg_description pgd
215
+ ON pgd.objoid = st.relid
216
+ AND pgd.objsubid = c.ordinal_position
217
+ WHERE c.table_schema = 'public'
218
+ AND c.table_name = p_table_name
219
+ ORDER BY c.ordinal_position;
220
+ END;
221
+ $$;
222
+
223
+ REVOKE ALL ON FUNCTION public.get_table_structure(text) FROM PUBLIC;
224
+ GRANT EXECUTE ON FUNCTION public.get_table_structure(text) TO authenticated, anon;
225
+
226
+ COMMENT ON FUNCTION public.get_table_structure(text) IS
227
+ 'Returns the structure of a table including column names, types, constraints, and foreign key relationships';
228
+
@@ -0,0 +1,123 @@
1
+
2
+ -- ===========================================================================
3
+ -- Helper: is_superadmin (if not already present)
4
+ -- ===========================================================================
5
+ CREATE OR REPLACE FUNCTION public.is_superadmin(user_id uuid)
6
+ RETURNS boolean
7
+ LANGUAGE sql
8
+ STABLE
9
+ SECURITY DEFINER
10
+ SET search_path = public, auth
11
+ AS $function$
12
+ SELECT COALESCE((
13
+ SELECT
14
+ COALESCE((raw_app_meta_data ->> 'is_super_admin')::boolean, FALSE)
15
+ OR COALESCE((raw_user_meta_data ->> 'is_super_admin')::boolean, FALSE)
16
+ OR COALESCE((raw_app_meta_data -> 'roles') ? 'admin', FALSE)
17
+ OR COALESCE((raw_user_meta_data -> 'roles') ? 'admin', FALSE)
18
+ FROM auth.users
19
+ WHERE id = user_id
20
+ ), FALSE);
21
+ $function$;
22
+
23
+ REVOKE ALL ON FUNCTION public.is_superadmin(uuid) FROM PUBLIC;
24
+ GRANT EXECUTE ON FUNCTION public.is_superadmin(uuid) TO authenticated, service_role, anon;
25
+
26
+
27
+
28
+ -- ===========================================================================
29
+ -- Storage Buckets (for avatars and file storage)
30
+ -- ===========================================================================
31
+ INSERT INTO storage.buckets (id, name, public)
32
+ VALUES ('avatar', 'avatar', TRUE)
33
+ ON CONFLICT (id) DO NOTHING;
34
+
35
+ INSERT INTO storage.buckets (id, name, public)
36
+ VALUES ('app', 'app', FALSE)
37
+ ON CONFLICT (id) DO NOTHING;
38
+
39
+ -- ===========================================================================
40
+ -- Avatar bucket policies
41
+ -- ===========================================================================
42
+ DROP POLICY IF EXISTS avatar_public_read ON storage.objects;
43
+ CREATE POLICY avatar_public_read ON storage.objects
44
+ FOR SELECT USING (bucket_id = 'avatar');
45
+
46
+ DROP POLICY IF EXISTS avatar_insert_owner ON storage.objects;
47
+ CREATE POLICY avatar_insert_owner ON storage.objects
48
+ FOR INSERT WITH CHECK (
49
+ bucket_id = 'avatar' AND (
50
+ owner = auth.uid() OR public.is_superadmin(auth.uid())
51
+ )
52
+ );
53
+
54
+ DROP POLICY IF EXISTS avatar_manage_owner ON storage.objects;
55
+ CREATE POLICY avatar_manage_owner ON storage.objects
56
+ FOR UPDATE USING (
57
+ bucket_id = 'avatar' AND (
58
+ owner = auth.uid() OR public.is_superadmin(auth.uid())
59
+ )
60
+ )
61
+ WITH CHECK (
62
+ bucket_id = 'avatar' AND (
63
+ owner = auth.uid() OR public.is_superadmin(auth.uid())
64
+ )
65
+ );
66
+
67
+ DROP POLICY IF EXISTS avatar_delete_owner ON storage.objects;
68
+ CREATE POLICY avatar_delete_owner ON storage.objects
69
+ FOR DELETE USING (
70
+ bucket_id = 'avatar' AND (
71
+ owner = auth.uid() OR public.is_superadmin(auth.uid())
72
+ )
73
+ );
74
+
75
+ -- ===========================================================================
76
+ -- App bucket policies (per-user folders app/{uuid})
77
+ -- ===========================================================================
78
+ DROP POLICY IF EXISTS app_user_select ON storage.objects;
79
+ CREATE POLICY app_user_select ON storage.objects
80
+ FOR SELECT USING (
81
+ bucket_id = 'app' AND (
82
+ owner = auth.uid()
83
+ OR split_part(name, '/', 1) = auth.uid()::text
84
+ OR public.is_superadmin(auth.uid())
85
+ )
86
+ );
87
+
88
+ DROP POLICY IF EXISTS app_user_insert ON storage.objects;
89
+ CREATE POLICY app_user_insert ON storage.objects
90
+ FOR INSERT WITH CHECK (
91
+ bucket_id = 'app' AND (
92
+ split_part(name, '/', 1) = auth.uid()::text
93
+ OR owner = auth.uid()
94
+ OR public.is_superadmin(auth.uid())
95
+ )
96
+ );
97
+
98
+ DROP POLICY IF EXISTS app_user_manage ON storage.objects;
99
+ CREATE POLICY app_user_manage ON storage.objects
100
+ FOR UPDATE USING (
101
+ bucket_id = 'app' AND (
102
+ owner = auth.uid()
103
+ OR split_part(name, '/', 1) = auth.uid()::text
104
+ OR public.is_superadmin(auth.uid())
105
+ )
106
+ )
107
+ WITH CHECK (
108
+ bucket_id = 'app' AND (
109
+ owner = auth.uid()
110
+ OR split_part(name, '/', 1) = auth.uid()::text
111
+ OR public.is_superadmin(auth.uid())
112
+ )
113
+ );
114
+
115
+ DROP POLICY IF EXISTS app_user_delete ON storage.objects;
116
+ CREATE POLICY app_user_delete ON storage.objects
117
+ FOR DELETE USING (
118
+ bucket_id = 'app' AND (
119
+ owner = auth.uid()
120
+ OR split_part(name, '/', 1) = auth.uid()::text
121
+ OR public.is_superadmin(auth.uid())
122
+ )
123
+ );
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@lastbrain/app",
3
+ "version": "0.1.0",
4
+ "description": "Framework modulaire Next.js avec CLI et système de modules",
5
+ "private": false,
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "keywords": [
10
+ "nextjs",
11
+ "framework",
12
+ "modular",
13
+ "typescript",
14
+ "supabase",
15
+ "monorepo",
16
+ "cli"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/lastpublication/starter.git",
21
+ "directory": "packages/app"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "bin": {
27
+ "lastbrain": "./dist/cli.js"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "src/app-shell",
32
+ "src/layouts",
33
+ "src/scripts",
34
+ "src/templates"
35
+ ],
36
+ "dependencies": {
37
+ "@lastbrain/core": "0.1.0",
38
+ "@lastbrain/module-auth": "0.1.0",
39
+ "@lastbrain/ui": "0.1.0",
40
+ "@supabase/supabase-js": "^2.36.0",
41
+ "chalk": "^5.3.0",
42
+ "commander": "^12.1.0",
43
+ "fs-extra": "^11.2.0",
44
+ "lucide-react": "^0.554.0",
45
+ "next-themes": "^0.4.6",
46
+ "react": "^19.0.0",
47
+ "react-dom": "^19.0.0"
48
+ },
49
+ "peerDependencies": {
50
+ "next": ">=15.0.0"
51
+ },
52
+ "devDependencies": {
53
+ "@types/fs-extra": "^11.0.4",
54
+ "@types/inquirer": "^9.0.9",
55
+ "@types/node": "^20.0.0",
56
+ "inquirer": "^13.0.1",
57
+ "typescript": "^5.4.0"
58
+ },
59
+ "scripts": {
60
+ "dev": "tsc -p tsconfig.json --watch",
61
+ "build": "tsc -p tsconfig.json && npm run copy:templates && chmod +x dist/cli.js",
62
+ "copy:templates": "mkdir -p dist/templates/migrations dist/templates/env.example dist/templates/gitignore && cp src/templates/migrations/* dist/templates/migrations/ 2>/dev/null || true && cp src/templates/env.example/.env.example dist/templates/env.example/ 2>/dev/null || true && cp src/templates/gitignore/.gitignore dist/templates/gitignore/ 2>/dev/null || true",
63
+ "module:build": "node dist/scripts/module-build.js",
64
+ "module:create": "node dist/scripts/module-create.js",
65
+ "module:add": "node dist/scripts/module-add.js",
66
+ "module:remove": "node dist/scripts/module-remove.js",
67
+ "module:list": "node dist/scripts/module-list.js",
68
+ "build:modules": "node dist/scripts/module-build.js",
69
+ "db:migrations:sync": "node dist/scripts/db-migrations-sync.js",
70
+ "db:init": "node dist/scripts/db-init.js",
71
+ "readme:create": "node dist/scripts/readme-build.js",
72
+ "dev:shell": "next dev ./src/app-shell -p 3001",
73
+ "dev:sync": "node dist/scripts/dev-sync.js"
74
+ }
75
+ }
@@ -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,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
+ }