# Ktalogu — Estado Atual + Deploy (VPS/Apache) + Próximos Passos

Este arquivo serve para retomar o trabalho em outro projeto/sessão (Trae/Chat) sem perder contexto.

## 1) Visão Geral

- Produto: **Ktalogu**
- Objetivo: catálogo público por usuário, com **admin** separado e **vitrine pública**.
- Domínios:
  - Público: **ktalogu.com.br**
  - Admin: **admin.ktalogu.com.br**
- Hospedagem: **VPS Hostinger** (Brasil/SP)
- SO: **Ubuntu 22.04 LTS**
- Web server: **Apache2** (sem nginx).
- Banco/autenticação: **Supabase** (mesmo projeto do LazzerPro, por enquanto).
- Estratégia: **monorepo Node/Next** com 2 apps (public/admin) e Apache fazendo reverse proxy nas portas internas.

## 2) Estrutura do projeto Ktalogu

Diretório na VPS:
- `/var/www/ktalogu`

Repositório Git:
- `git@github.com:natanrocha95/ktalogu.git` (privado)

Monorepo com npm workspaces:
- `apps/public` → Next.js (vitrine pública), porta **3001**
- `apps/admin`  → Next.js (painel admin), porta **3002**
- `packages/shared` → ainda vazio

Arquivo raiz `/var/www/ktalogu/package.json`:
- `workspaces`: `apps/*`, `packages/*`
- scripts importantes:
  - `dev:public` → `next dev` (apps/public) porta 3001
  - `dev:admin`  → `next dev` (apps/admin) porta 3002
  - `build` → build dos dois apps
  - `start:public` / `start:admin` → para produção (serão usados com pm2)

Node na VPS:
- `node -v` → v20.19.6
- `npm -v` → 10.8.2
- `pm2 -v` → 6.0.14

## 3) Supabase

Em ambos apps existem:

- `.env.local` com:
  - `NEXT_PUBLIC_SUPABASE_URL=...`
  - `NEXT_PUBLIC_SUPABASE_ANON_KEY=...`

Arquivo de client:
- `apps/public/src/lib/supabase.ts`
- `apps/admin/src/lib/supabase.ts`

Conteúdo (resumo):

```ts
import { createClient } from "@supabase/supabase-js";

const url = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const anon = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;

export const supabase = createClient(url, anon);
```

**Importante:** `.env.local` NÃO deve ser commitado.

### Assinatura pela Cakto (admin)

A rota oficial dos avisos de pagamento é `https://admin.ktalogu.com.br/api/webhooks/cakto`
(produto "Ktalogu Gestão - Gráficas, Brindes e Personalizados", ofertas `b9jcqix` mensal e
`gvndd3a` anual). O `.env.local` do **admin** precisa de:

- `CAKTO_WEBHOOK_SECRET` — o `secret` que a Cakto devolve ao criar o webhook. Sem ele a rota
  responde 503, e o link de pagamento do painel sai sem a marca da empresa.
- `CAKTO_CLIENT_ID` e `CAKTO_CLIENT_SECRET` — chave da API da Cakto (escopo de leitura de
  webhooks). Com elas, o servidor confere o histórico de entregas a cada 6 horas e aplica o
  que não chegou. Sem elas, só o reprocessamento de avisos que falharam roda.
- `OWNER_ACCESS_EMAILS` — e-mails (separados por vírgula) que podem usar `/owner/acesso-manual`.
  Sem ela, ninguém usa aquela tela.

O webhook antigo da Cakto (`app.lazzerpro.com/api/webhook-cakto.php`) continua cuidando dos
planos antigos e não deve ser mexido.

### Assistentes e Apps: o conector MCP (admin)

O endereço que o cliente cola no Claude, no ChatGPT, no Codex ou no Cursor é
`https://mcp.ktalogu.com.br/mcp`. Ele chega no mesmo servidor do admin (porta 3002): o
`proxy.ts` reconhece o endereço `mcp.` e só serve ali o conector (`/`, `/mcp` e `/api/mcp`) e
as portas do login por botão (`/.well-known/oauth-protected-resource`,
`/.well-known/oauth-authorization-server`, `/oauth/register`, `/oauth/token`, `/oauth/revoke`).
O painel não abre por esse endereço. A tela de permitir (`/oauth/authorize`) mora no endereço do
painel (`admin.`), onde a pessoa está logada.

