gmoonc 0.0.21 → 0.0.23

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/README.md CHANGED
@@ -37,6 +37,44 @@ npx gmoonc --base /dashboard
37
37
  - `--skip-router-patch`: Skip automatic router integration (only copy files and inject CSS)
38
38
  - `--dry-run`: Show what would be done without making changes
39
39
 
40
+ ## Supabase Integration
41
+
42
+ ### Setup Supabase Auth + RBAC
43
+
44
+ ```bash
45
+ npx gmoonc supabase --vite
46
+ ```
47
+
48
+ This command:
49
+ - Installs `@supabase/supabase-js` dependency
50
+ - Generates Supabase client, env validation, and RBAC helpers
51
+ - Creates/updates `.env.example` with Supabase variables
52
+ - Automatically patches existing code to use Supabase provider
53
+
54
+ ### Seed Database
55
+
56
+ After setting up Supabase integration, seed your database with the complete schema:
57
+
58
+ ```bash
59
+ npx gmoonc supabase-seed --vite
60
+ ```
61
+
62
+ **Prerequisites:**
63
+ - `gmoonc` must be installed (`npx gmoonc`)
64
+ - `supabase --vite` must have been executed
65
+ - `.env.local` must contain `SUPABASE_DB_URL` (connection string from Supabase Dashboard → Settings → Database)
66
+
67
+ **What it does:**
68
+ - Executes SQL files in order: tables → functions/triggers → RLS → seed data
69
+ - Creates marker file `.gmoonc/supabase-seed.json` to prevent duplicate execution
70
+ - One-shot by default (delete marker to re-seed)
71
+
72
+ **To get `SUPABASE_DB_URL`:**
73
+ 1. Go to your Supabase project dashboard
74
+ 2. Navigate to Settings → Database
75
+ 3. Copy the "Connection string (URI)"
76
+ 4. Add to `.env.local` as: `SUPABASE_DB_URL=postgresql://postgres:[PASSWORD]@[HOST]:5432/postgres`
77
+
40
78
  ## After installation
41
79
 
42
80
  Your dashboard is now available at:
@@ -63,6 +101,19 @@ The logo is installed at `src/gmoonc/assets/gmoonc-logo.png`. You can replace it
63
101
 
64
102
  ## Changelog
65
103
 
104
+ ### 0.0.23
105
+ - Fix: Improved package root detection for SQL files in supabase-seed command
106
+ - Fix: Added validation to check SQL files exist before attempting to copy
107
+ - Fix: Better error messages showing where SQL files are being searched
108
+ - Fix: SQL files are now correctly located when package is installed from npm
109
+
110
+ ### 0.0.22
111
+ - Feature: New `supabase-seed --vite` command to seed Supabase database
112
+ - Feature: Automatic SQL file execution (tables, functions, RLS, seed data)
113
+ - Feature: One-shot protection with marker file
114
+ - Feature: SQL files copied to project for transparency
115
+ - Feature: SUPABASE_DB_URL added to .env.example
116
+
66
117
  ### 0.0.21
67
118
  - Fix: Navigate import now always added when route protection is present in GMooncAppLayout.tsx
68
119
  - Fix: Improved Navigate import detection - checks for route protection code, not just usage
