Files
bot-pontos/docs/DEPLOY.md
2026-10-23 22:31:02 -03:00

17 KiB

Guia de deploy — Bot de Pontos v2

Passo a passo para colocar o Bot de Pontos no ar no servidor de produção:

  • Servidor: máquina física com Debian 12, fora do Proxmox, rodando Docker.
  • Acesso ao painel: pelo Nginx Proxy Manager (NPM) que já existe em outra máquina da rede, com certificado interno.
  • Endereço do painel: https://botpontos.lan (troque pelo nome que você usar).

O código ainda não foi compilado nem testado. A etapa 6 faz essa validação no próprio servidor antes de subir tudo. Se algo falhar, guarde a saída do comando para correção.

Visão geral

Navegador ──HTTPS──► NPM (outra máquina) ──HTTP :3000──► Servidor Debian (Docker)
                                                          ├── web       (Next.js, porta 3000)
                                                          ├── api       (Go, só rede interna)
                                                          ├── worker    (Go: bot + lembretes)
                                                          ├── postgres  (só rede interna)
                                                          └── backup    (pg_dump diário)
Etapa O quê Onde
1 Reunir informações e credenciais —
2 Preparar o Debian (IP fixo, NTP, atualizações) servidor
3 Instalar o Docker servidor
4 Copiar o projeto Windows → servidor
5 Criar .env e segredos servidor
6 Validar o código (testes) servidor
7 Subir os containers servidor
8 Firewall da porta 3000 servidor
9 DNS, certificado e Proxy Host rede / NPM
10 Primeiro acesso e primeiro admin navegador / Telegram
11 Backup fora da máquina servidor
12 Conferência final —

Os comandos no servidor são executados como root (ou com sudo).


1. Reunir informações e credenciais

Tenha em mãos antes de começar:

Item Exemplo Como obter
IP fixo do servidor 192.168.18.50 reserva DHCP no roteador ou IP estático (etapa 2)
IP do NPM 192.168.18.10 máquina onde o NPM roda
Nome do painel botpontos.lan escolha um nome interno
Token do bot 123456:ABC... no Telegram, fale com @BotFather → /newbot
Chave da feriadosapi.com — conta em feriadosapi.com (opcional: sem ela vale segunda a sexta)
Certificado interno .crt + .key emitido pela CA interna da rede para botpontos.lan (com o nome no SAN)
Credenciais PontoMais e-mail e senha de cada usuário cada usuário

No @BotFather, depois de criar o bot, é recomendado:

  • /setjoingroups → Disable (o bot só atende conversas privadas);
  • /setcommands com a lista abaixo, para aparecer o menu de comandos:
status - situação do cadastro e dos lembretes
entrada2 - retorno previsto do almoço
saida2 - saída prevista
ativarponto - ligar os lembretes
desativarponto - pausar os lembretes
cadastrar - solicitar acesso
ajuda - lista de comandos

2. Preparar o Debian

2.1 Atualizar o sistema

apt update && apt full-upgrade -y

2.2 IP fixo

O jeito mais simples é uma reserva DHCP no roteador para o MAC do servidor. Se preferir IP estático, edite /etc/network/interfaces (ajuste interface, IP e gateway):

auto enp1s0
iface enp1s0 inet static
    address 192.168.18.50/24
    gateway 192.168.18.1
    dns-nameservers 192.168.18.1

Confira:

ip -4 addr show

2.3 Relógio (NTP) — obrigatório

Os lembretes dependem da hora certa.

apt install -y systemd-timesyncd
timedatectl set-ntp true
timedatectl

A saída precisa mostrar System clock synchronized: yes. O fuso do sistema não importa (a aplicação grava em UTC e calcula no fuso de cada usuário), mas America/Sao_Paulo facilita a leitura dos logs:

timedatectl set-timezone America/Sao_Paulo

2.4 Ferramentas

apt install -y ca-certificates curl openssl iptables

3. Instalar o Docker

Use o repositório oficial da Docker (o pacote docker.io do Debian costuma estar defasado).

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list
apt update && apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Ligue o Docker na inicialização e confira:

systemctl enable --now docker
docker compose version

4. Copiar o projeto

O projeto vai para /opt/botpontos.