Há dois jeitos de ligar um assistente:

- **Botão Conectar** (Claude, ChatGPT, e também Claude Code, Codex e Cursor): a pessoa cola o
  endereço, clica em Conectar, entra na tela da Ktalogu e permite. Por trás é OAuth 2.1 com PKCE,
  servidor de permissão próprio dentro do admin (`src/lib/assistentes/oauth/`). Registro de
  programa só para retornos de uma lista fechada; identidade publicada (CIMD) só dos documentos
  do Claude, do ChatGPT/Codex e do VS Code. Migrations 155 e 157.
- **Chave de acesso**: criada na aba, colada no programa que pede uma.

O que precisa existir fora do código:

- **DNS (Cloudflare):** um registro `mcp`, igual ao do `admin` (mesmo destino, nuvem laranja ligada).
- **Apache:** o vhost do admin atendendo também `mcp.ktalogu.com.br` (`ServerAlias`), com as
  mesmas regras do admin.
- **`.env.local` do admin:**
  - `ASSISTENTES_LIBERADOS` — para quem o recurso está aberto. Vazia ou ausente: fechado para
    todo mundo, e a aba "Assistentes e Apps" nem aparece. Lista de ids separados por vírgula
    (id da empresa ou do dono): só essas empresas. `todas`: aberto para todas.
  - `MCP_BASE_URL` (opcional) — o endereço do conector, mostrado na aba e anunciado aos
    programas. Sem ela, sai do endereço do painel trocando `admin.` por `mcp.` e somando `/mcp`
    (endereço seco também ganha `/mcp`: com caminho, nenhum programa tropeça na barra do fim).
  - `ASSISTENTES_SEGREDO` — **definir antes de abrir para clientes**: um texto longo e aleatório
    (por exemplo, a saída de `openssl rand -hex 32`). Assina os códigos de confirmação, marca as
    chaves e deriva os acessos e renovações do botão Conectar. Sem ela, tudo isso deriva da
    `SUPABASE_SERVICE_ROLE_KEY`, e quem tiver essa chave consegue forjar acessos. Trocar o
    segredo depois é seguro: as conexões continuam renovando sozinhas.

Depois de mexer nessas variáveis é preciso reiniciar o admin (`pm2 restart`), porque elas são
lidas quando o servidor sobe.

No Cloudflare, o endereço `mcp.` não pode ter regra que guarde em cache ou bloqueie
`/.well-known/` e `/oauth/` (as respostas já saem com `no-store`), nem desafio de robô: quem
chama ali são os servidores do Claude e do ChatGPT, não uma pessoa.

Limites que valem saber: 120 pedidos por minuto por chave, com um comando por pedido; 30
chaves erradas por minuto por endereço (depois disso o endereço espera um minuto); pedidos de
até 256 KB; cada pessoa tem até 10 chaves ativas e cria no máximo 30 por dia; o registro de
atividade guarda seis meses.

A sessão de cada pessoa é criada uma vez por subida do servidor e renovada de hora em hora,
então logo depois de um deploy cada pessoa que usa um assistente gera um link de entrada novo.
O Supabase aceita perto de 30 trocas dessas a cada 5 minutos por servidor: é folga grande para
hoje, e o ponto a rever quando houver centenas de pessoas usando ao mesmo tempo. Também por
causa do deploy: uma prévia de criação feita antes de o servidor subir precisa ser pedida de
novo, porque o código de confirmação dela deixa de valer.

O freio de chave errada identifica o endereço de quem chama pelo cabeçalho do Cloudflare. Ele
só é confiável se o servidor do admin (porta 3002) não aceitar conexão direta de fora, só a que
vem pelo Apache.

