إعادة صياغة رسائل البريد الإلكتروني: مهمة اختبار rake الإخراج

لقد قمت مؤخرًا بمراجعة رسائل البريد الإلكتروني: المهمة والتعليمات البرمجية ذات الصلة بهدف اختبار جميع مسارات فشل التعليمات البرمجية وتعديل نص الخطأ.

اكتشفت أيضًا أن آثار تعيين DISCOURSE_SMTP_ENABLE_STARTTLS=false هي (في الغالب) صفرية. لم يؤدِ تعيين هذا إلى تعطيل STARTTLS فعليًا ، وفي الواقع ، يمكن أن يتعايش بشكل جيد مع TLS عند الاتصال (DISCOURSE_SMTP_FORCE_TLS=true).

لذلك لقد:

قبل أن أقوم بدمج هذا ، أعتقد أن تحذيرًا إداريًا في لوحة التحكم عند تعيين DISCOURSE_SMTP_ENABLE_STARTTLS=false مناسب. أتخيل أن هناك على الأقل مستضيفًا ذاتيًا واحدًا قام بتعيين هذا ولكنه لا يحتاجه ويعتمد فعليًا على STARTTLS.

4 إعجابات

هذا يبدو عملاً جيدًا! شيء واحد لاحظته (أعتقد أنني لاحظته) هو أن مهمة rake لا تستخدم في الواقع نفس الكود الذي يستخدمه الإرسال الفعلي (مثل صفحة اختبار البريد الإلكتروني /admin/email). أنا متأكد من أنني واجهت حالة عمل فيها في تجربة المستخدم ولكن ليس في مهمة rake (أو ربما كان العكس؟)

بينما لا يزال الأمر حاضرًا في ذهنك، إذا كان بإمكانك التأكد من أنه عند الإرسال الفعلي، فإنه يستخدم نفس الكود الذي يستخدمه Discourse، فسيكون ذلك رائعًا.

إعجابَين (2)

نحن نعمل أيضًا على ذلك، وعلى تحسين التسجيل عند فشل مهمة بريد إلكتروني في قائمة الانتظار. :+1:

4 إعجابات

هل هناك أي شيء أحتاج إلى القيام به في المنتدى الذي تستضيفه؟

4 إعجابات

لا، يجب ألا يظهر هذا التنبيه على الاستضافة الخاصة بنا. سنقوم بإصلاحه، شكرًا على التقرير كالعادة.

5 إعجابات

@supermathie هل يستحق هذا إرسال رسالة خاصة لكل مسؤول على كل موقع لديه هذا المتغير معينًا؟ سيقوم نظام فحص المشكلات الحالي لدينا بذلك، ولست متأكدًا من أن هذا مبرر هنا، نظرًا لأنه في الغالب، يكون لهذا تأثير nil. من الناحية المثالية، أود فقط عرض هذا على لوحة المعلومات دون إرسال إشعارات للمستخدمين المسؤولين، ولست متأكدًا من أن بنية فحص المشكلات الحالية لدينا تدعم حالة الاستخدام هذه.

أعتقد ذلك - أجد أنه من المحتمل للغاية أنه نظرًا لمدى ارتباك العديد من المسؤولين حول إعداد البريد الإلكتروني، فإن شخصًا ما لديه هذا المتغير معينًا بينما يعتمدون فعليًا على starttls.

لا ينبغي لأحد أن يكون لديه هذا المتغير معينًا، على الأرجح.

أفضل أن يكون لدي تحذير خاطئ بدلاً من كسر إعداد البريد الإلكتروني لشخص ما بصمت.

البديل هو إزالة التحقق وتعطيل المتغير بحيث لا يفعل شيئًا على الإطلاق.

إعجاب واحد (1)

سيكون من الجيد ألا يظهر التحذير عندما يكون خادم SMTP الصادر هو localhost (أي يتطابق مع اسم نطاق Discourse) حيث أن بروتوكول TLS بين حاوية Docker والجهاز المضيف غير مطلوب لأنهما نفس الجهاز.

