fix(kit): a chave de IA deixa de ser obrigatória — instala e cadastra depois pela tela

`docs/deploy-selfhost` promete, na lista de campos do `.env`: "deixe vazio e
cadastre a chave depois em /app/ai/credentials — fica cifrada no banco". O
runtime concorda: em `lib/env.ts` as três chaves são
`z.string().optional().default("")`, e faltar todas gera apenas um `warn`, não
erro. O instalador, não — exigia uma chave que PASSASSE numa chamada real ao
provedor, e sem ela a instalação morria na Fase 2/4. Na prática, não havia
caminho para subir o produto sem antes abrir conta num provedor de IA, mesmo o
produto subindo perfeitamente sem ela (issue #670).

O campo do provedor ESCOLHIDO passa a ser `opcional` — o mesmo mecanismo que o
`CAMPO_OPENAI_EXTRA` (áudio e base de conhecimento) já usava. Quem digita uma
chave continua com a validação na hora, chamada real incluída; o que mudou é
que pular deixou de ser erro.

E pular não pode terminar em silêncio: a tela final ganha `pendencia_da_ia`,
que nomeia o provedor escolhido e o caminho exato de cadastro (IA ›
Credenciais, cifrado no banco) — na tela que a pessoa lê inteira, não no meio
de um log de dez minutos. A pendência só aparece quando NENHUMA credencial de
ambiente existe (nem o AI Gateway, que tem precedência na resolução do chat):
quem já tem a chave não vê aviso nenhum.

═══ O TESTE ═══

`hostgator-setup-kit/test-validators.sh` ganha um cenário que roda o
`install.sh --yes` REAL (VPS dublê) com um `.env` sem NENHUMA chave de IA e
exige a tela final, o `.env` inteiro e o aviso com o caminho de volta. O
controle oposto, com a chave presente, exige que o aviso NÃO apareça — sem ele,
um `pendencia_da_ia` que imprimisse sempre passaria e viraria ruído em toda
instalação. Com o `opcional` removido do campo, o instalador morre em "Falta
ANTHROPIC_API_KEY (modo --yes exige .env preenchido)" e é a primeira asserção
do cenário que fica vermelha.

Ref: #670
This commit is contained in:
webtecnica
2026-09-12 01:43:07 -03:00
parent e142504dae
commit bfa2e49f7d
14 changed files with 196 additions and 22 deletions
+3 -2
View File
@@ -109,8 +109,9 @@ Se precisar do modo sem perguntas (`--yes` com `.env` pronto), duas armadilhas m
- escreva `SENTRY_DSN=off` se a pessoa **não** quer enviar relatórios de erro. Com o `.env` copiado
do exemplo, a pergunta de telemetria não aparece e a instalação sai enviando (issue #668).
Também medido (issue #670): o instalador **exige** uma chave de IA válida, mesmo que a documentação
diga que dá para deixar vazia e cadastrar depois. Enquanto isso não muda, peça a chave antes.
A chave de IA é **opcional** (issue #670, resolvida): dá para instalar sem conta em provedor de IA
e cadastrar depois em IA › Credenciais — a tela final da instalação lembra quem pulou, com o
caminho. Se a pessoa já tiver a chave em mãos, ótimo: ela é validada na hora.
### 6. Depois do "Instalação concluída!"
@@ -21,7 +21,6 @@ a do provedor escolhido. No modo `--yes`, sem escolha, o padrão é Anthropic.
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | anon key (Settings › API) | lê o papel dentro da chave e testa |
| `SUPABASE_SERVICE_ROLE_KEY` | service_role key — **secreta** | idem, contra a admin API |
| `SUPABASE_DB_URL` | connection string **Session pooler, modo URI** (Settings › Database) — secreta | recusa `[YOUR-PASSWORD]`, recusa host `db.<ref>.supabase.co` (Direct, só IPv6), confere que é o mesmo projeto da URL, e **abre uma conexão de verdade** |
| chave do provedor de IA | `ANTHROPIC_API_KEY` (`sk-ant-`), `OPENROUTER_API_KEY` (`sk-or-`) ou `OPENAI_API_KEY` (`sk-`) | prefixo + chamada real ao provedor (uma chave sem crédito passa; "recusada" é 401) |
| `OWNER_EMAIL` | e-mail do primeiro admin (o dono) | formato |
| `OWNER_PASSWORD` | senha do dono — secreta | mínimo 8 caracteres |
@@ -33,6 +32,7 @@ projeto** e preenche as quatro sozinho (`hostgator-setup-kit/supabase-provision.
| campo | se pular |
|---|---|
| `SUPABASE_ACCESS_TOKEN` (token pessoal, **não fica salvo**) | projeto criado à mão (4 cópias); os e-mails de acesso saem no modelo em inglês do Supabase e o **Site URL fica `localhost:3000`** — "esqueci minha senha", confirmação de cadastro e aceite de convite chegam com link quebrado até alguém configurar Authentication › URL Configuration (`Site URL = https://DOMÍNIO`, `Redirect = https://DOMÍNIO/auth/confirm`). O instalador imprime essa pendência no fim; `hostgator-setup-kit/marca-emails.sh` resolve depois, com o token |
| **chave do provedor escolhido** (`ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY` ou `OPENAI_API_KEY`) | o agente **não responde** até alguém cadastrar a chave em **IA › Credenciais** — cifrada no banco, sem mexer no `.env` e sem reiniciar. A tela final da instalação lembra o caminho. Quem digita uma chave tem ela validada na hora (prefixo + chamada real ao provedor: uma chave sem crédito passa; "recusada" é 401) |
| `OPENAI_API_KEY` extra (quando a IA não é OpenAI) | o agente **não ouve áudio** nem indexa/consulta a base de conhecimento até alguém cadastrar a chave da OpenAI em IA › Credenciais (não precisa mexer no `.env`) |
| `APP_NAME` (padrão `DeskcommCRM`) | é a **semente** do nome; depois muda em Configurações › Marca |
| `APP_LOCALE` (1 = Português, 2 = Español) | grava o idioma da organização |
@@ -109,9 +109,11 @@ o baseline de hoje deduplica sozinho ao atualizar (`update.sh`), depois reinicie
## Instalação e atualização
**Instalador exige chave de IA "válida" e para** (issue #670): a documentação diz que dá para
deixar vazia e cadastrar depois, mas o instalador não aceita. Enquanto isso não muda, consiga a
chave antes (OpenRouter é o caminho mais rápido de criar).
**Instalador exigia chave de IA "válida" e parava** (issue #670, **resolvida**): a documentação
dizia que dava para deixar vazia e cadastrar depois, mas o instalador não aceitava — era preciso
conseguir a chave antes (OpenRouter é o caminho mais rápido de criar). Agora a chave é opcional:
dá para instalar sem ela e cadastrar depois em IA › Credenciais; a tela final lembra quem pulou.
Vale para quem atualizar para a versão seguinte.
**Telemetria ligada sem ninguém escolher** (issue #668): acontece quando o `.env` foi copiado do
exemplo. Para desligar: `SENTRY_DSN=off` no `.env` e `docker compose -f docker-compose.prod.yml up -d`.
@@ -5,7 +5,7 @@ Todos vivem em `hostgator-setup-kit/` e rodam **de dentro da VPS**, a partir da
| script | quando a pessoa diz | o que faz | como ler a saída |
|---|---|---|---|
| `install.sh` | "instala", "troca a configuração", "coloquei um dado errado" | instala do zero **ou** re-roda sobre o que existe (idempotente): retoma respostas, corrige pelo número da conferência, re-aplica o schema, sobe tudo | termina em "Instalação concluída!" com a pendência de e-mails (se sem token). `--yes` = sem perguntas, exige `.env` completo |
| `install.sh` | "instala", "troca a configuração", "coloquei um dado errado" | instala do zero **ou** re-roda sobre o que existe (idempotente): retoma respostas, corrige pelo número da conferência, re-aplica o schema, sobe tudo | termina em "Instalação concluída!" com a pendência de e-mails (se sem token). `--yes` = sem perguntas, exige `.env` preenchido — a chave de IA pode ficar vazia |
| `healthcheck.sh` | "está tudo no ar?", "o site caiu?" | lista os contêineres, chama `/api/v1/health` **de dentro** do contêiner do app, confere o cron do agente e o log dele | `✓ app saudável` se o JSON traz `"status":"ok"`; `⚠` nomeia o subsistema (supabase/redis/waha) degradado |
| `diagnostico.sh` | "o agente parou de melhorar", "a versão está solta", "atualizei e nada mudou" | **só lê** (nada de escrever, puxar ou reiniciar): diz se a instalação foi afetada pelo worker que nunca era atualizado. Roda até avulso: `curl -fsSL https://raw.githubusercontent.com/melgarafael/DeskcommCRM/main/hostgator-setup-kit/diagnostico.sh \| bash` | código de saída 1 = afetada; explica o conserto |
| `update.sh` | "atualiza", "tem versão nova?" | confere se há versão nova (senão sai na hora), **faz backup antes**, puxa o código, re-aplica o `baseline.sql` (idempotente e auto-curativo; muitos "já existe" são esperados), puxa as imagens da tag, confere a saúde | código 3 = recusou e **nada foi tocado** (ex.: sem internet para confirmar o que é mais novo, ou a versão pedida é anterior). `--force` volta no tempo de propósito; `--to <tag>` fixa uma versão; `--skip-backup` não recomendado |
+19
View File
@@ -0,0 +1,19 @@
---
impacto: nada_mudou
secao: corrigido
titulo: A instalação deixa de exigir chave de IA — dá para cadastrar depois pela tela
---
O instalador exigia uma chave de IA que **passasse numa chamada real** ao
provedor: sem ela, a instalação morria na Fase 2/4. Só que a documentação
(`docs/deploy-selfhost`) sempre prometeu outra coisa — *"deixe vazio e cadastre
a chave depois"* —, e o próprio sistema concorda com a doc: faltar todas as
chaves é um aviso, não um erro.
Agora o campo é opcional de verdade: dá para instalar sem abrir conta em
provedor de IA e cadastrar a chave depois pela tela, em **IA › Credenciais**,
onde ela fica cifrada no banco. A tela final da instalação lembra quem pulou o
passo, com o caminho exato.
Quem digita uma chave continua com ela validada na hora — o que mudou é que
pular deixou de ser erro.
+3 -2
View File
@@ -109,8 +109,9 @@ Se precisar do modo sem perguntas (`--yes` com `.env` pronto), duas armadilhas m
- escreva `SENTRY_DSN=off` se a pessoa **não** quer enviar relatórios de erro. Com o `.env` copiado
do exemplo, a pergunta de telemetria não aparece e a instalação sai enviando (issue #668).
Também medido (issue #670): o instalador **exige** uma chave de IA válida, mesmo que a documentação
diga que dá para deixar vazia e cadastrar depois. Enquanto isso não muda, peça a chave antes.
A chave de IA é **opcional** (issue #670, resolvida): dá para instalar sem conta em provedor de IA
e cadastrar depois em IA › Credenciais — a tela final da instalação lembra quem pulou, com o
caminho. Se a pessoa já tiver a chave em mãos, ótimo: ela é validada na hora.
### 6. Depois do "Instalação concluída!"
@@ -21,7 +21,6 @@ a do provedor escolhido. No modo `--yes`, sem escolha, o padrão é Anthropic.
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | anon key (Settings › API) | lê o papel dentro da chave e testa |
| `SUPABASE_SERVICE_ROLE_KEY` | service_role key — **secreta** | idem, contra a admin API |
| `SUPABASE_DB_URL` | connection string **Session pooler, modo URI** (Settings › Database) — secreta | recusa `[YOUR-PASSWORD]`, recusa host `db.<ref>.supabase.co` (Direct, só IPv6), confere que é o mesmo projeto da URL, e **abre uma conexão de verdade** |
| chave do provedor de IA | `ANTHROPIC_API_KEY` (`sk-ant-`), `OPENROUTER_API_KEY` (`sk-or-`) ou `OPENAI_API_KEY` (`sk-`) | prefixo + chamada real ao provedor (uma chave sem crédito passa; "recusada" é 401) |
| `OWNER_EMAIL` | e-mail do primeiro admin (o dono) | formato |
| `OWNER_PASSWORD` | senha do dono — secreta | mínimo 8 caracteres |
@@ -33,6 +32,7 @@ projeto** e preenche as quatro sozinho (`hostgator-setup-kit/supabase-provision.
| campo | se pular |
|---|---|
| `SUPABASE_ACCESS_TOKEN` (token pessoal, **não fica salvo**) | projeto criado à mão (4 cópias); os e-mails de acesso saem no modelo em inglês do Supabase e o **Site URL fica `localhost:3000`** — "esqueci minha senha", confirmação de cadastro e aceite de convite chegam com link quebrado até alguém configurar Authentication › URL Configuration (`Site URL = https://DOMÍNIO`, `Redirect = https://DOMÍNIO/auth/confirm`). O instalador imprime essa pendência no fim; `hostgator-setup-kit/marca-emails.sh` resolve depois, com o token |
| **chave do provedor escolhido** (`ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY` ou `OPENAI_API_KEY`) | o agente **não responde** até alguém cadastrar a chave em **IA › Credenciais** — cifrada no banco, sem mexer no `.env` e sem reiniciar. A tela final da instalação lembra o caminho. Quem digita uma chave tem ela validada na hora (prefixo + chamada real ao provedor: uma chave sem crédito passa; "recusada" é 401) |
| `OPENAI_API_KEY` extra (quando a IA não é OpenAI) | o agente **não ouve áudio** nem indexa/consulta a base de conhecimento até alguém cadastrar a chave da OpenAI em IA › Credenciais (não precisa mexer no `.env`) |
| `APP_NAME` (padrão `DeskcommCRM`) | é a **semente** do nome; depois muda em Configurações › Marca |
| `APP_LOCALE` (1 = Português, 2 = Español) | grava o idioma da organização |
@@ -109,9 +109,11 @@ o baseline de hoje deduplica sozinho ao atualizar (`update.sh`), depois reinicie
## Instalação e atualização
**Instalador exige chave de IA "válida" e para** (issue #670): a documentação diz que dá para
deixar vazia e cadastrar depois, mas o instalador não aceita. Enquanto isso não muda, consiga a
chave antes (OpenRouter é o caminho mais rápido de criar).
**Instalador exigia chave de IA "válida" e parava** (issue #670, **resolvida**): a documentação
dizia que dava para deixar vazia e cadastrar depois, mas o instalador não aceitava — era preciso
conseguir a chave antes (OpenRouter é o caminho mais rápido de criar). Agora a chave é opcional:
dá para instalar sem ela e cadastrar depois em IA › Credenciais; a tela final lembra quem pulou.
Vale para quem atualizar para a versão seguinte.
**Telemetria ligada sem ninguém escolher** (issue #668): acontece quando o `.env` foi copiado do
exemplo. Para desligar: `SENTRY_DSN=off` no `.env` e `docker compose -f docker-compose.prod.yml up -d`.
@@ -5,7 +5,7 @@ Todos vivem em `hostgator-setup-kit/` e rodam **de dentro da VPS**, a partir da
| script | quando a pessoa diz | o que faz | como ler a saída |
|---|---|---|---|
| `install.sh` | "instala", "troca a configuração", "coloquei um dado errado" | instala do zero **ou** re-roda sobre o que existe (idempotente): retoma respostas, corrige pelo número da conferência, re-aplica o schema, sobe tudo | termina em "Instalação concluída!" com a pendência de e-mails (se sem token). `--yes` = sem perguntas, exige `.env` completo |
| `install.sh` | "instala", "troca a configuração", "coloquei um dado errado" | instala do zero **ou** re-roda sobre o que existe (idempotente): retoma respostas, corrige pelo número da conferência, re-aplica o schema, sobe tudo | termina em "Instalação concluída!" com a pendência de e-mails (se sem token). `--yes` = sem perguntas, exige `.env` preenchido — a chave de IA pode ficar vazia |
| `healthcheck.sh` | "está tudo no ar?", "o site caiu?" | lista os contêineres, chama `/api/v1/health` **de dentro** do contêiner do app, confere o cron do agente e o log dele | `✓ app saudável` se o JSON traz `"status":"ok"`; `⚠` nomeia o subsistema (supabase/redis/waha) degradado |
| `diagnostico.sh` | "o agente parou de melhorar", "a versão está solta", "atualizei e nada mudou" | **só lê** (nada de escrever, puxar ou reiniciar): diz se a instalação foi afetada pelo worker que nunca era atualizado. Roda até avulso: `curl -fsSL https://raw.githubusercontent.com/melgarafael/DeskcommCRM/main/hostgator-setup-kit/diagnostico.sh \| bash` | código de saída 1 = afetada; explica o conserto |
| `update.sh` | "atualiza", "tem versão nova?" | confere se há versão nova (senão sai na hora), **faz backup antes**, puxa o código, re-aplica o `baseline.sql` (idempotente e auto-curativo; muitos "já existe" são esperados), puxa as imagens da tag, confere a saúde | código 3 = recusou e **nada foi tocado** (ex.: sem internet para confirmar o que é mais novo, ou a versão pedida é anterior). `--force` volta no tempo de propósito; `--to <tag>` fixa uma versão; `--skip-backup` não recomendado |
+3 -1
View File
@@ -135,7 +135,9 @@ NEXT_PUBLIC_ADMIN_URL=https://crm.seudominio.com.br
# -----------------------------------------------------------------------------
# 4) IA (Anthropic via AI Gateway) — paga por uso, chave sua
# -----------------------------------------------------------------------------
# ou ANTHROPIC_API_KEY abaixo
# ou ANTHROPIC_API_KEY abaixo. As chaves de IA são OPCIONAIS: o CRM sobe sem
# nenhuma delas e você cadastra a sua depois pela tela, em IA › Credenciais
# (fica cifrada no banco). Sem chave, o agente de IA não responde.
AI_GATEWAY_API_KEY=
ANTHROPIC_API_KEY=
+2 -1
View File
@@ -47,7 +47,8 @@ discordarem, vale o guia; quando o guia e `install.sh` discordarem, vale o insta
- **Qual IA vai atender** — o instalador pergunta antes das chaves: OpenRouter (uma chave, muitos
modelos, o mais simples), Anthropic (Claude, padrão do Enter) ou OpenAI. Se não for OpenAI, ele
pede a chave da OpenAI à parte e **opcional** (áudio e base de conhecimento — dá para cadastrar
depois pela tela).
depois pela tela). A chave do provedor principal **também é opcional**: dá para instalar sem ela
e cadastrar depois pela tela (IA › Credenciais).
- O **e-mail e a senha** que ela quer para entrar no CRM (o primeiro admin).
## Passo a passo que você conduz
+3 -3
View File
@@ -42,8 +42,8 @@ bash install.sh
> contrato desse modo. Se preferir instalar por conta própria, responda `n` e rode
> `curl -fsSL https://get.docker.com | sh` antes.
O instalador pergunta o que precisa (domínio, chaves do Supabase e da Anthropic,
e-mail/senha do admin), gera o resto e sobe tudo.
O instalador pergunta o que precisa (domínio, chaves do Supabase, provedor de IA
— a chave pode ficar para depois —, e-mail/senha do admin), gera o resto e sobe tudo.
> Modo não-interativo: copie `.env.hostgator.example` (do repositório) para `.env`,
> preencha, e rode `bash install.sh --yes`.
@@ -85,7 +85,7 @@ Owner/Admin. Não dá para hospedar vários clientes numa conta só.
| VPS (Docker) | HostGator — VPS com Docker (n8n/OpenClaw/GatorClaw). Outras hospedagens com Docker também servem — se a sua já tiver proxy próprio nas portas 80/443, [veja aqui](#vps-que-já-vem-com-proxy-próprio-hostinger-coolify-dokploy) |
| Domínio | Registro de domínio (aponte um A-record pro IP do VPS) |
| Banco de dados | Conta grátis no [supabase.com](https://supabase.com) (3 chaves + connection string) |
| IA | Chave da [Anthropic](https://console.anthropic.com) |
| IA | Chave da [Anthropic](https://console.anthropic.com) — opcional: dá para instalar sem ela e cadastrar depois pela tela (IA › Credenciais) |
| WhatsApp | Seu número — conectado por QR code no onboarding |
| Token do Supabase (opcional) | [supabase.com/dashboard/account/tokens](https://supabase.com/dashboard/account/tokens) — com ele o instalador configura sozinho os links dos e-mails de acesso. **Ele não fica salvo:** é usado uma vez e some com o processo |
+49 -3
View File
@@ -1161,10 +1161,19 @@ escolher_provedor
# O campo da chave do provedor ESCOLHIDO — e só dele. Pedir as três faria a
# pessoa achar que precisa das três.
#
# O campo é `opcional` (issue #670). `docs/deploy-selfhost` promete que dá para
# "deixar vazio e cadastrar a chave depois", e o runtime concorda (`lib/env.ts`
# trata as três chaves como opcionais; faltar todas é `warn`, não erro) — mas o
# instalador exigia uma chave que PASSASSE numa chamada real, e não havia
# caminho para subir o produto sem antes abrir conta num provedor de IA. Quem
# pula instala, e o caminho de volta sai na tela final (`pendencia_da_ia`, no
# fecho). O validador continua valendo para quem digita uma chave — o que
# mudou é que pular deixou de ser erro.
case "$AI_PROVIDER" in
openrouter) CAMPO_IA="OPENROUTER_API_KEY|Chave da OpenRouter — a IA que atende (openrouter.ai/keys)||v_openrouter|secret|";;
openai) CAMPO_IA="OPENAI_API_KEY|Chave da OpenAI — a IA que atende (platform.openai.com/api-keys)||v_openai|secret|";;
*) CAMPO_IA="ANTHROPIC_API_KEY|Chave da Anthropic — a IA que atende (console.anthropic.com)||v_anthropic|secret|";;
openrouter) CAMPO_IA="OPENROUTER_API_KEY|Chave da OpenRouter — a IA que atende (openrouter.ai/keys; Enter pula: dá para cadastrar depois pela tela, em IA › Credenciais)||v_openrouter|secret|opcional";;
openai) CAMPO_IA="OPENAI_API_KEY|Chave da OpenAI — a IA que atende (platform.openai.com/api-keys; Enter pula: dá para cadastrar depois pela tela, em IA › Credenciais)||v_openai|secret|opcional";;
*) CAMPO_IA="ANTHROPIC_API_KEY|Chave da Anthropic — a IA que atende (console.anthropic.com; Enter pula: dá para cadastrar depois pela tela, em IA › Credenciais)||v_anthropic|secret|opcional";;
esac
# A chave da OpenAI é pedida À PARTE quando ela NÃO é o provedor de conversa,
@@ -1927,6 +1936,42 @@ $(sed 's/^/ /' "$PENDENCIA_EMAIL")
PEND
}
# ── A pendência da IA, quando a chave ficou para depois ─────────────────────
# A #670 tornou o campo da chave `opcional`: antes o instalador morria sem uma
# chave que passasse numa chamada real, contra a doc e contra o runtime.
# Instalar sem chave é legítimo; o que não pode é a pessoa terminar sem saber
# que a IA ainda não atende e ONDE cadastrar depois. Este bloco repete o
# caminho na TELA FINAL, que é a única tela que a pessoa lê inteira.
#
# Critério: nenhuma credencial DE AMBIENTE preenchida — nem a do provedor
# escolhido, nem o AI Gateway (que tem precedência na resolução do chat, ver
# `.env.hostgator.example`). Credencial cadastrada pela tela (banco) não dá
# para ver daqui; quem já cadastrou reconhece o aviso e ignora.
pendencia_da_ia() {
local chave="" rotulo=""
case "${AI_PROVIDER:-anthropic}" in
openrouter) chave="${OPENROUTER_API_KEY:-}"; rotulo="OpenRouter" ;;
openai) chave="${OPENAI_API_KEY:-}"; rotulo="OpenAI" ;;
*) chave="${ANTHROPIC_API_KEY:-}"; rotulo="Anthropic" ;;
esac
[ -n "$chave" ] && return 0
[ -n "${AI_GATEWAY_API_KEY:-}" ] && return 0
cat <<PEND
$(c_ylw " ─── A IA ainda não atende — falta cadastrar a chave ───")
Você deixou a chave de IA para depois, e o CRM está no ar sem ela. O que
ainda não funciona é o agente: ele responde quando uma credencial existir.
Quando tiver a chave da ${rotulo}, cadastre em:
IA › Credenciais
A chave fica CIFRADA no banco — não precisa mexer no .env nem reiniciar nada.
PEND
}
PENDENCIA_EMAIL="$(mktemp)"
PENDENCIA_ARQUIVO="$PENDENCIA_EMAIL" \
SUPABASE_ACCESS_TOKEN="${SUPABASE_ACCESS_TOKEN:-}" \
@@ -2139,6 +2184,7 @@ $(c_grn " Instalação concluída!")
$(c_grn "═══════════════════════════════════════════════════════")
$(pendencia_dos_emails)
$(pendencia_da_ia)
1. Acesse: https://${DOMAIN}
(o SSL leva ~1min pra emitir no primeiro acesso)
+100
View File
@@ -1912,6 +1912,106 @@ provedor_ok "OpenAI: instala e o .env sai inteiro" OPENAI_API_KEY sk-te
provedor_ok "Anthropic: instala e o .env sai inteiro" ANTHROPIC_API_KEY sk-ant-teste anthropic
echo "integração: instalar SEM chave de IA — o caminho que a documentação prometia (issue #670)"
# A issue #670: `docs/deploy-selfhost` promete "deixe vazio e cadastre a chave
# depois em IA › Credenciais", e o runtime concorda — `lib/env.ts` trata as três
# chaves como opcionais, e faltar todas é `warn`, não erro. O instalador, não:
# exigia uma chave que PASSASSE numa chamada real ao provedor, e a instalação
# inteira parava sem ela. Não havia caminho para subir o produto sem antes abrir
# conta num provedor de IA.
#
# O que este cenário mede é o caminho inteiro, com o .env de quem não tem conta
# em provedor nenhum: BASE_ENV sem as três chaves e sem o AI Gateway (que tem
# precedência na resolução do chat). Com o campo de volta a obrigatório, o
# `ask_one` morre em "Falta ANTHROPIC_API_KEY (modo --yes exige .env
# preenchido)" na coleta de configuração, e é a PRIMEIRA asserção que fica
# vermelha.
TMP_SEM_IA="$(mktemp -d)"
(
montar_vps "$TMP_SEM_IA" "crmsemia" <<'STUB'
#!/usr/bin/env bash
printf '%s\n' "$*" >> "$DOCKER_LOG"
case "$1" in
compose) case "$*" in *" exec "*) printf 'healthy\n{"data":{"status":"healthy"}}\n' ;; esac; exit 0 ;;
esac
exit 0
STUB
mkdir -p "$VPS_PROJ/supabase"; : > "$VPS_PROJ/supabase/baseline.sql"
# O .env da entrevista pulada: BASE_ENV sem NENHUMA chave de IA.
printf '%s\n' "$BASE_ENV" \
| grep -vE '^(ANTHROPIC|OPENROUTER|OPENAI)_API_KEY=|^AI_GATEWAY_API_KEY=' > "$VPS_PROJ/.env"
rodar_sem_ia() {
: > "$VPS_LOG"
(cd "$VPS_PROJ" && env PATH="$VPS_RAIZ/bin:$PATH" DOCKER_LOG="$VPS_LOG" \
CRONTAB_SANDBOX="$CRONTAB_SANDBOX" SUPABASE_ACCESS_TOKEN= \
bash "$VPS_RAIZ/install.sh" --yes 2>&1 || true) | sed -E 's/\x1b\[[0-9;]*m//g'
}
saida="$(rodar_sem_ia)"
# A marca do defeito: com o campo obrigatório, o instalador morre aqui.
if printf '%s' "$saida" | grep -q 'exige .env preenchido'; then
printf ' ✗ o instalador ainda morre sem chave de IA — o campo do provedor não é `opcional`\n'
printf ' %s\n' "$(printf '%s' "$saida" | grep -m1 'exige .env preenchido')"
exit 1
fi
# CONTROLE POSITIVO: "não morreu" só significa alguma coisa se a instalação
# chegou ao fim; sem esta âncora, um install que parasse antes passaria.
if ! printf '%s' "$saida" | grep -q 'Instalação concluída'; then
printf ' ✗ a instalação sem chave de IA não chegou à tela final — cenário inconclusivo, não verde\n'
printf ' última linha: %s\n' "$(printf '%s' "$saida" | grep -v '^$' | tail -1)"
exit 1
fi
# O .env sai INTEIRO: sem chave, a última linha do bloco continua presente —
# a mesma régua dos cenários de provedor acima.
if ! grep -qE '^OWNER_PASSWORD="' "$VPS_PROJ/.env"; then
printf ' ✗ o .env saiu pela metade na instalação sem chave de IA\n'
printf ' últimas chaves gravadas: %s\n' \
"$(grep -oE '^[A-Z_]+=' "$VPS_PROJ/.env" | tail -3 | tr '\n' ' ')"
exit 1
fi
# A chave que ninguém respondeu sai DECLARADA e vazia — a mesma distinção
# entre ausente e declarada-e-vazia que o caso do APP_ACCENT_HEX guarda.
if ! grep -qE '^ANTHROPIC_API_KEY=' "$VPS_PROJ/.env"; then
printf ' ✗ ANTHROPIC_API_KEY nem apareceu no .env (esperado: declarada e vazia)\n'; exit 1
fi
if [ -n "$(valor_no_env "$VPS_PROJ/.env" ANTHROPIC_API_KEY)" ]; then
printf ' ✗ ANTHROPIC_API_KEY veio com valor [%s] — ninguém digitou nada\n' \
"$(valor_no_env "$VPS_PROJ/.env" ANTHROPIC_API_KEY)"; exit 1
fi
# A TELA FINAL lembra o caminho de volta. A medição é no RABO (depois de
# "Instalação concluída"), como no caso do Site URL: é a única tela que a
# pessoa lê inteira, e um aviso no meio do log de dez minutos não conta.
rabo="${saida##*Instalação concluída}"
if ! printf '%s' "$rabo" | grep -q 'A IA ainda não atende'; then
printf ' ✗ a tela final não avisa que a IA ainda não atende\n'; exit 1
fi
if ! printf '%s' "$rabo" | grep -q 'IA › Credenciais'; then
printf ' ✗ o aviso da tela final não diz ONDE cadastrar a chave (IA › Credenciais)\n'; exit 1
fi
printf ' ✓ sem chave de IA: instala, .env inteiro, e a tela final dá o caminho de volta\n'
# ── O outro lado: com a chave, o aviso NÃO aparece ────────────────────────
# Sem isto, um `pendencia_da_ia` que imprimisse sempre passaria no caso acima
# e viraria ruído em toda instalação que já tem chave — inclusive nas rodadas
# de `provedor_ok` logo acima.
printf '%s\n' "$BASE_ENV" > "$VPS_PROJ/.env"
saida="$(rodar_sem_ia)"
if ! printf '%s' "$saida" | grep -q 'Instalação concluída'; then
printf ' ✗ (controle) a segunda rodada, com chave, não chegou à tela final — cenário inconclusivo\n'
exit 1
fi
rabo="${saida##*Instalação concluída}"
if printf '%s' "$rabo" | grep -q 'A IA ainda não atende'; then
printf ' ✗ com a chave presente, a tela final avisou que falta chave de IA\n'; exit 1
fi
printf ' ✓ com a chave presente, o lembrete não aparece (o aviso não é ruído permanente)\n'
) || fail=1
rm -rf "$TMP_SEM_IA"
echo "integração: instalação NOVA numa VPS com Traefik em modo host"
# O install.sh roda contra um `docker` dublê que imita a Hostinger: 80/443
# ocupadas, NINGUÉM publicando, um Traefik em `--network host`, e a rede do