# 🍷 Changelog — پنل‌وینو (PanelVino)

## [۱۶٫۱٫۱] — ۲۹ شهریور ۱۴۰۵ (بسته‌ی نصب آماده‌ی سی‌پنل 📦 + فیکس بحرانی جادوگر روی بوت تازه)

### 📦 بسته‌ی نصب یک‌کلیکی (ZIP)
- **`scripts/build-dist.sh`** — ساخت خودکار `release/panelvino-vX-cpanel.zip`: فقط فایل‌های اجرایی (`app.js`، `src/`، `public/`، `agent/`، اسکریپت‌های بهره‌برداری، `docs/INSTALL.md`)، **بدون** `data/` و بدون هیچ سکرت/قفلی → جادوگر فارسی روی هاست اجرا می‌شود
- `package.json` اختصاصی پروداکشن داخل بسته: فقط **۶ وابستگی اجرایی** (express، better-sqlite3، jsonwebtoken، nodemailer، qrcode، web-push) — حذف puppeteer از نصب هاست (بدون دانلود ۱۷۰مگ Chromium روی هاست اشتراکی!) + **`package-lock.json`** برای نصب تکرارپذیر
- وابستگی‌های تست/توسعه (puppeteer، vue، apexcharts، vazirmatn، tailwindcss) به `devDependencies` منتقل شدند (همه vendored یا فقط‌تست‌اند) → `npm install --omit=dev` سبک و سریع
- فایل `.env.example` فارسی + `engines: node>=18` + `main: app.js`
- ✅ احراز سرتاسری روی بسته‌ی استخراج‌شده: `npm install ← بوت ← جادوگر ← نصب واقعی ← قفل ← ورود ادمین جدید`

### 🚨 فیکس بحرانی — جادوگر روی نصب تازه هرگز ظاهر نمی‌شد
- ریشه: `autoMigrateLock()` داخل `install(app,db)` اجرا می‌شد، یعنی **بعد از** require دیتابیس/auth؛ بوت اولِ نصب تازه خودش `data/.jwt-secret` و `panel.db` را می‌ساخت و چکِ مهاجرت، نصبِ **تازه** را «بالغ» تشخیص داده و قفل می‌کرد → روی هاست واقعی `/setup` همیشه ۴۱۰! (تست‌ها چون `JWT_SECRET` ست می‌کردند، این مسیر پوشیده می‌ماند — با احراز روی بسته‌ی واقعی کشف شد)
- راه‌حل: `migrateLockIfMature()` حالا در `server.js` **قبل از** `require('./db')` صدا زده می‌شود؛ نشانه‌ی بلوغ = وجود `.jwt-secret` یا `panel.db` از **قبل** از بوت (ارتقای نسخه‌های قدیمی همچنان امن قفل می‌شوند)
- `auth.js` و `security.js` حالا مسیر `data/` را با **`PANEL_DATA`** سازگار می‌کنند (سکرت JWT/CSRF کنار دیتابیس واقعی — مهم برای والیوم داکر)

## [۱۶٫۱٫۰] — ۲۹ شهریور ۱۴۰۵ (آسان‌نصب تحت وب برای هاست سی‌پنل 🛠️)

### 🛠️ جادوگر آسان‌نصب (نصب بدون SSH — مثل وردپرس)
- ماژول `src/setup.js`: تا وقتی `data/.install-lock` نیست، همه‌ی صفحات به **`/setup`** هدایت می‌شوند؛ بعد از نصب، مسیرها برای همیشه **۴۱۰/ریدایرکت** می‌شوند
- **قفل مهاجرت خودکار** برای نصب‌های بالغ فعلی (تشخیص با `.jwt-secret`) تا روی پنل‌های در حال کار جادوگر ظاهر نشود
- **قدم‌بندی ۴تایی با UI فارسی کامل** (وزیرمتن محلی، گرادیان برند، انیمیشن پلکانی): 🔍 بررسی خودکار محیط ← 🏪 برند و مدیر ← 🤖 ربات تلگرام (اختیاری) ← 🚀 نصب + صفحه‌ی موفقیت 🎉 با پولینگ سلامت و دکمه‌ی ورود
- **چک محیط** (`GET /api/setup/check`): نسخه‌ی Node ۱۸+، قابل‌نوشتن‌بودن `data/` و `.env`، سلامت SQLite، HTTPS توصیه‌ای، تشخیص محیط «سی‌پنل/Passenger»
- **نصب** (`POST /api/setup/install`): جایگزینی امن مدیر اصلی (ایمیل+رمز هش‌شده+نام پنل+دامنه)، ساخت سکرت‌های قوی `JWT_SECRET`/`PANEL_SECRET` و نوشتن `.env`، گزینه‌ی **«شروع تمیز ✨»** (حذف کامل داده‌های نمایشی دمو با حفظ پلن‌ها/گروه‌های آماده)، رویداد نصب، قفل فایل، **ری‌استارت خودکار** (Passenger/runner دوباره بالا می‌آورد و سکرت جدید لود می‌شود) — قابل غیرفعال‌سازی برای تست با `PANEL_SETUP_NOEXIT`
- **امنیت**: گارد CSRF موجود (جادوگر توکن را از `/api/csrf` می‌گیرد) + ریت‌لیمیت ۵ تلاش معتبر/۱۰دقیقه + اعتبارسنجی کامل سمت سرور (ایمیل/رمز/آدرس/توکن ربات)
- `src/env.js` — **لود خودکار `.env` بدون وابستگی** (برای سی‌پنل؛ اولویت با env واقعی) + پشتیبانی `PANEL_DATA`/`PANEL_ENV_PATH` برای ایزوله‌سازی
- `app.js` در ریشه — **Application startup file** آماده برای «Setup Node.js App» سی‌پنل
- راهنمای کامل نصب سی‌پنل (آپلود ← ساخت Node App ← باز کردن جادوگر) در ابتدای `docs/INSTALL.md`

### 🧪 تست
- **`scripts/test-setup.js` (۳۱ چک)** روی نمونه‌ی ایزوله (پورت موقت + دیتای tmp): گیت هدایت، چک محیط، ۵ سناریوی اعتبارسنجی + CSRF، نصب واقعی (قفل/.env/ادمین/شروع تمیز/رویداد)، گیت ۴۱۰ بعد از نصب، ورود واقعی با مدیر جدید
- **`scripts/test-setup-ui.js` (۱۶ چک پاپیتیر)**: رندر و فونت، چک‌لیست زنده، فرم چندمرحله‌ای با اعتبارسنجی مرورگری، نصب واقعی از UI، صفحه‌ی موفقیت + دکمه ورود، گیت پس از نصب

## [۱۶٫۰٫۰] — ۲۹ شهریور ۱۴۰۵ (فاز ۱۶: امنیت سخت‌گیرانه 🔐 + خوددرمانی نودها 🩺 + بکاپ ابری ☁️ + حالت دمو 🧪)

### 🤖 کپچای ورود (ضد Brute-Force)
- `GET /api/auth/captcha` ← سؤال جمع تصادفی + آی‌دی یک‌بارمصرف (TTL ۵د، استور حافظه‌ای با gc) — ماژول `src/passkeys.js`
- بعد از **۳ تلاش ناموفق** (همان شمارنده‌ی قفل ورود) ورود فروشنده/نماینده بدون `captcha_id+captcha_answer` رد می‌شود (`captcha_required:true`) — با رعایت سوییچ `captcha_enabled` در تنظیمات
- فرم ورود: بلوک کپچا با سؤال، فیلد پاسخ، دکمه‌ی 🔄 سؤال جدید — UI فقط وقتی سرور خواست ظاهر می‌شود

### 🌍 GeoIP سبک‌وزن روی تاریخچه‌ی ورود
- `src/geoip.js` — نگاشت رنج‌های پرکاربرد ایران (بدون وابستگی/دیتابیس)، لوکال (`LO`) برای IPهای خصوصی/لوکال، `XX` برای ناشناخته
- ستون `login_history.country` + برچسب در همه‌ی ثبت‌های ورود (موفق/ناموفق/پاسکی) + fallback روی رکوردهای قدیمی؛ ستون **«کشور 🌍»** در جدول تاریخچه‌ی صفحه امنیت