@@ -0,0 +1,117 @@
1
+ -- ============================================
2
+ -- 001_tables.sql: Definição de Tabelas (gmoonc)
3
+ -- ============================================
4
+ -- Contém apenas CREATE TABLE no formato final
5
+ -- Sem ALTER TABLE redundantes
6
+ -- Schema para gmoonc (Sicoop-app refatorado)
7
+
8
+ -- ============================================
9
+ -- profiles - Usuários do Sistema
10
+ -- ============================================
11
+ CREATE TABLE IF NOT EXISTS public.profiles (
12
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
13
+ email TEXT UNIQUE NOT NULL,
14
+ name TEXT NOT NULL,
15
+ role TEXT NOT NULL,
16
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
17
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
18
+ );
19
+
20
+ -- ============================================
21
+ -- roles - Papéis/Funções do Sistema
22
+ -- ============================================
23
+ CREATE TABLE IF NOT EXISTS public.roles (
24
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
25
+ name TEXT UNIQUE NOT NULL,
26
+ description TEXT,
27
+ is_system_role BOOLEAN DEFAULT true,
28
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
29
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
30
+ );
31
+
32
+ -- ============================================
33
+ -- modules - Módulos da Aplicação
34
+ -- ============================================
35
+ CREATE TABLE IF NOT EXISTS public.modules (
36
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
37
+ name TEXT UNIQUE NOT NULL,
38
+ display_name TEXT NOT NULL,
39
+ description TEXT,
40
+ is_active BOOLEAN DEFAULT true,
41
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
42
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
43
+ );
44
+
45
+ -- ============================================
46
+ -- permissions - Permissões por Role e Módulo
47
+ -- ============================================
48
+ CREATE TABLE IF NOT EXISTS public.permissions (
49
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
50
+ role_id UUID NOT NULL REFERENCES public.roles(id) ON DELETE CASCADE,
51
+ module_id UUID NOT NULL REFERENCES public.modules(id) ON DELETE CASCADE,
52
+ can_access BOOLEAN DEFAULT false,
53
+ can_create BOOLEAN DEFAULT false,
54
+ can_read BOOLEAN DEFAULT false,
55
+ can_update BOOLEAN DEFAULT false,
56
+ can_delete BOOLEAN DEFAULT false,
57
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
58
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
59
+ );
60
+
61
+ -- ============================================
62
+ -- messages - Sistema de Mensagens (renomeado de mensagens)
63
+ -- ============================================
64
+ CREATE TABLE IF NOT EXISTS public.messages (
65
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
66
+ user_id UUID REFERENCES public.profiles(id) ON DELETE CASCADE,
67
+ name TEXT NOT NULL,
68
+ email TEXT NOT NULL,
69
+ phone TEXT,
70
+ company_farm TEXT NOT NULL,
71
+ message TEXT NOT NULL,
72
+ status TEXT CHECK (status IS NULL OR status IN ('draft', 'pending', 'in_analysis', 'completed', 'cancelled')) DEFAULT 'pending',
73
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
74
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
75
+ );
76
+
77
+ -- ============================================
78
+ -- notification_categories - Categorias de Notificação
79
+ -- ============================================
80
+ CREATE TABLE IF NOT EXISTS public.notification_categories (
81
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
82
+ name TEXT UNIQUE NOT NULL,
83
+ display_name TEXT NOT NULL,
84
+ description TEXT,
85
+ is_active BOOLEAN DEFAULT true,
86
+ email_template_subject TEXT,
87
+ email_template_body TEXT,
88
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
89
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
90
+ );
91
+
92
+ -- ============================================
93
+ -- notification_settings - Preferências de Notificação
94
+ -- ============================================
95
+ CREATE TABLE IF NOT EXISTS public.notification_settings (
96
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
97
+ user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
98
+ category_id UUID NOT NULL REFERENCES public.notification_categories(id) ON DELETE CASCADE,
99
+ is_enabled BOOLEAN DEFAULT true,
100
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
101
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
102
+ );
103
+
104
+ -- ============================================
105
+ -- notification_logs - Histórico de Notificações
106
+ -- ============================================
107
+ CREATE TABLE IF NOT EXISTS public.notification_logs (
108
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
109
+ category_id UUID NOT NULL REFERENCES public.notification_categories(id) ON DELETE CASCADE,
110
+ user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
111
+ entity_type TEXT NOT NULL,
112
+ entity_id TEXT NOT NULL,
113
+ email_sent BOOLEAN,
114
+ email_error TEXT,
115
+ sent_at TIMESTAMP WITH TIME ZONE,
116
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
117
+ );
@@ -0,0 +1,223 @@
1
+ -- ============================================
2
+ -- 002_rls.sql: Row Level Security e Políticas (gmoonc)
3
+ -- ============================================
4
+ -- Habilita RLS e define políticas de acesso
5
+ -- Políticas para gmoonc
6
+
7
+ -- ============================================
8
+ -- ENABLE RLS em todas as tabelas
9
+ -- ============================================
10
+
11
+ ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
12
+ ALTER TABLE public.roles ENABLE ROW LEVEL SECURITY;
13
+ ALTER TABLE public.modules ENABLE ROW LEVEL SECURITY;
14
+ ALTER TABLE public.permissions ENABLE ROW LEVEL SECURITY;
15
+ ALTER TABLE public.messages ENABLE ROW LEVEL SECURITY;
16
+ ALTER TABLE public.notification_categories ENABLE ROW LEVEL SECURITY;
17
+ ALTER TABLE public.notification_settings ENABLE ROW LEVEL SECURITY;
18
+ ALTER TABLE public.notification_logs ENABLE ROW LEVEL SECURITY;
19
+
20
+ -- ============================================
21
+ -- POLÍTICAS: profiles
22
+ -- ============================================
23
+
24
+ -- Administradores podem ver todos os perfis
25
+ CREATE POLICY "admin_can_read_all_profiles" ON public.profiles
26
+ FOR SELECT
27
+ USING (
28
+ auth.uid() IN (
29
+ SELECT id FROM public.profiles WHERE role = 'administrator'
30
+ )
31
+ );
32
+
33
+ -- Usuários podem ver seu próprio perfil
34
+ CREATE POLICY "users_can_read_own_profile" ON public.profiles
35
+ FOR SELECT
36
+ USING (auth.uid() = id);
37
+
38
+ -- Administradores podem atualizar qualquer perfil
39
+ CREATE POLICY "admin_can_update_all_profiles" ON public.profiles
40
+ FOR UPDATE
41
+ USING (
42
+ auth.uid() IN (
43
+ SELECT id FROM public.profiles WHERE role = 'administrator'
44
+ )
45
+ );
46
+
47
+ -- Usuários podem atualizar seu próprio perfil
48
+ CREATE POLICY "users_can_update_own_profile" ON public.profiles
49
+ FOR UPDATE
50
+ USING (auth.uid() = id);
51
+
52
+ -- ============================================
53
+ -- POLÍTICAS: roles
54
+ -- ============================================
55
+
56
+ -- Todos podem ler roles (necessário para aplicação)
57
+ CREATE POLICY "all_can_read_roles" ON public.roles
58
+ FOR SELECT
59
+ USING (true);
60
+
61
+ -- Apenas administradores podem criar/atualizar/deletar roles
62
+ CREATE POLICY "admin_can_manage_roles" ON public.roles
63
+ FOR ALL
64
+ USING (
65
+ auth.uid() IN (
66
+ SELECT id FROM public.profiles WHERE role = 'administrator'
67
+ )
68
+ );
69
+
70
+ -- ============================================
71
+ -- POLÍTICAS: modules
72
+ -- ============================================
73
+
74
+ -- Todos podem ler módulos
75
+ CREATE POLICY "all_can_read_modules" ON public.modules
76
+ FOR SELECT
77
+ USING (true);
78
+
79
+ -- Apenas administradores podem gerenciar módulos
80
+ CREATE POLICY "admin_can_manage_modules" ON public.modules
81
+ FOR ALL
82
+ USING (
83
+ auth.uid() IN (
84
+ SELECT id FROM public.profiles WHERE role = 'administrator'
85
+ )
86
+ );
87
+
88
+ -- ============================================
89
+ -- POLÍTICAS: permissions
90
+ -- ============================================
91
+
92
+ -- Todos podem ler permissões (necessário para verificar acesso)
93
+ CREATE POLICY "all_can_read_permissions" ON public.permissions
94
+ FOR SELECT
95
+ USING (true);
96
+
97
+ -- Apenas administradores podem gerenciar permissões
98
+ CREATE POLICY "admin_can_manage_permissions" ON public.permissions
99
+ FOR ALL
100
+ USING (
101
+ auth.uid() IN (
102
+ SELECT id FROM public.profiles WHERE role = 'administrator'
103
+ )
104
+ );
105
+
106
+ -- ============================================
107
+ -- POLÍTICAS: messages
108
+ -- ============================================
109
+
110
+ -- Administradores podem ver todas as mensagens
111
+ CREATE POLICY "admin_can_read_all_messages" ON public.messages
112
+ FOR SELECT
113
+ USING (
114
+ auth.uid() IN (
115
+ SELECT id FROM public.profiles WHERE role = 'administrator'
116
+ )
117
+ );
118
+
119
+ -- Usuários podem ver suas próprias mensagens
120
+ CREATE POLICY "users_can_read_own_messages" ON public.messages
121
+ FOR SELECT
122
+ USING (user_id = auth.uid() OR user_id IS NULL);
123
+
124
+ -- Usuários podem criar mensagens
125
+ CREATE POLICY "users_can_create_messages" ON public.messages
126
+ FOR INSERT
127
+ WITH CHECK (user_id = auth.uid() OR user_id IS NULL);
128
+
129
+ -- Administradores podem atualizar qualquer mensagem
130
+ CREATE POLICY "admin_can_update_all_messages" ON public.messages
131
+ FOR UPDATE
132
+ USING (
133
+ auth.uid() IN (
134
+ SELECT id FROM public.profiles WHERE role = 'administrator'
135
+ )
136
+ );
137
+
138
+ -- Usuários podem atualizar suas próprias mensagens
139
+ CREATE POLICY "users_can_update_own_messages" ON public.messages
140
+ FOR UPDATE
141
+ USING (user_id = auth.uid());
142
+
143
+ -- ============================================
144
+ -- POLÍTICAS: notification_categories
145
+ -- ============================================
146
+
147
+ -- Todos podem ler categorias de notificação
148
+ CREATE POLICY "all_can_read_notification_categories" ON public.notification_categories
149
+ FOR SELECT
150
+ USING (true);
151
+
152
+ -- Apenas administradores podem gerenciar categorias
153
+ CREATE POLICY "admin_can_manage_notification_categories" ON public.notification_categories
154
+ FOR ALL
155
+ USING (
156
+ auth.uid() IN (
157
+ SELECT id FROM public.profiles WHERE role = 'administrator'
158
+ )
159
+ );
160
+
161
+ -- ============================================
162
+ -- POLÍTICAS: notification_settings
163
+ -- ============================================
164
+
165
+ -- Usuários podem ver suas próprias configurações
166
+ CREATE POLICY "users_can_read_own_notification_settings" ON public.notification_settings
167
+ FOR SELECT
168
+ USING (user_id = auth.uid());
169
+
170
+ -- Administradores podem ver todas as configurações
171
+ CREATE POLICY "admin_can_read_all_notification_settings" ON public.notification_settings
172
+ FOR SELECT
173
+ USING (
174
+ auth.uid() IN (
175
+ SELECT id FROM public.profiles WHERE role = 'administrator'
176
+ )
177
+ );
178
+
179
+ -- Usuários podem criar suas próprias configurações
180
+ CREATE POLICY "users_can_create_notification_settings" ON public.notification_settings
181
+ FOR INSERT
182
+ WITH CHECK (user_id = auth.uid());
183
+
184
+ -- Usuários podem atualizar suas próprias configurações
185
+ CREATE POLICY "users_can_update_own_notification_settings" ON public.notification_settings
186
+ FOR UPDATE
187
+ USING (user_id = auth.uid());
188
+
189
+ -- Administradores podem gerenciar todas as configurações
190
+ CREATE POLICY "admin_can_manage_all_notification_settings" ON public.notification_settings
191
+ FOR ALL
192
+ USING (
193
+ auth.uid() IN (
194
+ SELECT id FROM public.profiles WHERE role = 'administrator'
195
+ )
196
+ );
197
+
198
+ -- ============================================
199
+ -- POLÍTICAS: notification_logs
200
+ -- ============================================
201
+
202
+ -- Usuários podem ver seus próprios logs
203
+ CREATE POLICY "users_can_read_own_notification_logs" ON public.notification_logs
204
+ FOR SELECT
205
+ USING (user_id = auth.uid());
206
+
207
+ -- Administradores podem ver todos os logs
208
+ CREATE POLICY "admin_can_read_all_notification_logs" ON public.notification_logs
209
+ FOR SELECT
210
+ USING (
211
+ auth.uid() IN (
212
+ SELECT id FROM public.profiles WHERE role = 'administrator'
213
+ )
214
+ );
215
+
216
+ -- Apenas administradores podem criar logs (via sistema)
217
+ CREATE POLICY "admin_can_create_notification_logs" ON public.notification_logs
218
+ FOR INSERT
219
+ WITH CHECK (
220
+ auth.uid() IN (
221
+ SELECT id FROM public.profiles WHERE role = 'administrator'
222
+ )
223
+ );
@@ -0,0 +1,244 @@
1
+ -- ============================================
2
+ -- 003_functions_triggers.sql: Funções e Triggers (gmoonc)
3
+ -- ============================================
4
+ -- Contém todas as funções PL/pgSQL e triggers
5
+ -- Otimizadas para gmoonc
6
+
7
+ -- ============================================
8
+ -- FUNÇÃO: update_updated_at_column
9
+ -- ============================================
10
+ -- Atualiza o campo updated_at automaticamente
11
+ CREATE OR REPLACE FUNCTION public.update_updated_at_column()
12
+ RETURNS TRIGGER AS $$
13
+ BEGIN
14
+ NEW.updated_at = NOW();
15
+ RETURN NEW;
16
+ END;
17
+ $$ LANGUAGE plpgsql;
18
+
19
+ -- ============================================
20
+ -- TRIGGERS: updated_at
21
+ -- ============================================
22
+
23
+ CREATE TRIGGER update_profiles_updated_at
24
+ BEFORE UPDATE ON public.profiles
25
+ FOR EACH ROW
26
+ EXECUTE FUNCTION public.update_updated_at_column();
27
+
28
+ CREATE TRIGGER update_roles_updated_at
29
+ BEFORE UPDATE ON public.roles
30
+ FOR EACH ROW
31
+ EXECUTE FUNCTION public.update_updated_at_column();
32
+
33
+ CREATE TRIGGER update_modules_updated_at
34
+ BEFORE UPDATE ON public.modules
35
+ FOR EACH ROW
36
+ EXECUTE FUNCTION public.update_updated_at_column();
37
+
38
+ CREATE TRIGGER update_permissions_updated_at
39
+ BEFORE UPDATE ON public.permissions
40
+ FOR EACH ROW
41
+ EXECUTE FUNCTION public.update_updated_at_column();
42
+
43
+ CREATE TRIGGER update_messages_updated_at
44
+ BEFORE UPDATE ON public.messages
45
+ FOR EACH ROW
46
+ EXECUTE FUNCTION public.update_updated_at_column();
47
+
48
+ CREATE TRIGGER update_notification_categories_updated_at
49
+ BEFORE UPDATE ON public.notification_categories
50
+ FOR EACH ROW
51
+ EXECUTE FUNCTION public.update_updated_at_column();
52
+
53
+ CREATE TRIGGER update_notification_settings_updated_at
54
+ BEFORE UPDATE ON public.notification_settings
55
+ FOR EACH ROW
56
+ EXECUTE FUNCTION public.update_updated_at_column();
57
+
58
+ -- ============================================
59
+ -- FUNÇÃO: get_user_permissions
60
+ -- ============================================
61
+ -- Retorna as permissões de um usuário
62
+ CREATE OR REPLACE FUNCTION public.get_user_permissions(user_id UUID)
63
+ RETURNS TABLE (
64
+ module_name TEXT,
65
+ can_access BOOLEAN,
66
+ can_create BOOLEAN,
67
+ can_read BOOLEAN,
68
+ can_update BOOLEAN,
69
+ can_delete BOOLEAN
70
+ ) AS $$
71
+ BEGIN
72
+ RETURN QUERY
73
+ SELECT
74
+ m.name,
75
+ p.can_access,
76
+ p.can_create,
77
+ p.can_read,
78
+ p.can_update,
79
+ p.can_delete
80
+ FROM public.permissions p
81
+ JOIN public.modules m ON p.module_id = m.id
82
+ JOIN public.roles r ON p.role_id = r.id
83
+ WHERE r.id = (
84
+ SELECT role FROM public.profiles WHERE id = user_id
85
+ )::UUID;
86
+ END;
87
+ $$ LANGUAGE plpgsql;
88
+
89
+ -- ============================================
90
+ -- FUNÇÃO: check_user_permission
91
+ -- ============================================
92
+ -- Verifica se um usuário tem permissão para uma ação
93
+ CREATE OR REPLACE FUNCTION public.check_user_permission(
94
+ user_id UUID,
95
+ module_name TEXT,
96
+ action TEXT
97
+ ) RETURNS BOOLEAN AS $$
98
+ DECLARE
99
+ user_role TEXT;
100
+ has_permission BOOLEAN;
101
+ BEGIN
102
+ -- Obter role do usuário
103
+ SELECT role INTO user_role FROM public.profiles WHERE id = user_id;
104
+
105
+ IF user_role IS NULL THEN
106
+ RETURN false;
107
+ END IF;
108
+
109
+ -- Verificar permissão baseado na ação
110
+ CASE action
111
+ WHEN 'create' THEN
112
+ SELECT can_create INTO has_permission
113
+ FROM public.permissions p
114
+ JOIN public.roles r ON p.role_id = r.id
115
+ JOIN public.modules m ON p.module_id = m.id
116
+ WHERE r.name = user_role AND m.name = module_name;
117
+ WHEN 'read' THEN
118
+ SELECT can_read INTO has_permission
119
+ FROM public.permissions p
120
+ JOIN public.roles r ON p.role_id = r.id
121
+ JOIN public.modules m ON p.module_id = m.id
122
+ WHERE r.name = user_role AND m.name = module_name;
123
+ WHEN 'update' THEN
124
+ SELECT can_update INTO has_permission
125
+ FROM public.permissions p
126
+ JOIN public.roles r ON p.role_id = r.id
127
+ JOIN public.modules m ON p.module_id = m.id
128
+ WHERE r.name = user_role AND m.name = module_name;
129
+ WHEN 'delete' THEN
130
+ SELECT can_delete INTO has_permission
131
+ FROM public.permissions p
132
+ JOIN public.roles r ON p.role_id = r.id
133
+ JOIN public.modules m ON p.module_id = m.id
134
+ WHERE r.name = user_role AND m.name = module_name;
135
+ ELSE
136
+ RETURN false;
137
+ END CASE;
138
+
139
+ RETURN COALESCE(has_permission, false);
140
+ END;
141
+ $$ LANGUAGE plpgsql;
142
+
143
+ -- ============================================
144
+ -- FUNÇÃO: log_notification
145
+ -- ============================================
146
+ -- Registra uma notificação no histórico
147
+ CREATE OR REPLACE FUNCTION public.log_notification(
148
+ p_category_id UUID,
149
+ p_user_id UUID,
150
+ p_entity_type TEXT,
151
+ p_entity_id TEXT,
152
+ p_email_sent BOOLEAN DEFAULT false,
153
+ p_email_error TEXT DEFAULT NULL
154
+ ) RETURNS UUID AS $$
155
+ DECLARE
156
+ log_id UUID;
157
+ BEGIN
158
+ INSERT INTO public.notification_logs (
159
+ category_id,
160
+ user_id,
161
+ entity_type,
162
+ entity_id,
163
+ email_sent,
164
+ email_error,
165
+ sent_at,
166
+ created_at
167
+ ) VALUES (
168
+ p_category_id,
169
+ p_user_id,
170
+ p_entity_type,
171
+ p_entity_id,
172
+ p_email_sent,
173
+ p_email_error,
174
+ CASE WHEN p_email_sent THEN NOW() ELSE NULL END,
175
+ NOW()
176
+ ) RETURNING id INTO log_id;
177
+
178
+ RETURN log_id;
179
+ END;
180
+ $$ LANGUAGE plpgsql;
181
+
182
+ -- ============================================
183
+ -- FUNÇÃO: is_user_admin
184
+ -- ============================================
185
+ -- Verifica se um usuário é administrador
186
+ CREATE OR REPLACE FUNCTION public.is_user_admin(user_id UUID)
187
+ RETURNS BOOLEAN AS $$
188
+ BEGIN
189
+ RETURN EXISTS (
190
+ SELECT 1 FROM public.profiles
191
+ WHERE id = user_id AND role = 'administrator'
192
+ );
193
+ END;
194
+ $$ LANGUAGE plpgsql;
195
+
196
+ -- ============================================
197
+ -- FUNÇÃO: is_user_employee
198
+ -- ============================================
199
+ -- Verifica se um usuário é funcionário
200
+ CREATE OR REPLACE FUNCTION public.is_user_employee(user_id UUID)
201
+ RETURNS BOOLEAN AS $$
202
+ BEGIN
203
+ RETURN EXISTS (
204
+ SELECT 1 FROM public.profiles
205
+ WHERE id = user_id AND role = 'employee'
206
+ );
207
+ END;
208
+ $$ LANGUAGE plpgsql;
209
+
210
+ -- ============================================
211
+ -- FUNÇÃO: is_user_customer
212
+ -- ============================================
213
+ -- Verifica se um usuário é cliente
214
+ CREATE OR REPLACE FUNCTION public.is_user_customer(user_id UUID)
215
+ RETURNS BOOLEAN AS $$
216
+ BEGIN
217
+ RETURN EXISTS (
218
+ SELECT 1 FROM public.profiles
219
+ WHERE id = user_id AND role = 'customer'
220
+ );
221
+ END;
222
+ $$ LANGUAGE plpgsql;
223
+
224
+ -- ============================================
225
+ -- FUNÇÃO: get_notification_settings_for_user
226
+ -- ============================================
227
+ -- Retorna as configurações de notificação de um usuário
228
+ CREATE OR REPLACE FUNCTION public.get_notification_settings_for_user(user_id UUID)
229
+ RETURNS TABLE (
230
+ category_id UUID,
231
+ category_name TEXT,
232
+ is_enabled BOOLEAN
233
+ ) AS $$
234
+ BEGIN
235
+ RETURN QUERY
236
+ SELECT
237
+ ns.category_id,
238
+ nc.name,
239
+ ns.is_enabled
240
+ FROM public.notification_settings ns
241
+ JOIN public.notification_categories nc ON ns.category_id = nc.id
242
+ WHERE ns.user_id = user_id;
243
+ END;
244
+ $$ LANGUAGE plpgsql;
@@ -0,0 +1,126 @@
1
+ -- ============================================
2
+ -- 004_seed.sql: Dados Iniciais (gmoonc - Fake Data)
3
+ -- ============================================
4
+ -- Dados fictícios 100% fake para desenvolvimento
5
+ -- Todos em inglês conforme gmoonc
6
+ -- Inspirado em Missão Apollo
7
+
8
+ -- ============================================
9
+ -- 1. ROLES (System Roles)
10
+ -- ============================================
11
+
12
+ INSERT INTO public.roles (id, name, description, is_system_role, created_at, updated_at) VALUES
13
+ ('550e8400-e29b-41d4-a716-446655440001'::uuid, 'administrator', 'System Administrator', true, NOW(), NOW()),
14
+ ('550e8400-e29b-41d4-a716-446655440002'::uuid, 'employee', 'Employee/Collaborator', true, NOW(), NOW()),
15
+ ('550e8400-e29b-41d4-a716-446655440003'::uuid, 'customer', 'Customer/End User', true, NOW(), NOW());
16
+
17
+ -- ============================================
18
+ -- 2. MODULES (Application Modules)
19
+ -- ============================================
20
+
21
+ INSERT INTO public.modules (id, name, display_name, description, is_active, created_at, updated_at) VALUES
22
+ ('550e8400-e29b-41d4-a716-446655440010'::uuid, 'admin', 'Administrative Module', 'Administrative functions and system management', true, NOW(), NOW()),
23
+ ('550e8400-e29b-41d4-a716-446655440011'::uuid, 'financial', 'Financial Module', 'Financial management and reporting', true, NOW(), NOW()),
24
+ ('550e8400-e29b-41d4-a716-446655440012'::uuid, 'technical', 'Technical Support Module', 'Technical support and help desk', true, NOW(), NOW()),
25
+ ('550e8400-e29b-41d4-a716-446655440013'::uuid, 'customer', 'Customer Module', 'Customer portal and self-service', true, NOW(), NOW());
26
+
27
+ -- ============================================
28
+ -- 3. PERMISSIONS (Role-Based Access Control)
29
+ -- ============================================
30
+
31
+ INSERT INTO public.permissions (id, role_id, module_id, can_access, can_create, can_read, can_update, can_delete, created_at, updated_at) VALUES
32
+ -- Administrator: Full access to all modules
33
+ ('550e8400-e29b-41d4-a716-446655440100'::uuid, '550e8400-e29b-41d4-a716-446655440001'::uuid, '550e8400-e29b-41d4-a716-446655440010'::uuid, true, true, true, true, true, NOW(), NOW()),
34
+ ('550e8400-e29b-41d4-a716-446655440101'::uuid, '550e8400-e29b-41d4-a716-446655440001'::uuid, '550e8400-e29b-41d4-a716-446655440011'::uuid, true, true, true, true, true, NOW(), NOW()),
35
+ ('550e8400-e29b-41d4-a716-446655440102'::uuid, '550e8400-e29b-41d4-a716-446655440001'::uuid, '550e8400-e29b-41d4-a716-446655440012'::uuid, true, true, true, true, true, NOW(), NOW()),
36
+ ('550e8400-e29b-41d4-a716-446655440103'::uuid, '550e8400-e29b-41d4-a716-446655440001'::uuid, '550e8400-e29b-41d4-a716-446655440013'::uuid, true, true, true, true, true, NOW(), NOW()),
37
+
38
+ -- Employee: Read and update access (no delete)
39
+ ('550e8400-e29b-41d4-a716-446655440110'::uuid, '550e8400-e29b-41d4-a716-446655440002'::uuid, '550e8400-e29b-41d4-a716-446655440010'::uuid, true, false, true, true, false, NOW(), NOW()),
40
+ ('550e8400-e29b-41d4-a716-446655440111'::uuid, '550e8400-e29b-41d4-a716-446655440002'::uuid, '550e8400-e29b-41d4-a716-446655440011'::uuid, true, false, true, true, false, NOW(), NOW()),
41
+ ('550e8400-e29b-41d4-a716-446655440112'::uuid, '550e8400-e29b-41d4-a716-446655440002'::uuid, '550e8400-e29b-41d4-a716-446655440012'::uuid, true, true, true, true, false, NOW(), NOW()),
42
+ ('550e8400-e29b-41d4-a716-446655440113'::uuid, '550e8400-e29b-41d4-a716-446655440002'::uuid, '550e8400-e29b-41d4-a716-446655440013'::uuid, true, false, true, false, false, NOW(), NOW()),
43
+
44
+ -- Customer: Read-only access (create messages)
45
+ ('550e8400-e29b-41d4-a716-446655440120'::uuid, '550e8400-e29b-41d4-a716-446655440003'::uuid, '550e8400-e29b-41d4-a716-446655440010'::uuid, false, false, false, false, false, NOW(), NOW()),
46
+ ('550e8400-e29b-41d4-a716-446655440121'::uuid, '550e8400-e29b-41d4-a716-446655440003'::uuid, '550e8400-e29b-41d4-a716-446655440011'::uuid, false, false, false, false, false, NOW(), NOW()),
47
+ ('550e8400-e29b-41d4-a716-446655440122'::uuid, '550e8400-e29b-41d4-a716-446655440003'::uuid, '550e8400-e29b-41d4-a716-446655440012'::uuid, true, true, true, false, false, NOW(), NOW()),
48
+ ('550e8400-e29b-41d4-a716-446655440123'::uuid, '550e8400-e29b-41d4-a716-446655440003'::uuid, '550e8400-e29b-41d4-a716-446655440013'::uuid, true, false, true, false, false, NOW(), NOW());
49
+
50
+ -- ============================================
51
+ -- 4. PROFILES (Users - Apollo Astronauts)
52
+ -- ============================================
53
+
54
+ INSERT INTO public.profiles (id, email, name, role, created_at, updated_at) VALUES
55
+ -- Administrators
56
+ ('550e8400-e29b-41d4-a716-446655440200'::uuid, 'neil.armstrong@apollo.nasa.gov', 'Neil Armstrong', 'administrator', NOW(), NOW()),
57
+ ('550e8400-e29b-41d4-a716-446655440201'::uuid, 'buzz.aldrin@apollo.nasa.gov', 'Buzz Aldrin', 'administrator', NOW(), NOW()),
58
+
59
+ -- Employees
60
+ ('550e8400-e29b-41d4-a716-446655440210'::uuid, 'michael.collins@apollo.nasa.gov', 'Michael Collins', 'employee', NOW(), NOW()),
61
+ ('550e8400-e29b-41d4-a716-446655440211'::uuid, 'alan.bean@apollo.nasa.gov', 'Alan Bean', 'employee', NOW(), NOW()),
62
+ ('550e8400-e29b-41d4-a716-446655440212'::uuid, 'pete.conrad@apollo.nasa.gov', 'Pete Conrad', 'employee', NOW(), NOW()),
63
+
64
+ -- Customers
65
+ ('550e8400-e29b-41d4-a716-446655440220'::uuid, 'john.glenn@apollo.nasa.gov', 'John Glenn', 'customer', NOW(), NOW()),
66
+ ('550e8400-e29b-41d4-a716-446655440221'::uuid, 'alan.shepard@apollo.nasa.gov', 'Alan Shepard', 'customer', NOW(), NOW()),
67
+ ('550e8400-e29b-41d4-a716-446655440222'::uuid, 'gus.grissom@apollo.nasa.gov', 'Gus Grissom', 'customer', NOW(), NOW());
68
+
69
+ -- ============================================
70
+ -- 5. NOTIFICATION CATEGORIES
71
+ -- ============================================
72
+
73
+ INSERT INTO public.notification_categories (id, name, display_name, description, is_active, email_template_subject, email_template_body, created_at, updated_at) VALUES
74
+ ('550e8400-e29b-41d4-a716-446655440300'::uuid, 'message_received', 'Message Received', 'Notification when a new message is received', true, 'New message from {{sender}}', 'You have received a new message from {{sender}}: {{message}}', NOW(), NOW()),
75
+ ('550e8400-e29b-41d4-a716-446655440301'::uuid, 'message_updated', 'Message Updated', 'Notification when a message status is updated', true, 'Message status updated', 'Your message status has been updated to: {{status}}', NOW(), NOW()),
76
+ ('550e8400-e29b-41d4-a716-446655440302'::uuid, 'system_alert', 'System Alert', 'General system alerts and notifications', true, 'System Alert', 'System Alert: {{message}}', NOW(), NOW()),
77
+ ('550e8400-e29b-41d4-a716-446655440303'::uuid, 'support_ticket', 'Support Ticket', 'Support ticket related notifications', true, 'Support Ticket {{ticket_id}}', 'Your support ticket {{ticket_id}} has been updated', NOW(), NOW());
78
+
79
+ -- ============================================
80
+ -- 6. NOTIFICATION SETTINGS (User Preferences)
81
+ -- ============================================
82
+
83
+ INSERT INTO public.notification_settings (id, user_id, category_id, is_enabled, created_at, updated_at) VALUES
84
+ -- Neil Armstrong (Admin) - receives all notifications
85
+ ('550e8400-e29b-41d4-a716-446655440400'::uuid, '550e8400-e29b-41d4-a716-446655440200'::uuid, '550e8400-e29b-41d4-a716-446655440300'::uuid, true, NOW(), NOW()),
86
+ ('550e8400-e29b-41d4-a716-446655440401'::uuid, '550e8400-e29b-41d4-a716-446655440200'::uuid, '550e8400-e29b-41d4-a716-446655440301'::uuid, true, NOW(), NOW()),
87
+ ('550e8400-e29b-41d4-a716-446655440402'::uuid, '550e8400-e29b-41d4-a716-446655440200'::uuid, '550e8400-e29b-41d4-a716-446655440302'::uuid, true, NOW(), NOW()),
88
+ ('550e8400-e29b-41d4-a716-446655440403'::uuid, '550e8400-e29b-41d4-a716-446655440200'::uuid, '550e8400-e29b-41d4-a716-446655440303'::uuid, true, NOW(), NOW()),
89
+
90
+ -- Michael Collins (Employee) - selective notifications
91
+ ('550e8400-e29b-41d4-a716-446655440410'::uuid, '550e8400-e29b-41d4-a716-446655440210'::uuid, '550e8400-e29b-41d4-a716-446655440300'::uuid, true, NOW(), NOW()),
92
+ ('550e8400-e29b-41d4-a716-446655440411'::uuid, '550e8400-e29b-41d4-a716-446655440210'::uuid, '550e8400-e29b-41d4-a716-446655440301'::uuid, true, NOW(), NOW()),
93
+ ('550e8400-e29b-41d4-a716-446655440412'::uuid, '550e8400-e29b-41d4-a716-446655440210'::uuid, '550e8400-e29b-41d4-a716-446655440302'::uuid, false, NOW(), NOW()),
94
+ ('550e8400-e29b-41d4-a716-446655440413'::uuid, '550e8400-e29b-41d4-a716-446655440210'::uuid, '550e8400-e29b-41d4-a716-446655440303'::uuid, true, NOW(), NOW()),
95
+
96
+ -- John Glenn (Customer) - limited notifications
97
+ ('550e8400-e29b-41d4-a716-446655440420'::uuid, '550e8400-e29b-41d4-a716-446655440220'::uuid, '550e8400-e29b-41d4-a716-446655440300'::uuid, true, NOW(), NOW()),
98
+ ('550e8400-e29b-41d4-a716-446655440421'::uuid, '550e8400-e29b-41d4-a716-446655440220'::uuid, '550e8400-e29b-41d4-a716-446655440301'::uuid, true, NOW(), NOW()),
99
+ ('550e8400-e29b-41d4-a716-446655440422'::uuid, '550e8400-e29b-41d4-a716-446655440220'::uuid, '550e8400-e29b-41d4-a716-446655440302'::uuid, false, NOW(), NOW()),
100
+ ('550e8400-e29b-41d4-a716-446655440423'::uuid, '550e8400-e29b-41d4-a716-446655440220'::uuid, '550e8400-e29b-41d4-a716-446655440303'::uuid, true, NOW(), NOW());
101
+
102
+ -- ============================================
103
+ -- 7. MESSAGES (System Messages)
104
+ -- ============================================
105
+
106
+ INSERT INTO public.messages (id, user_id, name, email, phone, company_farm, message, status, created_at, updated_at) VALUES
107
+ -- From Neil Armstrong
108
+ ('550e8400-e29b-41d4-a716-446655440600'::uuid, '550e8400-e29b-41d4-a716-446655440200'::uuid, 'Neil Armstrong', 'neil.armstrong@apollo.nasa.gov', '+55 11 98765-4321', 'NASA - Apollo Mission', 'Requesting analysis of lunar region coverage', 'completed', NOW(), NOW()),
109
+
110
+ -- From John Glenn
111
+ ('550e8400-e29b-41d4-a716-446655440601'::uuid, '550e8400-e29b-41d4-a716-446655440220'::uuid, 'John Glenn', 'john.glenn@apollo.nasa.gov', '+55 21 99999-8888', 'NASA - Mercury Program', 'Question about coverage analysis', 'in_analysis', NOW(), NOW()),
112
+
113
+ -- From Alan Shepard
114
+ ('550e8400-e29b-41d4-a716-446655440602'::uuid, '550e8400-e29b-41d4-a716-446655440221'::uuid, 'Alan Shepard', 'alan.shepard@apollo.nasa.gov', '+55 85 97777-6666', 'NASA - Gemini Program', 'Feedback on previous analysis', 'pending', NOW(), NOW()),
115
+
116
+ -- Draft message
117
+ ('550e8400-e29b-41d4-a716-446655440603'::uuid, '550e8400-e29b-41d4-a716-446655440222'::uuid, 'Gus Grissom', 'gus.grissom@apollo.nasa.gov', '+55 31 98888-7777', 'NASA - Apollo Applications', 'Support request for system access', 'draft', NOW(), NOW());
118
+
119
+ -- ============================================
120
+ -- 8. NOTIFICATION LOGS (Notification History)
121
+ -- ============================================
122
+
123
+ INSERT INTO public.notification_logs (id, category_id, user_id, entity_type, entity_id, email_sent, email_error, sent_at, created_at) VALUES
124
+ ('550e8400-e29b-41d4-a716-446655440700'::uuid, '550e8400-e29b-41d4-a716-446655440300'::uuid, '550e8400-e29b-41d4-a716-446655440200'::uuid, 'messages', '550e8400-e29b-41d4-a716-446655440600'::uuid, true, null, NOW(), NOW()),
125
+ ('550e8400-e29b-41d4-a716-446655440701'::uuid, '550e8400-e29b-41d4-a716-446655440301'::uuid, '550e8400-e29b-41d4-a716-446655440200'::uuid, 'messages', '550e8400-e29b-41d4-a716-446655440600'::uuid, true, null, NOW(), NOW()),
126
+ ('550e8400-e29b-41d4-a716-446655440702'::uuid, '550e8400-e29b-41d4-a716-446655440300'::uuid, '550e8400-e29b-41d4-a716-446655440220'::uuid, 'messages', '550e8400-e29b-41d4-a716-446655440601'::uuid, false, 'User disabled email notifications', null, NOW());
package/dist/index.cjs CHANGED
@@ -4,8 +4,8 @@
4
4
  // src/cli/index.ts
