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.
8.9 KiB
8.9 KiB
طرح افزودن درگاه پیامکی جدید (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
smsConfig()متغیرهای جدید را هم میخواند (جدول ۳.۲).smsConfigured()برایrelay: کافی استSMS_RELAY_SECRETوSMS_RELAY_HOSTپر باشند.assertSmsConfigured()مقدارrelayرا میشناسد و متغیرهای خالی را با نام گزارش میکند (بدون throw).- متد جدید
deliverViaRelay(mobile, text, platform):fetchباContent-Type: application/jsonو هدرsecret_key، بدنهی{ phone, host, text, platform }، timeout ۱۵ ثانیه باAbortController— همشکلdeliverViaTrez.- موفقیت = پاسخ HTTP 2xx (تا وقتی قالب پاسخ سرویس واسط مشخص شود — سؤال ۴).
SmsSendOptionsیک گزینهی اختیاریplatform?: 'kave'میگیرد؛ درایور Trez آن را نادیده میگیرد.- اجبار 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 نشود.
۴. هشدار امنیتی ⚠
- کلید
secret_keyلو رفته فرض شود. در متن پیام چت آمده و احتمالاً در git پروژهی PHP هم هست (مشابه SEC-12). باید از صاحب سرویس واسط کلید جدید گرفت و فقط در env سرور گذاشت. این کلید در کد این مخزن نوشته نمیشود. - ارتباط HTTP ساده است؛ کلید و متن پیام (از جمله کد OTP) رمزنشده از اینترنت عبور میکنند. اگر سرویس واسط HTTPS دارد از آن استفاده شود.
۵. سؤالها (با پیشنهاد)
- جایگزین یا کنار Trez؟ پیشنهاد: کنار Trez بماند و با
SMS_PROVIDER=relayانتخاب شود؛ پیشفرض همانtrezبماند تا رفتار فعلی عوض نشود. یا پیشفرضrelayشود؟ sendFromKaveکی درست است؟ در کد اصلی این متغیر از کجا مقدار میگیرد؟ پیشنهاد: فقط OTP ورود از کاوهنگار برود (معمولاً برای سرعت/تحویل بهتر) و بقیه از خط پیشفرض — قابل تنظیم با env.- مقدار
host(montek) چیست؟ پیشنهاد: اجباری در env. fallback به IP خود سرور پیاده نمیشود، چون داخل Docker، IP کانتینر (مثلاً172.x) برمیگردد که برای سرویس واسط بیمعناست. - پاسخ سرویس واسط چه شکلی است؟ اگر در خطا هم 200 برمیگرداند (مثلاً
{"status": false})، نمونهی پاسخ موفق و ناموفق لازم است تاokدرست تشخیص داده شود. پیشنهاد فعلی: فقط وضعیت HTTP. - 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)