Files
hedena_gold/docs/SMS-RELAY-PLAN.md
T
s.esmaeili ea86636027 Initial commit: Hedena Gold monorepo.
First commit of the Hedena Gold product (separate from gold-bank): branding, package names, and deploy defaults aligned to the new brand; legacy Henza trees stay local-only via .gitignore.
2026-10-15 17:19:10 +03:30

8.9 KiB
Raw Blame History

طرح افزودن درگاه پیامکی جدید (Relay / createsms)

وضعیت: اجرا شد (2026-10-05) — تصمیم ثبت‌شده: D26. تاریخ: 2026-10-05

۱. کد ورودی چه می‌کند

کد PHP ارسالی (Laravel) یک پیامک را به یک سرویس واسط (relay) می‌فرستد:

مورد مقدار
متد / آدرس POST http://185.231.112.101/api/createsms (HTTP ساده، بدون TLS)
بدنه JSON
هدر احراز هویت secret_key: <کلید>
timeout ۱۵ ثانیه
فیلد phone شماره گیرنده
فیلد text متن پیام، trim() شده
فیلد host Controller::Config('montek') و اگر خالی بود IP خود سرور (gethostbyname(gethostname())) — یعنی شناسه‌ی «این مشتری/سرور» نزد سرویس واسط
فیلد platform اگر $sendFromKave درست باشد "kave" (کاوه‌نگار)، وگرنه null (خط پیش‌فرض سرویس واسط)
CURLOPT_IPRESOLVE = V4 فقط اجبار IPv4؛ چون آدرس خودش IP است در عمل اثری ندارد

این کد در henza_server وجود ندارد (جستجو شد)، پس از سیستم دیگری آمده و مرجع parity ندارد.

۲. کجا قرار می‌گیرد

ماژول پیامک همین الان در apps/api/src/modules/sms/sms.service.ts هست و همه‌ی مصرف‌کننده‌ها (OTP ورود، پیامک خوش‌آمد، اعلان تکمیل پروفایل، …) فقط SmsService.send() را صدا می‌زنند. پس:

  • هیچ مصرف‌کننده‌ای تغییر نمی‌کند. فقط یک «درایور» دوم کنار Trez اضافه می‌شود و با SMS_PROVIDER=relay انتخاب می‌شود.
  • قوانین فعلی حفظ می‌شود: send() هرگز throw نمی‌کند (D20)، هر پیام در SmsMessage ثبت می‌شود، متن OTP ذخیره نمی‌شود (SEC-11)، نبود تنظیمات فقط هشدار می‌دهد و سرور بالا می‌آید.

۳. تغییرات پیشنهادی

۳.۱ کد — sms.service.ts

  1. smsConfig() متغیرهای جدید را هم می‌خواند (جدول ۳.۲).
  2. smsConfigured() برای relay: کافی است SMS_RELAY_SECRET و SMS_RELAY_HOST پر باشند.
  3. assertSmsConfigured() مقدار relay را می‌شناسد و متغیرهای خالی را با نام گزارش می‌کند (بدون throw).
  4. متد جدید deliverViaRelay(mobile, text, platform):
    • fetch با Content-Type: application/json و هدر secret_key، بدنه‌ی { phone, host, text, platform }، timeout ۱۵ ثانیه با AbortController — هم‌شکل deliverViaTrez.
    • موفقیت = پاسخ HTTP 2xx (تا وقتی قالب پاسخ سرویس واسط مشخص شود — سؤال ۴).
  5. SmsSendOptions یک گزینه‌ی اختیاری platform?: 'kave' می‌گیرد؛ درایور Trez آن را نادیده می‌گیرد.
  6. اجبار IPv4 پیاده نمی‌شود چون آدرس IP است؛ اگر بعداً دامنه شد، با dns.lookup با family: 4 اضافه می‌شود.

۳.۲ متغیرهای محیطی جدید

متغیر اجباری توضیح
SMS_PROVIDER — مقدار جدید relay
SMS_RELAY_URL خیر پیش‌فرض http://185.231.112.101/api/createsms
SMS_RELAY_SECRET بله مقدار هدر secret_key — فقط در env، هرگز در کد یا git
SMS_RELAY_HOST بله مقدار فیلد host (معادل Config('montek'))
SMS_RELAY_KAVE_FOR خیر کدام پیام‌ها از کاوه‌نگار بروند — بسته به پاسخ سؤال ۲

۳.۳ مستندات و استقرار

  • .env.example (ریشه)، apps/api/.env.example، docker-compose.yml، DELIVERY.md (بخش SMS و گام ۴): افزودن متغیرهای بالا.
  • docs/parity/DECISIONS.md: ثبت یک تصمیم جدید (D26) برای درایور relay.

