Compare commits

...

2 Commits

Author SHA1 Message Date
Fringg 29d3e8984b fix(smtp): use implicit TLS (SMTPS) for port 465 registration emails
The cabinet email service called smtplib.SMTP() and conditionally ran
starttls() regardless of port, which is wrong for port 465. Port 465
is SMTPS (implicit TLS, RFC 8314): the TLS handshake must happen on
connect, before any EHLO. With the old code, switching to 465
produced a corrupted handshake — sometimes one garbled message went
through, then the server dropped the source.

Now SMTP_SSL is used when SMTP_USE_SSL is true OR when SMTP_PORT is
465 (auto-detect). The new SMTP_USE_SSL setting defaults to false
to keep existing 587/25 deployments unchanged.
2026-05-10 10:02:46 +03:00
Fringg b5a066628f fix(cabinet): return original_price_per_device_kopeks for device addon
Endpoint /cabinet/subscription/devices/price did not include
original_price_per_device_kopeks when a promo group discount applied,
causing the cabinet UI to render a strikethrough 'Бесплатно' next to
the per-device price line.
2026-05-10 09:59:52 +03:00
4 changed files with 22 additions and 6 deletions
+4 -1
View File
@@ -72,8 +72,11 @@ SMTP_PASSWORD=
# Email отправителя (если не указан, используется SMTP_USER)
SMTP_FROM_EMAIL=
SMTP_FROM_NAME=VPN Service
# Использовать TLS шифрование
# Использовать STARTTLS (порт 587 / 25). Не путать с SMTP_USE_SSL.
SMTP_USE_TLS=true
# Использовать implicit TLS (SMTPS). Автоматически включается при SMTP_PORT=465.
# Для портов 25/587 оставить false.
SMTP_USE_SSL=false
# Уведомления администраторов
ADMIN_NOTIFICATIONS_ENABLED=true
@@ -828,6 +828,9 @@ async def get_device_price(
response['discount_percent'] = devices_discount_percent
response['discount_kopeks'] = discount_value
response['base_total_price_kopeks'] = base_total_price
response['original_price_per_device_kopeks'] = (
base_total_price // devices if devices > 0 else 0
)
return response
+13 -5
View File
@@ -45,18 +45,26 @@ class EmailService:
def use_tls(self) -> bool:
return settings.SMTP_USE_TLS
@property
def use_ssl(self) -> bool:
# Port 465 always implies implicit TLS (SMTPS, RFC 8314).
return settings.SMTP_USE_SSL or self.port == 465
def is_configured(self) -> bool:
"""Check if SMTP is properly configured."""
return settings.is_smtp_configured()
def _get_smtp_connection(self) -> smtplib.SMTP:
"""Create and return SMTP connection."""
smtp = smtplib.SMTP(self.host, self.port, timeout=30)
smtp.ehlo()
if self.use_tls:
smtp.starttls()
if self.use_ssl:
smtp: smtplib.SMTP = smtplib.SMTP_SSL(self.host, self.port, timeout=30)
smtp.ehlo()
else:
smtp = smtplib.SMTP(self.host, self.port, timeout=30)
smtp.ehlo()
if self.use_tls:
smtp.starttls()
smtp.ehlo()
# Only attempt login if credentials are provided AND server supports AUTH
if self.user and self.password:
+2
View File
@@ -1014,6 +1014,8 @@ class Settings(BaseSettings):
SMTP_FROM_EMAIL: str | None = None
SMTP_FROM_NAME: str = 'VPN Service'
SMTP_USE_TLS: bool = True
# Implicit TLS (SMTPS) — required for port 465. Auto-enabled when SMTP_PORT == 465.
SMTP_USE_SSL: bool = False
# Ban System Integration (BedolagaBan monitoring)
BAN_SYSTEM_ENABLED: bool = False