Add script for sending test message
This commit is contained in:
@@ -62,11 +62,11 @@ The command prints `SMTP_CONNECTION_OK` only after the SMTP server accepts the S
|
|||||||
|
|
||||||
6. Send a delivery test to an inbox you control:
|
6. Send a delivery test to an inbox you control:
|
||||||
|
|
||||||
```powershell
|
```sh
|
||||||
.\scripts\test-smtp.ps1 -To you@example.com
|
python ./scripts/send-test-email.py --to you@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
The command prints `SMTP_TEST_SENT_TO=<address>` only after the SMTP server accepts the message. Confirm the message arrives, then use **Forgot your password?** in the application to verify a real reset email and link.
|
The command works on Windows, Linux, and macOS. It prints `SMTP_TEST_SENT_TO=<address>` only after the SMTP server accepts the message. Confirm the message arrives, then use **Forgot your password?** in the application to verify a real reset email and link. The existing PowerShell alternative remains available as `./scripts/test-smtp.ps1 -To you@example.com`.
|
||||||
|
|
||||||
## Production notes
|
## Production notes
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
- [x] Local accounts, administrator approval, and TOTP MFA.
|
- [x] Local accounts, administrator approval, and TOTP MFA.
|
||||||
- [x] QR-based MFA enrollment for iPhone and Android authenticators.
|
- [x] QR-based MFA enrollment for iPhone and Android authenticators.
|
||||||
- [x] Email addresses, member password changes, and expiring password-reset links.
|
- [x] Email addresses, member password changes, and expiring password-reset links.
|
||||||
- [ ] Configure production SMTP credentials and verify outgoing email delivery. Run `python ./scripts/check-smtp.py`, then complete after the delivery test in [README.md](README.md) is received.
|
- [ ] Configure production SMTP credentials and verify outgoing email delivery. Run `python ./scripts/check-smtp.py`, then `python ./scripts/send-test-email.py --to you@example.com`; complete after the delivery test in [README.md](README.md) is received.
|
||||||
- [ ] Run `./scripts/rotate-admin.ps1` on Windows or `sh ./scripts/rotate-admin.sh` on Linux/macOS, then sign in as `admin` and scan the new MFA QR code to complete authenticator enrollment.
|
- [ ] Run `./scripts/rotate-admin.ps1` on Windows or `sh ./scripts/rotate-admin.sh` on Linux/macOS, then sign in as `admin` and scan the new MFA QR code to complete authenticator enrollment.
|
||||||
- [x] Add CSRF protection to all state-changing forms.
|
- [x] Add CSRF protection to all state-changing forms.
|
||||||
- [ ] Add automated database backups and test restoration.
|
- [ ] Add automated database backups and test restoration.
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Send one explicit SMTP delivery test email using local .env settings."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import smtplib
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from email.message import EmailMessage
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REQUIRED_SETTINGS = ("MAIL_HOST", "MAIL_PORT", "MAIL_USERNAME", "MAIL_PASSWORD", "MAIL_FROM")
|
||||||
|
|
||||||
|
|
||||||
|
def load_dotenv(path):
|
||||||
|
if not path.is_file():
|
||||||
|
raise RuntimeError("Missing .env. Copy .env.example to .env and enter the SMTP settings.")
|
||||||
|
for line in path.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
key, separator, value = line.partition("=")
|
||||||
|
if separator and key.strip():
|
||||||
|
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Send an Eternity Project SMTP test email.")
|
||||||
|
parser.add_argument("--to", required=True, help="Recipient email address")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
project_root = Path(__file__).resolve().parent.parent
|
||||||
|
load_dotenv(project_root / ".env")
|
||||||
|
missing = [setting for setting in REQUIRED_SETTINGS if not os.environ.get(setting)]
|
||||||
|
if missing:
|
||||||
|
raise RuntimeError(f"Missing required SMTP settings: {', '.join(missing)}")
|
||||||
|
|
||||||
|
message = EmailMessage()
|
||||||
|
message["Subject"] = "Eternity Project SMTP verification"
|
||||||
|
message["From"] = os.environ["MAIL_FROM"]
|
||||||
|
message["To"] = args.to
|
||||||
|
message.set_content(
|
||||||
|
f"SMTP delivery verification completed at {datetime.now(timezone.utc).isoformat()}.\n"
|
||||||
|
"You can now test password-reset delivery from the application."
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with smtplib.SMTP(os.environ["MAIL_HOST"], int(os.environ["MAIL_PORT"]), timeout=15) as client:
|
||||||
|
client.ehlo()
|
||||||
|
client.starttls(context=ssl.create_default_context())
|
||||||
|
client.ehlo()
|
||||||
|
client.login(os.environ["MAIL_USERNAME"], os.environ["MAIL_PASSWORD"])
|
||||||
|
client.send_message(message)
|
||||||
|
except (OSError, smtplib.SMTPException) as error:
|
||||||
|
print(f"SMTP_TEST_FAILED: {error}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(f"SMTP_TEST_SENT_TO={args.to}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
raise SystemExit(main())
|
||||||
|
except RuntimeError as error:
|
||||||
|
print(f"SMTP_CONFIGURATION_ERROR: {error}", file=sys.stderr)
|
||||||
|
raise SystemExit(2)
|
||||||
Reference in New Issue
Block a user