کلود VPS عمومی (Provision واقعی)
> version: 2.14 | last_updated: 2026-09-26 | audience: admin
خلاصه
ماژولهای Provision برای خرید/تعلیق/حذف/ارتقا روی کلودهای عمومی — **بدون Sandbox**. بدون توکن فقط خطای شفاف «api_token تنظیم نشده».
وضعیت تأیید زنده (2026-09-23)
| ماژول | وضعیت | پوشش تست زنده |
|-------|--------|----------------|
| **`linode`** | ✅ کامل | create · پاور · reboot · وضعیت زنده · reinstall · تغییر رمز (خاموش خودکار) · Weblish کنسول · username=`root` — **تعویض IP در پنل نیست** |
| **`litenode`** | ✅ کامل | create (async) · پاور · وضعیت · reinstall async · تغییر رمز · username=`root`/`Administrator` — **تعویض IP در API نیست** |
چکلیست per-module: [cloud-vps-inventory-checklist.md](./cloud-vps-inventory-checklist.md)
افزونه منابع (`ram_mb` / `cores` / `disk_gb` / `num_ips` / `bandwidth`) از همان [`VpsResourceAddonService`](../../modules/Provisioning/Services/VpsResourceAddonService.php) اعمال میشود و `changePackage` را صدا میزند.
پایهٔ مشترک برای ارائهدهندگان REST جدید: `ConfigurableRestCloudClient` + `AbstractConfigurableCloudModule`.
پروکسی خروجی (SOCKS) برای API خارجی
همهٔ درخواستهای HTTP به کلودهای **خارجی** از `OutboundHttpProxy` رد میشوند (پیشفرض gost محلی):
| تنظیم | مقدار پیشنهادی |
|--------|----------------|
| `.env` → `PROVISIONING_FOREIGN_HTTP_PROXY` | `socks5h://127.0.0.1:11080` |
| جایگزین HTTP | `http://127.0.0.1:11081` |
| Bypass | `PROVISIONING_FOREIGN_HTTP_PROXY_BYPASS` — پیشفرض `arvancloud.ir,asiatech.cloud,asiatech.ir,.ir` |
روی سرور بیرونی میتوانید `http_proxy` را override کنید یا `direct` بگذارید تا همان ارائهدهنده مستقیم برود. آروان/آسیاتک و دامنههای `.ir` پروکسی نمیشوند.
اگر خطای `cURL error 28: Resolving timed out` دیدید: DNS از مسیر `socks5h` (gost → آپلینک) تایماوت شده؛ معمولاً لحظهای است. کلاینت کلود **یکبار retry** میکند و پیام فارسی واضح میدهد. پایدار نبود → وضعیت `gost` روی `127.0.0.1:11080` و آپلینک را چک کنید.
`AbstractBearerCloudClient` برای POST/PUT/PATCH بدون بدنه، JSON را بهصورت `{}` میفرستد (نه `[]`) — Linode روی `[]` خطای `Invalid JSON` / HTTP 400 میدهد (reboot، lish/console، shutdown، …). پیام خطا از `errors[].reason` خوانده میشود. تغییر رمز Linode: اگر سرور روشن باشد BSS خاموش → ریست رمز → روشن میکند.
پنل مدیریت سرویس (پورتال / ریسلر)
`VpsPanelEmbedService` برای همهٔ `PUBLIC_CLOUD_MODULES` حالت **native** میدهد (نه iframe). عملیات از `VpsHypervisorPanelService`:
| قابلیت | ارائهدهندگان |
|--------|----------------|
| پاور (روشن / خاموش / reboot) | همهٔ کلودهای `PUBLIC_CLOUD_MODULES` — از طریق `suspend`/`unsuspend` ماژول یا کلاینت reboot |
| وضعیت زنده (GET instance) | Hetzner · DO · Vultr · Linode · آروان · Contabo · LightNode |
| نصب مجدد OS (انتخاب ایمیج) | Hetzner · DO · Vultr · Linode · آروان · Contabo · LightNode |
| تغییر رمز ورود | LightNode / Linode / Proxmox (فرم) · Hetzner (تولید) · DigitalOcean (ایمیل) |
| کنسول | Hetzner (WSS) · Vultr (VNC host/port) · Linode (**Weblish روی دامنهٔ منطقهای** `*.webconsole.linode.com:8181` — نه `lish.linode.com`) — **DigitalOcean ندارد** (API عمومی مسیر کنسول ندارد؛ فقط کنترلپنل DO) |
کنسول بدون URL مستقیم وب → صفحهٔ `cloud-console-bridge` (رمز/WSS/host). بقیهٔ کلودها فعلاً پاور دارند؛ rebuild/console وقتی API پایدار باشد اضافه میشود.
UI: همان `vps-native-panel` — برای کلود فرم «نصب مجدد OS» با `image_options` از موجودی سرور. کارت وضعیت: IP · Login · **Password** (از `root_password`/`password` ذخیرهشده، با دکمه کپی) · OS · Provider · VM ID.
نام کاربری ورود (SSH)
روی کلود عمومی **hostname ≠ username**. `ManagesPublicCloudVps::resolveGuestUsername` و `PublicCloudGuestLogin` همیشه `root` (لینوکس) یا `Administrator` (ویندوز) یا `guest_username` محصول را میگذارند — نه مقدار فیلد hostname سفارش. اگر دادهٔ قدیمی اشتباه (`username === hostname`) باشد، اولین باز شدن پنل VPS آن را heal میکند.
گیج Resources (CPU / RAM / Disk / Uptime)
| منبع | چه چیزی نشان داده میشود |
|------|---------------------------|
| **Proxmox** | مصرف لحظهای CPU٪ / RAM٪ / Disk٪ + uptime از boot (`metrics_live=true`) |
| **کلود عمومی** (Hetzner، DO، Vultr، Linode، LightNode، …) | API معمولاً فقط **ظرفیت اختصاصیافته** میدهد؛ گیجها با برچسب `Allocated capacity` پر میشوند |
| **LightNode** | `instance/detail` فیلد usage ندارد؛ Uptime از `createTime` بهصورت **سن نمونه** (Since created) محاسبه میشود — نه uptime از آخرین boot |
پیام راهنما زیر عنوان Resources وقتی `metrics_live=false` نمایش داده میشود.
LightNode — موجودی زنده (ADR-053) · ✅ تست زنده کامل
کلید ماژول در BSS: `litenode` (سازگاری لایسنس). API واقعی: [apidoc.lightnode.com](https://apidoc.lightnode.com/en)
**تأیید:** provision + پنل بومی مشتری (پاور / reinstall async / رمز / نمایش login) روی محیط واقعی انجام شده.
| محل | رفتار |
|-----|--------|
| سرور بیرونی | `api_token` → هدر `x-open-token` · `api_url=https://openapi.lightnode.com` |
| محصول | Select: region←`regionCode\|zoneCode` · server_type←packageCode · image |
| سفارش | hostname · region · image — بدون packageCode |
| create | `POST /instance/create` با `packageConfig`؛ شناسه = `ecsResourceUUID` |
| ورود | یوزر سیستمعامل = `root` (لینوکس) / `Administrator` (ویندوز) — **نه** hostname؛ hostname فقط نام instance است |
| وضعیت پاور | `ecsStatus` + `ecsPendingStatus` → برچسب فارسی (آنلاین / خاموش / در حال خاموش شدن — نه متن خام `PENDING`) |
| نصب مجدد | انتخاب **صریح** ایمیج مقصد (بدون پیشانتخاب OS فعلی و بدون fallback به ایمیج فعلی). اگر روشن باشد: stop + صف `awaiting_power_off`. قبل از `reinstallSystem` وضعیت STOPPED چک میشود؛ sshKey ارسال نمیشود (فقط password). بعد از SUCCESS، `imageResourceUUID` زنده با هدف مقایسه میشود |
| تعویض IP | **در OpenAPI LightNode endpoint تعویض/جایگزینی IP عمومی نیست**؛ IP فقط با ساخت instance جدید عوض میشود (release + create) |
Client: اگر بدنهٔ JSON با `success=false` یا `httpStatus>=400` بیاید حتی روی HTTP 200، درخواست fail میشود.
آروان — موجودی زنده از API (مثل Proxmox)
با ماژول `arvancloud` و **گروه سرور** (الزامی):
| محل | رفتار |
|-----|--------|
| سرور بیرونی | فقط `api_token` (+ پیشفرض `api_url=https://napi.arvancloud.ir`) |
| محصول → تب Provision | Select زنده برای `region` / `server_type` / `image` / `network_id` از API |
| محصول → فیلدهای سفارش | خودکار: `hostname`، `region`، `image` — **بدون** `server_type` |
| خرید مشتری | region و image را انتخاب میکند؛ پلن از تنظیم محصول میآید |
فیلدهای تنظیم Provision محصول آروان (فقط همینها):
`region` · `server_type` · `image` · `network_id` · `num_ips` · `ssh_key_id` · `guest_username` · `password_type` · `root_password`
`server_type` (Flavor) فقط روی محصول است — برای ساخت، ارتقا، کاهش و تمدید. مشتری انتخاب نمیکند.
`cores` / `ram_mb` / `disk_gb` از flavor میآید و در فرم محصول آروان نشان داده نمیشود. `shahkar_service_type` جدا از جدول Provision است.
اگر Selectهای API خالی است: **گروه سرور را انتخاب کنید** (باید سرور فعال `arvancloud` داخل گروه باشد).
اولویت resolve در provision:
سرویسها: `PublicCloudInventoryOptions` · `PublicCloudOrderCustomField`
API آروان:
Hetzner Cloud — موجودی زنده (ADR-053)
با ماژول `hetzner` و **گروه سرور** (الزامی):
| محل | رفتار |
|-----|--------|
| سرور بیرونی | `api_token` Bearer (+ پیشفرض `api_url=https://api.hetzner.cloud/v1`) |
| محصول → تب Provision | Select زنده: `region`←locations · `server_type`←server_types · `image` |
| فیلدهای محصول | `region` · `server_type` · `image` · `num_ips` · `ssh_key` · اعتبار — بدون cores/ram/disk |
| فیلدهای سفارش | `hostname` · `region` · `image` — **بدون** server_type |
API: `GET /locations` · `/server_types` · `/images?type=system`
لیستها تقریباً مستقلاند؛ `server_type` قفل محصول برای create/change_type است.
DigitalOcean — موجودی زنده (ADR-053)
با ماژول `digitalocean` و گروه سرور:
| محل | رفتار |
|-----|--------|
| محصول | Select: region · server_type←size · image؛ گزینههای backups/ipv6 |
| سفارش | hostname · region · image — بدون size |
| وابستگی | با انتخاب region، فقط sizeهای همان منطقه |
API: `GET /regions` · `/sizes` · `/images?type=distribution`
**کنسول:** دکمه Console در پنل BSS برای DO نمایش داده نمیشود. مسیر قدیمی `POST /v2/droplets/{id}/remote_consoles` در API عمومی DigitalOcean وجود ندارد (`404 route not found`). Web Console / Recovery Console فقط از داخل کنترلپنل DigitalOcean در دسترس است؛ از پنل BSS از SSH استفاده کنید.
Linode / Akamai — موجودی زنده (ADR-053) · ✅ تست زنده کامل
ماژول `linode`: `GET /regions` · `/linode/types` · `/images` — قفل type روی محصول؛ CF: region + image.
| قابلیت پنل | رفتار تأییدشده |
|------------|----------------|
| پاور / reboot | POST با بدنهٔ `{}` (نه `[]`) |
| کنسول | `POST …/lish` → Weblish روی `*.webconsole.linode.com:8181` (صفحهٔ BSS + xterm) — نه `lish.linode.com` |
| تغییر رمز | در صورت روشن بودن: خاموش → ریست → روشن |
| نام کاربری | همیشه `root` / `Administrator` — نه hostname |
| تعویض IP | در پنل BSS نیست (API Linode هم تعویض یککلیکی IP اصلی ندارد) |
**تأیید:** create + پنل بومی روی instance واقعی (مثلاً `gb-lon`) انجام شده.
Vultr — موجودی زنده (همتراز WHMCS رسمی)
ماژول `vultr`: `GET /regions` · `/plans` · `/os` — plan قفل محصول؛ CF: region + os_id؛ فیلتر plan بر اساس region.
Contabo — موجودی زنده (ADR-053)
| محل | رفتار |
|-----|--------|
| اتصال | OAuth: client_id · client_secret · api_user · api_password |
| محصول | Select: region←data-centers · server_type←productId (کاتالوگ ثابت) · image · period |
| سفارش | hostname · region · image — بدون productId |
| پلن | Contabo API کاتالوگ create ندارد → `ContaboClient::catalogProducts()` |
API: `GET /v1/data-centers` · `/v1/compute/images?type=standard`
OVHcloud — موجودی زنده (ADR-053)
| محل | رفتار |
|-----|--------|
| اتصال | application_key · application_secret · consumer_key · **project_id** |
| محصول | Select: region · server_type←flavorId · image · monthly_billing · num_ips |
| سفارش | hostname · region · image — بدون flavorId |
| وابستگی | flavor و image با `?region=` فیلتر میشوند |
API: `GET /cloud/project/{id}/region` · `/flavor?region=` · `/image?region=`
ابر آسیاتک — موجودی (provisional / ADR-053)
| محل | رفتار |
|-----|--------|
| اتصال | Bearer `api_token` · `api_url` (پیشفرض api.asiatech.cloud) |
| محصول | region · server_type←plan · image · num_ips — بدون cores/ram/disk |
| سفارش | hostname · region · image |
| مسیرها | `v1/regions` · `v1/plans` · `v1/images` — قابل override با `inventory_paths` |
**توجه:** سند عمومی API منتشر نشده؛ مسیرها از قرارداد create فعلی گرفته شدهاند. با توکن زنده تأیید کنید.
Gcore — موجودی زنده (ADR-053)
| محل | رفتار |
|-----|--------|
| اتصال | `apikey` + **project_id** |
| محصول | region←id · server_type←flavor_name · image |
| سفارش | hostname · region · image |
| وابستگی | flavors/images زیر `project_id` + `region_id` |
API: `GET /cloud/v1/regions` · `/flavors/{project}/{region}` · `/images/{project}/{region}`
IONOS — موجودی زنده (ADR-053)
| محل | رفتار |
|-----|--------|
| اتصال | Bearer یا Basic (email+token) · `datacenter_id` پیشفرض |
| محصول | region←datacenter UUID · server_type←templateUuid · image |
| سفارش | hostname · region · image |
| وابستگی | ایمیجها با `location` دیتاسنتر فیلتر میشوند |
API: `GET /datacenters` · `/templates` · `/images`
Aeza — موجودی زنده (ADR-053)
| محل | رفتار |
|-----|--------|
| اتصال | `X-API-Key` |
| محصول | region←group · server_type←productId · image←os · period |
| سفارش | hostname · region · image |
| create | `POST /services/orders` |
API: `GET /services/groups` · `/services/products` · `/os`
Alibaba / Aruba / فاز۴ / Hyperscalers
فاز ۱–۴ ماژولها
| کلید | ارائهدهنده | احراز |
|------|-------------|-------|
| `hetzner` / `digitalocean` / `vultr` / `linode` | کلود عمومی | Bearer |
| `contabo` / `ovh` / `arvancloud` / `abrasiatech` | فاز ۲ | OAuth / Apikey / Bearer |
| `aws` / `gcp` / `azure` | فاز ۳ | کلیدهای ابری |
| `gcore` / `ionos` / `aeza` … `ipxon` | فاز ۴ REST | Bearer / X-API-Key |
با انتخاب ماژول در سرور بیرونی، `api_url` / `verify_ssl` / `timeout` از `connectionFieldDefaults` پر میشود.
محدودیتها
راهاندازی آروان
تست
```bash
php artisan test --filter='InventoryTest|RemainingCloudInventoryTest|PublicCloudVpsPanelTest'
```