۳.۴ تست — sms.service.spec.ts

  • تنظیمات: پیش‌فرض URL، smsConfigured با/بدون secret و host، هشدار با نام متغیرهای خالی.
  • ارسال با fetch ماک‌شده: آدرس، هدر secret_key، بدنه‌ی JSON، trim متن، platform درست؛ و این‌که خطای شبکه/timeout/HTTP 500 به { ok: false } تبدیل شود و throw نشود.

۴. هشدار امنیتی ⚠

  1. کلید secret_key لو رفته فرض شود. در متن پیام چت آمده و احتمالاً در git پروژه‌ی PHP هم هست (مشابه SEC-12). باید از صاحب سرویس واسط کلید جدید گرفت و فقط در env سرور گذاشت. این کلید در کد این مخزن نوشته نمی‌شود.
  2. ارتباط HTTP ساده است؛ کلید و متن پیام (از جمله کد OTP) رمزنشده از اینترنت عبور می‌کنند. اگر سرویس واسط HTTPS دارد از آن استفاده شود.

۵. سؤال‌ها (با پیشنهاد)

  1. جایگزین یا کنار Trez؟ پیشنهاد: کنار Trez بماند و با SMS_PROVIDER=relay انتخاب شود؛ پیش‌فرض همان trez بماند تا رفتار فعلی عوض نشود. یا پیش‌فرض relay شود؟
  2. sendFromKave کی درست است؟ در کد اصلی این متغیر از کجا مقدار می‌گیرد؟ پیشنهاد: فقط OTP ورود از کاوه‌نگار برود (معمولاً برای سرعت/تحویل بهتر) و بقیه از خط پیش‌فرض — قابل تنظیم با env.
  3. مقدار host (montek) چیست؟ پیشنهاد: اجباری در env. fallback به IP خود سرور پیاده نمی‌شود، چون داخل Docker، IP کانتینر (مثلاً 172.x) برمی‌گردد که برای سرویس واسط بی‌معناست.
  4. پاسخ سرویس واسط چه شکلی است؟ اگر در خطا هم 200 برمی‌گرداند (مثلاً {"status": false})، نمونه‌ی پاسخ موفق و ناموفق لازم است تا ok درست تشخیص داده شود. پیشنهاد فعلی: فقط وضعیت HTTP.
  5. HTTPS دارد؟ (بند ۴.۲)

۷. پاسخ‌های مالک (2026-10-05)

سؤال پاسخ اثر روی پیاده‌سازی
کلید لو نرفته؛ تأیید دومرحله‌ای جای دیگری هست؛ داخل پروژه قرار بگیرد کلید به‌عنوان مقدار پیش‌فرض در کد می‌آید و SMS_RELAY_SECRET فقط آن را بازنویسی می‌کند. بند ۴.۱ لغو شد.
۱. جایگزین Trez؟ پیش‌فرض relay شود؛ Trez حذف نشود؛ ممکن است بعداً سرویس‌های بیشتری بیاید سرویس به ساختار «درایور» بازنویسی می‌شود: یک اینترفیس SmsDriver و هر سرویس یک فایل جدا (drivers/relay.driver.ts، drivers/trez.driver.ts). برای اضافه کردن سرویس بعدی فقط یک فایل و یک خط ثبت لازم است. پیش‌فرض SMS_PROVIDER برابر relay می‌شود.
۳. host IP سرور درست است SMS_RELAY_HOST اختیاری است. اگر خالی باشد، مثل PHP اولین IPv4 غیرداخلی سرور فرستاده می‌شود.
۴. پاسخ با تشخیص خودم ok فقط وقتی است که HTTP 2xx باشد و بدنه‌ی JSON هم نشانه‌ی شکست صریح نداشته باشد (status: false، success: false، error). در غیر این صورت ok: false + لاگ؛ هیچ‌وقت throw نمی‌شود.
۵. HTTPS دارد بررسی شد: روی https://185.231.112.101 گواهی *.montpar.ir نصب است که از 2026-08-22 منقضی شده و روی IP هم معتبر نیست ← سؤال ب

سؤال‌های باز

  • الف) sendFromKave: کدام پیام‌ها از کاوه‌نگار بروند؟ پیشنهاد: فقط OTP ورود.
  • ب) آدرس HTTPS: نام دامنه‌ی سرویس (زیرمجموعه‌ای از montpar.ir) چیست؟ گواهی هم باید تمدید شود. تا آن موقع پیش‌فرض همان http://185.231.112.101/api/createsms می‌ماند و با SMS_RELAY_URL عوض می‌شود.

۶. وضعیت اجرا

  • تأیید طرح و پاسخ سؤال‌ها (الف: همه‌ی پیام‌ها از کاوه‌نگار؛ ب: همان IP و HTTP درست است)
  • کد + تست (apps/api/src/modules/sms/drivers/، ۱۳۵ تست سبز)
  • env / compose / DELIVERY / DECISIONS (D26) / ISSUES (REL-02)