### 🔑 پاسکی — ورود یک‌لمسی بدون رمز (WebAuthn-lite)
- WebCrypto **ECDSA P-256**: کلید خصوصی روی مرورگر کاربر (localStorage) می‌ماند و فقط SPKI عمومی ثبت می‌شود — حداکثر ۸ کلید
- `POST /api/passkeys` (۴۰۰ کلید خراب / ۴۰۹ تکراری) · `GET` لیست · `DELETE` · `POST /api/auth/passkey/begin|complete` — چالش ۳۲ بایتی یک‌بارمصرف، تأیید امضای خام r‖s با `dsaEncoding:'ieee-p1363'`، ریت‌لیمیت مستقل، ورود با `ua='Passkey 🔑'` در تاریخچه
- UI: کارت «کلیدهای امنیتی» در صفحه امنیت (ساخت/نام‌گذاری/بدج «روی این دستگاه ✅»/حذف) + دکمه‌ی **«ورود با کلید امنیتی»** در صفحه ورود (چند کلید پشت‌سرهم امتحان می‌شوند)

### 🩺 خوددرمانی نودها (Self-Heal)
- هارت‌بیت ایجنت: اگر نود `down` بود و ۱۵ دقیقه از آخرین تلاش گذشته بود ← پاسخ `heal:{action:'restart'}` + `heal_count++` + رویداد 🩺؛ با گزارش `healed` به‌خط `active` برمی‌گردد (ضدِ اسپمِ ری‌استارت)
- ستون‌های `nodes.heal_count` / `last_healed_at` + **نشان «🩺 خوددرمانی ×N»** روی کارت نود

### ☁️ بکاپ ابری رمزنگاری‌شده
- `src/backup.js` — VACUUM INTO ← gzip ← **AES-256-GCM** با هدر `PVBK1` + کلید منحصربه‌فرد هر فروشنده (scrypt از secret پنل) — نمایش یک‌بارباره‌ی کلید از `GET /api/backups/cloud/key`
- `POST /api/backups/cloud/run` · `GET /api/backups/cloud` (۳۰ اخیر) · `GET /:id/download` · کرون ۳۰ دقیقه‌ای `tickCloudBackups` (روزانه ~۰۰:۰۳ تهران، فقط در صورت فعال‌بودن و نبود بکاپ هم‌روز) — مقصد `local` یا `stub-cloud` (آماده‌ی اتصال واقعی S3)
- نماینده ← ۴۰۳ روی همه‌ی مسیرهای بکاپ

### 🔄 آپدیت یک‌دکمه‌ای (guarded)
- `POST /api/system/self-update` — بدون رمز ۴۰۰ / رمز غلط ۴۰۱ (تأیید با auth.login) / بدون `PANEL_ALLOW_SELF_UPDATE=1` ← **۵۰۳ شفاف** با نسخه‌ی فعلی؛ با اجازه، `scripts/self-update.sh` جداشده اجرا می‌شود (pull → npm i → ری‌استارت با لاگ)

### 🧪 حالت نمایشی (Demo Mode)
- `POST /api/demo/seed` — ۴۰ کاربر واقع‌نما + فروش/تمدید پخش در ~۸۵ روز اخیر روی چند پلن (مارک `clients.demo=1` + فعال‌شدن `demo_mode`)، دوبار اجرا ← idempotent (`already`)
- `POST /api/demo/purge` — حذف کامل کاربران دمو + تراکنش/پلن/تیکت وابسته در تراکنش + خاموش‌شدن بنر
- **بنر کهربایی «حالت نمایشی»** بالای همه‌ی صفحات پنل وقتی فعال است

