@lastbrain/app 0.1.8 → 0.1.10

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 +7 -10
  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 +1076 -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
@@ -0,0 +1,385 @@
1
+ // SIMPLE VERSION FOR DEBUGGING
2
+ import React from "react";
3
+ import NextLink from "next/link";
4
+ import {
5
+ Button,
6
+ Card,
7
+ CardBody,
8
+ CardHeader,
9
+ Chip,
10
+ Snippet,
11
+ } from "@lastbrain/ui";
12
+ import {
13
+ Rocket,
14
+ Package,
15
+ Database,
16
+ Code,
17
+ Zap,
18
+ Shield,
19
+ ArrowRight,
20
+ BookOpen,
21
+ } from "lucide-react";
22
+
23
+ interface HomePageProps {
24
+ showAuth?: boolean;
25
+ }
26
+
27
+ export function SimpleHomePage({ showAuth }: HomePageProps) {
28
+ return (
29
+ <div className="min-h-screen">
30
+ {/* Hero Section */}
31
+ <section className="relative overflow-hidden bg-linear-to-br from-blue-50 via-purple-50 to-pink-50 dark:from-slate-900 dark:via-purple-900/20 dark:to-blue-900/20">
32
+ <div className="absolute inset-0 bg-grid-slate-200/50 dark:bg-grid-slate-700/25 mask-[radial-gradient(ellipse_at_center,transparent_20%,black)]"></div>
33
+
34
+ <div className="relative container mx-auto px-4 py-24 md:py-32">
35
+ <div className="max-w-4xl mx-auto text-center">
36
+ <div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 mb-8">
37
+ <Zap size={16} />
38
+ <span className="text-sm font-medium">
39
+ Framework modulaire Next.js
40
+ </span>
41
+ </div>
42
+
43
+ <h1 className="text-5xl md:text-7xl font-bold mb-6 bg-linear-to-r from-blue-600 via-purple-600 to-pink-600 bg-clip-text text-transparent">
44
+ Bienvenue sur LastBrain
45
+ </h1>
46
+
47
+ <p className="text-xl md:text-2xl text-slate-600 dark:text-slate-300 mb-12 max-w-2xl mx-auto">
48
+ Créez des applications modernes avec une architecture modulaire,
49
+ puissante et évolutive.
50
+ </p>
51
+
52
+ <div className="flex flex-col sm:flex-row gap-4 justify-center">
53
+ <NextLink href="/docs">
54
+ <Button
55
+ size="lg"
56
+ color="primary"
57
+ endContent={<ArrowRight size={20} />}
58
+ className="text-base font-medium"
59
+ >
60
+ Voir la documentation
61
+ </Button>
62
+ </NextLink>
63
+ {showAuth && (
64
+ <NextLink href="/signup">
65
+ <Button
66
+ size="lg"
67
+ variant="bordered"
68
+ className="text-base font-medium"
69
+ >
70
+ Créer un compte
71
+ </Button>
72
+ </NextLink>
73
+ )}
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </section>
78
+
79
+ {/* Features Section */}
80
+ <section className="py-20 bg-white dark:bg-slate-950">
81
+ <div className="container mx-auto px-4">
82
+ <div className="max-w-6xl mx-auto">
83
+ <div className="text-center mb-16">
84
+ <h2 className="text-3xl md:text-4xl font-bold mb-4">
85
+ Tout ce dont vous avez besoin
86
+ </h2>
87
+ <p className="text-lg text-slate-600 dark:text-slate-400">
88
+ Une stack moderne pour démarrer rapidement
89
+ </p>
90
+ </div>
91
+
92
+ <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
93
+ <Card className="hover:shadow-xl transition-shadow">
94
+ <CardHeader className="pb-2">
95
+ <div className="flex items-center gap-3">
96
+ <div className="p-2 rounded-lg bg-blue-100 dark:bg-blue-900/30">
97
+ <Package
98
+ className="text-blue-600 dark:text-blue-400"
99
+ size={24}
100
+ />
101
+ </div>
102
+ <h3 className="text-xl font-semibold">Modulaire</h3>
103
+ </div>
104
+ </CardHeader>
105
+ <CardBody className="pt-2">
106
+ <p className="text-slate-600 dark:text-slate-400">
107
+ Architecture basée sur des modules réutilisables. Ajoutez ou
108
+ retirez des fonctionnalités en une commande.
109
+ </p>
110
+ </CardBody>
111
+ </Card>
112
+
113
+ <Card className="hover:shadow-xl transition-shadow">
114
+ <CardHeader className="pb-2">
115
+ <div className="flex items-center gap-3">
116
+ <div className="p-2 rounded-lg bg-purple-100 dark:bg-purple-900/30">
117
+ <Database
118
+ className="text-purple-600 dark:text-purple-400"
119
+ size={24}
120
+ />
121
+ </div>
122
+ <h3 className="text-xl font-semibold">Supabase</h3>
123
+ </div>
124
+ </CardHeader>
125
+ <CardBody className="pt-2">
126
+ <p className="text-slate-600 dark:text-slate-400">
127
+ Base de données PostgreSQL, authentification et migrations
128
+ automatiques. Tout en local pour le développement.
129
+ </p>
130
+ </CardBody>
131
+ </Card>
132
+
133
+ <Card className="hover:shadow-xl transition-shadow">
134
+ <CardHeader className="pb-2">
135
+ <div className="flex items-center gap-3">
136
+ <div className="p-2 rounded-lg bg-green-100 dark:bg-green-900/30">
137
+ <Zap
138
+ className="text-green-600 dark:text-green-400"
139
+ size={24}
140
+ />
141
+ </div>
142
+ <h3 className="text-xl font-semibold">Rapide</h3>
143
+ </div>
144
+ </CardHeader>
145
+ <CardBody className="pt-2">
146
+ <p className="text-slate-600 dark:text-slate-400">
147
+ Next.js 15, React 19, Tailwind CSS v4. Hot reload et
148
+ génération automatique des routes.
149
+ </p>
150
+ </CardBody>
151
+ </Card>
152
+
153
+ <Card className="hover:shadow-xl transition-shadow">
154
+ <CardHeader className="pb-2">
155
+ <div className="flex items-center gap-3">
156
+ <div className="p-2 rounded-lg bg-orange-100 dark:bg-orange-900/30">
157
+ <Code
158
+ className="text-orange-600 dark:text-orange-400"
159
+ size={24}
160
+ />
161
+ </div>
162
+ <h3 className="text-xl font-semibold">TypeScript</h3>
163
+ </div>
164
+ </CardHeader>
165
+ <CardBody className="pt-2">
166
+ <p className="text-slate-600 dark:text-slate-400">
167
+ 100% TypeScript avec types partagés entre frontend et
168
+ backend. Monorepo pnpm pour une gestion optimale.
169
+ </p>
170
+ </CardBody>
171
+ </Card>
172
+
173
+ <Card className="hover:shadow-xl transition-shadow">
174
+ <CardHeader className="pb-2">
175
+ <div className="flex items-center gap-3">
176
+ <div className="p-2 rounded-lg bg-pink-100 dark:bg-pink-900/30">
177
+ <Shield
178
+ className="text-pink-600 dark:text-pink-400"
179
+ size={24}
180
+ />
181
+ </div>
182
+ <h3 className="text-xl font-semibold">Sécurisé</h3>
183
+ </div>
184
+ </CardHeader>
185
+ <CardBody className="pt-2">
186
+ <p className="text-slate-600 dark:text-slate-400">
187
+ Middleware d'authentification automatique. Routes protégées
188
+ pour /auth et /admin configurées par défaut.
189
+ </p>
190
+ </CardBody>
191
+ </Card>
192
+
193
+ <Card className="hover:shadow-xl transition-shadow">
194
+ <CardHeader className="pb-2">
195
+ <div className="flex items-center gap-3">
196
+ <div className="p-2 rounded-lg bg-cyan-100 dark:bg-cyan-900/30">
197
+ <BookOpen
198
+ className="text-cyan-600 dark:text-cyan-400"
199
+ size={24}
200
+ />
201
+ </div>
202
+ <h3 className="text-xl font-semibold">Documenté</h3>
203
+ </div>
204
+ </CardHeader>
205
+ <CardBody className="pt-2">
206
+ <p className="text-slate-600 dark:text-slate-400">
207
+ Documentation complète et interactive. Chaque module
208
+ documente automatiquement ses pages et APIs.
209
+ </p>
210
+ </CardBody>
211
+ </Card>
212
+ </div>
213
+ </div>
214
+ </div>
215
+ </section>
216
+
217
+ {/* Quick Start Section */}
218
+ <section className="py-20 bg-slate-50 dark:bg-slate-900/50">
219
+ <div className="container mx-auto px-4">
220
+ <div className="max-w-4xl mx-auto">
221
+ <div className="text-center mb-12">
222
+ <div className="inline-flex items-center gap-2 mb-4">
223
+ <Rocket size={24} className="text-blue-600" />
224
+ <h2 className="text-3xl md:text-4xl font-bold">
225
+ Démarrez en 4 étapes
226
+ </h2>
227
+ </div>
228
+ <p className="text-lg text-slate-600 dark:text-slate-400">
229
+ Votre application prête en quelques minutes
230
+ </p>
231
+ </div>
232
+
233
+ <div className="space-y-6">
234
+ <Card className="border-l-4 border-l-blue-500">
235
+ <CardBody>
236
+ <div className="flex items-start gap-4">
237
+ <div className="shrink-0 w-8 h-8 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center font-bold text-blue-600 dark:text-blue-400">
238
+ 1
239
+ </div>
240
+ <div className="flex-1">
241
+ <h3 className="text-lg font-semibold mb-2">
242
+ Créez votre application
243
+ </h3>
244
+ <Snippet className="w-full">
245
+ pnpx @lastbrain/create-app mon-app
246
+ </Snippet>
247
+ <p className="text-sm text-slate-600 dark:text-slate-400 mt-2">
248
+ L'assistant interactif vous guidera pour :
249
+ </p>
250
+ <ul className="text-sm text-slate-600 dark:text-slate-400 mt-2 ml-4 space-y-1">
251
+ <li>• Choisir les modules à installer (auth, ai...)</li>
252
+ <li>• Créer la structure complète du projet</li>
253
+ <li>• Installer automatiquement les dépendances</li>
254
+ <li>• Générer les routes des modules</li>
255
+ </ul>
256
+ </div>
257
+ </div>
258
+ </CardBody>
259
+ </Card>
260
+
261
+ <Card className="border-l-4 border-l-purple-500">
262
+ <CardBody>
263
+ <div className="flex items-start gap-4">
264
+ <div className="shrink-0 w-8 h-8 rounded-full bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center font-bold text-purple-600 dark:text-purple-400">
265
+ 2
266
+ </div>
267
+ <div className="flex-1">
268
+ <h3 className="text-lg font-semibold mb-2">
269
+ Accédez au dossier
270
+ </h3>
271
+ <Snippet className="w-full">cd mon-app</Snippet>
272
+ <p className="text-sm text-slate-600 dark:text-slate-400 mt-2">
273
+ Entrez dans le dossier de votre nouvelle application
274
+ </p>
275
+ </div>
276
+ </div>
277
+ </CardBody>
278
+ </Card>
279
+
280
+ <Card className="border-l-4 border-l-orange-500">
281
+ <CardBody>
282
+ <div className="flex items-start gap-4">
283
+ <div className="shrink-0 w-8 h-8 rounded-full bg-orange-100 dark:bg-orange-900/30 flex items-center justify-center font-bold text-orange-600 dark:text-orange-400">
284
+ 3
285
+ </div>
286
+ <div className="flex-1">
287
+ <h3 className="text-lg font-semibold mb-2">
288
+ Initialisez Supabase
289
+ </h3>
290
+ <Snippet className="w-full">pnpm db:init</Snippet>
291
+ <p className="text-sm text-slate-600 dark:text-slate-400 mt-2">
292
+ Démarre Supabase en local et applique toutes les
293
+ migrations (base + modules)
294
+ </p>
295
+ </div>
296
+ </div>
297
+ </CardBody>
298
+ </Card>
299
+
300
+ <Card className="border-l-4 border-l-green-500">
301
+ <CardBody>
302
+ <div className="flex items-start gap-4">
303
+ <div className="shrink-0 w-8 h-8 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center font-bold text-green-600 dark:text-green-400">
304
+ 4
305
+ </div>
306
+ <div className="flex-1">
307
+ <h3 className="text-lg font-semibold mb-2">
308
+ Lancez le serveur
309
+ </h3>
310
+ <Snippet className="w-full">pnpm dev</Snippet>
311
+ <p className="text-sm text-slate-600 dark:text-slate-400 mt-2">
312
+ Ouvre l'application sur{" "}
313
+ <strong>http://localhost:3000</strong>
314
+ </p>
315
+ </div>
316
+ </div>
317
+ </CardBody>
318
+ </Card>
319
+
320
+ {/* Info supplémentaire */}
321
+ <Card className="bg-blue-50 dark:bg-blue-950/30 border-blue-200 dark:border-blue-800">
322
+ <CardBody>
323
+ <div className="flex items-start gap-3">
324
+ <div className="p-2 rounded-lg bg-blue-100 dark:bg-blue-900/30 shrink-0">
325
+ <Package
326
+ className="text-blue-600 dark:text-blue-400"
327
+ size={20}
328
+ />
329
+ </div>
330
+ <div>
331
+ <h4 className="font-semibold mb-2 text-blue-900 dark:text-blue-100">
332
+ Ajouter des modules plus tard
333
+ </h4>
334
+ <div className="space-y-2">
335
+ <Snippet className="w-full" size="sm">
336
+ pnpm lastbrain add-module auth
337
+ </Snippet>
338
+ <Snippet className="w-full" size="sm">
339
+ pnpm build:modules
340
+ </Snippet>
341
+ </div>
342
+ <div className="text-sm text-blue-700 dark:text-blue-300 mt-2">
343
+ Modules disponibles :{" "}
344
+ <Chip size="sm" color="primary" variant="flat">
345
+ auth
346
+ </Chip>{" "}
347
+ <Chip size="sm" color="primary" variant="flat">
348
+ ai
349
+ </Chip>
350
+ </div>
351
+ </div>
352
+ </div>
353
+ </CardBody>
354
+ </Card>
355
+ </div>
356
+ </div>
357
+ </div>
358
+ </section>
359
+
360
+ {/* CTA Section */}
361
+ <section className="py-20 bg-linear-to-r from-blue-600 to-purple-600 text-white">
362
+ <div className="container mx-auto px-4">
363
+ <div className="max-w-3xl mx-auto text-center">
364
+ <h2 className="text-3xl md:text-4xl font-bold mb-4">
365
+ Prêt à commencer ?
366
+ </h2>
367
+ <p className="text-lg text-blue-100 mb-8">
368
+ Explorez la documentation complète pour découvrir toutes les
369
+ fonctionnalités
370
+ </p>
371
+ <NextLink href="/docs">
372
+ <Button
373
+ size="lg"
374
+ className="bg-white text-blue-600 hover:bg-blue-50 font-medium"
375
+ endContent={<BookOpen size={20} />}
376
+ >
377
+ Consulter la documentation
378
+ </Button>
379
+ </NextLink>
380
+ </div>
381
+ </div>
382
+ </section>
383
+ </div>
384
+ );
385
+ }
@@ -0,0 +1,6 @@
1
+ # GENERATED BY LASTBRAIN - DO NOT EDIT
2
+ NEXT_PUBLIC_SUPABASE_URL=https://example.supabase.co
3
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=public-anon-key
4
+ SUPABASE_URL=https://example.supabase.co
5
+ SUPABASE_ANON_KEY=service-role-key
6
+ SUPABASE_SERVICE_ROLE_KEY=service-role-key
@@ -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
+