إعجاب واحد (1)

في هذه الحالة يمكنك إزالة المتغير من البيئة.

سيستخدم STARTTLS فقط إذا تم تقديمه.

إعجاب واحد (1)

لقد واجهت في الإصدار الحالي الحالة التي أوقفت فيها السطور المعلقة (القيمة الافتراضية هي true) إرسال البريد الإلكتروني مع force_tls. لذلك، قمت بإلغاء التعليق عليها وضبطها على false. يعمل إرسال البريد الإلكتروني الآن - ولكن لدي هذه الرسالة في الواجهة الخلفية…

يعتمد الأمر قليلاً على المنفذ (port) الذي يعمل عليه مزود خدمة البريد الإلكتروني الخاص بك… لا يمكن أن يعمل كلاهما - وله تأثير بالتأكيد.

لقد لاحظت هذا أيضًا. بعد تحديث Discourse في 28 نوفمبر، بدأت أرى فشلًا في تسليم البريد مع رسالة الخطأ Job exception: :enable_starttls and :tls are mutually exclusive. Set :tls if you're on an SMTPS connection. Set :enable_starttls if you're on an SMTP connection and using STARTTLS for secure TLS upgrade.

تتبع المكدس
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/network/delivery_methods/smtp.rb:159:in `build_smtp_session'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/network/delivery_methods/smtp.rb:154:in `start_smtp_session'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/network/delivery_methods/smtp.rb:108:in `deliver!'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/message.rb:269:in `deliver!'
/usr/local/lib/ruby/3.3.0/delegate.rb:87:in `method_missing'
/var/www/discourse/lib/email/sender.rb:296:in `send'
/var/www/discourse/app/jobs/regular/user_email.rb:80:in `send_user_email'
/var/www/discourse/app/jobs/regular/user_email.rb:40:in `execute'
/var/www/discourse/app/jobs/base.rb:318:in `block (2 levels) in perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-7.0.0/lib/rails_multisite/connection_management/null_instance.rb:49:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-7.0.0/lib/rails_multisite/connection_management.rb:17:in `with_connection'
/var/www/discourse/app/jobs/base.rb:305:in `block in perform'
/var/www/discourse/app/jobs/base.rb:301:in `each'
/var/www/discourse/app/jobs/base.rb:301:in `perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:220:in `execute_job'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:185:in `block (4 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:180:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/suppress_user_email_errors.rb:6:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/discourse_event.rb:6:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/pausable.rb:131:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job/interrupt_handler.rb:9:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/metrics/tracking.rb:26:in `track'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/metrics/tracking.rb:134:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:173:in `invoke'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:184:in `block (3 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:145:in `block (6 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_retry.rb:118:in `local'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:144:in `block (5 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/config.rb:39:in `block in <class:Config>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:139:in `block (4 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:281:in `stats'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:134:in `block (3 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_logger.rb:15:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:133:in `block (2 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_retry.rb:85:in `global'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:132:in `block in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_logger.rb:40:in `prepare'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:131:in `dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:183:in `block (2 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:182:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:182:in `block in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:181:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:181:in `process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:86:in `process_one'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:76:in `run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/component.rb:10:in `watchdog'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/component.rb:19:in `block in safe_thread'

تمامًا مثل يوهان كريستوف، كنت سابقًا أُبقي DISCOURSE_SMTP_ENABLE_START_TLS معلقًا (مُعلَّقًا) و DISCOURSE_SMTP_FORCE_TLS مضبوطًا على true لـ TLS الضمني وكان يعمل بشكل ممتاز. ولكن الآن يجب عليّ ضبط DISCOURSE_SMTP_ENABLE_START_TLS صراحةً على false لكي يعمل تسليم البريد، وهو ما يؤدي بالطبع إلى ظهور الرسالة المذكورة أعلاه في لوحة تحكم المسؤول.

إعجاب واحد (1)

سيكون force_tls هو الذي يمنع إرسال البريد الإلكتروني - فهذا يفرض استخدام TLS عند الاتصال وربما يكون مناسبًا فقط للمنفذ 465.

يا له من وحيد قرن!

ما هي معلمات SMTP التي تستخدمها (باستثناء اسم المستخدم/كلمة المرور)؟

المنفذ 465 مع ENABLE_START_TLS=false و FORCE_TLS=true و OPEN_TIMEOUT=10. هذا الأخير بسبب أخطاء مهلة غريبة بدأت أراها بعد تحديث للنظام قبل حوالي ثلاث سنوات، ولكني سآكل قبعتي إذا كان ذلك مرتبطًا بالمشكلة الموصوفة أعلاه.

ألقيت نظرة على سجل الالتزامات (commit history) في هذه الأثناء ولاحظت تحديث مكتبة البريد (mail gem) إلى الإصدار 2.9.0 قبل ثلاثة أيام فقط من التحديث الذي أدخل المشكلة من جهتي (#36254). هناك إدخال في ملاحظات الإصدار يقول: “SMTP: إعادة هيكلة وقبول starttls :always و :auto بواسطة @eval في #1536”. أنا لا أعرف الكثير عن Ruby، ولكن هذا التغيير في الطلب المشار إليه (referenced PR) يبدو مشبوهًا بعض الشيء بالنسبة لي:

نظرًا لأن DISCOURSE_SMTP_ENABLE_START_TLS موثق بأنه صحيح افتراضيًا (أي إذا تم التعليق عليه)، فهل من الممكن أن يكون هذا هو أصل المشكلة؟

كما كتبت - لقد قمت بتعيين force_TLS:

DISCOURSE_SMTP_FORCE_TLS = true

و ENABLE_START_TLS صراحةً على false… عندها يعمل إرسال البريد - إذا كان خادم البريد يعمل على المنفذ 465.

بالنسبة لخوادم البريد التي تعمل على المنفذ 567 - يجب أن يكون الأمر عكس ذلك تمامًا.

منذ ذلك الحين، لدي تلميح على لوحة التحكم في الواجهة الخلفية… ولكن إرسال البريد يعمل بدون مشاكل.

لتمكين تعيين DISCOURSE_SMTP_ENABLE_START_TLS على false، يجب إلغاء التعليق عليه - لأنه افتراضيًا مضبوط على true. هذا بالضبط ما تسبب لي في المشكلة. ومع ذلك، حدث ذلك فقط مع أحدث إصدار (2025.12.0-latest).

يا إلهي، لقد كان هذا تغييراً سيئاً.

enable_starttls سيطلب استخدام starttls، لكن enable_starttls_auto انتهازي - سيتفاوض على تشفير TLS فقط إذا تم عرضه.

وإذا تم الاتصال بخادم البريد عبر تشفير TLS مبدئي، فلن يعرض starttls:

○ → openssl s_client -connect localhost:5587 -starttls smtp
250 CHUNKING
EHLO localhost
250-testmailrelay
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH PLAIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250 CHUNKING

لماذا بحق الجحيم فعلوا ذلك؟ :facepalm:
تكمن الصعوبة هنا في أنه لا ينبغي لنا أبدًا أن نعرض هذا التكوين في المقام الأول، بل كان يجب أن يكون شيئًا مثل:

DISCOURSE_SMTP_TLS_MODE = starttls_auto # [ none | starttls | starttls_auto (افتراضي) | tls ]

حرصًا منا على أن يقوم الأشخاص بإجراء إعدادات أقل وليس أكثر، أعتقد أن هذا النهج هو الأفضل:

إعجاب واحد (1)

تم دمج هذا الآن، ولم يعد هناك حاجة لتعطيل STARTTLS بشكل صريح، سيتولى Discourse الأمر إذا تم تمكين TLS.

نعم، يعمل بشكل صحيح مرة أخرى. شكرًا لك!