Update: I was able to get the SMTP feature working with the help of ChatGPT. I used port 2525 with Mailgun but I had to tweak the util.py file. I found that Gramsweb SMTP code was forcing a implicit SSL socket smtplilb.SMTP_SSL for every send and this works great for 465 but my server and Mailgun was using port 2525 which speaks plain SMTP and expects a STARTTLS.
Because the app tried SMTP_SSL against a STARTTLS port it failed with SSL: WRONG_VERSION_NUMBER and similar TLS/auth errors.
To fix it I updated util.py so the app:
-uses smtplib.SMTP() for the connection,
-calls starttls() when EMAIL_USE_TLS is set, and
-only uses implicit SSL (SMTP_SSL) if explicitly configured to do so.
I then built a small custom Docker image that bakes the patched util.py into the Gramps-Web image and redeployed the containers. The fix preserves encryption (STARTTLS is still TLS), and it’s reproducible because it’s included in a built image rather than edited manually inside the container.
For those interested in the for send_email, find it below:
”def send_email(
subject: str,
body: str,
to: Sequence[str],
from_email: str | None = None,
body_html: str | None = None,
) → None:
“”“Send an e-mail message via SMTP with optional STARTTLS.”“”
msg = EmailMessage()
msg.set_content(body)
if body_html:
msg.add_alternative(body_html, subtype=“html”)
msg[“Subject”] = subject
if not from_email:
from_email = get_config(“DEFAULT_FROM_EMAIL”)
msg[“From”] = from_email
msg[“To”] = ", ".join(to)
msg[“Message-ID”] = make_msgid()
host = get_config("EMAIL_HOST")
port = int(get_config("EMAIL_PORT"))
user = get_config("EMAIL_HOST_USER")
password = get_config("EMAIL_HOST_PASSWORD")
use_tls = get_config("EMAIL_USE_TLS")
try:
smtp = smtplib.SMTP(host=host, port=port, timeout=10)
smtp.ehlo()
# Upgrade to TLS if requested (STARTTLS)
if use_tls:
smtp.starttls()
smtp.ehlo()
if user:
smtp.login(user, password)
smtp.send_message(msg)
smtp.quit()
except ConnectionRefusedError:
current_app.logger.error("Connection to SMTP server refused.")
raise ValueError("Connection was refused.")
except socket.timeout:
current_app.logger.error("SMTP connection attempt timed out.")
raise ValueError("Connection attempt timed out.")
except OSError as e:
current_app.logger.error(f"Error while trying to send e-mail: {e}")
raise ValueError("Error while trying to send e-mail.")"
Then I made sure that this new util.py file is in the same folder as the Dockerfile which has the following code:
FROM http://ghcr.io/gramps-project/grampsweb:latest
COPY util.py /usr/local/lib/python3.11/dist-packages/gramps_webapi/api/util.py
, adjusted the docker-compose file to use this new Dockerfile
“grampsweb:
container_name: grampsweb
build:
context: /opt/grampsweb ( folder of where the new dockerfile is)
container_name: grampsweb_celery
build:
context: /opt/grampsweb
dockerfile: Dockerfile
And rebuild with docker-compose