### 🎛️ تنظیمات + فیکس‌ها
- تب **«سیستم و بکاپ 🛡️»**: سوییچ کپچا، کارت بکاپ ابری (فعال‌سازی/مقصد/بکاپ دستی/کلید/لیست+دانلود)، کارت دمو (سید/پاک‌سازی)، کارت آپدیت
- **فیکس دیرینه‌ی فاز۱۰**: کارت «توکن‌های API» به‌خاطر replace شکسته (`'</div>`'` بدون مچ) هیچ‌وقت رندر نمی‌شد — بازنویسی با درج قطعی IIFE قبل از `</div>` انتهایی
- گارد جدید **`scripts/test-vue-compile.js`**: کامپایل واقعی هر ۳۰ قالب Vue با استاب DOM (دیکود entityها) — تله‌ی newline در template-literal را می‌گیرد (`npm run test:vuec`)

### 🧪 تست
- سوئیت **`scripts/test-phase16.js` (۵۷ چک)**: کپچا (۷)، GeoIP (۴)، پاسکی (۱۱)، خوددرمانی (۶)، بکاپ ابری با رمزگشایی واقعی SQLite (۹)، گارد آپدیت (۳)، دمو (۶)، UI استاتیک (۱۱) — رانر `npm run test:p16`

## [۱۵٫۰٫۰] — ۱۶ شهریور ۱۴۰۵ (فاز ۱۵: مهاجرت بی‌دردسر 🧩 + بهره‌وری اپراتور ⌨️ + تحلیل رشد 📊)

### 🧩 ایمپورتر پنل‌های رایج (مهاجرت یک‌مرحله‌ای)
- ماژول `src/importers.js` — سه فرمت جدید کنار CSV قبلی، همه در همان `POST /api/clients/import` (حالت `{format, text}`):
  - **🟣 مرزبان/مرزنشین**: خروجی `/api/users` — تبدیل خودکار `data_limit` بایت→گیگ و `expire` ثانیه→روز
  - **🔵 S-UI**: فایل بکاپ پنل — استخراج کلاینت‌ها از `settings` (رشته‌ی JSON) همه‌ی اینباندها؛ `totalGB` یا `total` بایت
  - **🟢 هیدیفای‌منیجر**: `usage_limit_GB` / `package_days` — نام‌های غیرلاتین به **یوزرنیم امن مهاجرت** (`m_xxxxxxxx`) تبدیل می‌شوند تا کاربر گم نشود
- **حفظ UUID پنل مبدأ** (تا کانفیگ‌های فعلی مشترک نشکند) — در برخورد تکراری، UUID جدید صادر و «هشدار» در گزارش ثبت می‌شود؛ پاسخ شامل `uuid_kept`
- مودال ایمپورت: انتخابگر ۴ فرمت + راهنمای زنده‌ی هر فرمت (نمونه‌ی JSON + نکته‌ی تبدیل واحدها)

### ⌨️ جستجوی سراسری + شرت‌کات‌ها (بهره‌وری اپراتور)
- **پالت `Ctrl/⌘+K` (یا `/`)**: جستجوی زنده (۲۵۰ms دیبانس) در اکانت‌ها/نمایندگان/پلن‌ها/فاکتور (شماره)/صفحات پنل — ناوبری کیبوردی ↓↑ Enter Esc، خروج به صفحه‌ی مربوط (اکانت ← با فیلتر مستقیم)
- `GET /api/search` — اسکوپ‌دار (نماینده فقط اکانت‌های خودش)، خنثی‌سازی wildcardهای LIKE، حداقل ۲ حرف
- **شرت‌کات `g d/c/p/i/s/a/t/r/n/w`** برای پرش بین صفحات + `?` مودال راهنما — غیرفعال هوشمند داخل فیلدهای متنی

### 🔔 مرکز اعلان
- زنگوله‌ی هدر با بدج خوانده‌نشده (پولینگ ۶۰ ثانیه‌ای)، دراپ‌داون ۳۰ رویداد آخر (از `events`)، «همه خوانده شد» با `sellers.notif_seen_at` (مهاجرت: رویدادهای تاریخی خوانده‌شده فرض شدند)
- `GET /api/notifications` + `POST /api/notifications/read` + `GET /api/notifications/unread-count` (برای نماینده هم فعال)

### 📊 داشبورد تحلیل رشد (`/growth`)
- `GET /api/growth` — KPI (فروش ماه + Δ٪ نسبت به ماه قبل، مشتری فعال/کل، ریزش، LTV میانگین خریدار، 신규 ماه)
- **کوهرت نگهداشت ۶ ماه** (۳۰/۶۰ روزه) — **نقشه‌ی حرارتی فروش ۷×۲۴ به وقت تهران** — پرفروش‌های ماه
- **هدف فروش ماهانه** (`sellers.sales_target` از PUT /settings) + نوار پیشرفت لحظه‌ای
- ویو جدید `growth.js` (روت 📈 + میان‌بر `g w`)

### 🧪 تست
- سوئیت **`scripts/test-phase15.js` (۴۸ چک)**: هر ۳ ایمپورتر (+UUID/تکراری/clash/خراب/رگرسیون rows/RBAC)، جستجو (محتوا/wildcard/اسکوپ نماینده)، اعلان (شمارنده/خواندن)، رشد (هیت‌مپ سلولی/KPI/کوهرت/هدف/RBAC)، UI استاتیک + **۸ چک مرورگر واقعی** (رندر رشد، پالت، زنگوله، راهنمای `?`) — رانر `npm run test:p15`

## [۱۴٫۰٫۰] — ۱۶ شهریور ۱۴۰۵ (فاز ۱۴: تم‌ساز ساب‌پیج 🎨 + ایمیل تراکنشی SMTP 📮)

### 🎨 تم‌ساز فروشگاه/ساب‌پیج (وایت‌لیبل بصری)
- `sellers.sub_theme` (JSON) + `src/theme.js`: پیش‌فرض‌ها، اعتبارسنجی هگز رنگ (`#rrggbb`)، clamp شعاع (۰..۲۸)، سنیتایز کامل متن‌ها (حذف تگ `<…>` + کاراکترهای خطرناک — جلوگیری از XSS روی ساب‌پیج)
- اندپوینت‌ها: `GET/PUT /api/theme` (فقط فروشنده؛ نماینده ۴۰۳) — تغییر تم `bustAll` کش ساب + رویداد `addEvent`
- تزریق **لایه‌ی override** در انتهای CSS صفحه‌ی اشتراک (~۱۰ سلکتور: رنگ اصلی/ثانویه، پس‌زمینه، کارت، گرادیان هیرو، شعاع گوشه‌ها، لینک‌ها، دکمه‌ها) — کم‌ریسک، بدون بازنویسی قالب
- ایموجی هیرو + زیرعنوان دلخواه + **`data-theme-default="dark"`** (پیش‌فرض تیره سمت سرور؛ ترجیح کاربر در localStorage همچنان اولویت دارد)
- تب **«سفارشی‌سازی 🧩»** در تنظیمات: انتخابگر رنگ + پیش‌نمایش زنده + بازنشانی

### 📮 ایمیل تراکنشی (SMTP اختیاری)
- `nodemailer` + ستون‌های `sellers.smtp_*` (رمز با `••stashed` ماسک می‌شود و در GET لو نمی‌رود) + جدول `email_log`
- سه رویداد قابل‌تنظیم: **welcome** (خوش‌آمد پس از ساخت کلاینت با ایمیل) • **receipt** (رسید خرید اعتباری/درگاهی) • **expiry** (هشدار نزدیک انقضا با dedupe ۲۰ساعته در کرون)
- 🧪 **حالت stub** (`smtp_host='stub'`): بدون سرور واقعی، ایمیل‌ها با وضعیت `stubbed` در لاگ ثبت می‌شوند — توسعه/تست بدون SMTP
- وضعیت‌های لاگ: `sent` / `stubbed` / `failed` / `disabled` / `bad-rcpt` — قالب HTML راست‌چین با برند فروشنده + CTA + استک وزیرمتن
- اندپوینت‌ها: `PUT /api/email/settings` • `POST /api/email/test` • `GET /api/email/log`

### 🧪 تست
- سوئیت **`scripts/test-phase14.js` (۲۵ چک)**: اعتبارسنجی/clamp/سنیتایز تم، تزریق زنده روی ساب‌پیج (رنگ/ایموجی/زیرعنوان/dark-default)، RBAC، چرخه‌ی کامل ایمیل (stub/test/welcome/receipt/expiry+dedupe/خاموشی رویداد/خطاها)، پاک‌سازی کامل — رانر `npm run test:p14`

## [۱۳٫۰٫۰] — ۱۵ شهریور ۱۴۰۵ (فاز ۱۳: خودکارسازی مالی — تمدید خودکار/تسویه ماهانه/سقف اعتبار)

### 🔄 اشتراک خودتجدیدشونده (تمدید خودکار از اعتبار)
- پرچم per-client روی کلاینت‌ها (`auto_renew`) — روشن/خاموش از **صفحه‌ی اشتراک** (تاگل «🔄 تمدید خودکار از اعتبار» در کارت فروشگاه) یا از پنل (`PUT /api/clients/:id`)
- کرون ۳۰ دقیقه‌ای (`tickAutoRenew`): پلنِ ≤۲۴ساعت به انقضا + اعتبار کافی ← تمدید خودکار + تراکنش `renew` + پیام تلگرام؛ قیمت از موتور `pricing` (فلش/VIP لحظه‌ای محترم!)
- **اعتبار ناکافی ← فقط یک هشدار در هر چرخه** (`auto_renew_warned_at`) و پس از شارژ، تمدید فوری
- 🔗 **سازگاری با موتور B3 ربات:** dedupe مشترک از طریق `reminder_log(kind='autorenew')` — هیچ سناریویی تمدید دوبل رخ نمی‌دهد

### 🚧 سقف اعتبار نماینده (سقف بدهی فاکتورهای باز)
- ستون `sub_sellers.credit_limit` (۰=نامحدود) + ویرایش از `PUT /api/sub-sellers/:id` (فقط فروشنده؛ خود نماینده ۴۰۳)
- **در نقطه‌ی فروش** (فروشگاه ساب‌پیج، هر دو روش اعتبار/درگاه): `بدهی فاکتورهای باز + سهم پایه‌ی پلن > سقف` ← ۴۰۳ با پیام شفاف و بدون هیچ تغییری در موجودی‌ها
- پیام خطا: مبلغ دقیق بدهی + سهم پایه + سقف — نماینده می‌فهمد باید تسویه کند

### 🧾 تسویه‌ی خودکار ماهانه‌ی نمایندگان
- `sellers.settle_day` (۰=خاموش؛ ۱..۲۸ از طریق `PUT /api/settings`) — در روز تعیین‌شده‌ی هر ماه (به وقت تهران) برای هر نماینده فاکتور «از پایان آخرین فاکتور تا الان» به‌صورت خودکار صادر می‌شود (اقلام از همان منطق صدور دستی + ارزش‌افزوده ۹٪)
- `settle_last_key` تک‌بارگی روزانه + **clamp بازه** (فاکتور آینده‌تاریخ چرخه را خراب نکند) + رویداد `addEvent`

### 🧪 تست
- سوئیت **`scripts/test-phase13.js` (۲۵ چک)**: چرخه‌ی کامل تمدید (مبلغ دقیق/کیف/تراکنش/outbox)، dedupe طبیعی + هشدار یک‌بار، مسدودی تمیز سقف اعتبار (بدون تغییر موجودی)، آزادی پس از بالا بردن سقف، صدور+تک‌بارگی تسویه، RBAC — رانر `npm run test:p13`


## [۱۲٫۰٫۰] — ۱۵ شهریور ۱۴۰۵ (فاز ۱۲: موتور اتومیشن بازیافت مشتری 🤖)

### قوانین «اگر–آنگاه» + کد تخفیف خودکار
- جداول `automation_rules` + `automation_log` (با `UNIQUE(rule_id,client_id,ref)` برای تک‌بارگی امن در برابر اجرای مکرر کرون)
- **۴ تریگر:** `usage_pct` (مصرف ≥ X٪ حجم پلن فعال) • `days_before_expiry` (X روز مانده) • `days_after_expiry` (بازگشت—پنجره‌ی X تا X+14 روز تا اسپم نشود) • `inactive_days` (X روز بدون ترافیک از `last_seen`)
- **اکشن:** پیام تلگرام با قالب و متغیرهای `{name} {username} {percent} {days} {remaining_gb} {plan} {coupon} {coupon_hint}` + ضمیمه‌ی خودکار **کد تخفیف یک‌بارمصرف** (درصد قابل‌تنظیم، پیشوند دلخواه)
- وضعیت‌های لاگ: `sent` / `queued` (بدون توکن ربات ← صندوق شبیه‌ساز) / `no-telegram` / `failed` — با جزئیات
- **پیش‌نمایش زنده‌ی مخاطبان** (بدون ارسال) با لیست نمونه + متن رندرشده + تفکیک تازه/ارسال‌شده
- کرون هر ۳۰ دقیقه (`tickAll`) + اجرای اول با تأخیر ۹۰ ثانیه + دکمه‌ی «اجرای الان» + رویدادهای `addEvent`
- API کامل `/api/automation/rules` (CRUD + audience + run + log) با اعتبارسنجی — فقط فروشنده (نماینده ۴۰۳)
- 🖥 رابط کاربری کامل «🤖 اتومیشن»: ساخت/ویرایش قانون، قالب آماده‌ی هر تریگر، پیش‌نمایش، لاگ، تاگل فعال، آمار اجرا/ارسال — با فونت وزیر و استایل پنل

### 🔧 سخت‌سازی
- رفع کرش پروسه روی خطای SQL در هندلرهای async (ریجکشن مداخله‌ناپذیر اکسپرس ۴ ← try/catch صریح + هندلر sync برای audience)

### 🧪 تست
- سوئیت **`scripts/test-phase12.js` (۲۸ چک)**: اعتبارسنجی، مخاطب‌یابی هر ۴ تریگر روی فیکسچر واقعی، کد ۱۵٪/۲۰٪ یک‌بارمصرف، رندر متغیرها، dedupe کامل در اجرای دوم، مشتری تازه ← کد متفاوت، PATCH/DELETE، بقای لاگ پس از حذف قانون، smoke کل کرون بدون ارسال تکراری، RBAC — رانر `npm run test:p12`


## [۱۱٫۰٫۰] — ۱۵ شهریور ۱۴۰۵ (فاز ۱۱: موتور رشد فروش ۲ — فلش‌فروش/باشگاه VIP/لینک خرید/چاپ فاکتور/جادوگر)

### ⚡ فروش فلش زمان‌دار
- هر پلن `flash_price` + `flash_ends_at` می‌گیرد؛ تا پایان مهلت همان قیمت اعتبار دارد و پس‌از آن **خودکار صفر** می‌شود (PUT/POST /api/plans اعتبارسنجی دارند)
- فروشگاه ساب‌پیج: `price_effective` + آبجکت `flash{price,ends_at}` + شمارش‌گر زنده‌ی `tickFlash` + استایل `.flashcd`؛ **ربات تلگرام** هم پلن فلش را با ⚡🔥 و قیمت فلش نشان می‌دهد
- قیمت نهایی سمت سرور از `src/pricing.js` (قیمت کلاینت هرگز اعتبار نیست)

### 🏅 باشگاه VIP (تخفیف خودکار بر اساس مجموع خرید)
- تنظیمات `sellers.vip_tiers` (JSON: آستانه‌ی خرید ← درصد)؛ `priceForClient` روی قیمت فلش/پایه درصد اعمال می‌کند؛ سربرگ VIP در ساب‌پیج (`.vip-head`)

### 🔗 لینک خرید مستقیم
- ویوی «لینک خرید» (`ViewPayLinks` + ناوبری `/pay-links`) برای ساخت لینک آماده‌ی خرید/شارژ جهت ارسال به مشتری

### 🖨️ فاکتور چاپی نماینده
- `GET /invoice-print/:id?token=JWT` — برگ A4 با برند فروشنده + «پلتفرم پنل‌وینو»، اقلام بازه، جمع جزء/تخفیف/ارزش‌افزوده/جمع کل، مُهر پرداخت‌شده/جاری، چاپ خودکار + دکمه‌ی چاپ، @media print
- احراز: فروشنده‌ی صادرکننده یا نماینده‌ی طرف فاکتور (توکن نامعتبر ۴۰۱، نماینده‌ی بیگانه ۴۰۳)

### 🧭 جادوگر راه‌اندازی
- جادوگر سلامت/راه‌اندازی برای راه‌اندازی سریع فروشگاه

### 🧪 تست
- سوئیت **`scripts/test-phase11.js` (۵۶ چک)**: ستون‌های فلش در DB، انقضای خودکار فلش، قیمت مؤثر فروشگاه، ⚡ ربات، دقت کیف‌پول در خرید فلش، چاپ فاکتور + کنترل سطح دسترسی، موتور شمارش فلش در ساب‌پیج، ثبت ویو و ناوبری — رانر `npm run test:p11` + مدخل بورد (۲۰ آیتم)

### 🔧 ترمیمات تست
- قالب چاپ فاکتور: برند پلتفرم فارسی «پنل‌وینو» اضافه شد (وایت‌لیبل: برند فروشنده بزرگ + پلتفرم کوچک)
- تست‌ها با جداکننده‌ی استاندارد locale فارسی (٬ U+066C) و ثبت کامپوننت به‌سبک spread هم‌راستا شدند؛ رجکس چک «\n در قالب ویو» اصلاح شد


## [۱۰٫۲٫۱] — ۱۵ شهریور ۱۴۰۵ (تایپوگرافی: فونت وزیرمتن محلی در همه‌ی صفحات 🔡)

- قاعده‌ی دائمی کاربر: «در صفحات وب فونت از نوع وزیر باشد» — **`docs/STYLE-GUIDE.md`**
- فونت **وزیرمتن** (npm `vazirmatn`) به‌صورت **محلی** در `public/fonts/` (Regular/Medium/Bold/ExtraBold + متغیر، woff2) + `fonts.css` با ۵ `@font-face` و `font-display: swap`
- **حذف کامل CDN گوگل‌فونتز** — پنل بدون اینترنت-خارجی هم با فونت درست رندر می‌شود؛ لینک فونت در هر ۶ تمپلیت وب (SPA/ساب‌پیج/PWA/درگاه سندباکس/pageLite/چاپ فاکتور) + نرمال‌سازی استک‌ها به `Vazirmatn,Tahoma,'Segoe UI',sans-serif`
- سوئیت **`scripts/test-fonts.js` (۱۷ چک)** با پاپیتیر: سرو فایل‌ها، computed font در صفحات ورود/ساب/وضعیت، لود وزن‌های ۴۰۰/۷۰۰/۸۰۰، صفر درخواست CDN — رانر `npm run test:fonts`


## [۱۰٫۲٫۰] — ۱۵ شهریور ۱۴۰۵ (قرارداد توسعه‌دهنده + امنیت میزبان: API Keys کامل، CSRF HMAC، قرنطینه‌ی کلید، نشست‌های نماینده)

### 🔑 قرارداد کلیدهای API — کامل و اجباری‌شده
- **مسیرهای قراردادی با کلید زنده شدند:** `GET/POST /api/clients` (اسکوپ admin) و `GET /api/announcements` (اسکوپ read) حالا هدر `X-API-Key` / `Bearer pvk_*` را می‌پذیرند (میانی‌افزار `apiKeyOrJwt` در `src/auth.js`) — قبلاً گارد سطح روتر `authRequired` جلوی کلید را می‌گرفت.
- **تفکیک گاردها در `routes-sales.js`:** `/clients` → `apiKeyOrJwt('admin')` (کلید admin یا JWT؛ با JWT نماینده همان لیست ایزوله‌ی قبلی)، `/transactions` → فقط JWT.
- **اطلاعیه‌ها:** GET با read/JWT، POST/DELETE با `apiKeyOrJwt('admin')` (در `routes-features.js`).
- **🚧 قرنطینه‌ی کلید (جدید در `server.js`):** هر درخواست دارای `pvk_*`/`pvsk_*` روی مسیری غیر از فهرست قراردادی (`/api/v1/*`, `/api/api-tokens*`, `/api/sub-token/*`, `/api/announcements`, `/api/clients*`, `/api/csrf`, `/api/health`, `/api/status`, `/api/openapi.json`, `/api/docs`) پاسخ **۴۰۳** می‌گیرد — نه ۴۰۱. (مطابق بند «کلید مجاز در مسیر نامتناسب → 403»)

### 🛡 CSRF — ترمیم باگ پورت هاست
- **`csrfValid` بازنویسی شد:** پارس قبلی با `split(':')` روی هاست‌های دارای پورت (`127.0.0.1:3000`) می‌شکست و توکن معتبر هم **۴۰۳** می‌گرفت. پارس جدید prefix/suffix است: `payload = host:day[:jti]` با تطبیق prefix هاست و suffix ایجتی‌آی — سناریوی «Origin معتبر + CSRF معتبر → ۲۰۰» سبز شد و کراس نیمه‌شب (سطل دیروز/امروز) حفظ است.

### 👥 قرارداد رسمی نشست‌های نماینده (spec فاز ۱۰)
- **`GET /api/auth/sub/sessions`** — لیست نشست‌های فعال خود نماینده (ایزوله از فروشنده؛ `current:true`)
- **`POST /api/auth/sub/sessions/revoke`** — ابطال یک نشست با `{jti}`
- **`POST /api/auth/sub/revoke-all`** — ابطال همه‌ی نشست‌ها + رویداد
- JWT فروشنده روی این مسیرها **۴۰۳** می‌گیرد. معادل‌های قبلی (`/security/status`، `/security/sessions/revoke`) دست‌نخورده باقی ماندند.

### 🐛 ترمیم رگرسیون
- `sellerOnly` که ناخواسته روی `GET/POST /api/clients` نشسته بود برداشته شد — نماینده با JWT دوباره کلاینت‌های خودش را می‌بیند و می‌سازد (دقیقاً مثل فازهای قبل؛ کلید API همچنان admin لازم دارد).
- **فرم‌های عمومی ساب‌پیج در برابر CSRF شکسته شده بودند** (باگ واقعی محصول — نه فقط تست): با روشن‌شدن گارد CSRF، POSTهای مرورگری صفحه‌ی اشتراک (تیکت/فروشگاه/گیفت‌کارت/خانواده/Push) چون کاربر ناشناس است ۴۰۳ می‌گرفتند. حالا خود ساب‌پیج هنگام بارگیری از `/api/csrf` توکن عمومی (امضاشده به میزبان) می‌گیرد و کمک‌کننده‌ی `pvCsrf()` آن را به همه‌ی fetchها (`api()` + `push/subscribe`) تزریق می‌کند — سوئیت مرورگر دوباره ۴۵/۴۵ با صفر خطای console.
- سوئیت مرورگر: کلیک روی چیپ رویداد وب‌هوک به داخل مودال محدود شد (آیتم جدید منوی «🔗 لینک خرید» که خارج مودال با regex «خرید» مچ می‌شد دور زده شد)؛ تست‌های legacy مسیر v1 قدیمی به `/v1-legacy/*` به‌روز شدند.

### 📚 مستندات
- **`docs/OPENAPI.md`:** تصحیح اسکوپ `GET /api/clients` (admin)، بند «قرنطینه‌ی کلید» در قرارداد خطاها، بخش جدید «نشست‌های نماینده — قرارداد /api/auth/sub/*».
- **`scripts/test-phase10-legacy.js`:** نگهداری سوئیت اصلی فاز ۱۰ (۳۸ تست: کمپین/مهاجرت/وضعیت/2FA).

### 🧪 تست
- **بازنویسی `scripts/test-phase10.js` ← ۴۷ تست:** ساخت/لیست/چرخش کلید، scope-گاردها (read→۴۰۳ روی admin)، قرنطینه‌ی کلید روی مسیر غیرقراردادی، v1 (ping/clients/users/resellers/accounts/apply)، کارت‌های sub_token (show/rotate/ابطال لینک قدیمی ۴۰۴)، ایمپورت مرزبان ردیف‌محور، CSV، CSRF چهارگانه (بدون توکن/Origin بد/توکن معتبر/معافیت کلید/بدون Origin)، هدرهای امنیتی، ایزولاسیون نماینده‌ی تازه.
- **`scripts/test-e2e-sub-refresh.js` ← ۲۶ تست:** ساخت نماینده با کلید ادمین → ورود → 2FA کامل (setup/enable/needs_totp/کد غلط/کد درست) → نشست‌ها (لیست/ابطال تکی/مسیرهای قراردادی spec/revoke-all) → غیرفعال‌سازی توسط فروشنده (ورود ۴۰۳ + مرگ توکن‌های قبلی) → بروت‌فورس sub-login ← ۴۲۹.
- `e2e-all.sh` ← بخش فاز ۱۰ سه‌گانه شد (قرارداد ۴۷ + چرخه‌ی نماینده ۲۶ + legacy ۳۸) و اسکریپت‌های `test:p10` / `test:p10-legacy` / `test:sub-refresh` به package.json اضافه شد — بورد ← **۱۸/۱۸**.

---

## [۱۰٫۱٫۰] — ۱۵ شهریور ۱۴۰۵ (پولیش: انیمیشن هیرو «فرود سر جای خود» + ترمیم روت پرداخت‌ها)

### ✨ انیمیشن ورود هیرو — محتوا با زنجیره‌ی پلکانی دقیقاً سر جای خود می‌نشیند
- **اصل مهندسی:** انیمیشن فقط روی `transform`/`opacity` با `animation-fill-mode: backwards` ⇒ باکس لای‌اوت هر عنصر از همان فریم اول رزرو است؛ هیچ ریفلویی رخ نمی‌دهد و **هیچ عنصری پس از پایان انیمیشن جابه‌جا یا خارج از محل طبیعی خود نیست** (فرود = ترنسفورم همانی صفر). «به‌رویٔ کاربر با کاهش‌حرکت» (`prefers-reduced-motion: reduce`) انیمیشن کاملاً خاموش می‌شود.
- **📱 صفحه‌ی اشتراک (`page()` سرور):** خودِ کارت هیرو + هر فرزندش (برند 🍷، نام کاربر، ۳ آمار، نوار مصرف) با تأخیر پلکانی ۰٫۱۰←۰٫۵۳ ثانیه بالا می‌آیند؛ نوار پیشرفت با `scaleX` از لبه‌ی راست رشد می‌کند (بدون تغییر عرض لای‌اوت)؛ کارت‌های بعدی هم با تأخیر ۰٫۱۵ ثانیه ورود نرم دارند — همان قواعد روی **📡 `/status`** و صفحات عمومی خطا نیز اعمال می‌شود.
- **🔐 صفحه‌ی ورود پنل:** لوگوی 🍷 با `pvPop` (عقب‌گرد فنری)، عنوان/زیرعنوان با `pvRise` و فرم لاگین با تأخیر ۰٫۳۰ ثانیه در محل خود فرود می‌آیند.
- **🛒 صفحات `pageLite`** (خرید لینک مستقیم/نتیجه‌ی پرداخت): کارت + سطرها پلکانی ورود می‌کنند.
- **🖼 گالری `docs/PREVIEW.html` + هر دو سازنده‌ی گالری:** هیرو (h1/p/badges/کارت‌ها) همان زنجیره را گرفت.

### 🐛 ترمیم قطعی میان‌فازی
- **`src/routes-gateway.js`:** تعریف روت `GET /api/gateway/payments` که در ویرایش ناتمام قبلی بلع شده بود بازگردانده شد (هدر + احراز + فیلتر فروشنده) و کامنت تکراری حذف شد — فایل دوباره `node --check` پاک و روت ۲۰۰ می‌دهد (در بورد تست رسمی شد).

### 🧪 تست
- سوئیت جدید **`scripts/test-hero-anim.js` (۳۴ چک، پاپیتیر)** با سه گرفت اثبات روی ۴ سطح (ساب/وضعیت/ورود/خرید): ۱) انیمیشن واقعاً در حال اجراست (`getAnimations()>0`) ۲) پس از پایان: `opacity=1` و جابه‌جایی صفر برای همه عناصر ۳) **یکسانی مختصات نهایی با رندر بدون‌انیمیشن با تلرانس <۱٫۵px** ← همان معیار «محتوا سر جای خود» — خروجی ۶ فریم اسکرین‌شات در `docs/screenshots/hero-anim/` + مدخل جدید در `e2e-all.sh` (بورد ← **۱۶/۱۶**) + اسکریپت `npm run test:hero`.

---

## [۱۰٫۰٫۰] — ۱۵ شهریور ۱۴۰۵ (فاز ۱۰: ترکیب برتر — کمپین/مهاجرت/وضعیت‌عمومی/2FA نماینده)

### 📣 کمپین پیام گروهی تلگرامی
- جدول `campaigns` + API کامل (`GET/POST /bot/campaigns`، پیش‌نمایش مخاطب `/audience`، پیگیری `/bot/campaigns/:id`) با سه سگمنت: **💤 غیرفعال/منقضی** (بازگرداندن مشتری)، **⏰ نزدیک انقضا ۷۲ساعت**، **👥 همه تلگرامی‌ها**
- ارسال آسنکرون با فاصله‌ی ۸۰ms (احترام به ریت‌لیمیت تلگرام) + شمارنده‌ی زنده‌ی موفق/ناموفق + رویداد شروع/پایان + UI کامل «📣 کمپین‌ها» (فرم با مخاطب‌شماری زنده، نوار پیشرفت، رفرش خودکار ۳ثانیه‌ای) — بدون توکن ربات خطای شفاف ۴۰۰، نماینده ۴۰۳

### 📥 مهاجرت از پنل‌های دیگر
- `POST /clients/import` (فروشنده، حداکثر ۵۰۰): هر ردیف `{username,password,traffic_gb,days,name}` ← یوزرنیم تکراری **رد می‌شود نه بازنویسی** (ایمنی داده) + پلن مهاجرت با حجم/اعتبار هر کاربر (type=`import`) + گزارش `created/skipped[reason]`
- خروجی CSV موجود سخت شد/رسمی: `GET /clients/export.csv` با اسکوپ نقش (نماینده فقط کاربران خودش) + دکمه‌های «📥 ایمپورت» و «⬇️ خروجی» در صفحه‌ی اکانت‌ها (مودال با راهنمای فرمت و گزارش ردها)

### 📡 صفحه‌ی وضعیت عمومی سرویس
- **`/status` (HTML عمومی)** + **`/api/status` (JSON)**: سلامت لحظه‌ای هر نود (هارت‌بیت)، پینگ RTT، CPU/RAM، **میله‌ی آپ‌تایم ۲۴ ساعته** از `node_metrics` فاز ۹ — بدون لو رفتن داده‌ی حساس؛ وایت‌لیبل روی دامنه‌ی نماینده (برند نماینده)؛ کش ۲۰ثانیه‌ای (`X-Sub-Cache`)؛ رفرش خودکار ۶۰ثانیه؛ لینک «📡 وضعیت سرویس» در فوتر ساب‌پیج

### 🔑 2FA برای نماینده‌ها (بستن آخرین خلأ امنیتی)
- ستون‌های `sub_sellers.totp_secret/totp_enabled` + لاگین نماینده با `needs_totp` (همان الگوی فروشنده) + همه‌ی اندپوینت‌های `/security/*` نقش‌آگاه شدند (status/setup/enable/disable/revoke برای نماینده با جدول وکلید لیمیتر خودش؛ `logins` همچنان فروشنده‌ای) + **ایزولاسیون نشست‌ها بر اساس `sub_seller_id`** + فرم لاگین UI کد را برای نماینده هم می‌فرستد + صفحه‌ی امنیت برای نقش نماینده فعال شد

### 🐛 رفع‌ میان‌فازی
- **الباطه‌ی قالب‌نویس Vue:** `split('\n')` در قالب رشته‌ای ⇒ خطای «Invalid or unexpected token» و خرابی کامل ویو (پیدا با پروب پاپیتیر) ⇒ `\\n` + رگرسیون مرورگری
- **`/api/status` ابتدا پشت راوتر ۴۰۴ ثبت شده بود** ⇒ انتقال منطق به `src/status.js` مشترک
- **مسیر کدغلط 2FA باید ۴۰۰ بدهد نه ۴۲۹** (۴۲۹ فقط هنگام قفل) + `reset` لیمیتر روی موفقیت + تالرانس سوئیت API برای قفل موازی سوئیت امنیت (ضد فلیکی‌بودن تست‌ها)
- تداخل روت دوبل‌کشف export.csv حل شد (نسخه‌ی اصلی اسکوپ‌دار نگه داشته شد)

### 🧪 تست
- سوئیت جدید **`test-phase10.js` (۳۸ چک)**: مخاطب‌شماری/هدف‌گیری سگمنت‌ها با شواهد outbox، RBAC، ایمپورت (ساخت/رد/بازنویسی‌نشدن)، اسکوپ CSV، وضعیت عمومی (ساختار/نشتی/کش MISS←HIT/باکت متریک)، 2FA نماینده سرتاسر — به‌علاوه **+۶ چک مرورگری فاز۱۰** (کمپین/ایمپورت/وضعیت/امنیت نماینده) ← مرورگر ۳۹ → **۴۵ چک** — رانر یکپارچه حالا **۱۴/۱۴** است (~۴۵۷ assertion)

---

## [۹٫۰٫۰] — ۱۵ شهریور ۱۴۰۵ (فاز ۹: تعمیق ربات تلگرام + فیلوور چندنود + پوشش کامل UI)

### 🤖 ربات تلگرام — از «اطلاع‌رسان» به «فروشنده‌ی کامل»
- **💰 کیف پول داخل ربات:** دکمه‌ی جدید منوی اصلی ← نمایش اعتبار فعلی + لینک شارژ صفحه‌ی اشتراک
- **🔄 تمدید فوری از داخل ربات:** زیر هر اکانت در «اکانت‌های من» دکمه‌ی تمدید ← پرداخت آنی از اعتبار کیف‌پول، ثبت پلن رزرو/فعال (`deliverPlan`)، نشست مبلغ به کیف‌پول فروشنده، رویداد + تراکنش مالی — مسیر ناموفق (اعتبار ناکافی/پلن هدیه) پیام راهنما با لینک شارژ می‌گیرد
- **📊 گزارش روزانه‌ی فروش (Digest):** کرون ساعت ۹ به وقت تهران ← فروش و درآمد ۲۴ساعت، مشتریان جدید، در شرف انقضای ۷۲ساعت، پرداخت‌های معلق، موجودی کیف‌پول پنل ← برای ادمین تلگرام؛ تاگل اختصاصی در تنظیمات ربات + ستون‌های `bot_daily_digest`/`last_digest_day` (یک‌بار در روز، بدون تکرار)

### 🌐 فیلوور چندنود — «پیشنهاد ما» دیگر نود مرده را نشان نمی‌دهد
- `/shop/:token/best` حالا **فقط نودهای زنده** (هارت‌بیت < ۱۸۰ ثانیه) را رتبه‌بندی می‌کند؛ با آفلاین شدن نود بهینه، به‌سرعت به نود سالم بعدی فیلوور می‌کند؛ اگر **همه** خاموش‌اند `best:null` + پرچم جدید `degraded:true` برمی‌گردد (قبلاً بی‌صدا یک نود مرده را بِج ⭐ می‌زد!) — پاسخ ساب‌پیج بدون تغییر شکل، مصرف‌کننده (`loadBest`) از قبل null-امن بود
- نود بدون اینباند فعال همچنان از چرخه خارج است (تست رسمی شد)

### 📈 متریک سری‌زمانی نود + نمودار سلامت
- جدول `node_metrics` (cpu/mem/disk/users_online) با **نمونه‌برداری تراستل‌شده** (~هر ۵۵ ثانیه) از هارت‌بیت ایجنت + هرس خودکار ۷۲ ساعت در کرون
- `GET /api/nodes/:id/metrics?hours=` (فروشنده‌ی صاحب نود؛ ۴۰۱/۴۰۴ محافظت‌شده) ← نقاط صعودی برای نمودار
- دکمه‌ی 📈 روی کارت هر نود ← مودال ApexCharts سه‌خطی (CPU/RAM/دیسک ۲۴ ساعت اخیر) با destroy صحیح

### 🧪 پوشش مرورگری UIهای فاز ۸ (خلأ قبلی بسته شد)
- صفحه‌ی رویدادها 📜 از مسیر واقعی مرورگر • **پیجر تراکنش‌ها** با ۵۵ ردیف سید (کلیک «بعدی» ← تغییر واقعی محتوا) • فیلد «دامنه‌ی اختصاصی» در فرم نماینده • گزینه‌ی «آقای‌پرداخت» در تب پرداخت تنظیمات — سوئیت مرورگر ۳۲ → **۳۹ چک**

### 🧪 تست
- دو سوئیت جدید: **`test-tgbot.js` (۲۲ چک)** — کیف‌پول/تمدید موفق و ناموفق با اثر مالی دقیق (اعتبار/والت/پلن رزرو/رویداد/تراکنش)، Digest با بای‌پس قطعی ساعت (`PANEL_DIGEST_ANYTIME=1`)، بلاک/رفع‌بلاک • **`test-failover.js` (۱۹ چک)** — فیلوور زنجیره‌ای سه‌نوده، degraded، حذف نود بی‌اینباند، ساب‌پیج در همه‌آفلاین، تراستل متریک، API سری، هرس کرونی — **رانر یکپارچه حالا ۱۳/۱۳ است (مجموع ~۴۱۳ assertion سبز)**

---

## [۸٫۰٫۰] — ۱۵ شهریور ۱۴۰۵ (فاز ۸: تقویت و آماده‌سازی پروداکشن)

### 🛡️ P0 — امنیت پروداکشن
- **ضد بروت‌فورس (rate-limit fail-count):** قفل موقت لغزنده پس از شکست‌های متوالی → لاگین (۱۰/۵د → ۱۰د قفل)، TOTP (۸)، حدس کد گیفت‌کارت (۱۲) — فقط **شکست‌ها** شمرده می‌شوند پس کاربر واقعی هرگز اذیت نمی‌شود؛ ایزوله‌ی کلید IP+اکانت؛ پاسخ ۴۲۹ با `retry_after`؛ قابل تنظیم با env (`PANEL_RL_*`)
- **هدرهای امنیتی:** `nosniff` • `Referrer-Policy: strict-origin-when-cross-origin` • `Permissions-Policy` • `X-XSS-Protection: 0` • HSTS خودکار روی HTTPS • `X-Frame-Options`+CSP frame-ancestors با `PANEL_FRAME_DENY=1` (پیش‌فرض غایب برای سازگاری با پیش‌نمایش‌های iframe)

### ⚙️ P1 — عملیات Go-Live
- **`GET /api/health/deep`** (فروشنده): `integrity_check` دیتابیس، حجم DB، فضای آزاد دیسک (`fs.statfs`)، نودهای stale هارت‌بیت، پرداخت‌های معلق، حافظه/آپ‌تایم — برای مانیتورینگ واقعی
- **لاگ خطا چرخان:** `src/rlog.js` — `console.error/warn` علاوه بر ترمینال در `data/logs/errors.log` با چرخش ۵MB×۳ (بدون وابستگی)
- **`scripts/preflight.sh`** (پیش‌پرواز ۱۲ چک: Node/RAM/دیسک/پورت/تلگرام/فایل‌ها/داکر) • **`scripts/backup-local.sh`** (VACUUM INTO آنلاین‌امن + gzip + نگهداری ۱۴ نسخه + خط کران) • **`docs/GO-LIVE.md`** چک‌لیست تعاملی استقرار ۱۰‌مرحله‌ای (~۳۰ دقیقه) با هشدارهای امنیتی دمو

### 💼 P2 — محصولی
- **دامنه‌ی اختصاصی وایت‌لیبل نماینده:** `sub_sellers.custom_domain` — ساب‌پیج با Host دامنه‌ی نماینده، **برند همان نماینده** را نشان می‌دهد (تایتل+بدنه) — راه‌اندازی CNAME در راهنما؛ فیلد در کارت نماینده + عادی‌سازی دامنه در API
- **درایور آقای‌پرداخت v2:** create/verify/callback کامل در کنار زرین‌پال/آیدی‌پی/زیبال/سندباکس + گزینه‌ی UI تنظیمات
- **کش رندر ساب‌پیج (۲۰ثانیه):** `src/subcache.js` — هدر `X-Sub-Cache: HIT` + **ابطال هدفمند** روی تغییر اینباندها (ساخت/ویرایش/حذف)
- **صفحه‌بندی صورتحساب:** `/transactions?page=&per=` ← `{rows,total,page,pages,per}` (بدون پارامتر: شکل قدیمی حفظ شد) + پیجر اختصاصی در UI (قبلی/بعدی + شماره‌ی صفحه‌ها)
- **صفحه‌ی رویدادها 📜 (Audit):** `GET /api/events` (فروشنده، فیلتر متنی) + ویوی کامل با آیکون‌بندی نوع رویداد و آیتم نوی‌بار
- **RTT پروب واقعی:** هر تیک بات، پنل با TCP-connect (مهلت ۱.۵s) زمان اتصال به پورت اینباند هر نود را اندازه می‌گیرد (`nodes.last_rtt_ms`) و «⭐ پیشنهاد ما» در امتیازهای مساوی، نود **کم‌تأخیرتر** را برمی‌دارد (+ فیلد `rtt_ms` در پاسخ)
  
### 🐛 رفع باگ‌های واقعی — فاز ۸
- **🔥 `PUT /sub-sellers/:id` بدون `is_enabled` نماینده را بی‌صدا غیرفعال می‌کرد!** (پرچم‌ها به‌جای `undefined→حفظ مقدار`، به صفر می‌افتادند) — با پروب DNS وایت‌لیبل کشف شد ⇒ برای `is_enabled`/`block_if_invoice_notpaid` پیش‌فرض = مقدار فعلی + **رگرسیون دائمی** در سوییت‌ی پروداکشن
- تزریق اشتباه require در `agentAuth` (نشت scope ایجنت)

### 🧪 تست
- دهمین سوییت خودکار: **پروداکشن (۴۲ چک)** — قفل بروت‌فورس با ایزولاسیون اکانت • هدرها • health/deep (۱۲+متریک) • audit API/UI • پیجر (ریاضی pages + سازگاری قدیمی) • کش HIT/MISS+ابطال واقعی با اینباند • برند دامنه (با http خام — undici هدر host را نادیده می‌گیرد!) • درایور جدید • تی‌بریک RTT با دو نود مصنوعی — رانر یکپارچه `npm run test:e2e` حالا **۱۱/۱۱** است (مجموع ~۳۶۵ assertion سبز)

---

## [۷٫۰٫۰] — ۱۱ شهریور ۱۴۰۵ (فاز ۷)

### ✨ افزوده‌شده — هر ۳۵ آیتم نقشه‌ی راه
- **ساخت و فروش پلن توسط نماینده (خواسته‌ی ویژه):** قالب سرویس فروشنده → ساخت پلن نماینده «از قالب» یا «کاملاً سفارشی» (با مجوز/سقف) → فروش در فروشگاه ساب‌پیج مشتری نماینده → **تسویه‌ی لحظه‌ای**: اصل پول به کیف‌پول فروشنده، مارجین به تراز نماینده + وایت‌لیبل `brand_name`
- **امنیت:** 2FA/TOTP (QR ستاپ، ورود دومرحله‌ای)، تاریخچه‌ی ورود، مدیریت نشست‌ها (خروج تکی/همه، پرچم نشست جاری)
- **فروشگاه/رشد:** خرید و تمدید از ساب‌پیج با اعتبار یا چهار درگاه، گیفت‌کارت دسته‌ای، بسته‌ی ترافیک، سیستم ریفرال با پورسانت درصدی، تمدید خودکار از کیف‌پول (کران + dedup)، گزارش مالی (ماهانه/پلن/نماینده) + خروجی CSV
- **پروتکل/زیرساخت:** Clash YAML و sing-box JSON با تایپ واقعی پروتکل‌ها، Hysteria2/TUIC (sing-box v1.9.7 روی نود)، Reality، multi-SNI، اولویت اینباند، بلاک تورنت (sniffing+rule)، ip_limit (پنجره‌ی ۹۰ثانیه + کیک + رویداد)، IPv6 dual-stack، Certbot با fallback به خودامضا (openssl)، مصرف روزانه (usage_daily)، پیش‌بینی اتمام حجم، مانیتور آپ‌تایم نودها، هشدار تلگرامی داون نود (ضداسپم ۶ساعته)، بکاپ شبانه‌ی دیتابیس به تلگرام (sendDocument)
- **تجربه‌ی مشتری:** تیکت پشتیبانی (ساب‌پیج/پنل/نماینده)، اطلاعیه‌ی زمان‌بندی‌شده، «⭐ پیشنهاد ما» بر اساس امتیاز سلامت نود، QR ایمپورت (shadowrocket/stash/hiddify)، تم تیره/روشن، سوییچ زبان فا/ان، اشتراک خانواده
- **عملیات:** وب‌هوک با امضای HMAC-SHA256 + دلیوری‌لاگ، API v1 با `x-api-key` + `/api/openapi.json` + `/api/docs`، Dockerfile + docker-compose، GitHub Actions (`e2e-all`)، آداپتور آزمایشی PostgreSQL (`db-pg.js`)

### 🐛 رفع باگ‌های واقعی
- **🔥 تسویه‌ی فاکتور عمومی به فاکتور اشتباه می‌رسید (باگ مالی P1):** در `/api/gateway/public/request` کوئری `SELECT i.*, s.*` بود و `s.id` جلوی `i.id` را می‌گرفت ⇒ `invoice_id` ذخیره‌شده = **شناسه‌ی فروشنده** بود نه فاکتور؛ پرداخت دومین فاکتور به‌بعد، فاکتور #۱ را paid می‌کرد و بدهی را با مبلغ اشتباه کم می‌کرد! (اثبات: فاکتور ۵٬۰۰۲٬۰۱۰ پرداخت شد ولی فاکتور ۴٬۵۴۴٬۲۱۰ تسویه شد — بدهی دقیقاً به‌اندازه‌ی اختلافشان مانده بود). چون فاکتور اول شانسی درست بود (id=۱ = seller=1) سال‌ها تست‌ها سبز می‌ماندند. ⇒ جوین بی‌فایده حذف شد؛ **رگرسیون دائمی** در سوییت‌ی کسب‌وکار (paid واقعی + بدهی دقیق + رفع‌بلاک + replay-safe)
- **🔥 سیستم ریفرال برای کلاینت‌های جدید مرده بود:** `ref_code` فقط در مهاجرت بوت برای کلاینت‌های قدیمی پر می‌شد و `INSERT` ساخت مشتری هرگز ستش نمی‌کرد ⇒ کد معرف هر مشتری جدید `null` بود و کسی نمی‌توانست معرف شود ⇒ `ref_code` در `createClient` و ساخت عضو خانواده تولید می‌شود؛ **رگرسیون دائمی** (ثبت معرف + پورسانت دقیق ۱۰٪ روی هر خرید)
- **🔥 نشت داده بین‌نقشی (RBAC):** با توکن نماینده، `/api/reports/finance` و `finance.csv` تمام مالیِ فروشنده (کیف‌پول ۳۰ میلیونی، فروش ماهانه، ریز فروش همه‌ی نماینده‌ها) را برمی‌گرداند و `/plans` `base_price` داخلی را لو می‌داد ⇒ `sellerOnly` روی گزارش مالی/اکسل + پلن/گروه/کوپن؛ **رگرسیون دائمی** در سوییت‌ی سخت‌افزاری (۱۲ مسیر × 403 + ۶ مسیر مجاز × 200)
- **🔥 شارژ مضاعف کیف‌پول در خرید درگاهی فروشگاه (باگ مالی P1):** تسویه‌ی درگاه هم «شارژ عمومی» می‌زد و هم قلاب فروشگاه سپلیت را اعتبار می‌داد (۹۸هزار خرید ⇒ ۱۹۶هزار رشد کیف‌پول — با پروب تجربی اثبات شد) ⇒ برای سفارش‌های `shop_plan` تسویه فقط از مسیر سپلیت (اصل→فروشنده / مارجین→نماینده) انجام می‌شود؛ خرید درگاهی پلن نماینده هم حالا دقیقاً base+margin=قیمت است؛ متن صفحه‌ی نتیجه‌ی پرداخت برای خرید فروشگاه «تحویل سفارش» شد + **رگرسیون دائمی** در سوییت‌ی هم‌زمانی
- **🔥 کرش نصب تازه (P1):** روی دیتابیس خالی، مهاجرت `gateway_ref` به جدول `online_payments` پیش از ساخته‌شدن خود جدول اجرا می‌شد و پنل هنگام اولین بوت کرش‌لوپ می‌کرد ⇒ `ensureColumn` حالا مهاجرت‌های زودهنگام را به‌تعویق می‌اندازد و پس از ساخت همه جدول‌ها اجرا می‌کند + سوییت‌ی دائمی بوت سرد (`scripts/test-coldstart.sh`) در CI
- **TDZ در رجیستر مهاجرت‌های معوق** — صف defer بالاتر از فراخوان migrate قرار گرفت
- **نقض FK** هنگام تحویل پلن نماینده: `client_plans.plan_id` به جدول `plans` اشاره می‌کرد که پلن نماینده در آن نیست ⇒ برای پلن نماینده `NULL` + snapshot نام — خطای ۵۰۰ خرید اعتباری پلن نماینده رفع شد
- **split-brain دیتابیس SQLite (WAL):** اتصال‌های کوتاه‌عمر بیرونی (تست‌ها/ابزارها) هنگام close، فایل `-wal` پروسه‌ی زنده را unlink و دو جهان ناسازگار می‌ساختند ⇒ مهاجرت به `journal_mode=DELETE` + `busy_timeout=5000` + واچداگ inode در `db.js` (خروج تمیز در صورت جایگزینی فایل DB توسط اسنپ‌شات)
- **سینتکس‌ارور کامل JS صفحه‌ی اشتراک:** نقل‌قول‌های تک‌لایه‌ی `\'` داخل template-literalِ `page()`، backslash را می‌بلعید (`buyPlan(3,'gateway')` → `buyPlan('+p.id+','gateway'))`) ⇒ اصلاح دو لایه + **گارد دائمی `vm.Script`** روی اسکریپت اینلاین خروجی واقعی در تست API
- **Clash/sing-box همه‌چیز را vless می‌ساخت:** متای تخت‌نشده ⇒ تخت‌سازی کامل meta در `clientConfigs` و اصلاح ارجاع‌های HTML + تست‌های **نوع‌محور** (assertion روی uuid/password/cipher/hy2/tuic)
- **سهمیه‌ی پلن سفارشی نماینده:** پلن‌های قالبی هم سقف را پر می‌کردند ⇒ شمارش فقط `template_id IS NULL`
- **نمودار گزارش مالی:** در برخی مسیرهای ناوبری `$refs.chart` دیرهنگام ثبت می‌شد و چارت خالی می‌ماند ⇒ callback-ref (`setChartEl`) + رندر idempotent با ارتفاع صریح
- **TDZ در agent.js:** `log` پیش از تعریف صدا زده می‌شد
- **قانون MATCH کلش** به گروه ناموجود ارجاع می‌داد ⇒ `MATCH,🚀 انتخاب خودکار`
- **کدهای خطای HTTP مبهم:** بدنه‌ی بزرگ ۵۰۰ می‌شد ⇒ هندلر خطا حالا استاتوس‌های معنادار Express را حفظ می‌کند (۴۱۳ حجم / ۴۰۰ JSON بدقواره) با پیام تمیز بدون نشت داخلی

### 🧪 تست
- نه سوییت خودکار: **بوت سرد نصب تازه** • **API (۱۱۴ چک)** • **کران‌ها (۱۲)** • **ایجنت (۲۱)** • **نشست چندمسیره** • **مرورگر headless با Puppeteer (۳۲)** • **هم‌زمانی/تمامیت مالی/بار (۴۴ چک)** • **جریان کسب‌وکار (۴۳ چک: تسویه‌ی فاکتور عمومی + بدهی/رفع‌بلاک، ریفرال ۱۰٪ دقیق، اطلاعیه‌ی زمان‌بندی‌شده، خانواده uuid مشترک + سقف اعضا، گیفت‌کارت پلن، ربات شبیه‌ساز)** • **سخت‌افزاری امنیت (۵۷ چک: جاروی ۲۴ مسیر، RBAC، SQLi/فاز/XSS، وب‌هوک مخرب، E8 دقیق، بازیابی بکاپ، v1)** — همه سبز و **تکرارپذیر**؛ رانر یکپارچه `npm run test:e2e` (**۱۰/۱۰**)
