Commita governance, user-wizard, operational-feed e catálogo RBAC; adiciona deploy-desk-full.sh, smoke-desk.sh e regra anti-deploy parcial; documenta credencial VM112 @betinplace. Co-authored-by: Cursor <cursoragent@cursor.com>
123 lines
3.9 KiB
Python
123 lines
3.9 KiB
Python
"""Email relay VM122 → VM112 — status, config e teste (Spec 004 / Infra CODE)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import smtplib
|
|
from datetime import datetime, timezone
|
|
from email.message import EmailMessage
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from app import mail_notify
|
|
|
|
CONFIG_PATH = Path(os.getenv("EMAIL_RELAY_CONFIG_PATH", "/data/email_relay_config.json"))
|
|
RELAY_HOST = os.getenv("DESK_RELAY_HOST", "10.10.10.112")
|
|
RELAY_PORT = int(os.getenv("DESK_RELAY_PORT", "25"))
|
|
SMTP_HOST = os.getenv("DESK_SMTP_HOST", "10.10.10.122")
|
|
SMTP_PORT = int(os.getenv("DESK_SMTP_PORT", "25"))
|
|
MAIL_FROM = os.getenv("DESK_MAIL_FROM", "ligbox-ops@ligbox.com.br")
|
|
|
|
DEFAULT_CONFIG: dict[str, Any] = {
|
|
"vm": "122",
|
|
"vm_label": "VM122 · Ops Desk",
|
|
"service_id": "vm122-email-relay",
|
|
"title": "Email Relay (Postfix)",
|
|
"relayhost": RELAY_HOST,
|
|
"relayport": RELAY_PORT,
|
|
"smtp_host": SMTP_HOST,
|
|
"smtp_port": SMTP_PORT,
|
|
"mail_from": MAIL_FROM,
|
|
"myorigin": "ligbox.com.br",
|
|
"transport_local": {
|
|
"ligbox.com.br": "LMTP [10.10.10.112]:7025",
|
|
"ibytera.com": "LMTP [10.10.10.112]:7025",
|
|
"dratcoin.com": "LMTP [10.10.10.112]:7025",
|
|
},
|
|
"external_route": "relayhost → mail.ligbox.com.br (DKIM/SPF)",
|
|
"docs": "docs/postfix-vm122.md",
|
|
}
|
|
|
|
|
|
def _now() -> str:
|
|
return datetime.now(timezone.utc).isoformat()
|
|
|
|
|
|
def load_config() -> dict[str, Any]:
|
|
cfg = dict(DEFAULT_CONFIG)
|
|
cfg["updated_at"] = _now()
|
|
if CONFIG_PATH.is_file():
|
|
try:
|
|
stored = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
|
|
if isinstance(stored, dict):
|
|
cfg.update(stored)
|
|
except Exception:
|
|
pass
|
|
return cfg
|
|
|
|
|
|
def save_config(patch: dict[str, Any]) -> dict[str, Any]:
|
|
cfg = load_config()
|
|
cfg.update(patch)
|
|
cfg["updated_at"] = _now()
|
|
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
CONFIG_PATH.write_text(json.dumps(cfg, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
return cfg
|
|
|
|
|
|
def _probe_smtp(host: str, port: int, timeout: float = 8.0) -> dict[str, Any]:
|
|
try:
|
|
with smtplib.SMTP(host, port, timeout=timeout) as smtp:
|
|
code, _ = smtp.ehlo()
|
|
smtp.noop()
|
|
ok = 200 <= code < 400
|
|
return {"ok": ok, "status": "online" if ok else "check", "detail": f"SMTP {host}:{port} EHLO {code}"}
|
|
except Exception as exc:
|
|
return {"ok": False, "status": "down", "detail": f"SMTP {host}:{port} — {exc}"}
|
|
|
|
|
|
def build_status() -> dict[str, Any]:
|
|
cfg = load_config()
|
|
local = _probe_smtp(cfg["smtp_host"], int(cfg["smtp_port"]))
|
|
relay = _probe_smtp(cfg["relayhost"], int(cfg["relayport"]))
|
|
ok = bool(local.get("ok") and relay.get("ok"))
|
|
detail_parts = [
|
|
local.get("detail", ""),
|
|
f"relay {cfg['relayhost']}:{cfg['relayport']} — {relay.get('detail', '')}",
|
|
]
|
|
return {
|
|
"ok": ok,
|
|
"status": "online" if ok else ("check" if local.get("ok") else "down"),
|
|
"detail": " · ".join(p for p in detail_parts if p),
|
|
"config": cfg,
|
|
"checks": {
|
|
"smtp_local": local,
|
|
"smtp_relay": relay,
|
|
},
|
|
"generated_at": _now(),
|
|
}
|
|
|
|
|
|
def probe_stack() -> dict[str, Any]:
|
|
st = build_status()
|
|
return {"ok": st["ok"], "status": st["status"], "detail": st["detail"]}
|
|
|
|
|
|
def send_test_email(to: str, subject: str | None = None) -> dict[str, Any]:
|
|
to = (to or "").strip()
|
|
if not to:
|
|
return {"ok": False, "detail": "destinatário vazio"}
|
|
subj = subject or "[Ligbox Ops] Teste Email Relay VM122"
|
|
body = (
|
|
"Teste do relay Postfix VM122 → VM112 (mail.ligbox.com.br).\n\n"
|
|
f"Gerado em: {_now()}\n"
|
|
)
|
|
ok = mail_notify.send_email(to, subj, body)
|
|
return {
|
|
"ok": ok,
|
|
"detail": "enviado via SMTP local" if ok else "falha SMTP — verificar Postfix/relay",
|
|
"to": to,
|
|
"from": MAIL_FROM,
|
|
"at": _now(),
|
|
}
|