anderson-ufrj commited on
Commit
a3e7e5b
·
1 Parent(s): 11026ec

docs(database): add comprehensive Supabase integration guide

Browse files

Complete technical documentation covering:
- Architecture overview and data flow
- Step-by-step setup instructions
- Environment configuration for backend and frontend
- Database schema documentation
- Code examples for Python and TypeScript
- Security and RLS configuration
- Performance optimization guidelines
- Troubleshooting common issues
- Deployment instructions for HuggingFace Spaces

Files changed (1) hide show
  1. docs/SUPABASE_INTEGRATION.md +413 -0
docs/SUPABASE_INTEGRATION.md ADDED
@@ -0,0 +1,413 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Integração com Supabase
2
+
3
+ Este documento descreve como configurar e usar a integração com Supabase para armazenamento centralizado de investigações.
4
+
5
+ ## 📋 Visão Geral
6
+
7
+ O **Cidadão.AI Backend** agora suporta Supabase como banco de dados central, permitindo que:
8
+
9
+ 1. **Backend** armazene resultados de investigações diretamente no Supabase
10
+ 2. **Frontend** consuma os dados em tempo real via Supabase Client
11
+ 3. **Row Level Security (RLS)** garanta que usuários só vejam suas próprias investigações
12
+ 4. **Realtime Subscriptions** permitam atualizações em tempo real no frontend
13
+
14
+ ## 🔧 Configuração
15
+
16
+ ### 1. Setup no Supabase Dashboard
17
+
18
+ #### 1.1 Criar Projeto
19
+ 1. Acesse [Supabase Dashboard](https://app.supabase.com)
20
+ 2. Crie um novo projeto ou use um existente
21
+ 3. Aguarde a criação do banco PostgreSQL
22
+
23
+ #### 1.2 Executar Migration
24
+ 1. Vá para **SQL Editor** no dashboard
25
+ 2. Copie o conteúdo de `migrations/supabase/001_create_investigations_table.sql`
26
+ 3. Execute a migration
27
+ 4. Verifique se a tabela `investigations` foi criada
28
+
29
+ #### 1.3 Obter Credenciais
30
+ 1. Vá para **Settings > API**
31
+ 2. Copie as seguintes informações:
32
+ - **Project URL**: `https://[project-id].supabase.co`
33
+ - **Anon Key**: Para acesso público (com RLS)
34
+ - **Service Role Key**: Para backend (bypass RLS)
35
+
36
+ 3. Vá para **Settings > Database > Connection string**
37
+ 4. Copie a **URI** de conexão PostgreSQL
38
+
39
+ ### 2. Configuração do Backend
40
+
41
+ #### 2.1 Variáveis de Ambiente
42
+
43
+ Adicione ao `.env`:
44
+
45
+ ```bash
46
+ # Supabase PostgreSQL Connection
47
+ SUPABASE_DB_URL=postgresql://postgres:[PASSWORD]@db.[PROJECT-ID].supabase.co:5432/postgres
48
+
49
+ # Supabase API Keys (opcional - para Row Level Security)
50
+ SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
51
+ SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
52
+
53
+ # Connection Pool Settings (opcional)
54
+ SUPABASE_MIN_CONNECTIONS=5
55
+ SUPABASE_MAX_CONNECTIONS=20
56
+ ```
57
+
58
+ **Importante**: Use `SUPABASE_SERVICE_ROLE_KEY` para que o backend possa escrever dados sem RLS.
59
+
60
+ #### 2.2 HuggingFace Spaces
61
+
62
+ No HuggingFace Spaces, adicione as variáveis em **Settings > Variables**:
63
+
64
+ ```
65
+ SUPABASE_DB_URL: postgresql://postgres:...
66
+ SUPABASE_SERVICE_ROLE_KEY: eyJhbGci...
67
+ ```
68
+
69
+ ### 3. Instalação de Dependências
70
+
71
+ ```bash
72
+ # Já incluído em requirements.txt
73
+ pip install asyncpg>=0.29.0
74
+ ```
75
+
76
+ ## 🚀 Uso no Backend
77
+
78
+ ### Exemplo 1: Criar Investigação
79
+
80
+ ```python
81
+ from src.services.investigation_service_supabase import investigation_service_supabase
82
+
83
+ # Criar investigação
84
+ investigation = await investigation_service_supabase.create(
85
+ user_id="user-uuid-from-auth",
86
+ query="Contratos com valores acima de R$ 1 milhão",
87
+ data_source="contracts",
88
+ filters={"min_value": 1000000},
89
+ anomaly_types=["price", "vendor"],
90
+ )
91
+
92
+ print(f"Investigation ID: {investigation['id']}")
93
+ ```
94
+
95
+ ### Exemplo 2: Executar Investigação
96
+
97
+ ```python
98
+ # Executar em background
99
+ await investigation_service_supabase.start_investigation(
100
+ investigation_id=investigation['id']
101
+ )
102
+
103
+ # A investigação roda em background e atualiza o Supabase
104
+ # O frontend pode monitorar via realtime subscription
105
+ ```
106
+
107
+ ### Exemplo 3: Consultar Investigações
108
+
109
+ ```python
110
+ # Listar investigações do usuário
111
+ investigations = await investigation_service_supabase.search(
112
+ user_id="user-uuid",
113
+ status="completed",
114
+ limit=10
115
+ )
116
+
117
+ for inv in investigations:
118
+ print(f"{inv['id']}: {inv['query']} - {inv['status']}")
119
+ ```
120
+
121
+ ## 🌐 Uso no Frontend (Next.js)
122
+
123
+ ### 1. Setup do Supabase Client
124
+
125
+ ```typescript
126
+ // lib/supabase.ts
127
+ import { createClient } from '@supabase/supabase-js'
128
+
129
+ const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
130
+ const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
131
+
132
+ export const supabase = createClient(supabaseUrl, supabaseAnonKey)
133
+ ```
134
+
135
+ ### 2. Listar Investigações do Usuário
136
+
137
+ ```typescript
138
+ // pages/investigations.tsx
139
+ import { supabase } from '@/lib/supabase'
140
+
141
+ export async function getInvestigations() {
142
+ const { data, error } = await supabase
143
+ .from('investigations')
144
+ .select('*')
145
+ .order('created_at', { ascending: false })
146
+ .limit(10)
147
+
148
+ if (error) {
149
+ console.error('Error fetching investigations:', error)
150
+ return []
151
+ }
152
+
153
+ return data
154
+ }
155
+ ```
156
+
157
+ ### 3. Monitorar Investigação em Tempo Real
158
+
159
+ ```typescript
160
+ // components/InvestigationMonitor.tsx
161
+ import { useEffect, useState } from 'react'
162
+ import { supabase } from '@/lib/supabase'
163
+
164
+ export function InvestigationMonitor({ investigationId }: { investigationId: string }) {
165
+ const [investigation, setInvestigation] = useState<any>(null)
166
+ const [progress, setProgress] = useState(0)
167
+
168
+ useEffect(() => {
169
+ // Fetch initial state
170
+ const fetchInvestigation = async () => {
171
+ const { data } = await supabase
172
+ .from('investigations')
173
+ .select('*')
174
+ .eq('id', investigationId)
175
+ .single()
176
+
177
+ setInvestigation(data)
178
+ setProgress(data?.progress || 0)
179
+ }
180
+
181
+ fetchInvestigation()
182
+
183
+ // Subscribe to real-time updates
184
+ const channel = supabase
185
+ .channel(`investigation:${investigationId}`)
186
+ .on(
187
+ 'postgres_changes',
188
+ {
189
+ event: 'UPDATE',
190
+ schema: 'public',
191
+ table: 'investigations',
192
+ filter: `id=eq.${investigationId}`,
193
+ },
194
+ (payload) => {
195
+ console.log('Investigation updated:', payload.new)
196
+ setInvestigation(payload.new)
197
+ setProgress(payload.new.progress || 0)
198
+ }
199
+ )
200
+ .subscribe()
201
+
202
+ // Cleanup
203
+ return () => {
204
+ supabase.removeChannel(channel)
205
+ }
206
+ }, [investigationId])
207
+
208
+ return (
209
+ <div>
210
+ <h3>Investigation Status: {investigation?.status}</h3>
211
+ <div>Progress: {Math.round(progress * 100)}%</div>
212
+ <div>Phase: {investigation?.current_phase}</div>
213
+ <div>Anomalies Found: {investigation?.anomalies_found || 0}</div>
214
+
215
+ {investigation?.status === 'completed' && (
216
+ <div>
217
+ <h4>Results</h4>
218
+ <pre>{JSON.stringify(investigation.results, null, 2)}</pre>
219
+ </div>
220
+ )}
221
+ </div>
222
+ )
223
+ }
224
+ ```
225
+
226
+ ### 4. Criar Nova Investigação via API
227
+
228
+ ```typescript
229
+ // services/investigationService.ts
230
+ const API_URL = process.env.NEXT_PUBLIC_API_URL
231
+
232
+ export async function createInvestigation(params: {
233
+ query: string
234
+ data_source: string
235
+ filters?: Record<string, any>
236
+ anomaly_types?: string[]
237
+ }) {
238
+ // Obter token JWT do usuário
239
+ const { data: { session } } = await supabase.auth.getSession()
240
+
241
+ const response = await fetch(`${API_URL}/api/v1/investigations/start`, {
242
+ method: 'POST',
243
+ headers: {
244
+ 'Content-Type': 'application/json',
245
+ 'Authorization': `Bearer ${session?.access_token}`,
246
+ },
247
+ body: JSON.stringify(params),
248
+ })
249
+
250
+ if (!response.ok) {
251
+ throw new Error('Failed to create investigation')
252
+ }
253
+
254
+ return await response.json()
255
+ }
256
+ ```
257
+
258
+ ## 📊 Schema da Tabela
259
+
260
+ ### Campos Principais
261
+
262
+ | Campo | Tipo | Descrição |
263
+ |-------|------|-----------|
264
+ | `id` | UUID | ID único da investigação |
265
+ | `user_id` | UUID | ID do usuário (auth.users) |
266
+ | `query` | TEXT | Query da investigação |
267
+ | `data_source` | VARCHAR | Fonte de dados (contracts, expenses, etc) |
268
+ | `status` | VARCHAR | Status: pending, processing, completed, failed, cancelled |
269
+ | `progress` | FLOAT | Progresso de 0.0 a 1.0 |
270
+ | `current_phase` | VARCHAR | Fase atual (data_retrieval, analysis, etc) |
271
+ | `results` | JSONB | Array de anomalias detectadas |
272
+ | `summary` | TEXT | Resumo da investigação |
273
+ | `confidence_score` | FLOAT | Confiança geral (0.0 a 1.0) |
274
+ | `anomalies_found` | INTEGER | Total de anomalias |
275
+ | `total_records_analyzed` | INTEGER | Total de registros analisados |
276
+
277
+ ### Exemplo de Result (JSONB)
278
+
279
+ ```json
280
+ {
281
+ "anomaly_id": "uuid",
282
+ "type": "price",
283
+ "severity": "high",
284
+ "confidence": 0.92,
285
+ "description": "Preço 45% acima da média",
286
+ "explanation": "Análise de 1.234 contratos similares...",
287
+ "affected_records": [
288
+ {
289
+ "contract_id": "123",
290
+ "value": 1500000,
291
+ "expected_value": 1034000
292
+ }
293
+ ],
294
+ "suggested_actions": [
295
+ "Solicitar justificativa do preço",
296
+ "Comparar com licitações similares"
297
+ ],
298
+ "metadata": {
299
+ "analysis_method": "fft_spectral",
300
+ "comparison_count": 1234
301
+ }
302
+ }
303
+ ```
304
+
305
+ ## 🔒 Segurança (Row Level Security)
306
+
307
+ ### Políticas Ativas
308
+
309
+ 1. **SELECT**: Usuários só veem suas próprias investigações
310
+ 2. **INSERT**: Usuários só criam investigações para si mesmos
311
+ 3. **UPDATE**: Usuários só atualizam suas próprias investigações
312
+ 4. **DELETE**: Usuários só deletam suas próprias investigações
313
+ 5. **Service Role**: Backend com `service_role_key` pode gerenciar tudo
314
+
315
+ ### Testando RLS
316
+
317
+ ```sql
318
+ -- Como usuário autenticado
319
+ SELECT * FROM investigations; -- Vê apenas suas investigações
320
+
321
+ -- Como service_role (backend)
322
+ -- Vê todas as investigações
323
+ ```
324
+
325
+ ## 🐛 Troubleshooting
326
+
327
+ ### Erro: "Connection refused"
328
+
329
+ Verifique se `SUPABASE_DB_URL` está correto:
330
+ ```bash
331
+ psql "postgresql://postgres:[PASSWORD]@db.[PROJECT-ID].supabase.co:5432/postgres"
332
+ ```
333
+
334
+ ### Erro: "Permission denied"
335
+
336
+ - Verifique se RLS está habilitado
337
+ - Use `SUPABASE_SERVICE_ROLE_KEY` no backend
338
+ - Verifique se `user_id` corresponde ao `auth.uid()`
339
+
340
+ ### Investigação não atualiza no frontend
341
+
342
+ - Verifique se Realtime está habilitado no Supabase
343
+ - Vá para **Database > Replication** e habilite `investigations`
344
+ - Verifique se o frontend tem a subscription correta
345
+
346
+ ## 📈 Monitoramento
347
+
348
+ ### Ver estatísticas do usuário
349
+
350
+ ```sql
351
+ SELECT * FROM get_investigation_stats('user-uuid');
352
+ ```
353
+
354
+ ### Ver investigações ativas
355
+
356
+ ```sql
357
+ SELECT id, query, status, progress, current_phase
358
+ FROM investigations
359
+ WHERE status IN ('pending', 'processing')
360
+ ORDER BY created_at DESC;
361
+ ```
362
+
363
+ ### Performance indexes
364
+
365
+ A migration cria 7 indexes otimizados:
366
+ - `user_id` (B-tree)
367
+ - `status` (B-tree)
368
+ - `created_at` (B-tree desc)
369
+ - `user_id, status` (composite)
370
+ - `filters` (GIN - JSONB)
371
+ - `results` (GIN - JSONB)
372
+
373
+ ## 🚢 Deploy
374
+
375
+ ### HuggingFace Spaces
376
+
377
+ ```bash
378
+ # Adicione as variáveis no dashboard
379
+ SUPABASE_DB_URL=postgresql://...
380
+ SUPABASE_SERVICE_ROLE_KEY=eyJhbGci...
381
+
382
+ # O app.py vai usar automaticamente
383
+ ```
384
+
385
+ ### Docker
386
+
387
+ ```yaml
388
+ # docker-compose.yml
389
+ services:
390
+ backend:
391
+ environment:
392
+ - SUPABASE_DB_URL=${SUPABASE_DB_URL}
393
+ - SUPABASE_SERVICE_ROLE_KEY=${SUPABASE_SERVICE_ROLE_KEY}
394
+ ```
395
+
396
+ ## 📚 Recursos
397
+
398
+ - [Supabase Documentation](https://supabase.com/docs)
399
+ - [Supabase Realtime](https://supabase.com/docs/guides/realtime)
400
+ - [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security)
401
+ - [asyncpg Documentation](https://magicstack.github.io/asyncpg/)
402
+
403
+ ## ✅ Checklist de Implementação
404
+
405
+ - [x] Criar serviço `SupabaseService`
406
+ - [x] Criar `InvestigationServiceSupabase`
407
+ - [x] Criar migration SQL
408
+ - [x] Documentar configuração
409
+ - [ ] Executar migration no Supabase
410
+ - [ ] Configurar variáveis de ambiente
411
+ - [ ] Testar criação de investigação
412
+ - [ ] Testar realtime no frontend
413
+ - [ ] Deploy no HuggingFace Spaces