feat(service): custom product, lịch sử giám sát VM và mã hoá response theo phiên
- Dịch vụ custom: bảng custom_products + services.custom_product_id; createService nhận
custom{} thay product_id, giá = đơn giá tháng x số tháng chu kỳ (1/3/6/12/24/36),
gia hạn tính theo cùng đơn giá, ServiceClientDetailResource trả custom_product và specs.
- Giám sát VM: GET v1/system/vm-metrics/history gom lịch sử về tối đa 240 điểm;
lỗi lấy dung lượng đĩa không còn làm hỏng cả thẻ metrics.
- SealResponse: AES-256-GCM theo khoá phiên do proxy Next cấp qua header x-seal-key
(HMAC của session id). Client opt-in bằng x-seal; chi tiết dịch vụ, mật khẩu VM và
console ticket luôn được mã hoá. Body gửi lên dạng {enc} được giải mã trước validate;
khoá lệch trả 428 để client lấy khoá mới. Đây là che mắt DevTools, không phải bảo mật
chống chính chủ tài khoản.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
8108e8fe53
commit
6315a392c0
@@ -47,8 +47,17 @@ class ServiceClientDetailResource extends JsonResource
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function resolveSpecs($vm, $product): ?array
|
||||
private function resolveSpecs($vm, $product, $custom = null): ?array
|
||||
{
|
||||
if ($custom) {
|
||||
return [
|
||||
'cpu' => (int) ($custom->cpu ?? 0),
|
||||
'ram' => round(($custom->ram_mb ?? 0) / 1024, 2),
|
||||
'disk' => (int) ($custom->disk_gb ?? 0),
|
||||
'bandwidth' => (int) ($custom->bandwidth_gb ?? 0),
|
||||
];
|
||||
}
|
||||
|
||||
if ($vm && ($vm->cpu !== null || $vm->ram_mb !== null || $vm->disk_gb !== null)) {
|
||||
return [
|
||||
'cpu' => (int) ($vm->cpu ?? 0),
|
||||
@@ -85,6 +94,8 @@ class ServiceClientDetailResource extends JsonResource
|
||||
$order = $orderItem?->order;
|
||||
$vm = $this->resource->vmware;
|
||||
$product = $orderItem?->product ?? $this->resource->product;
|
||||
$custom = $this->resource->customProduct;
|
||||
$renewalSource = $custom ?? $product;
|
||||
$hosting = $this->resource->hostingAccount;
|
||||
$pleskCredentials = $hosting ? $this->resolvePleskCredentials($hosting) : null;
|
||||
$dnsZone = $this->resolveDnsZone();
|
||||
@@ -102,18 +113,21 @@ class ServiceClientDetailResource extends JsonResource
|
||||
'version' => $orderItem?->os?->version ?? $vm?->os?->version,
|
||||
'group' => $orderItem?->os?->osGroup?->name ?? $vm?->os?->osGroup?->name,
|
||||
],
|
||||
'specs' => $this->type === 'hosting' ? null : $this->resolveSpecs($vm, $product),
|
||||
'specs' => $this->type === 'hosting' ? null : $this->resolveSpecs($vm, $product, $custom),
|
||||
'service_plan' => $this->type === 'hosting' ? $product?->servicePlan?->summary() : null,
|
||||
'custom_product' => $custom?->summary(),
|
||||
'order' => $order ? [
|
||||
'total' => (float) $order->total,
|
||||
'months' => (int) $orderItem->months,
|
||||
'billing_label' => $orderItem->billing_label,
|
||||
'cycle_price' => (float) ($product?->pricings()
|
||||
->where('billing_cycle', $orderItem->billing_cycle)
|
||||
->where('is_active', 1)
|
||||
->value('price') ?? 0),
|
||||
'cycle_price' => $custom
|
||||
? (float) $custom->unit_price * (int) $orderItem->months
|
||||
: (float) ($product?->pricings()
|
||||
->where('billing_cycle', $orderItem->billing_cycle)
|
||||
->where('is_active', 1)
|
||||
->value('price') ?? 0),
|
||||
'addon_total' => \App\Support\RenewalPricing::addonTotal($this->resource, 1),
|
||||
'renewal_options' => \App\Support\RenewalPricing::options($product),
|
||||
'renewal_options' => \App\Support\RenewalPricing::options($renewalSource),
|
||||
] : null,
|
||||
'vm' => $vm ? [
|
||||
'ip_address' => $vm->ipAddress?->ip ?? $vm->ip,
|
||||
|
||||
Reference in New Issue
Block a user