Refactor deployment process and enhance VM IP reservation logic; update email template for VM creation failure notifications

This commit is contained in:
baodev0
2026-07-13 11:53:00 +07:00
parent c1008d579c
commit 3403cb9ec5
4 changed files with 223 additions and 82 deletions
+4 -2
View File
@@ -58,9 +58,11 @@ jobs:
exit 1
fi
composer install --optimize-autoloader --no-interaction --no-dev
composer install
php artisan migrate --force
composer dump-autoload
php artisan migrate
php artisan config:clear
php artisan optimize
@@ -24,18 +24,18 @@ class VmIpReservationService
public function reserve(int $networkId, int $hostId, string $dns): ?array
{
return DB::transaction(function () use ($networkId, $hostId, $dns) {
// ponytail: exclude only IPs with an actual VM assigned, not stale __pending__ reservations
$usedIps = ModuleIpAddress::where('module_network_id', $networkId)
->where('vm_name', '!=', '__pending__')
// Exclude IPs that have any active reservation (real VM or pending).
// Also accept status='reserved' in ip_pools so stale reservations
// (ip_pool row stuck as 'reserved' with no matching module_ip_addresses row)
// are recoverable without manual DB cleanup.
$allUsedIps = ModuleIpAddress::where('module_network_id', $networkId)
->lockForUpdate()
->pluck('ip')
->toArray();
$ipPool = IpPool::where('module_network_id', $networkId)
->where('status', 'free')
->whereNotNull('gateway')
->where('gateway', '!=', '')
->whereNotIn('ip_address', $usedIps)
->whereIn('status', ['free', 'reserved'])
->whereNotIn('ip_address', $allUsedIps)
->lockForUpdate()
->inRandomOrder()
->first();
@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
public function up(): void
{
// Remove gateway IP (103.90.224.1) — không được cấp cho VM
DB::table('ip_pools')
->where('module_network_id', 1)
->where('ip_address', '103.90.224.1')
->delete();
// Backfill gateway cho toàn bộ pool network_id=1
DB::table('ip_pools')
->where('module_network_id', 1)
->whereNull('gateway')
->update(['gateway' => '103.90.224.1']);
}
public function down(): void
{
DB::table('ip_pools')
->where('module_network_id', 1)
->update(['gateway' => null]);
}
};
@@ -1,74 +1,184 @@
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thông báo lỗi khởi tạo máy chủ</title>
<style>
* { box-sizing: border-box; }
body { font-family: 'Segoe UI', Arial, sans-serif; background: #f0f4f8; margin: 0; padding: 0; }
.wrapper { max-width: 620px; margin: 30px auto; }
.card { background: #fff; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.08); }
.header { background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%); padding: 36px 40px; text-align: center; }
.header .icon { font-size: 48px; margin-bottom: 12px; }
.header h1 { color: #fff; margin: 0 0 6px; font-size: 24px; font-weight: 700; }
.header p { color: #fecaca; margin: 0; font-size: 14px; }
.body { padding: 36px 40px; }
.greeting { font-size: 16px; color: #374151; margin-bottom: 20px; line-height: 1.6; }
.section-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: #9ca3af; margin: 28px 0 10px; }
.info-table { width: 100%; border-collapse: collapse; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; }
.info-table td { padding: 10px 16px; font-size: 14px; border-bottom: 1px solid #f3f4f6; }
.info-table tr:last-child td { border-bottom: none; }
.info-table td:first-child { color: #6b7280; width: 45%; }
.info-table td:last-child { font-weight: 600; color: #111827; }
.error-box { background: #fef2f2; border: 1.5px solid #fca5a5; border-radius: 10px; padding: 16px 20px; margin-top: 12px; font-size: 14px; color: #991b1b; line-height: 1.6; }
.action-box { text-align: center; margin-top: 28px; }
.btn { display: inline-block; background: #2563eb; color: #fff; text-decoration: none; padding: 12px 28px; border-radius: 8px; font-weight: 600; font-size: 15px; }
.notice { background: #eff6ff; border-left: 3px solid #3b82f6; padding: 12px 16px; border-radius: 0 6px 6px 0; font-size: 13px; color: #1e40af; margin-top: 24px; line-height: 1.6; }
.footer { text-align: center; padding: 20px 40px; border-top: 1px solid #f3f4f6; font-size: 12px; color: #9ca3af; }
</style>
</head>
<body>
<div class="wrapper">
<div class="card">
<div class="header">
<div class="icon">⚠️</div>
<h1>Lỗi khởi tạo máy chủ ảo</h1>
<p>Đơn hàng #{{ $orderNum ?? 'N/A' }} - Cần xử lý</p>
</div>
@php
$appName = $appName ?? config('app.name', 'Thế Giới Số');
$fullName = $customerName ?? 'Quý khách';
$orderNum = $orderNum ?? 'N/A';
$productName = $productName ?? 'N/A';
$invoiceUrl = $invoiceUrl ?? rtrim((string) env('FRONTEND_URL', config('app.url')), '/') . '/hoa-don';
$supportEmail = $supportEmail ?? 'support@tgs.com.vn';
$supportPhone = $supportPhone ?? '1900. 6119';
@endphp
<div class="body">
<p class="greeting">
Xin chào <strong>{{ $customerName ?? 'Quý khách' }}</strong>,<br>
Rất tiếc, hệ thống gặp sự cố khi khởi tạo máy chủ ảo cho đơn hàng của bạn. Đội ngũ kỹ thuật của chúng tôi đã được thông báo và sẽ xử lý sớm nhất có thể.
</p>
<div class="section-label">Thông tin đơn hàng</div>
<table class="info-table">
<tr><td>Mã đơn hàng</td><td>#{{ $orderNum ?? 'N/A' }}</td></tr>
<tr><td>Sản phẩm</td><td>{{ $productName ?? 'N/A' }}</td></tr>
</table>
<div class="section-label">Chi tiết lỗi</div>
<div class="error-box">
{{ $errorMessage ?? 'Lỗi không xác định' }}
</div>
<div class="action-box">
<a href="{{ $invoiceUrl ?? '#' }}" class="btn">Xem đơn hàng của bạn</a>
</div>
<div class="notice">
Nếu cần hỗ trợ khẩn cấp, vui lòng liên hệ:<br>
📧 Email: <strong>{{ $supportEmail ?? 'support@tgs.com.vn' }}</strong><br>
📞 Điện thoại: <strong>{{ $supportPhone ?? '' }}</strong>
</div>
</div>
<div class="footer">
Email này được gửi tự động. Vui lòng không trả lời email này.
</div>
</div>
</div>
</body>
</html>
<table style="border-collapse: collapse; width: 100%; background-color: #efefef;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="padding: 20px;" align="center" valign="top">
<table style="padding-top: 20px; padding-bottom: 20px; border-collapse: collapse; background-color: #ffffff; width: 700px;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr class="NoiDungKM">
<td style="width: 700px;" valign="top">
<table style="border-collapse: collapse; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="background-color: #f06824; padding: 34px 30px 30px; text-align: center; font-family: Arial, Helvetica, sans-serif; color: #ffffff;" align="center" valign="top" bgcolor="#f06824">
<div style="font-size: 42px; line-height: 1; margin-bottom: 12px;">⚠️</div>
<h1 style="margin: 0 0 8px; padding: 0; color: #ffffff; font-family: Arial, Helvetica, sans-serif; font-size: 24px; line-height: 1.35; font-weight: 700;">
Thông báo về đơn hàng #{{ $orderNum }}
</h1>
<div style="font-size: 14px; line-height: 1.6; color: #ffffff;">
Đội ngũ kỹ thuật đang xử lý yêu cầu của Quý khách
</div>
</td>
</tr>
<tr class="MXH">
<td style="text-align: center; padding: 0 30px;" align="center" valign="top">
<table style="border-collapse: collapse; background-color: #ffffff; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="height: 15px; font-family: Arial, Helvetica, sans-serif; font-size: 14px; line-height: 1.7; color: #000000;" valign="top" height="15">
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: left;">Xin chào <strong>{{ $fullName }}</strong>,</div>
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: left;">
Chúng tôi đã nhận được yêu cầu khởi tạo dịch vụ cho đơn hàng của Quý khách. Hiện tại hệ thống đang trong quá trình xử lý và đội ngũ kỹ thuật của <strong>{{ $appName }}</strong> đã được thông báo để hỗ trợ nhanh nhất có thể.
</div>
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: left;"><strong>Thông tin đơn hàng:</strong></div>
<div style="text-align: left;">&nbsp;&nbsp;&nbsp;&nbsp;+ Mã đơn hàng: <strong>#{{ $orderNum }}</strong></div>
<div style="text-align: left;">&nbsp;&nbsp;&nbsp;&nbsp;+ Sản phẩm: <strong>{{ $productName }}</strong></div>
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: left;">
Quý khách không cần thực hiện thêm bất kỳ thao tác nào. Chúng tôi sẽ liên hệ lại khi dịch vụ được kích hoạt thành công.
</div>
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: center;">
<a style="display: inline-block; background-color: #f06824; color: #ffffff !important; text-decoration: none; padding: 12px 28px; border-radius: 3px; font-family: Arial, Helvetica, sans-serif; font-size: 14px; font-weight: 700;" href="{{ $invoiceUrl }}" target="_blank" rel="noopener">
Xem đơn hàng của bạn
</a>
</div>
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: left;">
Nếu cần hỗ trợ khẩn cấp, Quý khách vui lòng liên hệ qua email <strong>{{ $supportEmail }}</strong> hoặc tổng đài <strong>{{ $supportPhone }}</strong>.
</div>
<div style="text-align: left;">&nbsp;</div>
<div style="text-align: left; color: #666666; font-size: 13px;">
Email này được gửi tự động. Vui lòng không trả lời email này.
</div>
<div style="text-align: left;">&nbsp;</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr class="CacDV">
<td style="background-color: #f06824; width: 700px; color: #ffffff;" valign="top" bgcolor="#f06824">
<table style="border-collapse: collapse; background-color: #f06824; width: 100%; color: #ffffff;" border="0" cellspacing="0" cellpadding="0" align="left" bgcolor="#f06824">
<tbody>
<tr>
<td style="padding: 0;" width="100%">
<table style="border-collapse: collapse; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="width: 100%; height: 1px; background: #ffffff;" bgcolor="#ffffff" width="100%" height="1"><span style="font-size: 14px; color: #ffffff;">&nbsp;</span></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td style="height: 10px; color: #ffffff;" valign="top" height="10"><span style="font-size: 14px; color: #ffffff;">&nbsp;</span></td>
</tr>
<tr>
<td style="padding: 0; text-align: left; font-family: Helvetica, Arial, sans-serif; color: #ffffff; font-size: 16px; line-height: 150%;" align="center" valign="top">
<table style="border-collapse: collapse; color: #ffffff;" border="0" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td style="width: 3%; color: #ffffff;"><span style="color: #ffffff;">&nbsp;</span></td>
<td style="vertical-align: middle; text-align: center; padding: 5px 10px; border: 1px solid #ffffff; color: #ffffff;" valign="top">
<a style="color: #ffffff; font-family: Helvetica, Arial, sans-serif; font-size: 10px; line-height: 1; font-weight: normal; text-transform: uppercase; word-wrap: break-word; text-decoration: none;" href="https://tgs.com.vn/premium-hosting/" target="_blank" rel="noopener"><span style="color: #ffffff;">Premium HOSTING</span></a>
</td>
<td style="width: 3%; color: #ffffff;"><span style="color: #ffffff;">&nbsp;</span></td>
<td style="vertical-align: middle; text-align: center; padding: 5px 10px; border: 1px solid #ffffff; color: #ffffff;" valign="top">
<a style="color: #ffffff; font-family: Helvetica, Arial, sans-serif; font-size: 10px; line-height: 1; font-weight: normal; text-transform: uppercase; word-wrap: break-word; text-decoration: none;" href="https://tgs.com.vn/cloud-server/" target="_blank" rel="noopener"><span style="color: #ffffff;">Cloud SERVER</span></a>
</td>
<td style="width: 3%; color: #ffffff;"><span style="color: #ffffff;">&nbsp;</span></td>
<td style="vertical-align: middle; text-align: center; padding: 5px 10px; border: 1px solid #ffffff; color: #ffffff;" valign="top">
<a style="color: #ffffff; font-family: Helvetica, Arial, sans-serif; font-size: 10px; line-height: 1; font-weight: normal; text-transform: uppercase; word-wrap: break-word; text-decoration: none;" href="https://cloudemail.vn/" target="_blank" rel="noopener"><span style="color: #ffffff;">EMAIL DOANH NGHIỆP</span></a>
</td>
<td style="width: 3%; color: #ffffff;"><span style="color: #ffffff;">&nbsp;</span></td>
<td style="vertical-align: middle; text-align: center; padding: 5px 10px; border: 1px solid #ffffff; color: #ffffff;" valign="top">
<a style="color: #ffffff; font-family: Helvetica, Arial, sans-serif; font-size: 10px; line-height: 1; font-weight: normal; text-transform: uppercase; word-wrap: break-word; text-decoration: none;" href="https://tgs.com.vn/thiet-ke-website/" target="_blank" rel="noopener"><span style="color: #ffffff;">Thiết Kế WEBSITE</span></a>
</td>
<td style="width: 3%; color: #ffffff;"><span style="color: #ffffff;">&nbsp;</span></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td style="height: 10px; color: #ffffff;" valign="top" height="10"><span style="font-size: 14px; color: #ffffff;">&nbsp;</span></td>
</tr>
<tr>
<td style="padding: 0;" width="100%">
<table style="border-collapse: collapse; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="width: 100%; height: 1px; background: #ffffff;" bgcolor="#ffffff" width="100%" height="1"><span style="font-size: 14px; color: #ffffff;">&nbsp;</span></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr class="ThongTin">
<td align="center" valign="top" bgcolor="#065d88">
<table style="border-collapse: collapse; background-color: #f06824; border-top: 0; border-bottom: 0; width: 100%;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 30px;">
<td style="height: 30px;" valign="top" height="10">&nbsp;</td>
</tr>
<tr style="height: 148px;">
<td style="padding: 0; font-family: Helvetica, Arial, sans-serif; text-decoration: none; color: #e7e7e7; font-size: 16px; font-weight: normal; line-height: 150%; vertical-align: top; text-align: center; height: 148px;" align="center">
<p style="margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif; color: white; font-size: 11pt; line-height: 200%;">
<span style="color: #ffffff; font-family: 'times new roman', times, serif;"><strong style="font-size: 20px;">CÔNG TY CỔ PHẦN GIẢI PHÁP CÔNG NGHỆ THẾ GIỚI SỐ</strong></span><br>
<span style="color: #ffffff; font-family: arial, helvetica, sans-serif; font-size: 12pt;">Địa chỉ: 128/38 Tân Hương, Phường Phú Thọ Hòa, Thành Phố Hồ Chí Minh</span><br>
<span style="color: #ffffff; font-family: arial, helvetica, sans-serif; font-size: 12pt;">Tel: (028) 7309. 7379 - Tổng đài: {{ $supportPhone }}</span><br>
<span style="font-family: arial, helvetica, sans-serif; font-size: 12pt;">Email: <span style="color: #ffffff;"><a style="color: #ffffff; text-decoration: none;" href="mailto:{{ $supportEmail }}">{{ $supportEmail }}</a></span></span><br>
<span style="text-decoration: none; font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #ffffff !important;">
<a style="color: #ffffff !important; text-decoration: none;" href="https://www.tgs.com.vn">www.TGS.com.vn</a> &amp;
<a style="color: #ffffff !important; text-decoration: none;" href="https://www.thegioiso.cloud">www.thegioiso.CLOUD</a> &amp;
<a style="color: #ffffff !important; text-decoration: none;" href="https://www.thegioiserver.com">www.thegioiSERVER.com</a> &amp;
<a style="color: #ffffff !important; text-decoration: none;" href="https://www.cloudemail.vn">www.CloudEmail.vn</a>
</span>
</p>
<p style="margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif; color: white; font-size: 11pt; line-height: 200%;">
<span style="text-decoration: none; font-family: 'times new roman', times, serif; color: #ffffff !important; font-size: 12pt;">
<span style="color: #ffffff; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10pt;">
<span style="text-decoration: none; font-family: 'times new roman', times, serif; color: #ffffff !important;">
<span style="font-family: arial, sans-serif;"><strong>🥇Thế Giới Số🥇</strong>- Premium HOSTING • CLOUD • SERVER &amp; WEBSITE | 15+ năm Phụng Sự 🚀</span>
</span>
</span>
</span>
</p>
</td>
</tr>
<tr>
<td style="height: 30px; color: #ffffff;" valign="top" height="30"><span style="font-size: 14px; color: #ffffff;">&nbsp;</span></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>