**Importação da loja antiga** (`ler_loja`, `importar_produtos`, `ver_importacao`): o servidor do
admin lê sites de fora e baixa fotos. Ele nunca busca endereço interno (localhost, rede privada,
o serviço de metadados da nuvem), nem quando um nome de fora aponta para dentro: o IP é conferido
na hora de conectar. As fotos vão para uma fila no banco (`importacao_fotos`), baixadas duas de
cada vez, reduzidas para até 1600 px em WebP e guardadas no bucket `products`; um deploy no meio
não perde nada, a fila volta a andar na próxima importação, consulta ou abertura da aba. Limites
por empresa: 1000 produtos e 8000 fotos por dia, 1500 páginas lidas por hora. Nada a configurar
em produção. `ASSISTENTES_IMPORTACAO_LOCAL=1` só serve na máquina de quem desenvolve (deixa ler a
loja de mentira em 127.0.0.1) e é ignorada quando `NODE_ENV=production`.

Para testar na máquina de desenvolvimento, com o admin de pé na porta 3002 (todos rodam na
Aurora, a empresa de demonstração, apagam o que criaram e terminam dizendo "TUDO CERTO"):

- `node .cache/testar-mcp.mjs --gravar` — o conector com chave;
- `node .cache/testar-conectar.mjs [--identidade]` — o botão Conectar de ponta a ponta;
- `node .cache/testar-produtos-mcp.mjs` — cadastrar e alterar produto;
- `node .cache/testar-personalizacoes-mcp.mjs` — mensagens do WhatsApp, observação padrão e
  desfazer (devolve o perfil da Aurora como estava);
- `node .cache/testar-importacao-mcp.mjs` — trazer produtos de outra loja, com fotos, variações,
  faixas e os bloqueios de endereço interno (pede `ASSISTENTES_IMPORTACAO_LOCAL=1` no `.env.local`
  do admin); `node .cache/fotos-importacao.mjs` faz o mesmo pela tela (Publicar e Apagar lote).

A pasta `.cache` não vai para o repositório: os roteiros moram só na máquina de quem desenvolve.

## 4) Status atual da UI

### Admin (`apps/admin`)

- `src/app/page.tsx` foi substituído por uma tela de **login** com:
  - email/senha
  - `supabase.auth.signInWithPassword`
  - mensagem "Login OK." quando autentica
- Testado em DEV:
  - `npm run dev:admin`
  - acessível em `http://72.61.217.111:3002`

### Public (`apps/public`)

- Projeto Next.js gerado, UI ainda padrão (tela inicial do Next).
- Testado em DEV:
  - `npm run dev:public`
  - acessível em `http://72.61.217.111:3001`

## 5) Infra do Apache/LazzerPro (não mexer por enquanto)

LazzerPro:
- VHosts existentes:
  - `app.lazzerpro.com` (`app.conf` / `app-le-ssl.conf`)
- DocumentRoot: `/var/www/lazzerproapp/public`
- SSL via Let’s Encrypt já configurado.

Ktalogu ainda NÃO tem VirtualHosts criados. Plano futuro:

- `ktalogu.com.br` → proxy para `127.0.0.1:3001`
- `admin.ktalogu.com.br` → proxy para `127.0.0.1:3002`
- SSL com:
  - `certbot --apache -d ktalogu.com.br -d www.ktalogu.com.br -d admin.ktalogu.com.br`

## 6) Próximos passos recomendados

1. **UI Admin**
   - Criar `/dashboard` com:
     - bloco de configuração do catálogo (nome, slug, logo, cores, WhatsApp)
     - bloco de “Gerenciar produtos”.
   - Criar `/products` com:
     - lista de produtos (mock) e formulário de criação/edição.

2. **UI Pública**
   - Landing `/`
   - Rota `/<slug>` com:
     - header com logo/nome
     - lista de produtos (mock)
     - carrinho (state + localStorage)
     - botão “Finalizar no WhatsApp” montando mensagem de pedido.

3. **Banco (Supabase)**
   - Tabelas: `catalogs`, `products`, `product_images`.
   - RLS:
     - admin: `user_id = auth.uid()` (CRUD)
     - público: apenas `is_active = true`.

4. **Produção**
   - Configurar PM2:
     - `ktalogu-public` → `npm run start:public`
     - `ktalogu-admin` → `npm run start:admin`
   - Criar vhosts do Apache para ktalogu/admin.
   - SSL com certbot.

5. **CI/CD**
   - GitHub Actions: a cada `push` em `main`, conectar via SSH na VPS, rodar:
     - `git pull`
     - `npm ci`
     - `npm run build`
     - `pm2 reload`

---
