runv-server

server tooling for runv.club
Log | Files | Refs | README

commit c5d6449cf874081252752c0f1c7f96d397509c1f
parent dad81e31b11c4b493da9ebd77fe29b872c70dbf0
Author: Pablo Murad <pablo@pablomurad.com>
Date:   Sat, 21 Mar 2026 19:47:49 -0300

fix email setup

Diffstat:
Memail/configure_mailgun.py | 42++++++++++++++++++++++++++++++------------
Memail/docs/INSTALL.md | 15+++++++--------
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.