create-genia-os 2.0.0 → 2.1.1
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 +154 -0
- package/bin/index.js +240 -0
- package/package.json +40 -42
- package/template/.claude/CLAUDE.md +215 -0
- package/template/.claude/agent-memory/analyst/MEMORY.md +20 -0
- package/template/.claude/agent-memory/architect/MEMORY.md +20 -0
- package/template/.claude/agent-memory/dev/MEMORY.md +20 -0
- package/template/.claude/agent-memory/devops/MEMORY.md +20 -0
- package/template/.claude/agent-memory/pm/MEMORY.md +20 -0
- package/template/.claude/agent-memory/po/MEMORY.md +20 -0
- package/template/.claude/agent-memory/qa/MEMORY.md +20 -0
- package/template/.claude/agent-memory/reviewer/MEMORY.md +20 -0
- package/template/.claude/agent-memory/sm/MEMORY.md +20 -0
- package/template/.claude/hooks/enforce-git-push-authority.py +70 -0
- package/template/.claude/hooks/precompact-session-digest.cjs +87 -0
- package/template/.claude/hooks/sql-governance.py +65 -0
- package/template/.claude/hooks/synapse-engine.cjs +122 -0
- package/template/.claude/hooks/write-path-validation.py +59 -0
- package/template/.claude/rules/agent-authority.md +39 -0
- package/template/.claude/rules/agent-handoff.md +71 -0
- package/template/.claude/rules/agent-memory.md +61 -0
- package/template/.claude/rules/ids-principles.md +52 -0
- package/template/.claude/rules/mcp-usage.md +49 -0
- package/template/.claude/rules/story-lifecycle.md +87 -0
- package/template/.claude/rules/workflow-execution.md +68 -0
- package/template/.claude/settings.json +58 -0
- package/template/.claude/settings.local.json +14 -0
- package/template/.genia/CONSTITUTION.md +129 -0
- package/template/.genia/contexts/api-patterns.md +134 -0
- package/template/.genia/contexts/nextjs-react.md +210 -0
- package/template/.genia/contexts/projeto.md +18 -0
- package/template/.genia/contexts/supabase.md +152 -0
- package/template/.genia/contexts/whatsapp-cloud.md +176 -0
- package/template/.genia/core-config.yaml +192 -0
- package/template/.genia/development/agents/analyst.md +138 -0
- package/template/.genia/development/agents/architect.md +171 -0
- package/template/.genia/development/agents/dev.md +160 -0
- package/template/.genia/development/agents/devops.md +200 -0
- package/template/.genia/development/agents/pm.md +142 -0
- package/template/.genia/development/agents/po.md +165 -0
- package/template/.genia/development/agents/qa.md +183 -0
- package/template/.genia/development/agents/reviewer.md +198 -0
- package/template/.genia/development/agents/sm.md +230 -0
- package/template/.genia/development/checklists/architecture-review.md +189 -0
- package/template/.genia/development/checklists/pre-commit.md +205 -0
- package/template/.genia/development/checklists/pre-deploy.md +230 -0
- package/template/.genia/development/checklists/qa-gate.md +216 -0
- package/template/.genia/development/checklists/story-dod.md +155 -0
- package/template/.genia/development/tasks/code-review.md +197 -0
- package/template/.genia/development/tasks/criar-prd.md +170 -0
- package/template/.genia/development/tasks/criar-spec.md +188 -0
- package/template/.genia/development/tasks/criar-story.md +185 -0
- package/template/.genia/development/tasks/debug-sistematico.md +230 -0
- package/template/.genia/development/tasks/dev-implement.md +199 -0
- package/template/.genia/development/tasks/qa-review.md +224 -0
- package/template/.genia/development/workflows/brownfield.md +178 -0
- package/template/.genia/development/workflows/delivery.md +208 -0
- package/template/.genia/development/workflows/development.md +189 -0
- package/template/.genia/development/workflows/greenfield.md +166 -0
- package/template/.genia/development/workflows/planning.md +167 -0
- package/template/.genia/development/workflows/qa-loop.md +179 -0
- package/template/.genia/development/workflows/spec-pipeline.md +192 -0
- package/template/.genia/development/workflows/story-development-cycle.md +252 -0
- package/template/.genia/guidelines/clean-code.md +98 -0
- package/template/.genia/guidelines/testing.md +176 -0
- package/template/.genia/skills/design/canvas-design.md +109 -0
- package/template/.genia/skills/design/frontend-design.md +140 -0
- package/template/.genia/skills/dev/mcp-builder.md +172 -0
- package/template/.genia/skills/dev/webapp-testing.md +150 -0
- package/template/.genia/skills/documents/docx.md +153 -0
- package/template/.genia/skills/documents/pdf.md +134 -0
- package/template/.genia/skills/documents/pptx.md +118 -0
- package/template/.genia/skills/documents/xlsx.md +140 -0
- package/template/.synapse/agent-analyst +8 -0
- package/template/.synapse/agent-architect +8 -0
- package/template/.synapse/agent-dev +8 -0
- package/template/.synapse/agent-devops +8 -0
- package/template/.synapse/agent-pm +8 -0
- package/template/.synapse/agent-po +7 -0
- package/template/.synapse/agent-qa +8 -0
- package/template/.synapse/agent-reviewer +7 -0
- package/template/.synapse/agent-sm +7 -0
- package/template/.synapse/constitution +7 -0
- package/template/.synapse/context +8 -0
- package/template/.synapse/global +8 -0
- package/template/.synapse/manifest +14 -0
- package/template/README.md +53 -0
- package/bin/create.js +0 -181
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Contexto: API Patterns
|
|
2
|
+
|
|
3
|
+
> Principios de design de API e tomada de decisao.
|
|
4
|
+
|
|
5
|
+
## Mapa de Conteudo
|
|
6
|
+
|
|
7
|
+
| Topico | Quando Usar |
|
|
8
|
+
|--------|-------------|
|
|
9
|
+
| REST vs GraphQL vs tRPC | Escolher tipo de API |
|
|
10
|
+
| Resource naming, HTTP methods | Design REST |
|
|
11
|
+
| Envelope pattern, error format | Estrutura de resposta |
|
|
12
|
+
| Schema design | GraphQL |
|
|
13
|
+
| Type safety | TS fullstack |
|
|
14
|
+
|
|
15
|
+
## Arvore de Decisao: Escolher Tipo de API
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Projeto → TypeScript monorepo?
|
|
19
|
+
├── Sim → tRPC (type safety end-to-end)
|
|
20
|
+
│
|
|
21
|
+
└── Nao → Clientes diversos?
|
|
22
|
+
├── Sim → REST (universal, cacheable)
|
|
23
|
+
│
|
|
24
|
+
└── Nao → Queries complexas?
|
|
25
|
+
├── Sim → GraphQL (flexivel, self-documenting)
|
|
26
|
+
└── Nao → REST (simples, padrao da industria)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## REST Design
|
|
30
|
+
|
|
31
|
+
### Naming de Recursos
|
|
32
|
+
```
|
|
33
|
+
GET /users # Lista
|
|
34
|
+
GET /users/{id} # Detalhe
|
|
35
|
+
POST /users # Criar
|
|
36
|
+
PUT /users/{id} # Atualizar (completo)
|
|
37
|
+
PATCH /users/{id} # Atualizar (parcial)
|
|
38
|
+
DELETE /users/{id} # Remover
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Status Codes
|
|
42
|
+
| Code | Uso |
|
|
43
|
+
|------|-----|
|
|
44
|
+
| 200 | OK - Sucesso |
|
|
45
|
+
| 201 | Created - Recurso criado |
|
|
46
|
+
| 204 | No Content - Deletado |
|
|
47
|
+
| 400 | Bad Request - Input invalido |
|
|
48
|
+
| 401 | Unauthorized - Nao autenticado |
|
|
49
|
+
| 403 | Forbidden - Sem permissao |
|
|
50
|
+
| 404 | Not Found - Recurso nao existe |
|
|
51
|
+
| 422 | Unprocessable - Validacao falhou |
|
|
52
|
+
| 500 | Internal Error - Erro do servidor |
|
|
53
|
+
|
|
54
|
+
## Formato de Resposta
|
|
55
|
+
|
|
56
|
+
### Sucesso
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"data": { ... },
|
|
60
|
+
"meta": {
|
|
61
|
+
"pagination": { "page": 1, "total": 100 }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Erro
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"error": {
|
|
70
|
+
"code": "VALIDATION_ERROR",
|
|
71
|
+
"message": "Email invalido",
|
|
72
|
+
"details": [
|
|
73
|
+
{ "field": "email", "message": "Formato invalido" }
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Paginacao
|
|
80
|
+
|
|
81
|
+
### Offset-based
|
|
82
|
+
```
|
|
83
|
+
GET /users?page=2&limit=20
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Cursor-based (recomendado para grandes volumes)
|
|
87
|
+
```
|
|
88
|
+
GET /users?cursor=abc123&limit=20
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Versionamento
|
|
92
|
+
|
|
93
|
+
| Metodo | Exemplo | Uso |
|
|
94
|
+
|--------|---------|-----|
|
|
95
|
+
| URI | `/v1/users` | Mais comum, facil |
|
|
96
|
+
| Header | `Accept: application/vnd.api.v1+json` | Mais limpo |
|
|
97
|
+
| Query | `/users?version=1` | Nao recomendado |
|
|
98
|
+
|
|
99
|
+
## Autenticacao
|
|
100
|
+
|
|
101
|
+
| Metodo | Quando Usar |
|
|
102
|
+
|--------|-------------|
|
|
103
|
+
| JWT | SPAs, mobile apps |
|
|
104
|
+
| API Keys | Servicos, integracao |
|
|
105
|
+
| OAuth 2.0 | Login social, terceiros |
|
|
106
|
+
| Session | Apps tradicionais |
|
|
107
|
+
|
|
108
|
+
## Rate Limiting
|
|
109
|
+
|
|
110
|
+
### Headers de Resposta
|
|
111
|
+
```
|
|
112
|
+
X-RateLimit-Limit: 100
|
|
113
|
+
X-RateLimit-Remaining: 95
|
|
114
|
+
X-RateLimit-Reset: 1640995200
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Anti-Patterns
|
|
118
|
+
|
|
119
|
+
| NAO Fazer | Fazer |
|
|
120
|
+
|-----------|-------|
|
|
121
|
+
| `/getUsers` | `GET /users` |
|
|
122
|
+
| `/createUser` | `POST /users` |
|
|
123
|
+
| Retornar 200 para erros | Usar status codes corretos |
|
|
124
|
+
| Expor erros internos | Mensagens genericas |
|
|
125
|
+
| Pular rate limiting | Sempre limitar |
|
|
126
|
+
|
|
127
|
+
## Checklist
|
|
128
|
+
|
|
129
|
+
- [ ] Escolhi estilo de API para o contexto?
|
|
130
|
+
- [ ] Formato de resposta consistente?
|
|
131
|
+
- [ ] Estrategia de versionamento?
|
|
132
|
+
- [ ] Autenticacao definida?
|
|
133
|
+
- [ ] Rate limiting planejado?
|
|
134
|
+
- [ ] Documentacao (OpenAPI)?
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Contexto: Next.js & React
|
|
2
|
+
|
|
3
|
+
> Patterns de performance e boas praticas para Next.js e React.
|
|
4
|
+
|
|
5
|
+
## Eliminando Waterfalls
|
|
6
|
+
|
|
7
|
+
### Problema: Requests Sequenciais
|
|
8
|
+
```typescript
|
|
9
|
+
// ERRADO - waterfall
|
|
10
|
+
const user = await getUser(id);
|
|
11
|
+
const posts = await getPosts(user.id);
|
|
12
|
+
const comments = await getComments(posts);
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Solucao: Parallelizar
|
|
16
|
+
```typescript
|
|
17
|
+
// CORRETO - paralelo
|
|
18
|
+
const [user, posts] = await Promise.all([
|
|
19
|
+
getUser(id),
|
|
20
|
+
getPosts(id)
|
|
21
|
+
]);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Bundle Size Optimization
|
|
25
|
+
|
|
26
|
+
### Code Splitting
|
|
27
|
+
```typescript
|
|
28
|
+
// Dynamic imports
|
|
29
|
+
const HeavyComponent = dynamic(() => import('./Heavy'), {
|
|
30
|
+
loading: () => <Skeleton />
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Tree Shaking
|
|
35
|
+
```typescript
|
|
36
|
+
// ERRADO - importa tudo
|
|
37
|
+
import _ from 'lodash';
|
|
38
|
+
|
|
39
|
+
// CORRETO - importa so o necessario
|
|
40
|
+
import debounce from 'lodash/debounce';
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Server-Side Performance
|
|
44
|
+
|
|
45
|
+
### Streaming
|
|
46
|
+
```typescript
|
|
47
|
+
// app/page.tsx
|
|
48
|
+
export default function Page() {
|
|
49
|
+
return (
|
|
50
|
+
<main>
|
|
51
|
+
<Header />
|
|
52
|
+
<Suspense fallback={<Loading />}>
|
|
53
|
+
<SlowComponent />
|
|
54
|
+
</Suspense>
|
|
55
|
+
</main>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Caching
|
|
61
|
+
```typescript
|
|
62
|
+
// Revalidate a cada hora
|
|
63
|
+
export const revalidate = 3600;
|
|
64
|
+
|
|
65
|
+
// Ou fetch com cache
|
|
66
|
+
const data = await fetch(url, {
|
|
67
|
+
next: { revalidate: 3600 }
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Client-Side Data Fetching
|
|
72
|
+
|
|
73
|
+
### SWR ou React Query
|
|
74
|
+
```typescript
|
|
75
|
+
import useSWR from 'swr';
|
|
76
|
+
|
|
77
|
+
function Profile() {
|
|
78
|
+
const { data, error, isLoading } = useSWR('/api/user', fetcher);
|
|
79
|
+
|
|
80
|
+
if (isLoading) return <Loading />;
|
|
81
|
+
if (error) return <Error />;
|
|
82
|
+
return <User data={data} />;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Re-render Optimization
|
|
87
|
+
|
|
88
|
+
### useMemo para calculos pesados
|
|
89
|
+
```typescript
|
|
90
|
+
const expensiveValue = useMemo(() => {
|
|
91
|
+
return heavyCalculation(data);
|
|
92
|
+
}, [data]);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### useCallback para funcoes
|
|
96
|
+
```typescript
|
|
97
|
+
const handleClick = useCallback(() => {
|
|
98
|
+
doSomething(id);
|
|
99
|
+
}, [id]);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### React.memo para componentes
|
|
103
|
+
```typescript
|
|
104
|
+
const Card = React.memo(function Card({ user }) {
|
|
105
|
+
return <div>{user.name}</div>;
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Rendering Performance
|
|
110
|
+
|
|
111
|
+
### Keys corretas
|
|
112
|
+
```typescript
|
|
113
|
+
// ERRADO - index como key
|
|
114
|
+
{items.map((item, i) => <Item key={i} />)}
|
|
115
|
+
|
|
116
|
+
// CORRETO - id unico
|
|
117
|
+
{items.map(item => <Item key={item.id} />)}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Virtualizacao para listas longas
|
|
121
|
+
```typescript
|
|
122
|
+
import { FixedSizeList } from 'react-window';
|
|
123
|
+
|
|
124
|
+
<FixedSizeList
|
|
125
|
+
height={400}
|
|
126
|
+
itemCount={1000}
|
|
127
|
+
itemSize={35}
|
|
128
|
+
>
|
|
129
|
+
{Row}
|
|
130
|
+
</FixedSizeList>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## JavaScript Performance
|
|
134
|
+
|
|
135
|
+
### Debounce inputs
|
|
136
|
+
```typescript
|
|
137
|
+
const debouncedSearch = useDebouncedCallback(
|
|
138
|
+
(value) => search(value),
|
|
139
|
+
300
|
|
140
|
+
);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Web Workers para calculos pesados
|
|
144
|
+
```typescript
|
|
145
|
+
const worker = new Worker('/worker.js');
|
|
146
|
+
worker.postMessage(heavyData);
|
|
147
|
+
worker.onmessage = (e) => setResult(e.data);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Advanced Patterns
|
|
151
|
+
|
|
152
|
+
### Server Components
|
|
153
|
+
```typescript
|
|
154
|
+
// Componente servidor (default no App Router)
|
|
155
|
+
async function Posts() {
|
|
156
|
+
const posts = await db.posts.findMany(); // Direto no banco!
|
|
157
|
+
return <PostList posts={posts} />;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Server Actions
|
|
162
|
+
```typescript
|
|
163
|
+
async function submitForm(formData: FormData) {
|
|
164
|
+
'use server';
|
|
165
|
+
await db.users.create({ data: formData });
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Parallel Routes
|
|
170
|
+
```typescript
|
|
171
|
+
// app/@dashboard/page.tsx
|
|
172
|
+
// app/@sidebar/page.tsx
|
|
173
|
+
// Carregam em paralelo
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Image Optimization
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import Image from 'next/image';
|
|
180
|
+
|
|
181
|
+
<Image
|
|
182
|
+
src="/hero.jpg"
|
|
183
|
+
width={1200}
|
|
184
|
+
height={600}
|
|
185
|
+
placeholder="blur"
|
|
186
|
+
priority // Para imagens above-the-fold
|
|
187
|
+
/>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Font Optimization
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { Inter } from 'next/font/google';
|
|
194
|
+
|
|
195
|
+
const inter = Inter({
|
|
196
|
+
subsets: ['latin'],
|
|
197
|
+
display: 'swap',
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Checklist de Performance
|
|
202
|
+
|
|
203
|
+
- [ ] Requests em paralelo (Promise.all)?
|
|
204
|
+
- [ ] Code splitting implementado?
|
|
205
|
+
- [ ] Imagens otimizadas (next/image)?
|
|
206
|
+
- [ ] Fontes otimizadas (next/font)?
|
|
207
|
+
- [ ] Caching configurado?
|
|
208
|
+
- [ ] Listas virtualizadas?
|
|
209
|
+
- [ ] Memos apropriados?
|
|
210
|
+
- [ ] Bundle analisado?
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Contexto: {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
> Carregue com: `@load-context {{PROJECT_NAME}}`
|
|
4
|
+
|
|
5
|
+
## Sobre o Projeto
|
|
6
|
+
|
|
7
|
+
- **Nome**: {{PROJECT_NAME}}
|
|
8
|
+
- **Equipe**: {{TEAM_NAME}}
|
|
9
|
+
- **Stack**: {{STACK}}
|
|
10
|
+
|
|
11
|
+
## Recursos Principais
|
|
12
|
+
|
|
13
|
+
_Adicione aqui as informações de contexto do seu projeto:_
|
|
14
|
+
_APIs, autenticação, endpoints, banco de dados, etc._
|
|
15
|
+
|
|
16
|
+
## Links Úteis
|
|
17
|
+
|
|
18
|
+
- GitHub: https://github.com/{{GITHUB_USER}}/{{GITHUB_REPO}}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Contexto: Supabase
|
|
2
|
+
|
|
3
|
+
> Base de conhecimento para integracao com Supabase.
|
|
4
|
+
|
|
5
|
+
## Configuracao
|
|
6
|
+
|
|
7
|
+
### Credenciais
|
|
8
|
+
```
|
|
9
|
+
URL: [ver .env - SUPABASE_URL]
|
|
10
|
+
API Key: [ver .env - SUPABASE_KEY]
|
|
11
|
+
Service Key: [ver .env - SUPABASE_SERVICE_KEY]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Cliente Python
|
|
15
|
+
|
|
16
|
+
### Instalacao
|
|
17
|
+
```bash
|
|
18
|
+
pip install supabase
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Inicializacao
|
|
22
|
+
```python
|
|
23
|
+
from supabase import create_client
|
|
24
|
+
|
|
25
|
+
supabase = create_client(
|
|
26
|
+
supabase_url=os.getenv("SUPABASE_URL"),
|
|
27
|
+
supabase_key=os.getenv("SUPABASE_KEY")
|
|
28
|
+
)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Operacoes CRUD
|
|
32
|
+
|
|
33
|
+
### Select
|
|
34
|
+
```python
|
|
35
|
+
# Todos os registros
|
|
36
|
+
data = supabase.table("tabela").select("*").execute()
|
|
37
|
+
|
|
38
|
+
# Com filtro
|
|
39
|
+
data = supabase.table("tabela").select("*").eq("coluna", valor).execute()
|
|
40
|
+
|
|
41
|
+
# Com ordenacao
|
|
42
|
+
data = supabase.table("tabela").select("*").order("coluna", desc=True).execute()
|
|
43
|
+
|
|
44
|
+
# Com limite
|
|
45
|
+
data = supabase.table("tabela").select("*").limit(10).execute()
|
|
46
|
+
|
|
47
|
+
# Colunas especificas
|
|
48
|
+
data = supabase.table("tabela").select("id, nome, email").execute()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Insert
|
|
52
|
+
```python
|
|
53
|
+
data = supabase.table("tabela").insert({
|
|
54
|
+
"coluna1": "valor1",
|
|
55
|
+
"coluna2": "valor2"
|
|
56
|
+
}).execute()
|
|
57
|
+
|
|
58
|
+
# Bulk insert
|
|
59
|
+
data = supabase.table("tabela").insert([
|
|
60
|
+
{"col": "val1"},
|
|
61
|
+
{"col": "val2"}
|
|
62
|
+
]).execute()
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Update
|
|
66
|
+
```python
|
|
67
|
+
data = supabase.table("tabela").update({
|
|
68
|
+
"coluna": "novo_valor"
|
|
69
|
+
}).eq("id", 123).execute()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Delete
|
|
73
|
+
```python
|
|
74
|
+
data = supabase.table("tabela").delete().eq("id", 123).execute()
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Filtros
|
|
78
|
+
|
|
79
|
+
| Metodo | SQL | Uso |
|
|
80
|
+
|--------|-----|-----|
|
|
81
|
+
| eq | = | .eq("col", val) |
|
|
82
|
+
| neq | != | .neq("col", val) |
|
|
83
|
+
| gt | > | .gt("col", val) |
|
|
84
|
+
| gte | >= | .gte("col", val) |
|
|
85
|
+
| lt | < | .lt("col", val) |
|
|
86
|
+
| lte | <= | .lte("col", val) |
|
|
87
|
+
| like | LIKE | .like("col", "%val%") |
|
|
88
|
+
| ilike | ILIKE | .ilike("col", "%val%") |
|
|
89
|
+
| in_ | IN | .in_("col", [1,2,3]) |
|
|
90
|
+
| is_ | IS | .is_("col", None) |
|
|
91
|
+
|
|
92
|
+
## RPC (Functions)
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
# Chamar funcao
|
|
96
|
+
data = supabase.rpc("nome_funcao", {"param": "valor"}).execute()
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Storage
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
# Upload
|
|
103
|
+
supabase.storage.from_("bucket").upload("path/file.pdf", file_content)
|
|
104
|
+
|
|
105
|
+
# Download
|
|
106
|
+
data = supabase.storage.from_("bucket").download("path/file.pdf")
|
|
107
|
+
|
|
108
|
+
# URL publica
|
|
109
|
+
url = supabase.storage.from_("bucket").get_public_url("path/file.pdf")
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Auth
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
# Sign up
|
|
116
|
+
supabase.auth.sign_up({"email": "...", "password": "..."})
|
|
117
|
+
|
|
118
|
+
# Sign in
|
|
119
|
+
supabase.auth.sign_in_with_password({"email": "...", "password": "..."})
|
|
120
|
+
|
|
121
|
+
# Sign out
|
|
122
|
+
supabase.auth.sign_out()
|
|
123
|
+
|
|
124
|
+
# Get user
|
|
125
|
+
user = supabase.auth.get_user()
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Gotchas
|
|
129
|
+
1. Usar service key para operacoes admin
|
|
130
|
+
2. RLS (Row Level Security) afeta queries com api key
|
|
131
|
+
3. Timestamps em ISO 8601
|
|
132
|
+
4. UUIDs para IDs
|
|
133
|
+
|
|
134
|
+
## Patterns
|
|
135
|
+
|
|
136
|
+
### Upsert
|
|
137
|
+
```python
|
|
138
|
+
data = supabase.table("tabela").upsert({
|
|
139
|
+
"id": 123,
|
|
140
|
+
"coluna": "valor"
|
|
141
|
+
}).execute()
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Count
|
|
145
|
+
```python
|
|
146
|
+
count = supabase.table("tabela").select("*", count="exact").execute()
|
|
147
|
+
print(count.count)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
*Contexto carregado automaticamente quando detectado uso de Supabase.*
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Contexto: WhatsApp Cloud API
|
|
2
|
+
|
|
3
|
+
> Base de conhecimento para integracao com WhatsApp Cloud API (Meta).
|
|
4
|
+
|
|
5
|
+
## Configuracao
|
|
6
|
+
|
|
7
|
+
### Credenciais
|
|
8
|
+
```
|
|
9
|
+
Base URL: https://graph.facebook.com/v21.0
|
|
10
|
+
Phone Number ID: [ver .env - WA_PHONE_NUMBER_ID]
|
|
11
|
+
Access Token: [ver .env - WA_ACCESS_TOKEN]
|
|
12
|
+
Verify Token: [ver .env - WA_VERIFY_TOKEN]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Enviar Mensagens
|
|
16
|
+
|
|
17
|
+
### Mensagem de Texto
|
|
18
|
+
```python
|
|
19
|
+
import requests
|
|
20
|
+
|
|
21
|
+
url = f"https://graph.facebook.com/v21.0/{PHONE_NUMBER_ID}/messages"
|
|
22
|
+
headers = {
|
|
23
|
+
"Authorization": f"Bearer {ACCESS_TOKEN}",
|
|
24
|
+
"Content-Type": "application/json"
|
|
25
|
+
}
|
|
26
|
+
data = {
|
|
27
|
+
"messaging_product": "whatsapp",
|
|
28
|
+
"recipient_type": "individual",
|
|
29
|
+
"to": "5511999999999",
|
|
30
|
+
"type": "text",
|
|
31
|
+
"text": {
|
|
32
|
+
"preview_url": False,
|
|
33
|
+
"body": "Mensagem aqui"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
response = requests.post(url, headers=headers, json=data)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Template
|
|
40
|
+
```python
|
|
41
|
+
data = {
|
|
42
|
+
"messaging_product": "whatsapp",
|
|
43
|
+
"to": "5511999999999",
|
|
44
|
+
"type": "template",
|
|
45
|
+
"template": {
|
|
46
|
+
"name": "nome_template",
|
|
47
|
+
"language": {"code": "pt_BR"},
|
|
48
|
+
"components": [
|
|
49
|
+
{
|
|
50
|
+
"type": "body",
|
|
51
|
+
"parameters": [
|
|
52
|
+
{"type": "text", "text": "valor1"},
|
|
53
|
+
{"type": "text", "text": "valor2"}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Imagem
|
|
62
|
+
```python
|
|
63
|
+
data = {
|
|
64
|
+
"messaging_product": "whatsapp",
|
|
65
|
+
"to": "5511999999999",
|
|
66
|
+
"type": "image",
|
|
67
|
+
"image": {
|
|
68
|
+
"link": "https://url-da-imagem.jpg"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Documento
|
|
74
|
+
```python
|
|
75
|
+
data = {
|
|
76
|
+
"messaging_product": "whatsapp",
|
|
77
|
+
"to": "5511999999999",
|
|
78
|
+
"type": "document",
|
|
79
|
+
"document": {
|
|
80
|
+
"link": "https://url-do-documento.pdf",
|
|
81
|
+
"filename": "arquivo.pdf"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Botoes Interativos
|
|
87
|
+
```python
|
|
88
|
+
data = {
|
|
89
|
+
"messaging_product": "whatsapp",
|
|
90
|
+
"to": "5511999999999",
|
|
91
|
+
"type": "interactive",
|
|
92
|
+
"interactive": {
|
|
93
|
+
"type": "button",
|
|
94
|
+
"body": {"text": "Escolha uma opcao:"},
|
|
95
|
+
"action": {
|
|
96
|
+
"buttons": [
|
|
97
|
+
{"type": "reply", "reply": {"id": "btn1", "title": "Opcao 1"}},
|
|
98
|
+
{"type": "reply", "reply": {"id": "btn2", "title": "Opcao 2"}}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Webhook
|
|
106
|
+
|
|
107
|
+
### Verificacao
|
|
108
|
+
```python
|
|
109
|
+
@app.get("/webhook")
|
|
110
|
+
def verify_webhook(hub_mode: str, hub_verify_token: str, hub_challenge: str):
|
|
111
|
+
if hub_mode == "subscribe" and hub_verify_token == VERIFY_TOKEN:
|
|
112
|
+
return int(hub_challenge)
|
|
113
|
+
return {"error": "Invalid token"}, 403
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Receber Mensagens
|
|
117
|
+
```python
|
|
118
|
+
@app.post("/webhook")
|
|
119
|
+
def receive_message(payload: dict):
|
|
120
|
+
entry = payload.get("entry", [{}])[0]
|
|
121
|
+
changes = entry.get("changes", [{}])[0]
|
|
122
|
+
value = changes.get("value", {})
|
|
123
|
+
|
|
124
|
+
messages = value.get("messages", [])
|
|
125
|
+
for msg in messages:
|
|
126
|
+
from_number = msg.get("from")
|
|
127
|
+
msg_type = msg.get("type")
|
|
128
|
+
|
|
129
|
+
if msg_type == "text":
|
|
130
|
+
text = msg.get("text", {}).get("body")
|
|
131
|
+
elif msg_type == "interactive":
|
|
132
|
+
button_reply = msg.get("interactive", {}).get("button_reply", {})
|
|
133
|
+
button_id = button_reply.get("id")
|
|
134
|
+
|
|
135
|
+
return {"status": "ok"}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Status de Mensagem
|
|
139
|
+
|
|
140
|
+
### Marcar como Lida
|
|
141
|
+
```python
|
|
142
|
+
data = {
|
|
143
|
+
"messaging_product": "whatsapp",
|
|
144
|
+
"status": "read",
|
|
145
|
+
"message_id": "wamid.xxx"
|
|
146
|
+
}
|
|
147
|
+
requests.post(url, headers=headers, json=data)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Gotchas
|
|
151
|
+
1. Telefone com codigo do pais (5511...)
|
|
152
|
+
2. Templates precisam ser aprovados
|
|
153
|
+
3. Janela de 24h para mensagens normais
|
|
154
|
+
4. Fora da janela, so templates
|
|
155
|
+
5. Media precisa ser URL publica ou upload
|
|
156
|
+
|
|
157
|
+
## Rate Limits
|
|
158
|
+
- 80 mensagens/segundo (Business)
|
|
159
|
+
- 1000 mensagens/dia (free tier)
|
|
160
|
+
|
|
161
|
+
## Templates
|
|
162
|
+
|
|
163
|
+
### Criar Template
|
|
164
|
+
Via Business Manager ou API
|
|
165
|
+
|
|
166
|
+
### Estrutura
|
|
167
|
+
```
|
|
168
|
+
Header (opcional): texto, imagem, documento, video
|
|
169
|
+
Body (obrigatorio): texto com {{1}}, {{2}}...
|
|
170
|
+
Footer (opcional): texto
|
|
171
|
+
Buttons (opcional): ate 3 botoes
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
*Contexto carregado automaticamente quando detectado uso de WhatsApp.*
|