5
5
  var import_commander = require("commander");
6
6
  var import_process = require("process");
7
- var import_path7 = require("path");
8
- var import_fs11 = require("fs");
7
+ var import_path8 = require("path");
8
+ var import_fs13 = require("fs");
9
9
 
10
10
  // src/cli/lib/detect.ts
11
11
  var import_fs = require("fs");
@@ -1626,9 +1626,13 @@ export type { UserPermission } from './getUserPermissions';
1626
1626
  }
1627
1627
  function updateEnvExample(projectDir) {
1628
1628
  const envExamplePath = (0, import_path6.join)(projectDir, ".env.example");
1629
- const envExampleContent = `# Supabase Configuration
1629
+ const envExampleContent = `# Supabase Configuration (Client-side)
1630
1630
  VITE_SUPABASE_URL=
1631
1631
  VITE_SUPABASE_ANON_KEY=
1632
+
1633
+ # Supabase Database Connection (Server-only, for seeding)
1634
+ # Get this from Supabase Dashboard \u2192 Settings \u2192 Database \u2192 Connection string (URI)
1635
+ SUPABASE_DB_URL=
1632
1636
  `;
1633
1637
  if ((0, import_fs9.existsSync)(envExamplePath)) {
1634
1638
  const existing = (0, import_fs9.readFileSync)(envExamplePath, "utf-8");
@@ -1808,9 +1812,236 @@ function updateAllSessionImports(gmooncDir) {
1808
1812
  }
1809
1813
  }
1810
1814
 
1815
+ // src/cli/lib/supabaseSeedVite.ts
1816
+ var import_path7 = require("path");
1817
+ var import_fs11 = require("fs");
1818
+ var import_pg = require("pg");
1819
+ var import_dotenv = require("dotenv");
1820
+ function getPackageRoot() {
1821
+ const possiblePaths = [
1822
+ // Strategy 1: From __dirname (when running from dist/cli/lib)
1823
+ (() => {
1824
+ try {
1825
+ if (typeof __dirname !== "undefined") {
1826
+ const distPath = (0, import_path7.join)(__dirname, "../..");
1827
+ if ((0, import_fs11.existsSync)((0, import_path7.join)(distPath, "package.json"))) {
1828
+ return distPath;
1829
+ }
1830
+ }
1831
+ } catch {
1832
+ }
1833
+ return null;
1834
+ })(),
1835
+ // Strategy 2: From node_modules (when installed)
1836
+ (0, import_path7.join)(process.cwd(), "node_modules", "gmoonc"),
1837
+ // Strategy 3: From packages (when in monorepo dev)
1838
+ (0, import_path7.join)(process.cwd(), "packages", "gmoonc"),
1839
+ // Strategy 4: Try to find by looking for package.json with name "gmoonc"
1840
+ (() => {
1841
+ let current = typeof __dirname !== "undefined" ? __dirname : process.cwd();
1842
+ for (let i = 0; i < 5; i++) {
1843
+ const pkgPath = (0, import_path7.join)(current, "package.json");
1844
+ if ((0, import_fs11.existsSync)(pkgPath)) {
1845
+ try {
1846
+ const pkg = JSON.parse((0, import_fs11.readFileSync)(pkgPath, "utf-8"));
1847
+ if (pkg.name === "gmoonc") {
1848
+ return current;
1849
+ }
1850
+ } catch {
1851
+ }
1852
+ }
1853
+ current = (0, import_path7.join)(current, "..");
1854
+ }
1855
+ return null;
1856
+ })()
1857
+ ];
1858
+ for (const path of possiblePaths) {
1859
+ if (path && (0, import_fs11.existsSync)((0, import_path7.join)(path, "package.json"))) {
1860
+ try {
1861
+ const pkg = JSON.parse((0, import_fs11.readFileSync)((0, import_path7.join)(path, "package.json"), "utf-8"));
1862
+ if (pkg.name === "gmoonc") {
1863
+ return path;
1864
+ }
1865
+ } catch {
1866
+ }
1867
+ }
1868
+ }
1869
+ return (0, import_path7.join)(process.cwd(), "node_modules", "gmoonc");
1870
+ }
1871
+ var PACKAGE_ROOT = getPackageRoot();
1872
+ async function seedSupabase(options) {
1873
+ const { projectDir, dryRun } = options;
1874
+ try {
1875
+ (0, import_dotenv.config)({ path: (0, import_path7.join)(projectDir, ".env") });
1876
+ (0, import_dotenv.config)({ path: (0, import_path7.join)(projectDir, ".env.local") });
1877
+ const gmooncDir = (0, import_path7.join)(projectDir, "src/gmoonc");
1878
+ if (!(0, import_fs11.existsSync)(gmooncDir)) {
1879
+ logError('gmoonc is not installed. Run "npx gmoonc" first.');
1880
+ return { success: false, message: "gmoonc not installed" };
1881
+ }
1882
+ const supabaseDir = (0, import_path7.join)(gmooncDir, "supabase");
1883
+ if (!(0, import_fs11.existsSync)(supabaseDir)) {
1884
+ logError('Supabase integration not found. Run "npx gmoonc supabase --vite" first.');
1885
+ return { success: false, message: "Supabase not set up" };
1886
+ }
1887
+ const markerPath = (0, import_path7.join)(projectDir, ".gmoonc", "supabase-seed.json");
1888
+ if ((0, import_fs11.existsSync)(markerPath)) {
1889
+ logError("supabase-seed already executed in this project.");
1890
+ logError("To re-seed, delete the marker file: .gmoonc/supabase-seed.json");
1891
+ logError("Then manually clean the database in Supabase before re-running.");
1892
+ return { success: false, message: "Already seeded" };
1893
+ }
1894
+ const dbUrl = process.env.SUPABASE_DB_URL;
1895
+ if (!dbUrl) {
1896
+ logError("SUPABASE_DB_URL is not set in .env or .env.local");
1897
+ logError("");
1898
+ logError("To get the connection string:");
1899
+ logError("1. Go to your Supabase project dashboard");
1900
+ logError("2. Navigate to Settings \u2192 Database");
1901
+ logError('3. Copy the "Connection string (URI)"');
1902
+ logError("4. Add it to .env.local as:");
1903
+ logError(" SUPABASE_DB_URL=postgresql://postgres:[YOUR-PASSWORD]@[HOST]:5432/postgres");
1904
+ logError("");
1905
+ logError("\u26A0\uFE0F This is a server-only variable (no VITE_ prefix)");
1906
+ return { success: false, message: "SUPABASE_DB_URL not set" };
1907
+ }
1908
+ if (dryRun) {
1909
+ logInfo("\u{1F50D} Dry run mode - would execute SQL files");
1910
+ return { success: true };
1911
+ }
1912
+ const sqlFiles = [
1913
+ { name: "001_tables.sql", order: 1 },
1914
+ { name: "003_functions_triggers.sql", order: 2 },
1915
+ { name: "002_rls.sql", order: 3 },
1916
+ { name: "004_seed.sql", order: 4 }
1917
+ ];
1918
+ const projectSqlDir = (0, import_path7.join)(gmooncDir, "supabase", "sql");
1919
+ const packageSqlDir = (0, import_path7.join)(PACKAGE_ROOT, "assets", "supabase");
1920
+ const missingFiles = [];
1921
+ for (const sqlFile of sqlFiles) {
1922
+ const packagePath = (0, import_path7.join)(packageSqlDir, sqlFile.name);
1923
+ if (!(0, import_fs11.existsSync)(packagePath)) {
1924
+ missingFiles.push(sqlFile.name);
1925
+ }
1926
+ }
1927
+ if (missingFiles.length > 0) {
1928
+ logError(`SQL files not found in package at: ${packageSqlDir}`);
1929
+ logError(`Missing files: ${missingFiles.join(", ")}`);
1930
+ logError(`Package root resolved to: ${PACKAGE_ROOT}`);
1931
+ logError(`Please ensure SQL files are included in the npm package.`);
1932
+ return { success: false, message: "SQL files not found in package" };
1933
+ }
1934
+ if (!(0, import_fs11.existsSync)(projectSqlDir)) {
1935
+ ensureDirectoryExists((0, import_path7.join)(projectSqlDir, "dummy"));
1936
+ logInfo("\u{1F4CB} Copying SQL files to project...");
1937
+ for (const sqlFile of sqlFiles) {
1938
+ const packagePath = (0, import_path7.join)(packageSqlDir, sqlFile.name);
1939
+ const projectPath = (0, import_path7.join)(projectSqlDir, sqlFile.name);
1940
+ if ((0, import_fs11.existsSync)(packagePath)) {
1941
+ const content = (0, import_fs11.readFileSync)(packagePath, "utf-8");
1942
+ (0, import_fs11.writeFileSync)(projectPath, content, "utf-8");
1943
+ logSuccess(`Copied ${sqlFile.name} to src/gmoonc/supabase/sql/`);
1944
+ } else {
1945
+ logError(`Failed to copy ${sqlFile.name} - file not found at ${packagePath}`);
1946
+ return { success: false, message: `SQL file not found: ${sqlFile.name}` };
1947
+ }
1948
+ }
1949
+ }
1950
+ logInfo("\n\u{1F50C} Connecting to Supabase database...");
1951
+ const client = new import_pg.Client({
1952
+ connectionString: dbUrl,
1953
+ ssl: { rejectUnauthorized: false }
1954
+ });
1955
+ await client.connect();
1956
+ logSuccess("Connected to database");
1957
+ const executedFiles = [];
1958
+ for (const sqlFile of sqlFiles) {
1959
+ logInfo(`
1960
+ \u{1F4DD} Applying ${sqlFile.name}...`);
1961
+ let sqlPath = (0, import_path7.join)(projectSqlDir, sqlFile.name);
1962
+ if (!(0, import_fs11.existsSync)(sqlPath)) {
1963
+ sqlPath = (0, import_path7.join)(packageSqlDir, sqlFile.name);
1964
+ }
1965
+ if (!(0, import_fs11.existsSync)(sqlPath)) {
1966
+ logError(`SQL file not found: ${sqlFile.name}`);
1967
+ logError(` Searched in project: ${(0, import_path7.join)(projectSqlDir, sqlFile.name)}`);
1968
+ logError(` Searched in package: ${(0, import_path7.join)(packageSqlDir, sqlFile.name)}`);
1969
+ logError(` Package root: ${PACKAGE_ROOT}`);
1970
+ await client.end();
1971
+ return { success: false, message: `SQL file not found: ${sqlFile.name}` };
1972
+ }
1973
+ const sql = (0, import_fs11.readFileSync)(sqlPath, "utf-8");
1974
+ try {
1975
+ await client.query("BEGIN");
1976
+ await client.query(sql);
1977
+ await client.query("COMMIT");
1978
+ executedFiles.push(sqlFile.name);
1979
+ logSuccess(`\u2713 Applied ${sqlFile.name}`);
1980
+ } catch (error) {
1981
+ await client.query("ROLLBACK");
1982
+ const errorMessage = error.message || "Unknown error";
1983
+ const errorPosition = error.position ? ` at position ${error.position}` : "";
1984
+ logError(`\u2717 Failed to apply ${sqlFile.name}${errorPosition}`);
1985
+ logError(`Error: ${errorMessage}`);
1986
+ if (error.position && sql) {
1987
+ const lines = sql.split("\n");
1988
+ const charPos = error.position;
1989
+ let currentPos = 0;
1990
+ let errorLine = 0;
1991
+ for (let i = 0; i < lines.length; i++) {
1992
+ currentPos += lines[i].length + 1;
1993
+ if (currentPos >= charPos) {
1994
+ errorLine = i + 1;
1995
+ break;
1996
+ }
1997
+ }
1998
+ if (errorLine > 0) {
1999
+ const start = Math.max(0, errorLine - 3);
2000
+ const end = Math.min(lines.length, errorLine + 2);
2001
+ logError("\nSQL context around error:");
2002
+ for (let i = start; i < end; i++) {
2003
+ const marker = i === errorLine - 1 ? ">>> " : " ";
2004
+ logError(`${marker}${i + 1}: ${lines[i]}`);
2005
+ }
2006
+ }
2007
+ }
2008
+ await client.end();
2009
+ return { success: false, message: `SQL execution failed: ${sqlFile.name}` };
2010
+ }
2011
+ }
2012
+ await client.end();
2013
+ logSuccess("\n\u2705 All SQL files applied successfully");
2014
+ const markerDir = (0, import_path7.join)(projectDir, ".gmoonc");
2015
+ ensureDirectoryExists((0, import_path7.join)(markerDir, "dummy"));
2016
+ const packageJsonPath = (0, import_path7.join)(PACKAGE_ROOT, "package.json");
2017
+ let version = "0.0.22";
2018
+ try {
2019
+ if ((0, import_fs11.existsSync)(packageJsonPath)) {
2020
+ const packageJson = JSON.parse((0, import_fs11.readFileSync)(packageJsonPath, "utf-8"));
2021
+ version = packageJson.version || version;
2022
+ }
2023
+ } catch {
2024
+ }
2025
+ const markerContent = JSON.stringify({
2026
+ version,
2027
+ executedAt: (/* @__PURE__ */ new Date()).toISOString(),
2028
+ files: executedFiles
2029
+ }, null, 2);
2030
+ (0, import_fs11.writeFileSync)(markerPath, markerContent, "utf-8");
2031
+ logSuccess("Created marker file: .gmoonc/supabase-seed.json");
2032
+ return { success: true };
2033
+ } catch (error) {
2034
+ logError(`Error: ${error.message}`);
2035
+ if (error.stack && process.env.DEBUG) {
2036
+ console.error(error.stack);
2037
+ }
2038
+ return { success: false, message: error.message };
2039
+ }
2040
+ }
2041
+
1811
2042
  // src/cli/index.ts
1812
2043
  var program = new import_commander.Command();
1813
- program.name("gmoonc").description("Goalmoon Ctrl (gmoonc): Install complete dashboard into your React project").version("0.0.21").option("--base <path>", "Base path for dashboard routes", "/app").option("--skip-router-patch", "Skip automatic router integration (only copy files and inject CSS)").option("--dry-run", "Show what would be done without making changes").action(async (options) => {
2044
+ program.name("gmoonc").description("Goalmoon Ctrl (gmoonc): Install complete dashboard into your React project").version("0.0.23").option("--base <path>", "Base path for dashboard routes", "/app").option("--skip-router-patch", "Skip automatic router integration (only copy files and inject CSS)").option("--dry-run", "Show what would be done without making changes").action(async (options) => {
1814
2045
  try {
1815
2046
  logInfo("\u{1F680} Starting gmoonc installer...");
1816
2047
  logInfo("\u{1F4E6} Installing complete dashboard into your React project\n");
@@ -1818,9 +2049,9 @@ program.name("gmoonc").description("Goalmoon Ctrl (gmoonc): Install complete das
1818
2049
  const basePath = options.base || "/app";
1819
2050
  const dryRun = options.dryRun || false;
1820
2051
  const skipRouterPatch = options.skipRouterPatch || false;
1821
- const gmooncDir = (0, import_path7.join)(projectDir, "src/gmoonc");
1822
- const markerFile = (0, import_path7.join)(gmooncDir, ".gmoonc-installed.json");
1823
- if ((0, import_fs11.existsSync)(gmooncDir) || (0, import_fs11.existsSync)(markerFile)) {
2052
+ const gmooncDir = (0, import_path8.join)(projectDir, "src/gmoonc");
2053
+ const markerFile = (0, import_path8.join)(gmooncDir, ".gmoonc-installed.json");
2054
+ if ((0, import_fs13.existsSync)(gmooncDir) || (0, import_fs13.existsSync)(markerFile)) {
1824
2055
  logError("gmoonc already installed (src/gmoonc exists or marker file found).");
1825
2056
  logError("Remove src/gmoonc and restore backups to reinstall.");
1826
2057
  process.exit(1);
@@ -1861,7 +2092,7 @@ program.name("gmoonc").description("Goalmoon Ctrl (gmoonc): Install complete das
1861
2092
  process.exit(1);
1862
2093
  }
1863
2094
  logInfo("\n\u{1F4DD} Injecting CSS imports...");
1864
- const entrypointPath = (0, import_path7.join)(projectDir, project.entrypoint);
2095
+ const entrypointPath = (0, import_path8.join)(projectDir, project.entrypoint);
1865
2096
  const cssResult = patchEntryCss(entrypointPath, dryRun);
1866
2097
  if (!cssResult.success && !dryRun) {
1867
2098
  logError("Failed to inject CSS");
@@ -1883,11 +2114,11 @@ program.name("gmoonc").description("Goalmoon Ctrl (gmoonc): Install complete das
1883
2114
  }
1884
2115
  if (!dryRun) {
1885
2116
  ensureDirectoryExists(markerFile);
1886
- const packageJsonPath = (0, import_path7.join)(projectDir, "package.json");
2117
+ const packageJsonPath = (0, import_path8.join)(projectDir, "package.json");
1887
2118
  let version = "0.0.10";
1888
2119
  try {
1889
- if ((0, import_fs11.existsSync)(packageJsonPath)) {
1890
- const packageJson = JSON.parse((0, import_fs11.readFileSync)(packageJsonPath, "utf-8"));
2120
+ if ((0, import_fs13.existsSync)(packageJsonPath)) {
2121
+ const packageJson = JSON.parse((0, import_fs13.readFileSync)(packageJsonPath, "utf-8"));
1891
2122
  const gmooncVersion = packageJson.dependencies?.gmoonc || packageJson.devDependencies?.gmoonc;
1892
2123
  if (gmooncVersion) {
1893
2124
  version = gmooncVersion.replace(/^[\^~]/, "");
@@ -1899,7 +2130,7 @@ program.name("gmoonc").description("Goalmoon Ctrl (gmoonc): Install complete das
1899
2130
  version,
1900
2131
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
1901
2132
  }, null, 2);
1902
- (0, import_fs11.writeFileSync)(markerFile, markerContent, "utf-8");
2133
+ (0, import_fs13.writeFileSync)(markerFile, markerContent, "utf-8");
1903
2134
  }
1904
2135
  logSuccess("\n\u2705 Installation complete!");
1905
2136
  logInfo("\nYour dashboard is now available at:");
@@ -1945,4 +2176,34 @@ program.command("supabase").description("Setup Supabase integration (Auth + RBAC
1945
2176
  process.exit(1);
1946
2177
  }
1947
2178
  });
2179
+ program.command("supabase-seed").description("Seed Supabase database with schema, RLS, functions, and initial data").option("--vite", "Seed for Vite projects").action(async (options) => {
2180
+ try {
2181
+ logInfo("\u{1F331} Starting Supabase database seeding...");
2182
+ const projectDir = (0, import_process.cwd)();
2183
+ const dryRun = false;
2184
+ if (!options.vite) {
2185
+ logError("Missing platform flag. Use: gmoonc supabase-seed --vite (Next will be added later).");
2186
+ process.exit(1);
2187
+ }
2188
+ const result = await seedSupabase({
2189
+ projectDir,
2190
+ dryRun
2191
+ });
2192
+ if (!result.success) {
2193
+ process.exit(1);
2194
+ }
2195
+ logSuccess("\n\u2705 Database seeding complete!");
2196
+ logInfo("\nYour Supabase database is now ready with:");
2197
+ logInfo(" - Tables (profiles, roles, modules, permissions, etc.)");
2198
+ logInfo(" - Functions and triggers");
2199
+ logInfo(" - Row Level Security (RLS) policies");
2200
+ logInfo(" - Initial seed data (Apollo user)");
2201
+ } catch (error) {
2202
+ logError(`Error: ${error.message}`);
2203
+ if (error.stack && process.env.DEBUG) {
2204
+ console.error(error.stack);
2205
+ }
2206
+ process.exit(1);
2207
+ }
2208
+ });
1948
2209
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gmoonc",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "description": "Goalmoon Ctrl (gmoonc): Complete dashboard installer for React projects",
5
5
  "license": "MIT",
6
6
  "homepage": "https://gmoonc.com",
@@ -19,6 +19,7 @@
19
19
  "files": [
20
20
  "dist",
21
21
  "src/templates",
22
+ "assets",
22
23
  "scripts",
23
24
  "README.md",
24
25
  "LICENSE"
@@ -32,10 +33,13 @@
32
33
  "postinstall": "node scripts/block-global-install.cjs"
33
34
  },
34
35
  "dependencies": {
35
- "commander": "^12.0.0"
36
+ "commander": "^12.0.0",
37
+ "dotenv": "^16.4.5",
38
+ "pg": "^8.11.3"
36
39
  },
37
40
  "devDependencies": {
38
41
  "@types/node": "^20.0.0",
42
+ "@types/pg": "^8.10.9",
39
43
  "rimraf": "^6.0.0",
40
44
  "tsup": "^8.0.0",
41
45
  "typescript": "^5.0.0"