commit c5d6449cf874081252752c0f1c7f96d397509c1f
parent dad81e31b11c4b493da9ebd77fe29b872c70dbf0
Author: Pablo Murad <pablo@pablomurad.com>
Date: Sat, 21 Mar 2026 19:47:49 -0300
fix email setup
Diffstat:
2 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/email/configure_mailgun.py b/email/configure_mailgun.py
@@ -15,7 +15,6 @@ import logging
import os
import sys
import time
-from getpass import getpass
from pathlib import Path
from typing import Any
@@ -23,9 +22,10 @@ MODULE_ROOT = Path(__file__).resolve().parent
STATE_PATH = Path("/etc/runv-email.json")
SECRETS_PATH = Path("/etc/runv-email.secrets.json")
+MAILGUN_API_REGION = "us"
+
sys.path.insert(0, str(MODULE_ROOT))
from lib.mailgun_client import ( # noqa: E402
- MailgunHTTPError,
build_mailgun_messages_url,
mailgun_base_url,
mask_secret,
@@ -62,6 +62,24 @@ def prompt_yes_no(msg: str, default_no: bool = True) -> bool:
return r in ("s", "sim", "y", "yes")
+def prompt_api_key_twice() -> str:
+ print()
+ print(
+ "Aviso: a API key será mostrada ao digitar (para confirmar cópia/colar). "
+ "Evite ecrãs partilhados ou sessões gravadas.",
+ )
+ while True:
+ key = input("Mailgun API key: ").strip()
+ key2 = input("Repita a mesma API key: ").strip()
+ if not key:
+ print("Chave vazia — tente de novo.")
+ continue
+ if key != key2:
+ print("As duas entradas não coincidem — tente de novo.")
+ continue
+ return key
+
+
def interactive_config(*, email_package_root: str) -> tuple[dict[str, Any], dict[str, str]]:
print()
print("=== Configurador de email para Mailgun API ===")
@@ -76,14 +94,16 @@ def interactive_config(*, email_package_root: str) -> tuple[dict[str, Any], dict
api_key_kind = "domain_sending" if choice != "2" else "primary"
domain = prompt_line("Domínio de envio Mailgun (ex.: mg.exemplo.com ou exemplo.com)")
- region = prompt_line("Região da API (us ou eu)", "us").strip().lower()
- if region not in ("us", "eu"):
- raise ValueError("Região deve ser 'us' ou 'eu'.")
-
- key = getpass("Mailgun API key (não ecoa): ").strip()
- key2 = getpass("Repita a API key: ").strip()
- if key != key2:
- raise ValueError("As chaves não coincidem.")
+ region = MAILGUN_API_REGION
+ print()
+ print(
+ "Região API HTTP: US — https://api.mailgun.net/ "
+ "(alinhado ao SMTP smtp.mailgun.org indicado nas credenciais SMTP do painel).",
+ )
+ print("Contas Mailgun só na UE: edite depois mailgun_region e api_base_url em /etc/runv-email.json.")
+ print()
+
+ key = prompt_api_key_twice()
default_from = prompt_line("Remetente padrão (From)")
admin_email = prompt_line("Email do administrador (notificações / teste)")
@@ -153,8 +173,6 @@ def run_test_send(*, dry_run: bool) -> None:
return
try:
send_mail(admin, subj, body, from_addr=from_addr, _state=pub)
- except MailgunHTTPError:
- raise
except Exception as e:
log().debug("detalhe (sem segredos): %s", type(e).__name__)
raise
diff --git a/email/docs/INSTALL.md b/email/docs/INSTALL.md
@@ -6,7 +6,7 @@ Debian 13 (ou próximo). **Apenas envio** — caminho predefinido **Mailgun HTTP
## O que o predefinido faz (Mailgun)
-- Grava metadados em **`/etc/runv-email.json`** (0600, root): domínio Mailgun, região `us` ou `eu`, URL base da API, remetente padrão, email do admin, tipo de chave, caminho da pasta `email/` do repositório (`email_package_root`), etc. **Sem API key neste ficheiro.**
+- Grava metadados em **`/etc/runv-email.json`** (0600, root): domínio Mailgun, região da API (o configurador fixa **`us`** e `https://api.mailgun.net/`), remetente padrão, email do admin, tipo de chave, caminho da pasta `email/` do repositório (`email_package_root`), etc. **Sem API key neste ficheiro.**
- Grava segredos em **`/etc/runv-email.secrets.json`** (0600, root): apenas `mailgun_api_key`. **Não partilhar nem fazer backup deste ficheiro para repositórios públicos.**
### API key em variável de ambiente (opcional)
@@ -17,10 +17,8 @@ Em tempo de execução, **`RUNV_MAILGUN_API_KEY`** (se definida) **tem prioridad
- **Credenciais SMTP** do painel Mailgun são para clientes SMTP (ex.: msmtp); **não** são o mesmo fluxo que a API HTTP.
- A **HTTP API** usa autenticação **HTTP Basic**: username fixo **`api`**, password = **API key** (primary ou domain sending key).
-- **US:** `https://api.mailgun.net/v3/<domínio>/messages`
-- **EU:** `https://api.eu.mailgun.net/v3/<domínio>/messages`
-
-Escolha a região no painel Mailgun (conta EU vs US); o script **pergunta explicitamente** `us` ou `eu` — não adivinha em silêncio.
+- **US:** `https://api.mailgun.net/v3/<domínio>/messages` (o configurador usa sempre este endpoint; é o mesmo eixo que o SMTP **`smtp.mailgun.org`** nas credenciais SMTP do painel.)
+- **EU:** `https://api.eu.mailgun.net/v3/<domínio>/messages` — só para contas/domínios alojados na região UE; nesse caso **edite** `mailgun_region` (`eu`) e `api_base_url` em `/etc/runv-email.json` após correr o script, ou a API devolverá erros de autenticação/domínio.
### Obter uma API key
@@ -40,12 +38,13 @@ O script pergunta:
- tipo de chave (domain sending vs primary);
- domínio de envio Mailgun (ex.: `mg.exemplo.com`);
-- região da API: **`us`** ou **`eu`**;
-- API key (**não ecoa**);
+- API key (**ecoada** ao digitar; deve ser introduzida **duas vezes iguais** para continuar — útil para validar cópia/colar; evite terminais partilhados);
- remetente padrão (From);
- email do administrador (notificações / teste);
- caminho da pasta **`email/`** do repositório (para importações, ex. fluxo `entre` — por omissão é a pasta onde está o script).
+A região da API HTTP **não é perguntada**: fica **`us`** (`api.mailgun.net`). Conta só UE: ajuste manualmente o JSON (ver secção «SMTP vs HTTP API» acima).
+
## Ficheiros criados (Mailgun)
| Ficheiro | Descrição |
@@ -71,7 +70,7 @@ sudo python3 configure_mailgun.py --test
Em caso de falha, mensagens típicas:
-- **401 / 403** — API key inválida ou sem permissão para o domínio/região.
+- **401 / 403** — API key errada, **domain sending key** de outro domínio, ou **conta/região UE** a usar o endpoint US (`api.mailgun.net`); confira no painel Mailgun se o domínio é US ou EU e se a chave corresponde a esse domínio.
- **400** — payload inválido; From não autorizado no domínio; campos em falta.
- **404** — domínio errado ou URL/região incorreta (US vs EU).
- **Timeout / erro de rede** — DNS, firewall ou TLS.