No servidor:

mkdir -p /opt/botpontos

No Windows, no PowerShell, dentro de C:\Users\BrunoSantos\Documents\Codex\projeto prl3, envie a pasta (troque usuário e IP):

scp -r .\bot-pontos-v2 usuario@192.168.18.50:/tmp/

A pasta secrets/ deve ter só o README.md neste momento: os segredos são criados direto no servidor (etapa 5). Não é preciso enviar node_modules ou .next, se existirem.

De volta ao servidor, mova para o lugar final:

cp -r /tmp/bot-pontos-v2/. /opt/botpontos/ && rm -rf /tmp/bot-pontos-v2
cd /opt/botpontos && ls

Deve aparecer backend, web, docs, docker-compose.yml, .env.example etc.

Os scripts precisam de quebra de linha Linux (LF). Confira e, se aparecer CRLF, converta:

file postgres/init/01-create-app-role.sh scripts/backup-loop.sh
sed -i 's/\r$//' postgres/init/01-create-app-role.sh scripts/backup-loop.sh

5. Criar .env e segredos

Todos os comandos desta etapa rodam em /opt/botpontos.

5.1 .env

cp .env.example .env && nano .env

Ajuste:

BP_HOST_LAN_IP=192.168.18.50          # IP fixo do servidor
BP_PUBLIC_URL=https://botpontos.lan   # exatamente o endereço que o navegador usa
BP_NPM_IP=192.168.18.10               # IP do Nginx Proxy Manager
BP_COOKIE_SECURE=true
BP_LOG_LEVEL=info

5.2 Segredos

mkdir -p secrets
openssl rand -base64 32 | tr -d '\n' > secrets/pg_password
openssl rand -base64 32 | tr -d '\n' > secrets/pg_app_password
openssl rand -base64 48 | tr -d '\n' > secrets/encryption_key

Token do bot (cole o seu entre as aspas simples):

printf '%s' 'COLE_O_TOKEN_DO_BOTFATHER' > secrets/telegram_token

Chave da feriadosapi.com, ou arquivo vazio se não tiver:

printf '%s' 'COLE_A_CHAVE' > secrets/holiday_api_key
: > secrets/holiday_api_key

Permissões (a pasta protege; os arquivos precisam ser legíveis pelos containers):

chmod 700 secrets && chmod 644 secrets/*

5.3 Guardar a chave de criptografia fora do servidor

Sem a encryption_key, as senhas da PontoMais gravadas não podem ser recuperadas. Copie o conteúdo para o seu cofre de senhas agora:

cat secrets/encryption_key

6. Validar o código (testes)

O código nunca foi compilado. Rode a validação no servidor antes de subir. Esses comandos usam containers temporários e não instalam Go nem Node na máquina.

Backend — gera o go.sum, roda go vet e os testes:

docker run --rm -v "$PWD/backend:/src" -w /src golang:1.25-alpine sh -c "go mod tidy && go vet ./... && go test ./..."

Painel — gera o package-lock.json e checa os tipos:

docker run --rm -v "$PWD/web:/app" -w /app node:22-alpine sh -c "npm install && npx tsc --noEmit"
  • Tudo passou: siga para a etapa 7.
  • Algo falhou: pare aqui, salve a saída completa e traga para correção.

Os arquivos go.sum e package-lock.json gerados ficam no projeto e tornam os próximos builds reproduzíveis. Copie-os de volta para a pasta do Windows também.


7. Subir os containers

docker compose up -d --build

O primeiro build leva alguns minutos. Depois confira:

docker compose ps -a

Resultado esperado:

Serviço Estado
postgres running (healthy)
migrate exited (0)
api running (healthy)
worker running (healthy)
web running (healthy)
backup running

Logs, se algo não ficar saudável:

docker compose logs --tail=100 migrate api worker

O worker deve registrar liderança obtida; worker ativo e bot do Telegram iniciado.

Teste local da porta (no próprio servidor):

curl -s http://192.168.18.50:3000/healthz

Deve responder {"status":"ok"}.


8. Firewall da porta 3000

Portas publicadas pelo Docker ignoram regras comuns de ufw/iptables INPUT. A regra vai na cadeia DOCKER-USER, aplicada por um serviço do systemd.

Coloque o IP do NPM no arquivo:

sed -i 's/IP_DO_NPM/192.168.18.10/' deploy/botpontos-firewall.service

Instale e ative:

cp deploy/botpontos-firewall.service /etc/systemd/system/
systemctl daemon-reload && systemctl enable --now botpontos-firewall

Confira a regra:

iptables -S DOCKER-USER

Deve aparecer uma linha com --ctorigdstport 3000 e ! -s 192.168.18.10/32 -j DROP.

Teste de aceite: de um computador da rede que não seja o NPM, o acesso direto precisa falhar (timeout):

curl -m 5 http://192.168.18.50:3000/healthz

9. DNS, certificado e Proxy Host

9.1 DNS interno

No DNS da rede (roteador, Pi-hole, AdGuard etc.), crie:

botpontos.lan  →  192.168.18.10   (IP do NPM, não do servidor Docker)

9.2 Certificado no NPM

No NPM: SSL Certificates → Add SSL Certificate → Custom. Envie o .crt (com a cadeia, se houver) e a .key emitidos pela CA interna para botpontos.lan.

O certificado raiz da CA interna precisa estar instalado nos computadores que acessam o painel.

9.3 Proxy Host

Hosts → Proxy Hosts → Add Proxy Host:

Aba Campo Valor
Details Domain Names botpontos.lan
Details Scheme http
Details Forward Hostname / IP 192.168.18.50
Details Forward Port 3000
Details Block Common Exploits ligado
Details Websockets Support desligado
Details Access List uma lista que permita só a rede local (ex.: 192.168.18.0/24)
SSL SSL Certificate o certificado da etapa 9.2
SSL Force SSL / HTTP/2 / HSTS ligados

Abra https://botpontos.lan num computador da rede: deve aparecer a tela de login, sem alerta de certificado.

Anote a data de validade do certificado interno. Com HSTS ligado, um certificado vencido bloqueia o acesso em vez de só mostrar aviso.


10. Primeiro acesso e primeiro admin

10.1 Conta owner do painel

No servidor (a senha, de no mínimo 12 caracteres, é pedida sem eco):

docker compose run --rm api admin create-owner --username admin

Entre em https://botpontos.lan com essa conta.

10.2 Primeiro admin do Telegram

  1. A pessoa que será admin envia /cadastrar ao bot no Telegram.
  2. No painel, Usuários → abra o cadastro.
  3. Em Credencial PontoMais, informe e-mail e senha → Validar e salvar. A credencial é testada na hora; se a PontoMais recusar, nada é gravado.
  4. Clique em Ativar.
  5. Clique em Tornar admin do bot.
  6. A pessoa envia /ativarponto e confere com /status.

A partir daí, novos pedidos de cadastro chegam como aviso no Telegram desse admin.

10.3 Demais usuários

Para cada usuário: /cadastrar no Telegram → credencial no painel → Ativar → o usuário envia /ativarponto.

Recomendação: comece com 1 a 3 usuários (piloto) por alguns dias antes de liberar para todos.

10.4 Conta de leitura para um usuário (opcional)

Para que uma pessoa acompanhe só os próprios dados no painel:

  1. O usuário já deve estar cadastrado e ativo (item 10.2 ou 10.3).
  2. Entre com o owner → Contas → Nova conta: defina usuário e senha, papel leitura e escolha o Usuário do Telegram.
  3. Entregue a senha à pessoa. Ela verá apenas Início, Usuários, Lembretes e Feriados, e só os próprios dados.

Não há migration nem variável de ambiente nova para isso: o vínculo usa a coluna web_accounts.user_id, que já existe. O backend e o painel precisam ser atualizados juntos.

10.5 Feriados

Em Feriados, clique em Sincronizar para o ano atual (se houver chave da feriadosapi.com) e cadastre feriados manuais que faltarem.


11. Backup fora da máquina

O container backup faz um pg_dump por dia em /opt/botpontos/backups, guardando 14 dias. Um backup no mesmo disco não protege contra falha do disco. Copie para outra máquina ou NAS.

Exemplo com um compartilhamento montado em /mnt/nas e cron diário às 04:00:

mkdir -p /mnt/nas/botpontos
echo '0 4 * * * root cp -n /opt/botpontos/backups/*.dump /mnt/nas/botpontos/ 2>/dev/null' > /etc/cron.d/botpontos-backup

Guarde também, fora do servidor:

  • a encryption_key (etapa 5.3);
  • o arquivo .env;
  • uma cópia da pasta secrets/ (num local seguro).

Restaurar um backup

Pare os serviços que escrevem no banco:

docker compose stop api worker web

Restaure (troque o nome do arquivo):

docker compose exec -T postgres pg_restore -U botpontos -d botpontos --clean --if-exists --no-owner < backups/botpontos-AAAAMMDD-HHMMSS.dump

Suba de novo:

docker compose up -d

A encryption_key usada no restore precisa ser a mesma da época do backup.


12. Conferência final

  • docker compose ps -a com todos saudáveis e migrate em exited (0).
  • timedatectl mostra o relógio sincronizado.
  • Acesso direto à porta 3000 bloqueado para quem não é o NPM.
  • https://botpontos.lan abre sem alerta de certificado.
  • Login no painel funciona; conta owner criada.
  • Admin do Telegram recebe aviso ao surgir um novo /cadastrar.
  • Um usuário piloto recebeu o lembrete de retorno do almoço.
  • Feriados do ano sincronizados ou cadastrados.
  • Backup do dia existe em backups/ e foi copiado para fora.
  • encryption_key guardada no cofre de senhas.

Atualizar a aplicação

  1. Gere um backup manual antes:

    docker compose exec backup sh -c 'PGPASSWORD=$(cat /run/secrets/pg_password) pg_dump -h postgres -U botpontos -d botpontos -Fc -f /backups/antes-da-atualizacao.dump'
    
  2. Copie a nova versão do código para /opt/botpontos (como na etapa 4), sem sobrescrever .env, secrets/ e backups/.

  3. Reconstrua e suba (as migrations rodam sozinhas no serviço migrate):

    docker compose up -d --build
    
  4. Confira com docker compose ps -a e os logs.

Atualize também o sistema e as imagens base de tempos em tempos:

apt update && apt full-upgrade -y
docker compose pull postgres backup && docker compose up -d --build

Problemas comuns

Sintoma Causa provável O que fazer
docker compose up reclama de BP_HOST_LAN_IP .env ausente ou sem a variável etapa 5.1
secret ... file not found algum arquivo de secrets/ não existe etapa 5.2 (o holiday_api_key pode ser vazio, mas precisa existir)
migrate termina com erro de senha senhas do banco trocadas depois da primeira subida as senhas só valem na 1ª inicialização do volume; veja secrets/README.md
api/worker saem com BP_ENCRYPTION_KEY ... obrigatório chave ausente ou com menos de 32 caracteres refaça secrets/encryption_key
worker sai com BP_TELEGRAM_TOKEN ... obrigatório token ausente etapa 5.2
Worker com falha no getUpdates repetido token errado ou servidor sem internet confira o token e a saída para a internet
Painel abre, mas o login diz "Origem da requisição não permitida" BP_PUBLIC_URL diferente do endereço usado no navegador ajuste o .env (com https://, sem barra no fim) e rode docker compose up -d
Login funciona, mas toda ação diz "Token CSRF inválido" cookies de outra origem ou cache apague os cookies do site e entre de novo; use sempre o mesmo endereço
Após login, volta para a tela de login BP_COOKIE_SECURE=true acessando por http:// acesse pelo NPM com https://
Navegador alerta sobre o certificado CA interna não instalada no computador, ou nome fora do SAN etapa 9.2
Lembretes não chegam usuário sem /ativarponto, dia não útil, fora da janela, ou dia sem pontos /status no Telegram; tela do usuário e Auditoria no painel
Lembrete aparece como expirado ponto detectado com mais de 10 min de atraso normal em almoço muito cedo ou atraso da PontoMais; ver ARQUITETURA.md 9.3
Usuário pausado "após falhas na PontoMais" 3 falhas seguidas de consulta teste a credencial no painel e religue os lembretes
Alerta de "PontoMais fora do ar" todas as consultas do ciclo falharam aguarde; ninguém é pausado; o admin recebe aviso quando voltar