diff --git a/app/Http/Controllers/admin/CustomerController.php b/app/Http/Controllers/admin/CustomerController.php index bcf549d..33d7876 100644 --- a/app/Http/Controllers/admin/CustomerController.php +++ b/app/Http/Controllers/admin/CustomerController.php @@ -27,6 +27,7 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; +use Yajra\DataTables\Facades\DataTables; class CustomerController extends Controller @@ -120,7 +121,7 @@ class CustomerController extends Controller return redirect()->route('customer.index')->withErrors('No data found for this user'); } - $title = "#" . $customer->id . " " . $customer->firstname . " " . $customer->lastname; + $title = "#" . $customer->id . " " . $customer->fullname; return view("administrator.pages.customer.info", compact('customer', 'title', 'userid')); } @@ -156,7 +157,7 @@ class CustomerController extends Controller return redirect()->route('customer.index')->withErrors('No data found for this user'); } - $title = "#" . $customer->id . " " . $customer->firstname . " " . $customer->lastname; + $title = "#" . $customer->id . " " . $customer->fullname; return view("administrator.pages.customer.profile", compact('customer', 'title', 'userid')); } @@ -174,7 +175,7 @@ class CustomerController extends Controller //validate data after validate userid $request->validate([ - 'firstname' => 'required', + 'fullname' => 'required', 'email' => 'required' ]); //find user @@ -307,7 +308,7 @@ class CustomerController extends Controller break; } - $title = "#" . $customer->id . " " . $customer->firstname . " " . $customer->lastname; + $title = "#" . $customer->id . " " . $customer->fullname; return view( 'administrator.pages.customer.product', compact( @@ -428,7 +429,7 @@ class CustomerController extends Controller ['customer'] ); - $title = "#" . $customer->id . " " . $customer->firstname . " " . $customer->lastname; + $title = "#" . $customer->id . " " . $customer->fullname; return view('administrator.pages.customer.domain', compact('userid', 'title', 'domains')); } @@ -461,7 +462,7 @@ class CustomerController extends Controller ); - $title = "#" . $customer->id . " " . $customer->firstname . " " . $customer->lastname; + $title = "#" . $customer->id . " " . $customer->fullname; return view('administrator.pages.customer.invoice', compact('userid', 'title', 'invoices')); } @@ -490,7 +491,7 @@ class CustomerController extends Controller ] ); - $title = "#" . $customer->id . " " . $customer->firstname . " " . $customer->lastname; + $title = "#" . $customer->id . " " . $customer->fullname; return view('administrator.pages.customer.transaction', compact('userid', 'title', 'transactions')); } @@ -517,7 +518,7 @@ class CustomerController extends Controller ] ); - $title = "#" . $customer->id . " " . $customer->firstname . " " . $customer->lastname; + $title = "#" . $customer->id . " " . $customer->fullname; if (isset($request->email_id)) { $template = $this->mailRepository->find($request->get('email_id')); @@ -550,7 +551,7 @@ class CustomerController extends Controller 'page' => $request->get('page') ?? 1 ], ['customer']); - $title = "#" . $customer->id . " " . $customer->firstname . " " . $customer->lastname; + $title = "#" . $customer->id . " " . $customer->fullname; return view('administrator.pages.customer.log', compact('userid', 'title', 'logs')); } @@ -726,4 +727,24 @@ class CustomerController extends Controller Auth::guard('users')->logout(); return redirect()->route('customer.index'); } + + public function datatable(Request $request) + { + $query = $this->customerRepository->filter( + $request->all(), + [], + ['hosting', 'vps', 'domain'], + ['*'], + true + ); + + return DataTables::eloquent($query) + ->addColumn( + 'action', + function ($d) { + return view('administrator.pages.customer.datatable.action', ['row' => $d])->render(); + } + ) + ->make(true); + } } diff --git a/app/Http/Controllers/frontend/CartController.php b/app/Http/Controllers/frontend/CartController.php deleted file mode 100644 index 5a7747a..0000000 --- a/app/Http/Controllers/frontend/CartController.php +++ /dev/null @@ -1,713 +0,0 @@ -cart(customer()?->id); - $title = "Giỏ hàng"; - - return view("frontend.cart.index", compact('cartItems', 'title')); - } - - public function addCart(Request $request) - { - $userId = customer()?->id; - - if (!$userId) { - return back()->with('error', 'Bạn cần đăng nhập để thêm sản phẩm vào giỏ hàng.'); - } - - // Get request data - $productId = $request->product_id; - $typeId = $request->type_id; // Can be null for new Product system - $months = $request->input('months'); - $billingCycle = $request->input('billing_cycle'); // New: billing cycle - $productPrice = $request->input('product_price'); // Price from form - - // Validate basic input - if (!$productId) { - return back()->with('error', 'Sản phẩm không hợp lệ.'); - } - - try { - // Check if this is new Product system or legacy (Hosting/VPS) - $product = Product::find($productId); - - if ($product) { - // NEW FLOW: Using Product table - $pricingId = null; - $finalPrice = $productPrice; - - // Find the pricing based on billing_cycle - if ($billingCycle) { - $pricing = ProductPricings::where('product_id', $productId) - ->where('billing_cycle', $billingCycle) - ->where('is_active', 1) - ->first(); - - if ($pricing) { - $pricingId = $pricing->id; - $finalPrice = $pricing->price_sale ?: $pricing->price; - } - } - - // Add to cart - CartModel::updateOrCreate( - ['customer_id' => $userId], - [ - 'product_id' => $productId, - 'type_product' => null, // New system doesn't use type_product - 'months' => $months, - 'billing_cycle' => $billingCycle, - 'product_pricing_id' => $pricingId, - 'price' => $finalPrice, - 'quantity' => 1, - ] - ); - - return redirect()->route('cart.index')->with('success', 'Sản phẩm đã được thêm vào giỏ hàng!'); - } else { - // LEGACY FLOW: Using old Hosting/VPS system - if (!$typeId || !$months) { - return back()->with('error', 'Dữ liệu không hợp lệ.'); - } - - CartModel::updateOrCreate( - ['customer_id' => $userId], - [ - 'product_id' => $productId, - 'type_product' => $typeId, - 'months' => $months, - 'price' => $productPrice, - ] - ); - - return redirect()->route('cart.index')->with('success', 'Sản phẩm đã được thêm vào giỏ hàng!'); - } - } catch (\Exception $e) { - Log::error('Cart add error: ' . $e->getMessage(), [ - 'user_id' => $userId, - 'product_id' => $productId, - 'type_id' => $typeId, - 'billing_cycle' => $billingCycle ?? null, - 'trace' => $e->getTraceAsString() - ]); - return back()->with('error', 'Có lỗi xảy ra khi thêm sản phẩm vào giỏ hàng.'); - } - } - - public function store(Request $request, Cart $cart) - { - $validator = null; - $allOrderItem = []; - if ($request->vps) { - $validator = Validator::make( - $request->all(), - [ - 'vps.*.os_id' => 'required', - 'vps.*.os_version_id' => 'required', - 'vps.*.time_buy' => 'required', - 'vps.*.hostname' => 'required' - ], - [ - 'vps.*.os_id' => [ - 'required' => 'Vui lòng chọn hệ điều hành cần cài đặt!' - ], - 'vps.*.os_version_id' => [ - 'required' => 'Vui lòng chọn version cần cài đặt!' - ], - 'vps.*.hostname' => [ - 'required' => 'Vui lòng nhập hostname!' - ] - ] - ); - - foreach ($request->vps as $_key => $_item) { - $product = $cart->getItem(customer()?->id, $_key); - if ($product) { - $allOrderItem[] = [ - 'data' => $_item, - 'product' => $product - ]; - } - } - } - if ($request->hosting) { - $validator = Validator::make( - $request->all(), - [ - 'hosting.*.time_buy' => 'required', - 'hosting.*.domain' => 'required' - ], - [ - 'hosting.*.domain' => [ - 'required' => 'Vui lòng nhập tên miền để kích hoạt Hosting!' - ], - 'hosting.*.time_buy' => [ - 'required' => 'Vui lòng chọn thời gian thanh toán!' - ], - ] - ); - $regexFilterDomain = '/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/m'; - - foreach ($request->hosting as $_key => $_item) { - $filter = filter_var( - $request->hosting[$_key]['domain'], - FILTER_VALIDATE_REGEXP, - [ - 'options' => [ - 'regexp' => $regexFilterDomain - ] - ] - ); - - if ($filter === false) { - return back()->with($request->except(['_token']))->withErrors("Tên miền không hợp lệ"); - } - } - foreach ($request->hosting as $_key => $_item) { - $product = $cart->getItem(customer()?->id, $_key); - if ($product) { - $allOrderItem[] = [ - 'data' => $_item, - 'product' => $product - ]; - } - } - } - - if ($validator && $validator->fails()) { - return back()->with($request->except(['_token']))->withErrors($validator); - } - - - if (!count($allOrderItem)) { - return back()->with($request->except('_token'))->withErrors("Yêu cầu không hợp lệ"); - } - - $relid_data = []; - - foreach ($allOrderItem as $key => $item) { - $relid_code = Helper::generateUniqueCode(); - $product = null; - $relid_data[$key]['code'] = $relid_code; - $relid_data[$key]['data'] = [ - 'customer_id' => customer()?->id, - 'status' => 3 - ]; - - switch ($item['product']['product_type']) { - case 3: - $product = Hosting::find($item['product']['product']); - $relid_data[$key]['data']['name'] = $item['data']['domain']; - $relid_data[$key]['type'] = 3; - break; - case 4: - $product = VPS::find($item['product']['product']); - $relid_data[$key]['data']['hostname'] = $item['data']['hostname']; - $relid_data[$key]['data']['os_id'] = $item['data']['os_version_id']; - $relid_data[$key]['type'] = 4; - break; - } - - $subMessage = ""; - - $billing = 0; - - switch ($item['data']['time_buy']) { - case 1: - $billing = 2; - $subMessage = " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(1)->format('d/m/Y') . " )"; - $relid_data[$key]['data']['next_due_date'] = Carbon::today()->addMonths(1)->timestamp; - $relid_data[$key]['data']['next_invoice'] = Carbon::today()->addMonths(1)->timestamp; - break; - case 3: - $billing = 3; - $subMessage = " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(3)->format('d/m/Y') . " )"; - $relid_data[$key]['data']['next_due_date'] = Carbon::today()->addMonths(3)->timestamp; - $relid_data[$key]['data']['next_invoice'] = Carbon::today()->addMonths(3)->timestamp; - break; - case 6: - $billing = 4; - $subMessage = " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(6)->format('d/m/Y') . " )"; - $relid_data[$key]['data']['next_due_date'] = Carbon::today()->addMonths(6)->timestamp; - $relid_data[$key]['data']['next_invoice'] = Carbon::today()->addMonths(6)->timestamp; - break; - case 12: - $billing = 5; - $subMessage = " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(12)->format('d/m/Y') . " )"; - $relid_data[$key]['data']['next_due_date'] = Carbon::today()->addMonths(12)->timestamp; - $relid_data[$key]['data']['next_invoice'] = Carbon::today()->addMonths(12)->timestamp; - break; - } - $totalAddon = 0; - - $message = $product->name; - - - $product_item[] = [ - 'customer_id' => customer()?->id, - 'type_id' => $item['product']['product_type'], - 'product_id' => $item['product']['product'], - 'description' => $message . $subMessage, - 'amount' => $item['product']['price'] * $item['data']['time_buy'], - 'billing' => $billing, - 'invoice_type' => 1, - 'tax' => 1, - 'parent_id' => 0, - 'code' => $relid_code, - 'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp, - ]; - - if (isset($item['data']['addon'])) { - foreach ($item['data']['addon'] as $_key => $_sub) { - $productAddon = Addon::where('name', 'like', "%$_key%")->first(); - $message = "Addon " . $productAddon->name; - if ($_sub['value']) { - $totalAddon += $_sub['price'] * $_sub['value'] * $item['data']['time_buy']; - $product_item[$key]['addon'][] = [ - 'customer_id' => customer()?->id, - 'type_id' => $productAddon->type_id, - 'product_id' => $productAddon->id, - 'description' => $message . $subMessage, - 'amount' => $_sub['price'] * $_sub['value'] * $item['data']['time_buy'], - 'billing' => $billing, - 'invoice_type' => 1, - 'tax' => 1, - 'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp, - ]; - } - } - } - - $relid_data[$key]['data']['first_payment'] = $item['product']['price'] * $item['data']['time_buy'] + $totalAddon; - $relid_data[$key]['data']['next_payment'] = $item['product']['price'] * $item['data']['time_buy'] + $totalAddon; - } - - $allOrderItemWithAddon = array_map(function ($item) { - $codeCheckAddon = Helper::generateUniqueCode(); - $item['addon_code'] = $codeCheckAddon; - return $item; - }, $product_item); - - $addonArray = []; - - foreach ($allOrderItemWithAddon as $item) { - if (isset($item['addon'])) { - foreach ($item['addon'] as $_Sub) { - $_Sub['parent'] = $item['addon_code']; - $addonArray[] = $_Sub; - } - } - } - - $allOrderItemWithAddon = array_map(function ($item) { - if (isset($item['addon'])) { - unset($item['addon']); - } - return $item; - }, array_merge($addonArray, $allOrderItemWithAddon)); - - $total = array_sum(array_map(function ($item) { - return $item['amount']; - }, $allOrderItemWithAddon)); - - $discount = 0; - - $discount_value = 0; - - if (session('coupon')) { - $discount = session('coupon')['coupon_id']; - switch (session('coupon')['coupon_type']) { - case 1: - $discount_value = (int) ((session('coupon')['coupon_value'] / 100) * $total); - $total -= $discount_value; - break; - case 2: - $discount_value = (int) session('coupon')['coupon_value']; - $total -= $discount_value; - break; - } - } - - try { - DB::beginTransaction(); - $invoice_data = [ - 'date' => Carbon::today()->timestamp, - 'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp, - 'sub_total' => $total, - 'tax' => $total * (10 / 100), - 'total' => $total + ($total * (10 / 100)), - 'payment_id' => 1, - 'customer_id' => customer()?->id, - 'status' => 0 - ]; - $invoice_id = Invoice::insertGetId($invoice_data); - - $allOrderItemWithAddon = array_map(function ($array) use ($invoice_id) { - $array['invoice_id'] = $invoice_id; - return $array; - }, $allOrderItemWithAddon); - - $order_item = [ - 'order_num' => Helper::generateUniqueCode(), - 'customer_id' => customer()?->id, - 'date' => Carbon::today()->timestamp, - 'invoice_id' => $invoice_id, - 'discount_id' => $discount, - 'discount_value' => $discount_value, - 'amount' => $total + ($total * (10 / 100)), - 'payment_id' => 1, - 'user_id' => 0, - 'status' => 0, - 'client_ip' => $request->getClientIp() - ]; - - $order = Order::insertGetId($order_item); - - $relid_data = array_map(function ($array) use ($order) { - $array['data']['order_id'] = $order; - return $array; - }, $relid_data); - - $relid = []; - //insert + get id for item auto created by module - foreach ($relid_data as $item) { - $code = $item['code']; - - switch ($item['type']) { - case 3: - $relid_id = ModuleHosting::insertGetId($item['data']); - $relid[$code] = $relid_id; - break; - case 4: - $relid_id = ModuleVmware::insertGetId($item['data']); - $relid[$code] = $relid_id; - break; - } - - } - - $allInvoiceItem = array_map(function ($array) use ($relid) { - if (isset($array['code']) && array_key_exists($array['code'], $relid)) { - $array['relid'] = $relid[$array['code']]; - - } else { - $array['relid'] = 0; - } - unset($array['code']); - - return $array; - - }, $allOrderItemWithAddon); - - $productNoAddon = array_values(array_filter($allInvoiceItem, function ($item) { - if (isset($item['addon_code'])) { - return $item; - } - })); - $productAddon = array_values(array_filter($allInvoiceItem, function ($item) { - if (!isset($item['addon_code'])) { - return $item; - } - })); - - $allProduct = []; - - foreach ($productNoAddon as $item) { - foreach ($productAddon as $_sub) { - if ($item['addon_code'] === $_sub['parent']) { - $_Sub['parent_id'] = $item['relid']; - - $_sub['parent_id'] = $item['relid']; - unset($_sub['parent']); - $allProduct[] = $_sub; - } - } - if (isset($item['addon_code'])) { - unset($item['addon_code']); - } - $allProduct[] = $item; - } - - InvoiceItem::insert($allProduct); - - $last_id = InvoiceItem::latest('id')->limit(count($allProduct))->select('id')->get()->toArray(); - - $Order__order_item = array_map(function ($array) use ($order) { - return [ - 'order_id' => $order, - 'InvoiceItem_id' => $array['id'] - ]; - }, $last_id); - - DB::table('order__order_item')->insert($Order__order_item); - - - - Log::create([ - 'agent' => $request->userAgent(), - 'client_ip' => $request->getClientIp(), - 'username' => "System", - 'description' => customer()?->firstname . " created new Order with Order id: " . $order, - 'customer_id' => customer()?->id - ]); - if (session('coupon')) { - Discount::find(session('coupon')['coupon_id'])->increment('use', 1); - } - DB::commit(); - - } catch (Exception $exception) { - DB::rollBack(); - return back()->with($request->except(['_token']))->withErrors($exception->getMessage()); - } - session()->forget('cart'); - session()->forget('coupon'); - return redirect()->route('invoice.show', ['invoice_id' => $invoice_id])->with('sucess', 'Đặt hàng thành công'); - } - - - - - public function destroy(Request $request, Cart $cart) - { - $validator = Validator::make( - $request->all(), - [ - 'cart_id' => 'required' - ], - [ - 'cart_id' => [ - 'required' => 'Yêu cầu không hợp lệ' - ] - ] - ); - if ($validator->fails()) { - return back()->withErrors($validator); - } - - $status = $cart->removeCartItem(customer()?->id, $request->cart_id); - - if ($status) { - return back()->with('success', 'Xoá thông tin đơn hàng thành công'); - } - return back()->withErrors('Yêu cầu không hợp lệ'); - } - - public function applyCoupon(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - 'promotion_code' => 'required' - ], - [ - 'promotion_code' => [ - 'required' => 'Mã giảm giá không được để trống' - ] - ] - ); - if ($validator->fails()) { - return redirect() - ->route('cart') - ->with($request->except(['_token'])) - ->withErrors($validator); - } - $Coupon = Discount::where('code', $request->promotion_code)->first(); - if (!$Coupon) { - return redirect() - ->route('cart') - ->with($request->except(['_token'])) - ->withErrors("Mã giảm giá không hợp lệ"); - } - if ($Coupon->status === 0) { - return redirect() - ->route('cart') - ->with($request->except(['_token'])) - ->withErrors("Mã giảm giá không hợp lệ"); - } - if ($Coupon->end < Carbon::today()->timestamp) { - return redirect() - ->route('cart') - ->with($request->except(['_token'])) - ->withErrors("Mã giảm giá đã hết hạn sử dụng"); - } - if ($Coupon->start > Carbon::today()->timestamp) { - return redirect() - ->route('cart') - ->with($request->except(['_token'])) - ->withErrors("Mã giảm giá không hợp lệ"); - } - if ($Coupon->use === $Coupon->count) { - return redirect() - ->route('cart') - ->with($request->except(['_token'])) - ->withErrors("Mã giảm giá đã đạt giới hạn số lượng cho phép"); - } - $couponAdded = [ - 'coupon_value' => $Coupon->discount, - 'coupon_type' => $Coupon->type, - 'coupon_code' => $Coupon->code, - 'coupon_id' => $Coupon->id - ]; - session()->put('coupon', $couponAdded); - return redirect() - ->route('cart') - ->with($request->except(['_token'])) - ->with('success', 'Áp dụng mã giảm thành công!'); - } - - public function addon(Request $request) - { - $validator = Validator::make($request->all(), [ - 'product' => 'required|not__in:0' - ], ['product.required' => 'Yêu cầu không hợp lệ']); - - if ($validator->fails()) { - return response()->json(['error' => true, 'message' => $validator]); - } - - $count = 0; - $sumAddon = 0; - $addOn = []; - $billing = 0; - if ($request->addon) { - $i = 0; - foreach ($request->addon as $key => $item) { - if ($item['quantity'] != 0) { - $product = Addon::where('name', 'like', "%$key%")->first(); - $addOn[$i] = [ - 'customer_id' => customer()?->id, - 'type_id' => 1, - 'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp, - 'product_id' => $product->id, - 'parent_id' => $request->product, - 'relid' => 0, - 'invoice_type' => "1", - 'billing' => $request->billing, - 'amount' => $item['price'] * $item['quantity'], - 'tax' => 1, - ]; - $message = "Addon " . strtoupper($key); - switch ($request->billing) { - case 2: - $billing = 1; - $message .= " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(1)->format('d/m/Y') . " )"; - break; - case 3: - $billing = 3; - $message .= " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(3)->format('d/m/Y') . " )"; - break; - case 4: - $billing = 6; - $message .= " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(6)->format('d/m/Y') . " )"; - - break; - case 5: - $billing = 12; - $message .= " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(12)->format('d/m/Y') . " )"; - break; - } - $addOn[$i]['description'] = $message; - } - if ($item['quantity'] == 0) { - ++$count; - } - $sumAddon += $item['price'] * $item['quantity']; - $i++; - } - } - - $sumAddon *= $billing; - - if ($count === 3) { - return response()->json(['error' => true, 'message' => 'Vui lòng chọn một addon để nâng cấp cấu hình!']); - } - - $product = ModuleVmware::with('Order')->find($request->product); - - if (!$product) { - return response()->json(['error' => true, 'message' => 'Yêu cầu không hợp lệ']); - } - - $invoice_data = [ - 'date' => Carbon::today()->timestamp, - 'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp, - 'sub_total' => $sumAddon, - 'tax' => $sumAddon * (10 / 100), - 'total' => $sumAddon + ($sumAddon * (10 / 100)), - 'payment_id' => 1, - 'customer_id' => customer()?->id, - 'status' => 0 - ]; - - try { - DB::beginTransaction(); - $invoice = Invoice::insertGetId($invoice_data); - - - - $order = [ - 'order_num' => Helper::generateUniqueCode(), - 'customer_id' => customer()?->id, - 'date' => Carbon::today()->timestamp, - 'invoice_id' => $invoice, - 'discount_id' => 0, - 'discount_value' => 0, - 'amount' => $sumAddon + ($sumAddon * (10 / 100)), - 'payment_id' => 1, - 'user_id' => 0, - 'status' => 0, - 'client_ip' => $request->getClientIp() - ]; - $order_id = Order::insertGetId($order); - $AddonItem = array_map(function ($item) use ($invoice) { - $item['invoice_id'] = $invoice; - return $item; - }, $addOn); - - InvoiceItem::insert($AddonItem); - - $Order_item = InvoiceItem::select('id')->latest('id')->limit(count($AddonItem))->get(); - - foreach ($Order_item as $item) { - DB::table('order__order_item')->insert([ - 'InvoiceItem_id' => $item->id, - 'order_id' => $order_id - ]); - } - DB::commit(); - } catch (Exception $exception) { - DB::rollBack(); - return response()->json(['error' => true, 'message' => $exception->getMessage()]); - } - return response()->json(['error' => false, 'message' => 'Order thành công. Vui lòng thanh toán để tiến hành nâng cấp!']); - } -} diff --git a/app/Http/Controllers/frontend/CustomerLogController.php b/app/Http/Controllers/frontend/CustomerLogController.php deleted file mode 100644 index d93088d..0000000 --- a/app/Http/Controllers/frontend/CustomerLogController.php +++ /dev/null @@ -1,19 +0,0 @@ -id())->paginate(15); - - return view('frontend.logs',compact('title','logs')); - } -} diff --git a/app/Http/Controllers/frontend/DomainController.php b/app/Http/Controllers/frontend/DomainController.php deleted file mode 100644 index e935209..0000000 --- a/app/Http/Controllers/frontend/DomainController.php +++ /dev/null @@ -1,15 +0,0 @@ -invoiceRepository->findWithRelationships($order_num, [ - 'order', - 'invoiceItem.addons', - 'invoiceItem.os.osGroup', - 'customer' - ]); - - // Log để debug - if (!$invoice) { - Log::warning('Invoice not found', [ - 'invoice_id' => $invoice_id, - 'order_num' => $order_num, - 'user_id' => customer()?->id ?? 'guest' - ]); - } else { - // Kiểm tra quyền truy cập - chỉ cho phép customer xem invoice của chính mình - $currentCustomer = customer(); - if ($currentCustomer && $invoice->customer_id !== $currentCustomer->id) { - Log::warning('Unauthorized invoice access attempt', [ - 'invoice_id' => $invoice->id, - 'order_num' => $order_num, - 'customer_id' => $currentCustomer->id, - 'invoice_customer_id' => $invoice->customer_id - ]); - - abort(403, 'Bạn không có quyền xem hóa đơn này.'); - } - } - - return view("frontend.invoice.show", compact('title', 'order_num', 'invoice')); - } - - public function show_bk(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - 'invoice_id' => 'required|not__in:0|numeric' - ], - [ - 'invoice_id' => [ - 'required' => 'Yêu cầu không hợp lệ' - ] - ] - ); - if ($validator->fails()) { - return back()->withErrors($validator); - } - $invoice = Invoice::with('InvoiceItem', 'Payment', 'Customer', 'Order')->find($request->invoice_id); - - if (!$invoice) { - return back()->withErrors('Yêu cầu không hợp lệ'); - } - $title = "Chi tiết hoá đơn #" . $invoice->id; - - return view("frontend.invoice.view", compact('title', 'invoice')); - } - - - public function store( - Request $request, - $order_id, - $order_num, - InvoiceService $invoiceService, - Discord $discord, - vCenter $vCenter - ) { - $order = $this->orderRepository->findWithRelationships($order_id, [ - 'orderItems.addons', - 'orderItems.os.osGroup', - 'customer', - 'orderItems.ipAddress.host.oneDatastore', - 'orderItems.ipAddress.host.datacenter.vCenter' - ]); - - if (!$order) { - return redirect()->route('home')->with('error', 'Không tìm thấy đơn hàng.'); - } - - $user = customer(); - - if ($user->credit < (int) $order->amount) { - return redirect()->route('recharge.index')->with('error', 'Tài khoản không đủ số dư để thanh toán.'); - } - - DB::beginTransaction(); - - try { - $invoice = Invoice::where('order_id', $order_id)->firstOrFail(); - - // Cập nhật trạng thái đơn hàng thành "Processing" thay vì "Paid" - $order->update(['status' => OrderStatus::PROCESSING->value]); - - DB::commit(); - - // ✅ Gửi thông báo Discord về việc bắt đầu xử lý - $discord->SendJobRunInfo( - 'Order Payment Processing', - 'processing', - [ - 'order_id' => $order->id, - 'order_num' => $order->order_num, - 'customer_email' => $user->email, - 'amount' => $order->amount, - 'has_addons' => $order->orderItems->some(function($item) { - return $item->addons->isNotEmpty(); - }) - ] - ); - - // ✅ Dispatch job để xử lý thanh toán và tạo VM - \App\Jobs\VirtualMachine\ProcessOrderPaymentJob::dispatch($order) - ->delay(now()->addSeconds(5)); - - return redirect()->route('invoice.index', $order->id) - ->with('success', 'Đơn hàng đang được xử lý. Hệ thống sẽ khởi tạo máy ảo và trừ tiền khi hoàn thành. Bạn sẽ nhận được email thông báo kết quả.'); - - } catch (\Throwable $e) { - DB::rollBack(); - Log::error('Lỗi xử lý đơn hàng: ' . $e->getMessage()); - - $discord->SendErrorMessage([ - 'error' => $e->getMessage(), - 'order_id' => $order_id, - 'order_num' => $order_num, - 'customer_email' => $user->email - ], 'InvoiceController - Order Processing Error'); - - return back()->with('error', 'Có lỗi xảy ra: ' . $e->getMessage()); - } - } - -} diff --git a/app/Http/Controllers/frontend/MainController.php b/app/Http/Controllers/frontend/MainController.php deleted file mode 100644 index 9763506..0000000 --- a/app/Http/Controllers/frontend/MainController.php +++ /dev/null @@ -1,168 +0,0 @@ -moduleDomainRepository->filter( - [ - 'customer_id' => auth('users')->id(), - 'status' => 0 - ] - ); - - $invocie_unpaid = $this->invoiceRepository->filter( - [ - 'customer_id' => auth('users')->id(), - 'status' => 0 - ] - ); - - $ticket = $this->ticketRepository->filter( - [ - 'customer' => auth('users')->id() - ] - )->unique('ticket_num'); - - $discount = $this->discountRepository->filter( - [ - 'status' => 'active', - 'end' => [ - 'key' => 'end', - 'condition' => '>', - 'value' => Carbon::now()->timestamp - ] - ] - ); - - $posts = $this->postRepository->filter([ - 'published' => 1, - 'limit' => 4 - ]); - - $vmware = $this->vmwareRepository->filter([ - 'customer_id' => auth('users')->id(), - 'status' => 1 - ]); - $hosting = $this->moduleHostingRepository->filter([ - 'customer_id' => auth('users')->id(), - 'status' => 1 - ]); - - $title = "Khu vực khách hàng"; - return view('frontend.home', compact( - 'domain', - 'discount', - 'invocie_unpaid', - 'ticket', - 'title', - 'posts', - 'vmware', - 'hosting' - )); - } - - public function index_new() - { - $title = "Khu vực khách hàng"; - $domain = $this->moduleDomainRepository->filter( - [ - 'customer_id' => auth('users')->id(), - 'status' => 0 - ] - ); - - $invocie_unpaid = $this->invoiceRepository->filter( - [ - 'customer_id' => auth('users')->id(), - 'status' => 0 - ] - ); - - $ticket = $this->ticketRepository->filter( - [ - 'customer' => auth('users')->id() - ] - )->unique('ticket_num'); - - $discount = $this->discountRepository->filter( - [ - 'status' => 'active', - 'end' => [ - 'key' => 'end', - 'condition' => '>', - 'value' => Carbon::now()->timestamp - ] - ] - ); - - $posts = $this->postRepository->filter([ - 'published' => 1, - 'limit' => 4 - ]); - - $vmware = $this->vmwareRepository->filter([ - 'customer_id' => auth('users')->id(), - 'status' => 1 - ]); - - - $hosting = $this->moduleHostingRepository->filter([ - 'customer_id' => auth('users')->id(), - 'status' => 1 - ]); - - return view( - "frontend.home.index", - [ - 'title' => $title, - 'breadcrumbs' => $this->crums->add(__('Dashboard'))->add(__('Khu vực khách hàng')), - 'domain' => $domain, - 'discount' => $discount, - 'invocie_unpaid' => $invocie_unpaid, - 'ticket' => $ticket, - 'posts' => $posts, - 'vmware' => $vmware, - 'hosting' => $hosting - ] - ); - } -} diff --git a/app/Http/Controllers/frontend/ManagerService.php b/app/Http/Controllers/frontend/ManagerService.php deleted file mode 100644 index ef54fb0..0000000 --- a/app/Http/Controllers/frontend/ManagerService.php +++ /dev/null @@ -1,259 +0,0 @@ - $title, - 'breadcrumbs' => $this->crums->add(__('Dashboard'), route('home'))->add(__('Dịch vụ của tôi')), - ]); - } - - public function show(Request $request) - { - - $validator = Validator::make($request->all(), - [ - 'type' => 'required', - 'product' => 'required' - ], - [ - 'type' => [ - 'required' => 'Yêu cầu không hợp lệ' - ], - 'product' => [ - 'required' => 'Yêu cầu không hợp lệ' - ] - ] - ); - if ($validator->fails()) { - return back()->withErrors($validator); - } - - $product = null; - $information = null; - $addon = []; - $view = ""; - switch ($request->type) { - case 3: - $product = ModuleHosting::with('Server')->find($request->product); - $information = InvoiceItem::where('relid', $product->id) - ->where('type_id', 3) - ->where('invoice_type', 1) - ->first() - ->GetItemProduct(); - $view = "frontend.service.hosting"; - break; - case 4: - $product = ModuleVmware::with('IpAddress.Host')->find($request->product); - $information = InvoiceItem::where('relid', $product->id) - ->where('type_id', 4) - ->where('invoice_type', 1) - ->first() - ->GetItemProduct(); - $AllOn = InvoiceItem::whereHas('OrderItem', function ($query) { - $query->where('status', 1); - }) - ->where('parent_id', $product->id) - ->where('type_id', 1) - ->get(); - - foreach ($AllOn as $item) { - - $itemData = $item->GetItemProduct(); - - $price = 0; - - switch ($item->billing) { - case 2: - $price = $item->amount / 1; - break; - case 3: - $price = $item->amount / 3; - break; - case 4: - $price = $item->amount / 6; - break; - case 5: - $price = $item->amount / 12; - break; - } - if (array_key_exists(strtolower($itemData->name), $addon)) { - $addon[strtolower($itemData->name)] += $price / $itemData->price; - } else { - $addon[strtolower($itemData->name)] = $price / $itemData->price; - } - } - - - $view = "frontend.service.vmware"; - break; - } - - if (!$product) { - return back()->withErrors("Không tìm thấy thông tin dữ liệu"); - } - - $title = "Thông tin chi tiết dịch vụ"; - - return view($view, compact('title', 'product', 'information', 'addon')); - } - - public function reinstall(Request $request) - { - - $validator = Validator::make($request->all(), [ - 'userid' => 'numeric|required', - 'type' => 'numeric|required', - 'product' => 'numeric|required', - 'action' => 'required' - ]); - - if ($validator->fails()) { - return back()->withErrors($validator); - } - - $product = ModuleVmware::find($request->product); - - if (!$product) { - return back()->withErrors("Không tìm thấy thông tin dịch vụ"); - } - $product->update([ - 'status' => 3 - ]); - $jobs = new RemoveVirtualMachine($product->id); - - dispatch($jobs)->delay(now()->addSeconds(5)); - - return back()->with('success', 'The system has received the information. Please wait a second'); - } - - public function action(Request $request) - { - $validator = Validator::make($request->all(), [ - 'product' => 'numeric|required', - 'userid' => 'numeric|required', - 'action' => 'required' - ]); - - if ($validator->fails()) { - return response()->json(['error' => true, 'message' => $validator]); - } - - $jobs = new VMJobs($request->product, $request->action); - - $this->dispatch($jobs); - - } - - public function snapshot(Request $request) - { - $validator = Validator::make($request->all(), - [ - 'product' => 'numeric|required|not__in:0', - 'userid' => 'numeric|required|not__in:0', - 'action' => 'required' - ], - [ - 'product' => [ - 'required' => 'Yêu cầu không hợp lệ' - ], - 'userid' => [ - 'required' => 'Yêu cầu không hợp lệ' - ], - 'action' => [ - 'required' => 'Yêu cầu không hợp lệ' - ], - ] - ); - if ($validator->fails()) { - return response()->json(['error' => true, 'message' => $validator->getMessageBag()]); - } - - - $jobs = null; - - switch ($request->action) { - case "Create": - $SnapshotLimited = ModuleSnapshotLimit::where('customer_id', $request->userid)->first(); - - if ($SnapshotLimited->used == $SnapshotLimited->limit) { - return response()->json([ - 'error' => true, - 'message' => "Đã đạt giới hạn số lượng Snapshot. Vui lòng liên hệ quản trị viên để nâng cấp!" - ]); - } - $jobs = new CreateSnapshot($request->product); - break; - case "Revert": - $validator = Validator::make($request->all(), - [ - 'snap' => 'numeric|required|not__in:0', - ], - [ - 'snap' => [ - 'required' => 'Yêu cầu không hợp lệ' - ] - ] - ); - if ($validator->fails()) { - return response()->json(['error' => true, 'message' => $validator->getMessageBag()]); - } - $jobs = new RevertSnapshot($request->product, $request->snap); - break; - case "Remove": - $validator = Validator::make($request->all(), - [ - 'snap' => 'numeric|required|not__in:0', - ], - [ - 'snap' => [ - 'required' => 'Yêu cầu không hợp lệ' - ] - ] - ); - if ($validator->fails()) { - return response()->json(['error' => true, 'message' => $validator->getMessageBag()]); - } - $jobs = new RemoveSnapshot($request->product, $request->snap); - break; - } - $this->dispatch($jobs); - - } - - public function console(Request $request) - { - $validator = Validator::make($request->all(), [ - 'product' => 'required|not__in:0' - ], ['product.required' => 'Yêu cầu không hợp lệ']); - - if ($validator->fails()) { - return back()->withErrors($validator); - } - $product = ModuleVmware::find($request->product); - if (!$product) { - return back()->withErrors('Yêu cầu không hợp lệ'); - } - $title = "Console for #" . $product->name; - return view('frontend.service.console', compact('product', 'title')); - } -} diff --git a/app/Http/Controllers/frontend/Order/OrderController.php b/app/Http/Controllers/frontend/Order/OrderController.php deleted file mode 100644 index 2705a46..0000000 --- a/app/Http/Controllers/frontend/Order/OrderController.php +++ /dev/null @@ -1,156 +0,0 @@ -orderRepository->findWithRelationships($order_id, ['orderItems.addons.addonItems', 'orderItems.os.osGroup', 'customer']); - if (!$orders || $orders->customer_id !== customer()?->id) { - - return redirect()->route('client.order.index') - ->with('error', 'Đơn hàng không thuộc về bạn. Vui lòng kiểm tra lại!'); - } - - return view('frontend.order.payment', compact('orders')); - } - - - - public function destroy(Request $request, $order_id) - { - $order_num = $request->input('order_num'); - - $order = $this->orderRepository->findWithRelationships($order_id, [ - 'orderItems.addons', - 'orderItems.os.osGroup', - 'customer' - ]); - - if ( - !$order || - $order->customer_id !== customer()?->id || - $order->order_num !== $order_num - ) { - return redirect()->route('home') - ->with('error', 'Không tìm thấy đơn hàng hợp lệ để xoá!'); - } - - DB::beginTransaction(); - - try { - foreach ($order->orderItems as $item) { - $item->addons()->delete(); - } - - $order->orderItems()->delete(); - - $order->delete(); - - DB::commit(); - - return redirect()->route('home') - ->with('success', 'Đơn hàng đã được huỷ thành công.'); - } catch (\Exception $e) { - DB::rollBack(); - - return redirect()->route('home') - ->with('error', 'Đã có lỗi xảy ra khi huỷ đơn hàng. Vui lòng thử lại.'); - } - } - - - - public function store(OrderRequest $request, InvoiceService $invoiceService) - { - $data = $request->validated(); - - $orderNum = generate_order_number(); - - $order = Order::create([ - 'order_num' => $orderNum, - 'customer_id' => customer()?->id, - 'date' => now()->timestamp, - 'discount_id' => 0, - 'discount_value' => null, - 'amount' => $data['totalPrice'], - 'note' => null, - 'client_ip' => request()->ip(), - 'status' => 1, - ]); - - $orderItemId = OrderItem::create([ - 'order_id' => $order->id, - 'product_id' => $data['product_id'], - 'os_id' => $data['os_id'], - 'server_name' => $data['server_name'], - 'months' => $data['months'], - 'quantity' => 1, - 'price' => $data['totalPrice'], - 'product_type' => $data['product_type'], - ]); - - if (isset($data['addons']) && is_array($data['addons'])) { - foreach ($data['addons'] as $addonId => $addonData) { - if (isset($addonData['qty'], $addonData['price']) && intval($addonData['qty']) > 0) { - $qty = intval($addonData['qty']); - $unitPrice = intval($addonData['price']); - $totalPrice = $qty * $unitPrice; - - OrderAddon::create([ - 'order_item_id' => $orderItemId->id, - 'addon_id' => $addonId, - 'qty' => $qty, - 'price' => $totalPrice, - ]); - } - } - } - - $__order = $this->orderRepository->findWithRelationships($order->id, [ - 'orderItems.addons', - 'orderItems.os.osGroup', - 'customer' - ]); - - $invoice = $invoiceService->createInvoice($__order); - - if (!empty($data['cart_id'])) { - Cart::where('id', $data['cart_id'])->delete(); - } - - - - return redirect()->route('client.order.payment', ['order_id' => $order]) - ->with('success', 'Đặt đơn thành công'); - } - -} diff --git a/app/Http/Controllers/frontend/PostController.php b/app/Http/Controllers/frontend/PostController.php deleted file mode 100644 index 025f9f6..0000000 --- a/app/Http/Controllers/frontend/PostController.php +++ /dev/null @@ -1,22 +0,0 @@ -first(); - if (! $post) - { - return redirect()->route('home')->withErrors('Yêu cầu không hợp lệ'); - } - $title = $post->name; - return view('frontend.post',compact('post','title')); - } -} diff --git a/app/Http/Controllers/frontend/RechargeController.php b/app/Http/Controllers/frontend/RechargeController.php deleted file mode 100644 index 47a38ba..0000000 --- a/app/Http/Controllers/frontend/RechargeController.php +++ /dev/null @@ -1,129 +0,0 @@ -vnpayService = $vnPayService; - } - - public function index() - { - $title = 'Yêu cầu nạp tiền vào tài khoản'; - - $payment = Payment::whereNot('id', 1)->select('id', 'name')->get(); - $breadcrumbs = $this->crums->add(__('Dashboard'), route('home'))->add(__('Nạp tiền')); - return view('frontend.payment.index', compact('title', 'payment', 'breadcrumbs')); - } - - public function paymentProcess(RechargeRequest $request) - { - $data = $request->validated(); - - $auth = customer(); - if (!$auth) { - return redirect()->route('auth.index')->withErrors('Vui lòng đăng nhập'); - } - - // Chuẩn bị dữ liệu cho VNPAY - $paymentData = [ - 'amount' => $data['total_amount'], - 'language' => 'vn', - ]; - $paymentUrl = $this->vnpayService->createPaymentUrl($paymentData); - return redirect($paymentUrl); - } - - public function paymentReturn(Request $request) - { - $result = $this->vnpayService->verifyPayment($request->all()); - $data = $result['data'] ?? []; - - if (!empty($data) && ($data['vnp_TransactionStatus'] ?? '') === '00') { - $amount = ($data['vnp_Amount'] ?? 0) / 100; - $customerId = customer()->id; - - // Lưu vào bảng transaction - Transaction::create([ - 'trans' => $data['vnp_TxnRef'] ?? 'unknown', - 'amount' => $amount, - 'customer_id' => $customerId, - ]); - - // Cộng vào credit của Customer - $customer = Customer::find($customerId); - if ($customer) { - $customer->increment('credit', $amount); - $customer->increment('total', $amount); - } - dispatch(new GetPaymentMessage($customerId, $amount)) - ->delay(now()->addSeconds(10)); - - return redirect()->route('recharge.index')->with('success', 'Thanh toán thành công, tài khoản đã được cộng tiền!'); - } - - return redirect()->route('recharge.index')->withErrors('Thanh toán thất bại hoặc bị hủy.'); - } - - public function show(Request $request) - { - $amount = (int)$request->amount; - - if ($amount < 200000) { - return back()->withErrors('Yêu cầu không hợp lệ!'); - } - - Cookie::queue(Cookie::forget('start_checking')); - $memo = "NapTienUser" . customer()->id; - - $payment = Payment::find($request->payment); - - - if ($payment->type == 1 && !session()->has('start_checking')) { - \session()->put('start_checking', true); - - $time = Carbon::now()->addMinutes(5)->timestamp; - - dispatch(new GetTransactionPayment($memo, $time, customer()->id))->delay(now()->addSeconds(30)); - } - - $qr = "https://img.vietqr.io/image/$payment->bankCode-$payment->accountNo-qr_only.png?amount=$amount&addInfo=$memo&accountName=$payment->bankName"; - - $title = "Kiểm tra thanh toán"; - $breadcrumbs = $this->crums->add(__('Dashboard'), route('home'))->add(__('Nạp tiền'), route('recharge.index'))->add(__('Kiểm tra thanh toán')); - return view('frontend.payment.view', compact('title', 'qr', 'amount', 'payment', 'memo', 'breadcrumbs')); - } - - public function HistoryRecharge() - { - $title = 'Lịch sử giao dịch'; - $breadcrumbs = $this->crums->add(__('Dashboard'), route('home'))->add(__('Lịch sử giao dịch')); - return view('frontend.transaction', compact('title', 'breadcrumbs')); - } - - public function CallbackResponse(Request $request) - { - \session()->forget('start_checking'); - - return redirect()->route('home'); - } -} diff --git a/app/Http/Controllers/frontend/ServiceController.php b/app/Http/Controllers/frontend/ServiceController.php deleted file mode 100644 index fc046f0..0000000 --- a/app/Http/Controllers/frontend/ServiceController.php +++ /dev/null @@ -1,1483 +0,0 @@ -invoiceRepository->findWithRelationships($invoice_ordernum, [ - 'order', - 'invoiceItem.addons', - 'invoiceItem.os.osGroup', - 'customer' - ]); - - if (!$invoice) { - return redirect()->route('service.index')->with('error', 'Không tìm thấy dịch vụ.'); - } - - if ($invoice->customer_id !== customer()->id) { - return redirect()->route('service.index')->with('error', 'Bạn không có quyền truy cập dịch vụ này.'); - } - - $vmInfo = null; - if ($invoice->vm_id) { - $vmInfo = $this->getVMInfoFromVCenter($invoice, $vCenterService); - $this->dispatchVMInfoUpdateJob($invoice); - } - $title = "Chi tiết dịch vụ #" . $invoice->id; - return view('frontend.service.show', [ - 'title' => $title, - 'invoice' => $invoice, - 'vmInfo' => $vmInfo, - 'breadcrumbs' => $this->crums->add(__('Dashboard'))->add('Dịch vụ #' . $invoice->id), - ]); - } catch (\Exception $e) { - Log::error('Error in ServiceController@show: ' . $e->getMessage()); - return redirect()->route('service.index')->with('error', 'Có lỗi xảy ra khi tải thông tin dịch vụ.'); - } - } - - /** - * Lấy thông tin VM từ vCenter API - OPTIMIZED WITH CACHING - */ - private function getVMInfoFromVCenter($invoice, $vCenterService) - { - $cacheKey = "vm_info_{$invoice->vm_id}"; - $cacheDuration = 300; // 5 minutes - - if (Cache::has($cacheKey)) { - return Cache::get($cacheKey); - } - - try { - $orderItem = $invoice->invoiceItem->first(); - if (!$orderItem) { - Log::warning('Không tìm thấy order item cho invoice', ['invoice_id' => $invoice->id]); - $defaultInfo = $this->getDefaultVMInfo($invoice); - Cache::put($cacheKey, $defaultInfo, $cacheDuration); - return $defaultInfo; - } - - $product = $orderItem->getProductAttribute(); - if (!$product) { - Log::warning('Không tìm thấy product cho order item', ['order_item_id' => $orderItem->id]); - $defaultInfo = $this->getDefaultVMInfo($invoice); - Cache::put($cacheKey, $defaultInfo, $cacheDuration); - return $defaultInfo; - } - - $host = $product->host; - $datacenter = $host?->datacenter; - $vcenter = $datacenter?->vcenter; - - if (!$vcenter) { - $defaultInfo = $this->getDefaultVMInfo($invoice); - Cache::put($cacheKey, $defaultInfo, $cacheDuration); - return $defaultInfo; - } - - $loginSuccess = false; - try { - $loginSuccess = $vCenterService->loginServer($vcenter); - } catch (\Exception $e) { - Log::warning('vCenter login failed', ['vm_id' => $invoice->vm_id, 'error' => $e->getMessage()]); - } - - if (!$loginSuccess) { - $defaultInfo = $this->getDefaultVMInfo($invoice); - Cache::put($cacheKey, $defaultInfo, $cacheDuration); - return $defaultInfo; - } - - $vmDetail = null; - $ipInfo = null; - - try { - // Set timeout for API calls - $vmDetail = $vCenterService->GetDetailVirtualMachine($invoice->vm_id); - $ipInfo = $vCenterService->GetIPAddressVirtualMachine($invoice->vm_id); - } catch (\Exception $e) { - Log::warning('vCenter API calls failed', ['vm_id' => $invoice->vm_id, 'error' => $e->getMessage()]); - $defaultInfo = $this->getDefaultVMInfo($invoice); - Cache::put($cacheKey, $defaultInfo, $cacheDuration); - return $defaultInfo; - } - - if ($vmDetail['error'] === false) { - $vmData = $vmDetail['data']; - - // Lấy thông tin IP - $ipAddress = 'N/A'; - if ($ipInfo['error'] === false && !empty($ipInfo['data']['ip_address'])) { - $ipAddress = $ipInfo['data']['ip_address'] ?? 'N/A'; - } elseif ($ipInfo['error'] === false && isset($ipInfo['data']['value']['ip_address'])) { - // Fallback for nested structure - $ipAddress = $ipInfo['data']['value']['ip_address'] ?? 'N/A'; - } - - // Tính toán thông tin phần cứng - $cpuCount = $vmData['cpu']['count'] ?? 1; - $memoryMB = $vmData['memory']['size_MiB'] ?? 1024; - $memoryGB = round($memoryMB / 1024, 1); - - $diskSizeGB = 0; - if (isset($vmData['disks']) && is_array($vmData['disks'])) { - foreach ($vmData['disks'] as $disk) { - $diskSizeGB += round(($disk['value']['capacity'] ?? 0) / (1024 * 1024 * 1024), 1); - } - } - - $vmInfo = [ - 'vm_id' => $invoice->vm_id, - 'status' => $vmData['power_state'], - 'ip_address' => $ipAddress, - 'hostname' => $orderItem->server_name ?? 'Unknown', - 'os' => $orderItem->os->osGroup->name ?? 'Unknown', - 'cpu' => $cpuCount . ' cores', - 'ram' => $memoryGB . ' GB', - 'disk' => $diskSizeGB . ' GB', - 'uptime' => 'N/A', - 'last_updated' => now()->format('Y-m-d H:i:s'), - 'vm_name' => $vmData['name'] ?? 'Unknown', - 'vm_folder' => $vmData['folder'] ?? 'Unknown', - 'vm_resource_pool' => $vmData['resource_pool'] ?? 'Unknown', - 'vm_datastore' => $vmData['datastore'] ?? 'Unknown', - 'vm_network' => $vmData['nics'][0]['backing']['network'] ?? 'Unknown', - 'vm_mac' => $vmData['nics'][0]['mac_address'] ?? 'N/A', - 'vm_guest_id' => $vmData['guest_ID'] ?? 'Unknown', - 'vm_version' => $vmData['version'] ?? 'Unknown', - 'vm_hardware_version' => $vmData['hardware']['version'] ?? 'Unknown', - ]; - - // Cache the successful result - Cache::put($cacheKey, $vmInfo, $cacheDuration); - return $vmInfo; - } else { - $defaultInfo = $this->getDefaultVMInfo($invoice); - Cache::put($cacheKey, $defaultInfo, $cacheDuration); - return $defaultInfo; - } - } catch (\Exception $e) { - Log::error('Error in getVMInfoFromVCenter', ['vm_id' => $invoice->vm_id, 'error' => $e->getMessage()]); - $defaultInfo = $this->getDefaultVMInfo($invoice); - Cache::put($cacheKey, $defaultInfo, $cacheDuration); - return $defaultInfo; - } - } - - /** - * Lấy thông tin VM mặc định khi không thể kết nối vCenter - */ - private function getDefaultVMInfo($invoice) - { - $orderItem = $invoice->invoiceItem->first(); - return [ - 'vm_id' => $invoice->vm_id, - 'status' => 'unknown', - 'ip_address' => 'N/A', - 'hostname' => $orderItem->server_name ?? 'Unknown', - 'os' => $orderItem->os->osGroup->name ?? 'Unknown', - 'cpu' => 'N/A', - 'ram' => 'N/A', - 'disk' => 'N/A', - 'uptime' => 'N/A', - 'last_updated' => now()->format('Y-m-d H:i:s'), - 'vm_name' => 'Unknown', - 'vm_folder' => 'Unknown', - 'vm_resource_pool' => 'Unknown', - 'vm_datastore' => 'Unknown', - 'vm_network' => 'Unknown', - 'vm_mac' => 'N/A', - 'vm_guest_id' => 'Unknown', - 'vm_version' => 'Unknown', - 'vm_hardware_version' => 'Unknown', - ]; - } - - /** - * Action để bật/tắt VM (placeholder - chưa implement) - */ - public function toggleVM(Request $request, $invoice_ordernum) - { - try { - $invoice = Invoice::where('order_num', $invoice_ordernum)->first(); - - // Kiểm tra quyền truy cập - if ($invoice->customer_id !== customer()->id) { - return response()->json(['error' => true, 'message' => 'Bạn không có quyền thực hiện thao tác này.']); - } - - $action = $request->input('action'); - - $response = [ - 'success' => true, - 'message' => "Đã thực hiện thao tác: {$action}", - 'vm_id' => $invoice->vm_id, - 'action' => $action - ]; - - // Clear cache when VM state changes - if ($invoice->vm_id) { - Cache::forget("vm_info_{$invoice->vm_id}"); - } - - return response()->json($response); - } catch (\Exception $e) { - // Log::error('Error in ServiceController@toggleVM: ' . $e->getMessage()); - return response()->json(['error' => true, 'message' => 'Có lỗi xảy ra khi thực hiện thao tác. ' . $e->getMessage()]); - } - } - - /** - * Lấy thông tin VM từ vCenter (placeholder - chưa implement) - */ - public function getVMInfo($invoice_id) - { - try { - $invoice = Invoice::findOrFail($invoice_id); - - // Kiểm tra quyền truy cập - if ($invoice->customer_id !== customer()->id) { - return response()->json(['error' => true, 'message' => 'Bạn không có quyền truy cập thông tin này.']); - } - - if (!$invoice->vm_id) { - return response()->json(['error' => true, 'message' => 'Dịch vụ này chưa có VM ID.']); - } - - // TODO: Implement vCenter API calls here - // Hiện tại chỉ trả về data mẫu - $vmInfo = [ - 'vm_id' => $invoice->vm_id, - 'status' => 'running', - 'ip_address' => '192.168.1.100', - 'hostname' => $invoice->invoiceItem->first()->server_name ?? 'Unknown', - 'os' => $invoice->invoiceItem->first()->os->osGroup->name ?? 'Unknown', - 'cpu' => '2 cores', - 'ram' => '4 GB', - 'disk' => '50 GB', - 'uptime' => '2 days, 5 hours', - 'last_updated' => now()->format('Y-m-d H:i:s') - ]; - - return redirect()->back()->with('success', 'Lấy thông tin VM thành công'); - } catch (\Exception $e) { - // Log::error('Error in ServiceController@getVMInfo: ' . $e->getMessage()); - return response()->json(['error' => true, 'message' => 'Có lỗi xảy ra khi lấy thông tin VM.']); - } - } - - /** - * Dispatch background job to update VM info - */ - private function dispatchVMInfoUpdateJob($invoice): void - { - try { - // Check if cache is stale (older than 2 minutes) - $cacheKey = "vm_info_{$invoice->vm_id}"; - $cachedData = Cache::get($cacheKey); - - if ( - !$cachedData || - (isset($cachedData['last_updated']) && - now()->diffInMinutes($cachedData['last_updated']) > 2) - ) { - - // Dispatch background job to update VM info - \App\Jobs\VirtualMachine\Sync\UpdateVMInfoJob::dispatch($invoice->id, $invoice->vm_id) - ->delay(now()->addSeconds(5)) - ->onQueue('vm-updates'); - } - } catch (\Exception $e) { - Log::warning('Failed to dispatch VM info update job', [ - 'vm_id' => $invoice->vm_id, - 'error' => $e->getMessage() - ]); - } - } - - - public function vmConsole(Request $request, $invoice_ordernum) - { - $cacheKey = 'vm_console_invoice_' . $invoice_ordernum; - $data = cache()->remember($cacheKey, 300, function () use ($invoice_ordernum) { - $invoice = Invoice::where('order_num', $invoice_ordernum)->first(); - if (!$invoice) - return null; - $order = Order::find($invoice->order_id); - if (!$order) - return null; - $product = ModuleVmware::where('order_id', $order->id)->first(); - if (!$product) - return null; - $title = "VM Console - " . $product->name; - return compact('product', 'title', 'invoice'); - }); - if (!$data) { - abort(404, 'Không tìm thấy thông tin VM Console'); - } - return view('frontend.service.console', $data); - } - - public function createSnapshots(SnapShotRequest $request) - { - try { - $user = customer(); - if (!$user) { - return redirect()->back()->with('error', 'Chưa đăng nhập.'); - } - - $data = $request->validated(); - $productId = $data['product_id']; - - $product = ModuleVmware::with('ipAddress.host.datacenter.vCenter') - ->where('vmware_id', $productId) - ->where('customer_id', $user->id) - ->first(); - - if (!$product) { - return redirect()->back()->with('error', "Không tìm thấy VPS hoặc bạn không có quyền."); - } - - if ($product->status != 1) { - return redirect()->back()->with('error', 'Chỉ tạo snapshot cho VM đang hoạt động.'); - } - - $limit = ModuleSnapshotLimit::where('customer_id', $user->id)->first(); - if ($limit) { - $count = ModuleSnapshot::where('customer_id', $user->id) - ->where('module_vmware_id', $product->id) - ->count(); - - if ($count >= $limit->limit) { - return redirect()->back()->with( - 'error', - "Đã đạt giới hạn snapshot ({$limit->limit})." - ); - } - } - - $snapshot = ModuleSnapshot::create([ - 'name' => 'snapshot-' . now()->format('Ymd-His'), - 'customer_id' => $user->id, - 'module_vmware_id' => $product->id, - 'status' => 'pending', - 'created_by' => $user->id, - ]); - - \App\Jobs\VirtualMachine\Snapshot\CreateSnapshot::dispatch( - $snapshot->id - )->delay(now()->addSeconds(3)); - - return redirect()->back()->with( - 'success', - 'Đã gửi yêu cầu tạo snapshot. Hệ thống sẽ xử lý trong nền.' - ); - } catch (\Throwable $e) { - Log::error('Lỗi khi tạo snapshot', [ - 'product_id' => $request->product_id ?? null, - 'user_id' => $user->id ?? null, - 'error' => $e->getMessage(), - ]); - - return redirect()->back()->with( - 'error', - 'Lỗi hệ thống khi tạo snapshot.' - ); - } - } - - public function getSnapshots(Request $request) - { - $user = customer(); - if (!$user) { - return response()->json([ - 'error' => true, - 'message' => __('Chưa đăng nhập') - ], 401); - } - - $validator = Validator::make($request->all(), [ - 'product_id' => 'required|string|max:255', - ]); - - if ($validator->fails()) { - return response()->json([ - 'error' => true, - 'message' => __('Dữ liệu không hợp lệ'), - 'errors' => $validator->errors() - ], 422); - } - - $product = $this->findVmwareProduct( - $this->sanitizeInput($request->product_id), - $user->id - ); - - if (!$product) { - return response()->json([ - 'error' => true, - 'message' => __('Không tìm thấy VPS') - ], 404); - } - - $snapshots = ModuleSnapshot::query() - ->where('customer_id', $user->id) - ->where('module_vmware_id', $product->id) - ->orderByDesc('created_at') - ->get([ - 'id', - 'name', - 'status', - 'snapshot_ref', - 'error_message', - 'created_at', - ]); - - return response()->json([ - 'error' => false, - 'message' => __('Lấy danh sách snapshot thành công'), - 'data' => [ - 'vm_name' => e($product->name), - 'vm_id' => $product->vmware_id, - 'snapshots' => $snapshots, - 'total_count' => $snapshots->count(), - 'last_updated' => now()->format('Y-m-d H:i:s'), - ] - ]); - } - - - /** - * Reset VM password (Windows/Linux) via vCenter helpers - */ - public function resetVMPassword(Request $request) - { - $user = customer(); - if (!$user) { - return response()->json(['error' => true, 'message' => __('Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.')], 401); - } - - $validated = Validator::make($request->all(), [ - 'product_id' => 'required|string|max:255', - 'new_password' => 'required|string|min:8|max:128', - 'old_password' => 'required|string|min:8|max:128', - ]); - if ($validated->fails()) { - return response()->json(['error' => true, 'message' => __('Dữ liệu không hợp lệ'), 'errors' => $validated->errors()], 422); - } - - $productId = $this->sanitizeInput($request->product_id); - $newPassword = (string) $request->new_password; - $oldPassword = (string) $request->old_password; - - // load & ownership check nhanh (không cần toàn bộ nặng nề vì Job sẽ retry) - $product = ModuleVmware::where('vmware_id', $productId) - ->where('customer_id', $user->id) - ->first(); - if (!$product) { - return response()->json(['error' => true, 'message' => __('Không tìm thấy VPS hoặc bạn không có quyền truy cập')], 404); - } - - // Dispatch job với delay 5s - ResetVmPasswordJob::dispatch( - productId: $productId, - newPassword: $newPassword, - oldPassword: $oldPassword, - userId: $user->id, - email: $user->email - )->delay(now()->addSeconds(5)); - - // trả accepted để UI biết đang xử lý nền - return redirect()->back()->with('success', 'Đã gửi yêu cầu tạo đặt lại mật khẩu. Quá trình sẽ mất 3-5 phút, vui lòng chờ đợi.'); - } - - - - - /** - * Change Remote Desktop port (Windows only) - */ - public function changeRemotePort(Request $request) - { - $user = customer(); - if (!$user) { - return response()->json(['error' => true, 'message' => __('Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.')], 401); - } - - $validated = Validator::make($request->all(), [ - 'product_id' => 'required|string|max:255', - 'new_port' => 'required|string|min:4|max:128', - 'password' => 'required|string|min:4|max:128', - ]); - if ($validated->fails()) { - return response()->json(['error' => true, 'message' => __('Dữ liệu không hợp lệ'), 'errors' => $validated->errors()], 422); - } - - $productId = $this->sanitizeInput($request->product_id); - $newPort = (string) $request->new_port; - - // load & ownership check nhanh (không cần toàn bộ nặng nề vì Job sẽ retry) - $product = ModuleVmware::where('vmware_id', $productId) - ->where('customer_id', $user->id) - ->first(); - if (!$product) { - return response()->json(['error' => true, 'message' => __('Không tìm thấy VPS hoặc bạn không có quyền truy cập')], 404); - } - - // Dispatch job với delay 5s - ChangeRemotePortJob::dispatch( - productId: $productId, - newPort: $newPort, - password: (string) $request->password, - userId: $user->id, - email: $user->email - )->delay(now()->addSeconds(5)); - - // trả accepted để UI biết đang xử lý nền - return redirect()->back()->with('success', 'Đã gửi yêu cầu thay đổi cổng Remote Desktop. Quá trình sẽ mất 3-5 phút, vui lòng chờ đợi.'); - } - - /** - * Change public IP for VM (request-based), logs request. - */ - public function changeIP(Request $request) - { - try { - $user = customer(); - if (!$user) { - return response()->json([ - 'error' => true, - 'message' => __('Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.'), - ], 401); - } - - $validated = FacadesValidator::make($request->all(), [ - 'product_id' => 'required|string|max:255', - 'ip' => ['required', 'string', 'regex:/^(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})$/'], - ]); - - if ($validated->fails()) { - return response()->json([ - 'error' => true, - 'message' => __('Dữ liệu không hợp lệ'), - 'errors' => $validated->errors(), - ], 422); - } - - $productId = $this->sanitizeInput($request->product_id); - $desiredIp = $this->sanitizeInput($request->ip); - - // Persist an audit log entry for ops to handle allocation if needed - Log::info('Khách yêu cầu đổi IP', [ - 'user_id' => $user->id, - 'product_vmware_id' => $productId, - 'desired_ip' => $desiredIp, - ]); - - // Here you could enqueue a Job to validate/assign IP from pool - // For now, return accepted message - return response()->json([ - 'error' => false, - 'message' => __('Yêu cầu đổi IP đã được ghi nhận, chúng tôi sẽ xử lý trong thời gian sớm nhất.'), - ]); - } catch (\Exception $e) { - Log::error('Lỗi changeIP', [ - 'user_id' => customer()?->id, - 'error' => $e->getMessage(), - ]); - return response()->json([ - 'error' => true, - 'message' => __('Có lỗi xảy ra khi đổi IP'), - ], 500); - } - } - - - - /** - * Secure remove snapshot using session authentication - NO BEARER TOKEN - */ - public function removeSnapshot(Request $request) - { - try { - $user = customer(); - if (!$user) { - return response()->json([ - 'error' => true, - 'message' => 'Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.' - ], 401); - } - - $validator = Validator::make($request->all(), [ - 'product_id' => 'required|string', - 'snapshot_name' => 'required|string', - ]); - - if ($validator->fails()) { - return response()->json([ - 'error' => true, - 'message' => 'Dữ liệu không hợp lệ', - 'errors' => $validator->errors() - ], 422); - } - - $productId = $request->product_id; - $snapshotName = $request->snapshot_name; - $userId = $user->id; - - // Tìm VPS - $product = ModuleVmware::with('ipAddress.host.datacenter.vCenter') - ->where('vmware_id', $productId) - ->where('customer_id', $userId) - ->first(); - - if (!$product) { - return response()->json([ - 'error' => true, - 'message' => "Không tìm thấy VPS #{$productId} hoặc bạn không có quyền truy cập" - ], 404); - } - - // Tìm snapshot trong database - $snapshot = ModuleSnapshot::where('customer_id', $userId) - ->where('module_vmware_id', $product->id) - ->where('name', $snapshotName) - ->first(); - - if (!$snapshot) { - return response()->json([ - 'error' => true, - 'message' => "Không tìm thấy snapshot '{$snapshotName}' hoặc bạn không có quyền truy cập" - ], 404); - } - - // Kiểm tra trạng thái VPS - if ($product->status != 1) { - return response()->json([ - 'error' => true, - 'message' => 'Chỉ có thể xóa snapshot cho máy ảo đang hoạt động' - ], 400); - } - - // Dispatch job để xóa snapshot trong background - \App\Jobs\VirtualMachine\Snapshot\RemoveSnapshot::dispatch($product->id, $snapshot->id) - ->delay(now()->addSeconds(5)) - ->onQueue('snapshots'); - - return response()->json([ - 'error' => false, - 'message' => 'Đã gửi yêu cầu xóa snapshot. Quá trình sẽ mất 1-2 phút, vui lòng chờ đợi.' - ]); - } catch (\Exception $e) { - Log::error('❌ Lỗi khi xóa snapshot', [ - 'product_id' => $request->product_id ?? 'not_provided', - 'snapshot_name' => $request->snapshot_name ?? 'not_provided', - 'user_id' => $user->id ?? 'guest', - 'error' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine() - ]); - - return response()->json([ - 'error' => true, - 'message' => 'Lỗi hệ thống khi xóa snapshot: ' . $e->getMessage() - ], 500); - } - } - - /** - * Secure revert snapshot using session authentication - NO BEARER TOKEN - */ - public function revertSnapshot(Request $request) - { - try { - $user = customer(); - if (!$user) { - return response()->json([ - 'error' => true, - 'message' => 'Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.' - ], 401); - } - - $validator = Validator::make($request->all(), [ - 'product_id' => 'required|string', - 'snapshot_name' => 'required|string', - ]); - - if ($validator->fails()) { - return response()->json([ - 'error' => true, - 'message' => 'Dữ liệu không hợp lệ', - 'errors' => $validator->errors() - ], 422); - } - - $productId = $request->product_id; - $snapshotName = $request->snapshot_name; - $userId = $user->id; - - // Tìm VPS - $product = ModuleVmware::with('ipAddress.host.datacenter.vCenter') - ->where('vmware_id', $productId) - ->where('customer_id', $userId) - ->first(); - - if (!$product) { - return response()->json([ - 'error' => true, - 'message' => "Không tìm thấy VPS #{$productId} hoặc bạn không có quyền truy cập" - ], 404); - } - - // Tìm snapshot trong database - $snapshot = ModuleSnapshot::where('customer_id', $userId) - ->where('module_vmware_id', $product->id) - ->where('name', $snapshotName) - ->first(); - - if (!$snapshot) { - return response()->json([ - 'error' => true, - 'message' => "Không tìm thấy snapshot '{$snapshotName}' hoặc bạn không có quyền truy cập" - ], 404); - } - - // Kiểm tra trạng thái VPS - if ($product->status != 1) { - return response()->json([ - 'error' => true, - 'message' => 'Chỉ có thể khôi phục snapshot cho máy ảo đang hoạt động' - ], 400); - } - - // Dispatch job để khôi phục snapshot trong background - \App\Jobs\VirtualMachine\Snapshot\RevertSnapshot::dispatch($product->id, $snapshot->id) - ->delay(now()->addSeconds(5)) - ->onQueue('snapshots'); - - return response()->json([ - 'error' => false, - 'message' => 'Đã gửi yêu cầu khôi phục snapshot. Quá trình sẽ mất 2-3 phút, vui lòng chờ đợi.' - ]); - } catch (\Exception $e) { - Log::error('❌ Lỗi khi khôi phục snapshot', [ - 'product_id' => $request->product_id ?? 'not_provided', - 'snapshot_name' => $request->snapshot_name ?? 'not_provided', - 'user_id' => $user->id ?? 'guest', - 'error' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine() - ]); - - return response()->json([ - 'error' => true, - 'message' => 'Lỗi hệ thống khi khôi phục snapshot: ' . $e->getMessage() - ], 500); - } - } - - /** - * Secure start VM using session authentication - NO BEARER TOKEN - */ - /** - * Secure start VM using session authentication - NO BEARER TOKEN - */ - public function startVM(Request $request) - { - try { - $user = customer(); - if (!$user) { - return response()->json([ - 'error' => true, - 'message' => __('Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.') - ], 401); - } - - $validator = Validator::make($request->all(), [ - 'product_id' => 'required|string|max:255', - ]); - if ($validator->fails()) { - return response()->json([ - 'error' => true, - 'message' => __('Dữ liệu không hợp lệ'), - 'errors' => $validator->errors() - ], 422); - } - - $productId = $request->product_id; - $userId = $user->id; - - $product = ModuleVmware::where('vmware_id', $productId) - ->where('customer_id', $userId) - ->first(); - if (!$product) { - return response()->json([ - 'error' => true, - 'message' => __('Không tìm thấy VPS hoặc bạn không có quyền truy cập') - ], 404); - } - - // Dispatch Job - \App\Jobs\VirtualMachine\Power\StartVmJob::dispatch($product->id, $userId); - - return response()->json([ - 'error' => false, - 'message' => __('Yêu cầu khởi động máy ảo đã được gửi. Vui lòng chờ trong giây lát.') - ]); - - } catch (\Exception $e) { - Log::error('Lỗi khi khởi động máy ảo', [ - 'user_id' => $user->id ?? 'guest', - 'product_id' => $request->product_id ?? 'not_provided', - 'error' => $e->getMessage() - ]); - return response()->json([ - 'error' => true, - 'message' => __('Có lỗi xảy ra khi khởi động máy ảo') - ], 500); - } - } - - /** - * Secure stop VM using session authentication - NO BEARER TOKEN - */ - public function stopVM(Request $request) - { - try { - $user = customer(); - if (!$user) { - return response()->json([ - 'error' => true, - 'message' => __('Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.') - ], 401); - } - - $validator = Validator::make($request->all(), [ - 'product_id' => 'required|string|max:255', - ]); - if ($validator->fails()) { - return response()->json([ - 'error' => true, - 'message' => __('Dữ liệu không hợp lệ'), - 'errors' => $validator->errors() - ], 422); - } - - $productId = htmlspecialchars(strip_tags(trim($request->product_id)), ENT_QUOTES, 'UTF-8'); - $userId = $user->id; - - $product = ModuleVmware::where('vmware_id', $productId) - ->where('customer_id', $userId) - ->first(); - if (!$product) { - return response()->json([ - 'error' => true, - 'message' => __('Không tìm thấy VPS hoặc bạn không có quyền truy cập') - ], 404); - } - - // Dispatch Job - \App\Jobs\VirtualMachine\Power\StopVmJob::dispatch($product->id, $userId); - - return response()->json([ - 'error' => false, - 'message' => __('Yêu cầu tắt máy ảo đã được gửi. Vui lòng chờ trong giây lát.') - ]); - - } catch (\Exception $e) { - Log::error('Lỗi khi tắt máy ảo', [ - 'user_id' => $user->id ?? 'guest', - 'product_id' => $request->product_id ?? 'not_provided', - 'error' => $e->getMessage() - ]); - return response()->json([ - 'error' => true, - 'message' => __('Có lỗi xảy ra khi tắt máy ảo') - ], 500); - } - } - - /** - * Secure reboot VM using session authentication - NO BEARER TOKEN - */ - public function rebootVM(Request $request) - { - try { - $user = customer(); - if (!$user) { - return response()->json([ - 'error' => true, - 'message' => __('Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.') - ], 401); - } - - $validator = Validator::make($request->all(), [ - 'product_id' => 'required|string|max:255', - ]); - if ($validator->fails()) { - return response()->json([ - 'error' => true, - 'message' => __('Dữ liệu không hợp lệ'), - 'errors' => $validator->errors() - ], 422); - } - - $productId = htmlspecialchars(strip_tags(trim($request->product_id)), ENT_QUOTES, 'UTF-8'); - $userId = $user->id; - - $product = ModuleVmware::where('vmware_id', $productId) - ->where('customer_id', $userId) - ->first(); - if (!$product) { - return response()->json([ - 'error' => true, - 'message' => __('Không tìm thấy VPS hoặc bạn không có quyền truy cập') - ], 404); - } - - // Dispatch Job - \App\Jobs\VirtualMachine\Power\RebootVmJob::dispatch($product->id, $userId); - - return response()->json([ - 'error' => false, - 'message' => __('Yêu cầu khởi động lại máy ảo đã được gửi. Vui lòng chờ trong giây lát.') - ]); - - } catch (\Exception $e) { - Log::error('Lỗi khi reboot VM', [ - 'user_id' => $user->id ?? 'guest', - 'product_id' => $request->product_id ?? 'not_provided', - 'error' => $e->getMessage() - ]); - return response()->json([ - 'error' => true, - 'message' => __('Có lỗi xảy ra khi khởi động lại máy ảo') - ], 500); - } - } - - - public function reinstallVM(Request $request, vCenter $vCenterService) - { - try { - $user = customer(); - if (!$user) { - Log::warning('User not logged in for reinstallVM'); - return response()->json([ - 'error' => true, - 'message' => __('Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.') - ], 401); - } - - $validator = Validator::make($request->all(), [ - 'invoice_id' => 'required|integer', - ]); - - if ($validator->fails()) { - Log::warning('Validation failed for reinstallVM', [ - 'errors' => $validator->errors()->toArray() - ]); - return response()->json([ - 'error' => true, - 'message' => __('Dữ liệu không hợp lệ'), - 'errors' => $validator->errors() - ], 422); - } - - $invoice = Invoice::find($request->invoice_id); - if (!$invoice) { - return response()->json([ - 'error' => true, - 'message' => __('Không tìm thấy hóa đơn') - ], 404); - } - - // Debug: Log product lookup - $invoiceItem = $invoice->invoiceItem->first(); - $productId = $invoiceItem?->getProductAttribute()?->id; - $vmware = $productId; - $osId = $request->os_id; - $userId = $user->id; - - // Tìm product và kiểm tra quyền sở hữu - $vm = ModuleVmware::where('vmware_id', $vmware->vmware_id) - ->where('customer_id', $userId) - ->first(); - - Log::info('VM lookup result', [ - 'vm_found' => $vm ? 'yes' : 'no', - 'vm_data' => $vm ? $vm->toArray() : null - ]); - if (!$vm) { - return response()->json([ - 'error' => true, - 'message' => __('Không tìm thấy VPS hoặc bạn không có quyền truy cập') - ], 404); - } - - // Kiểm tra OS có tồn tại và hợp lệ không - $os = Os::find($osId); - if (!$os) { - return response()->json([ - 'error' => true, - 'message' => __('Hệ điều hành không tồn tại') - ], 404); - } - - // Kiểm tra OS có được phép cho VPS này không - $allowedOs = $vm->operatingSystems()->where('os.id', $osId)->first(); - if (!$allowedOs) { - return response()->json([ - 'error' => true, - 'message' => __('Hệ điều hành này không được phép cho VPS này') - ], 403); - } - - $result = $vCenterService->DeleteVirtualMachine($vm->vmware_id); - if (is_array($result) && isset($result['error']) && $result['error'] === false) { - // Cập nhật OS trong database - $vm->update([ - 'os_id' => $osId - ]); - - Cache::forget('vm_info_' . $vm->vmware_id); - return response()->json([ - 'error' => false, - 'message' => __('Yêu cầu cài lại máy ảo đã được gửi thành công. Quá trình sẽ mất 5-10 phút.') - ]); - } else { - $msg = is_array($result) && isset($result['data']) ? $result['data'] : __('Không thể cài lại máy ảo'); - Log::warning('vCenter call failed', [ - 'result' => $result, - 'message' => $msg - ]); - return response()->json([ - 'error' => true, - 'message' => $msg - ], 500); - } - } catch (\Exception $e) { - Log::error('Lỗi khi cài lại máy ảo', [ - 'user_id' => $user->id ?? 'guest', - 'product_id' => $request->product_id ?? 'not_provided', - 'os_id' => $request->os_id ?? 'not_provided', - 'error' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine() - ]); - return response()->json([ - 'error' => true, - 'message' => __('Có lỗi xảy ra khi cài lại máy ảo') - ], 500); - } - } - - /** - * Sanitize input data - */ - private function sanitizeInput($input): string - { - return htmlspecialchars(strip_tags(trim($input)), ENT_QUOTES, 'UTF-8'); - } - - /** - * Sanitize output data - */ - private function sanitizeOutput($output): string - { - return htmlspecialchars($output, ENT_QUOTES, 'UTF-8'); - } - - /** - * Kiểm tra cache có hết hạn chưa - */ - private function isCacheExpired($cachedData): bool - { - if (!isset($cachedData['last_updated'])) { - return true; - } - - $lastUpdated = \Carbon\Carbon::parse($cachedData['last_updated']); - return now()->diffInMinutes($lastUpdated) > 2; // Cache 2 phút - } - - /** - * Tìm VPS với eager loading - */ - private function findVmwareProduct($productId, $userId) - { - return ModuleVmware::with(['ipAddress.host.datacenter.vCenter']) - ->where('vmware_id', $productId) - ->where('customer_id', $userId) - ->first(); - } - - /** - * Validate vCenter configuration - */ - private function validateVcenterConfiguration($product) - { - if (!$product->ipAddress?->host?->datacenter?->vCenter) { - return null; - } - - return $product->ipAddress->host->datacenter->vCenter; - } - - /** - * Lấy snapshot từ vCenter - */ - private function fetchSnapshotsFromVCenter($serviceVCenter, $product, $vcenter) - { - try { - // Login to vCenter - if (!$serviceVCenter->loginServer($vcenter)) { - return false; - } - - // Kiểm tra method tồn tại - if (!method_exists($serviceVCenter, 'getSnapShot')) { - Log::error('getSnapShot method không tồn tại trong ServiceVCenter'); - return false; - } - - $result = $serviceVCenter->getSnapShot($product); - - // Retry logic - if ($result === "Login_Again") { - if ($serviceVCenter->loginServer($vcenter)) { - $result = $serviceVCenter->getSnapShot($product); - } else { - return false; - } - } - - // Validate result - if (is_string($result) || (isset($result['error']) && $result['error'])) { - return false; - } - - return $result['data'] ?? []; - } catch (\Exception $e) { - Log::error('Lỗi khi lấy snapshot từ vCenter', [ - 'product_id' => $product->id, - 'error' => $e->getMessage() - ]); - return false; - } - } - - /** - * Lấy snapshot từ database - */ - private function getDatabaseSnapshots($userId, $productId) - { - return ModuleSnapshot::where('customer_id', $userId) - ->where('module_vmware_id', $productId) - ->get() - ->keyBy('name'); - } - - /** - * Xử lý và sanitize snapshot data - */ - private function processSnapshotsData($snapshots, $dbSnapshots): array - { - $processed = []; - - if (!is_array($snapshots)) { - return $processed; - } - - foreach ($snapshots as $snapshot) { - $snapshotName = $this->sanitizeOutput($snapshot['name'] ?? $snapshot['Name'] ?? 'Unknown'); - $dbSnapshot = $dbSnapshots->get($snapshotName); - - $processed[] = [ - 'name' => $snapshotName, - 'description' => $this->sanitizeOutput($snapshot['description'] ?? $snapshot['Description'] ?? ''), - 'created' => $this->sanitizeOutput($snapshot['created'] ?? $snapshot['Created'] ?? ''), - 'power_state' => $this->sanitizeOutput($snapshot['power_state'] ?? $snapshot['PowerState'] ?? ''), - 'size_mb' => (int) ($snapshot['size_mb'] ?? $snapshot['SizeMB'] ?? 0), - 'size_gb' => (float) ($snapshot['size_gb'] ?? $snapshot['SizeGB'] ?? 0), - 'is_current' => (bool) ($snapshot['is_current'] ?? $snapshot['IsCurrent'] ?? false), - 'is_replay_supported' => (bool) ($snapshot['is_replay_supported'] ?? $snapshot['IsReplaySupported'] ?? false), - 'in_database' => $dbSnapshot ? true : false, - 'db_created_at' => $dbSnapshot ? $dbSnapshot->created_at->format('d/m/Y H:i:s') : null - ]; - } - - return $processed; - } - - /** - * Calculate renewal price (AJAX) - */ - public function renewPrice(Request $request) - { - try { - $user = customer(); - if (!$user) { - return response()->json(['error' => true, 'message' => __('Vui lòng đăng nhập.')], 401); - } - - $vmware_id = $this->sanitizeInput($request->product_id); - $plan = $request->input('plan'); - $months = $plan === 'other' ? (int) $request->input('months') : (int) $plan; - - if ($months < 1) - $months = 1; - - $vm = ModuleVmware::where('vmware_id', $vmware_id) - ->where('customer_id', $user->id) - ->first(); - - if (!$vm) { - return response()->json(['error' => true, 'message' => __('Không tìm thấy dịch vụ.')], 404); - } - - $order = Order::find($vm->order_id); - if (!$order) { - return response()->json(['error' => true, 'message' => __('Không tìm thấy đơn hàng gốc.')], 404); - } - - // Find OrderItem in order__order_item - // Rule: product_type = 4 (VPS) or 3 (Hosting) - // Ideally we match the product_id too, but ModuleVmware may not store original product_id directly except via relation? - // ModuleVmware has no 'product_id' column in the schema we saw, but it has 'order_id'. - // Let's assume we look for the item that created this VM. - // Since we are in ServiceController for VM, we look for Type 4 (VPS). - - $orderItem = \App\Models\OrderItem::where('order_id', $order->id) - ->where('product_type', 4) // VPS - ->first(); - - if (!$orderItem) { - // Fallback or error - return response()->json(['error' => true, 'message' => __('Không tìm thấy thông tin sản phẩm trong đơn hàng.')], 404); - } - - // Get Product Price - $product = null; - if ($orderItem->product_type == 4) { - $product = \App\Models\VPS::find($orderItem->product_id); - } elseif ($orderItem->product_type == 3) { - $product = \App\Models\Hosting::find($orderItem->product_id); - } - - if (!$product) { - return response()->json(['error' => true, 'message' => __('Sản phẩm không còn tồn tại.')], 404); - } - - // Priority: Price Sale -> Price - $base_unit_price = (float) ($product->price_sale > 0 ? $product->price_sale : $product->price); - - // Calculate Addons Price - // Look into order__order_addons via OrderItem relationship - $addons_unit_price = 0; - if ($orderItem->addons && $orderItem->addons->count() > 0) { - foreach ($orderItem->addons as $addonPivot) { - // $addonPivot is OrderAddon model - // We need current price of the addon? User said "lấy giá đã lưu" (take stored price) - // BUT for renewal, usually we take CURRENT price of the addon or the STORED price? - // User said: "lấy sản order hiện tại kèm các addon nếu có lấy giá đã lưu * với số tháng" - // "giá đã lưu" implies the price stored in the order/product table? - // But then user said: "nếu product có price_sale thì ưu tiên lấy price_sale còn lại thì lấy price". - // This implies looking at the ACTUAL Product table for the base Item. - - // For Addons: "kèm các addon nếu có". - // Let's look at the Addon model (Current Price). - if ($addonPivot->addonItems) { - $addons_unit_price += (float) ($addonPivot->addonItems->price); - } - } - } - - $total_unit_price = $base_unit_price + $addons_unit_price; - $total = $total_unit_price * $months; - - return response()->json([ - 'error' => false, - 'data' => [ - 'unit_price' => $total_unit_price, - 'months' => $months, - 'total' => $total - ] - ]); - - } catch (\Exception $e) { - Log::error("Error calculating renew price: " . $e->getMessage()); - return response()->json(['error' => true, 'message' => $e->getMessage()], 500); - } - } - - /** - * Renew VPS Service - */ - public function renew(Request $request) - { - try { - $user = customer(); - if (!$user) { - return redirect()->route('user.login')->with('error', __('Vui lòng đăng nhập.')); - } - - // 1. Validate Input - $validator = FacadesValidator::make($request->all(), [ - 'product_id' => 'required|string', - 'plan' => 'required', - 'months' => 'nullable|integer|min:2|max:60' - ]); - - if ($validator->fails()) { - return redirect()->back()->withErrors($validator)->withInput()->with('error', __('Dữ liệu không hợp lệ.')); - } - - $vmware_id = $this->sanitizeInput($request->product_id); - $plan = $request->input('plan'); - $months = $plan === 'other' ? (int) $request->input('months') : (int) $plan; - - if ($months < 2) { - return redirect()->back()->with('error', __('Thời hạn gia hạn tối thiểu là 2 tháng.')); - } - - // 2. Find Service & Verify Ownership - $vm = ModuleVmware::where('vmware_id', $vmware_id) - ->where('customer_id', $user->id) - ->first(); - - if (!$vm) { - return redirect()->back()->with('error', __('Không tìm thấy dịch vụ hoặc bạn không có quyền truy cập.')); - } - - // 3. Calculate Price based on Original Order -> OrderItem - $order = Order::find($vm->order_id); - if (!$order) { - return redirect()->back()->with('error', __('Không tìm thấy đơn hàng gốc của dịch vụ này.')); - } - - $orderItem = \App\Models\OrderItem::where('order_id', $order->id) - ->where('product_type', 4) // VPS - ->first(); - - if (!$orderItem) { - return redirect()->back()->with('error', __('Không tìm thấy thông tin sản phẩm trong đơn hàng.')); - } - - // Get Product Price - $product = null; - if ($orderItem->product_type == 4) { - $product = \App\Models\VPS::find($orderItem->product_id); - } elseif ($orderItem->product_type == 3) { - $product = \App\Models\Hosting::find($orderItem->product_id); - } - - if (!$product) { - return redirect()->back()->with('error', __('Sản phẩm không còn tồn tại.')); - } - - // Priority: Price Sale -> Price - $base_unit_price = (float) ($product->price_sale > 0 ? $product->price_sale : $product->price); - - // Calculate Addons Price - $addons_unit_price = 0; - if ($orderItem->addons && $orderItem->addons->count() > 0) { - foreach ($orderItem->addons as $addonPivot) { - if ($addonPivot->addonItems) { - $addons_unit_price += (float) ($addonPivot->addonItems->price); - } - } - } - - $total_unit_price = $base_unit_price + $addons_unit_price; - $total_cost = $total_unit_price * $months; - - // 4. Check Balance - if ($user->credit < $total_cost) { - return redirect()->back()->with('error', __('Số dư tài khoản không đủ để gia hạn (Cần: :amount). Vui lòng nạp thêm tiền.', ['amount' => number_format($total_cost) . ' VNĐ'])); - } - - // 5. Process Renewal - DB::beginTransaction(); - try { - // Deduct Credit - $user->decrement('credit', $total_cost); - - // Create Renewal Invoice (Paid) - $newInvoice = Invoice::create([ - 'customer_id' => $user->id, - 'status' => 1, // Paid - 'date' => now()->timestamp, // Using timestamp as per schema - 'due_date' => now()->timestamp, - 'total' => $total_cost, - // 'payment_method' => 'credit_balance', // Schema doesn't have this, maybe note? - 'note' => "Gia hạn dịch vụ VPS #{$vm->name} ({$months} tháng)" - ]); - - // Update VM Expiry - $current_due = $vm->next_due_date ? \Carbon\Carbon::parse($vm->next_due_date) : now(); - $new_due_date = $current_due->copy()->addMonths($months); - - $vm->update([ - 'next_due_date' => $new_due_date, - 'status' => 1, - ]); - - // TODO: Update OrderItem duration/dates if needed? Or just create new invoice record? - // The prompt was about "gia hạn thêm sản phẩm" (add renewal product?), but generally it just extends the service. - - DB::commit(); - - // Clear cache - Cache::forget("vm_info_{$vm->vmware_id}"); - - return redirect()->back()->with('success', __('Gia hạn dịch vụ thành công! Hạn sử dụng mới: :date', ['date' => $new_due_date->format('d/m/Y')])); - - } catch (\Exception $e) { - DB::rollBack(); - throw $e; - } - - } catch (\Exception $e) { - Log::error("Error renewing VM: " . $e->getMessage()); - return redirect()->back()->with('error', __('Có lỗi xảy ra khi gia hạn dịch vụ. Vui lòng thử lại sau.')); - } - } -} diff --git a/app/Http/Controllers/frontend/ServiceController_renew.php b/app/Http/Controllers/frontend/ServiceController_renew.php deleted file mode 100644 index 89ca262..0000000 --- a/app/Http/Controllers/frontend/ServiceController_renew.php +++ /dev/null @@ -1,158 +0,0 @@ - - /** - * Renew VPS Service - */ - public function renew(Request $request) - { - try { - $user = customer(); - if (!$user) { - return redirect()->route('user.login')->with('error', __('Vui lòng đăng nhập.')); - } - - // 1. Validate Input - $validator = FacadesValidator::make($request->all(), [ - 'product_id' => 'required|string', - 'plan' => 'required', - 'months' => 'nullable|integer|min:2|max:60' - ]); - - if ($validator->fails()) { - return redirect()->back()->withErrors($validator)->withInput()->with('error', __('Dữ liệu không hợp lệ.')); - } - - $vmware_id = $this->sanitizeInput($request->product_id); - $plan = $request->input('plan'); - $months = $plan === 'other' ? (int)$request->input('months') : (int)$plan; - - if ($months < 2) { - return redirect()->back()->with('error', __('Thời hạn gia hạn tối thiểu là 2 tháng.')); - } - - // 2. Find Service & Verify Ownership - $vm = ModuleVmware::where('vmware_id', $vmware_id) - ->where('customer_id', $user->id) - ->first(); - - if (!$vm) { - return redirect()->back()->with('error', __('Không tìm thấy dịch vụ hoặc bạn không có quyền truy cập.')); - } - - // 3. Calculate Price based on Original Order/InvoiceItem - // Strategy: Find the InvoiceItem from the Order associated with this VM. - // If multiple items, try to match type VPS (4). - - $order = Order::find($vm->order_id); - if (!$order) { - // Fallback if order missing (rare) - // Try finding by invoice_id if stored on VM? ModuleVmware has next_invoice but not current_invoice. - return redirect()->back()->with('error', __('Không tìm thấy đơn hàng gốc của dịch vụ này.')); - } - - // Find InvoiceItem - // Order has relationship to Invoice (order->invoice) - // Or Order has orderItems (which are InvoiceItems) - $invoiceItem = null; - if ($order->invoice) { - // Try finding item in the invoice related to this order - // Note: The modal uses $invoice->invoiceItem->first(). We should try to be specific. - // We don't have a direct link from ModuleVmware to InvoiceItem. - // But ModuleVmware links to Order, which links to items. - // Let's take the first item of type 4 (VPS) from the order's invoice. - $invoiceItem = $order->invoice->invoiceItem->where('type_id', 4)->first(); - if (!$invoiceItem) { - $invoiceItem = $order->invoice->invoiceItem->first(); - } - } - - if (!$invoiceItem) { - // Extreme fallback or failure - return redirect()->back()->with('error', __('Không tìm thấy thông tin giá của dịch vụ.')); - } - - // Calculate Unit Price (Monthly) - // 'amount' in InvoiceItem is total price for 'months' duration. - $billing_months = $invoiceItem->months > 0 ? $invoiceItem->months : 1; - if ($invoiceItem->amount <= 0) { - // If amount is 0 (free?), unit price is 0. - $base_unit_price = 0; - } else { - $base_unit_price = $invoiceItem->amount / $billing_months; - } - - // Calculate Addons Unit Price - $addons_unit_price = 0; - if ($invoiceItem->addons && $invoiceItem->addons->count() > 0) { - foreach ($invoiceItem->addons as $addonPivot) { - // Try to find price. Assuming Addon model has current price. - // Ideally we should use the price stored in pivot if available, but pivot structure is unknown. - // We'll use the Addon catalog price as fallback or primary if pivot doesn't store it. - // $addonPivot->addonItems returns the Addon model. - if ($addonPivot->addonItems) { - $addons_unit_price += $addonPivot->addonItems->price; - } - } - } - - $total_unit_price = $base_unit_price + $addons_unit_price; - $total_cost = $total_unit_price * $months; - - // 4. Check Balance - if ($user->credit < $total_cost) { - return redirect()->back()->with('error', __('Số dư tài khoản không đủ để gia hạn (Cần: :amount). Vui lòng nạp thêm tiền.', ['amount' => number_format($total_cost) . ' VNĐ'])); - } - - // 5. Process Renewal - DB::beginTransaction(); - try { - // Deduct Credit - $user->decrement('credit', $total_cost); - - // Create Renewal Invoice (Paid) - Optional but good for history - $newInvoice = Invoice::create([ - 'customer_id' => $user->id, - 'status' => 1, // Paid - 'date' => now(), - 'due_date' => now(), - 'total' => $total_cost, - 'payment_method' => 'credit_balance', // or similar - 'description' => "Gia hạn dịch vụ VPS #{$vm->name} ({$months} tháng)" - ]); - - // Update VM Expiry - // If current expiry is in the past, renew from NOW? Or from expiry? - // Usually from expiry if it's recent grace period. If long expired, maybe from now. - // Let's simply add to next_due_date. - - $current_due = $vm->next_due_date ? \Carbon\Carbon::parse($vm->next_due_date) : now(); - if ($current_due->isPast()) { - // If expired, maybe we should start from today? - // Or just extend the gap. User usually pays for the gap. - // Let's extend from current_due to maintain billing cycle. - } - $new_due_date = $current_due->copy()->addMonths($months); - - $vm->update([ - 'next_due_date' => $new_due_date, - 'status' => 1, // Ensure active if it was suspended - ]); - - // Log or Notify (Optional) - - DB::commit(); - - // Clear cache - Cache::forget("vm_info_{$vm->vmware_id}"); - - return redirect()->back()->with('success', __('Gia hạn dịch vụ thành công! Hạn sử dụng mới: :date', ['date' => $new_due_date->format('d/m/Y')])); - - } catch (\Exception $e) { - DB::rollBack(); - throw $e; - } - - } catch (\Exception $e) { - Log::error("Error renewing VM: " . $e->getMessage()); - return redirect()->back()->with('error', __('Có lỗi xảy ra khi gia hạn dịch vụ. Vui lòng thử lại sau.')); - } - } diff --git a/app/Http/Controllers/frontend/ShopController.php b/app/Http/Controllers/frontend/ShopController.php deleted file mode 100644 index 1d4ee01..0000000 --- a/app/Http/Controllers/frontend/ShopController.php +++ /dev/null @@ -1,161 +0,0 @@ - function ($query) { - $query->where('status', 1); // Only active groups - }]) - ->whereNotIn('id', [1, 2]) - ->where('status', 1) // Only active types - ->latest('id') - ->get(); - - // Handle product filtering - if ($request->type && $request->group) { - $product = $this->getProductsByTypeAndGroup($request->type, $request->group); - $group = $this->getGroupsByType($request->type); - - return view('frontend.shop', compact('title', 'type', 'group', 'product')); - } - - if ($request->type) { - $group = $this->getGroupsByType($request->type); - return view('frontend.shop', compact('title', 'type', 'group')); - } - - return view('frontend.shop', compact('title', 'type')); - } - - /** - * Add product to cart with validation - */ - public function store(AddToCartRequest $request, Cart $cart) - { - $product = $this->findProduct($request->type_id, $request->product_id); - - if (!$product) { - return back()->with('error', 'Sản phẩm không tồn tại!'); - } - - $cart->addToCart($request->user('users')->id, $request->product_price, $product); - - return redirect()->back()->with('success', 'Thêm vào giỏ hàng thành công!'); - } - - /** - * Display products by category slug (ProductCategories) - */ - public function show(Request $request, string $slug) - { - // Find category by slug - $category = ProductCategories::with(['parent', 'children']) - ->where('slug', $slug) - ->where('is_active', 1) - ->first(); - - if (!$category) { - abort(404, 'Danh mục sản phẩm không tồn tại'); - } - - // Get products with pagination and pricing - $products = $category->products() - ->with([ - 'pricings' => function ($query) { - $query->where('is_active', 1)->orderBy('price'); - }, - 'osVersions.osGroup', - 'controlPanels' - ]) - ->where('is_active', 1) - ->orderBy('created_at', 'desc') - ->paginate(12); - - return view('frontend.service.index', [ - 'category' => $category, - 'products' => $products, - 'breadcrumbs' => $this->crums->add(__('Dashboard'))->add('Sản phẩm thuộc ' . $category->name), - ]); - } - - /** - * Get products by type and group - */ - private function getProductsByTypeAndGroup(int $typeId, int $groupId) - { - return match ($typeId) { - 3 => Hosting::where('group_id', $groupId) - ->where('status', 1) - ->with(['group', 'type']) - ->get(), - 4 => VPS::where('group_id', $groupId) - ->where('status', 1) - ->with(['group', 'type', 'operatingSystems.osGroup']) - ->get(), - default => collect() - }; - } - - /** - * Get groups by type - */ - private function getGroupsByType(int $typeId) - { - return Group::with('type') - ->where('type_id', $typeId) - ->where('status', 1) - ->get(); - } - - /** - * Find product by type and ID - */ - private function findProduct(int $typeId, int $productId) - { - return match ($typeId) { - 3 => Hosting::where('id', $productId) - ->where('status', 1) - ->first(), - 4 => VPS::where('id', $productId) - ->where('status', 1) - ->first(), - default => null - }; - } -} diff --git a/app/Http/Controllers/frontend/TicketController.php b/app/Http/Controllers/frontend/TicketController.php deleted file mode 100644 index 683f973..0000000 --- a/app/Http/Controllers/frontend/TicketController.php +++ /dev/null @@ -1,212 +0,0 @@ -crums->add(__('Dashboard'), route('home'))->add(__('Yêu cầu hỗ trợ')); - return view("frontend.ticket.ticket", compact('title', 'breadcrumbs')); - } - - public function create() - { - $title = "Gửi yêu cầu hỗ trợ"; - $ticket = true; - $breadcrumbs = $this->crums->add(__('Dashboard'), route('home'))->add(__('DS Yêu cầu hỗ trợ'), route('ticket'))->add(__('Gửi yêu cầu hỗ trợ')); - $itemProduct = \App\Models\InvoiceItem::whereHas('invoice', function ($query) { - $query->where('customer_id', customer()->id); - }) - ->where('invoice_type', 1) - ->get(); - return view("frontend.ticket.add", compact('title', 'itemProduct', 'ticket', 'breadcrumbs')); - } - - public function store(Request $request, Upload $upload) - { - $validator = Validator::make( - $request->all(), - [ - 'full_name' => 'required', - 'email' => 'required', - 'subject' => 'required', - 'room' => 'required', - 'description' => 'required', - ], - [ - 'full_name' => [ - 'required' => 'Yêu cầu không hợp lệ' - ], - 'email' => [ - 'required' => 'Yêu cầu không hợp lệ' - ], - 'subject' => [ - 'required' => 'Tiêu đề không được để trống' - ], - 'room' => [ - 'required' => 'Vui lòng chọn phòng ban cần hỗ trợ' - ], - 'description' => [ - 'required' => 'Vui lòng nhập nội dung cần hỗ trợ' - ] - ] - ); - if ($validator->fails()) { - return back()->withInput()->withErrors($validator); - } - if ($request->fileTicket) { - $data = $upload->ImageUpload($request->fileTicket); - - if ($data['error']) { - return back()->withInput()->withErrors($data['message']); - } - } - $type = !empty($request->product) ? explode("|", $request->product) : []; - - try { - DB::beginTransaction(); - $dataJson = [ - 'product' => count($type) ? $type[1] : "", - 'product_type' => count($type) ? $type[0] : "", - 'file' => isset($data) ? $data['data'] : "" - ]; - $ticket = Ticket::create([ - 'subject' => $request->subject, - 'ticket_num' => random_int(1000000, 99999999), - 'description' => $request->description, - 'customer_id' => $request->user('users')->id, - 'data' => json_encode($dataJson), - 'room' => $request->room, - 'status' => 1 - ]); - DB::commit(); - // Dispatch job to send Telegram notification - $customer = \App\Models\Customer::find($ticket->customer_id); - \App\Jobs\Ticket\TicketJobs::dispatch($ticket, $customer); - } catch (\Exception $exception) { - DB::rollBack(); - return back()->withInput()->withErrors($exception->getMessage()); - } - return redirect()->route('ticket')->with('success', 'Gửi yêu cầu hỗ trợ thành công!'); - } - - public function show(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - 'ticket_num' => 'required' - ], - [ - 'ticket_num' => [ - 'required' => 'Yêu cầu không hợp lệ' - ] - ] - ); - if ($validator->fails()) { - return back()->withErrors($validator); - } - $ticket = Ticket::with([ - 'Reply' => function ($query) { - $query->latest('id'); - }, - 'Reply.User', - 'Customer' - ])->where('ticket_num', $request->ticket_num)->latest('id')->get(); - - $title = "Ticket #" . $request->ticket_num; - - return view("frontend.ticket.view", compact('title', 'ticket')); - } - - public function ReplyStore(Request $request, Upload $upload) - { - $validator = Validator::make( - $request->all(), - [ - 'ticket_num' => 'required', - 'description' => 'required', - ], - [ - 'ticket_num' => [ - 'required' => 'Yêu cầu không hợp lệ' - ], - 'description' => [ - 'required' => 'Vui lòng nhập nội dung cần hỗ trợ' - ] - ] - ); - if ($validator->fails()) { - return back()->withInput()->withErrors($validator); - } - $ticket = Ticket::where('ticket_num', $request->ticket_num)->first(); - - if (!$ticket) { - return back()->withInput()->withErrors('Yêu cầu không hợp lệ'); - } - - if ($request->fileTicket) { - $data = $upload->ImageUpload($request->fileTicket); - - if ($data['error']) { - return back()->withInput()->withErrors($data['message']); - } - } - try { - DB::beginTransaction(); - $dataJson = json_decode($ticket->data, true); - $dataJson['file'] = isset($data) ? $data['data'] : ""; - Ticket::create([ - 'subject' => $ticket->subject, - 'ticket_num' => $ticket->ticket_num, - 'description' => $request->description, - 'customer_id' => $ticket->customer_id, - 'data' => json_encode($dataJson), - 'room' => $ticket->room, - 'status' => 1 - ]); - DB::commit(); - } catch (\Exception $exception) { - DB::rollBack(); - return back()->withInput()->withErrors($exception->getMessage()); - } - return back()->with('success', 'Trả lời yêu cầu hỗ trợ thành công!'); - } - - public function ClosedTicket(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - 'ticket_num' => 'required' - ], - [ - 'ticket_num' => [ - 'required' => 'Yêu cầu không hợp lệ' - ] - ] - ); - if ($validator->fails()) { - return back()->withInput()->withErrors($validator); - } - try { - DB::beginTransaction(); - Ticket::where('ticket_num', $request->ticket_num)->update(['status' => 0]); - DB::commit(); - } catch (\Exception $exception) { - DB::rollBack(); - return back()->withInput()->withErrors($exception->getMessage()); - } - return redirect()->route('ticket')->with('success', 'Yêu cầu xử lý thành công!'); - } -} diff --git a/app/Http/Controllers/frontend/UserController.php b/app/Http/Controllers/frontend/UserController.php deleted file mode 100644 index 4ac8578..0000000 --- a/app/Http/Controllers/frontend/UserController.php +++ /dev/null @@ -1,408 +0,0 @@ -validate( - [ - 'email' => 'required', - 'password' => 'required' - ], - [ - 'email' => [ - 'required' => 'Vui lòng nhập Email' - ], - 'password' => [ - 'required' => 'Vui lòng nhập Mật Khẩu' - ] - ] - ); - $customer = Customer::where('email', $request->email); - - if (!$customer) { - return back()->withInput()->withErrors('Địa chỉ email không tồn tại'); - } - - if ( - auth('users')->attempt( - ['email' => $request->email, 'password' => $request->password], - $request->remember - ) - ) { - - if (auth('users')->user()->status == 2) { - - auth('users')->logout(); - - return back()->withInput()->withErrors('Vui lòng kích hoạt tài khoản để tiếp tục sử dụng dịch vụ.'); - } - if (auth('users')->user()->status == 0) { - - auth('users')->logout(); - - return back()->withInput()->withErrors('Tài khoản bị khoá, Vui lòng liên hệ admin để được hỗ trợ.'); - } - - $request->session()->regenerate(); - - $token = \auth('users')->user()->createToken('access_token', ['*'], Carbon::now()->addDay())->plainTextToken; - - Cookie::queue('aut_accessToken', $token, 24 * 60); - - $this->customerLogRepository->create([ - 'customer_id' => auth('users')->id(), - 'client_ip' => $request->getClientIp(), - 'agent' => $request->userAgent(), - 'action' => 'Đăng nhập' - ]); - - if (Cookie::has('prev_url') && Cookie::get('prev_url') !== route('home')) { - Cookie::queue(Cookie::forget('prev_url')); - return redirect(Cookie::get('prev_url')); - } - - return redirect()->route('home')->with('success', 'Đăng nhập tài khoản thành công!'); - } - return back()->withInput()->withErrors('Thông tin đăng nhập không chính xác!'); - } - - public function Logout() - { - $this->customerLogRepository->create([ - 'customer_id' => auth('users')->id(), - 'client_ip' => \request()->getClientIp(), - 'agent' => \request()->userAgent(), - 'action' => 'Đăng xuất' - ]); - - Cookie::queue(Cookie::forget('aut_accessToken')); - - auth('users')->user()->tokens()->delete(); - - auth('users')->logout(); - - return redirect()->route('user.login')->with('success', 'Đăng xuất tài khoản thành công!'); - } - - public function Information() - { - $title = "Thông tin tài khoản"; - $customer = auth('users')->user(); - - return view("frontend.user.account", compact('title', 'customer')); - } - - public function StoreInformation(UserStoreInformation $request) - { - $validated = $request->validated(); - - try { - DB::beginTransaction(); - - $user = user(); - $dataToUpdate = collect($validated)->except(['_token', '_method', 'email'])->toArray(); - $path = 'uploads/customers/' . $user->id . '/documents'; - - $fileFields = ['front_cccd', 'back_cccd', 'avatar']; - - foreach ($fileFields as $field) { - if ($request->hasFile($field)) { - $file = $request->file($field); - $filename = $field . '_' . $user->id . '_' . time() . '.' . $file->getClientOriginalExtension(); - - foreach (Storage::disk('public')->files($path) as $existingFile) { - if (Str::startsWith(basename($existingFile), $field . '_')) { - Storage::disk('public')->delete($existingFile); - } - } - - $file->storeAs($path, $filename, 'public'); - $dataToUpdate[$field] = $path . '/' . $filename; - } - } - - Customer::find($user->id)->update($dataToUpdate); - - DB::commit(); - return back()->with('success', 'Cập nhật thông tin tài khoản thành công'); - } catch (Exception $exception) { - DB::rollBack(); - return back()->withInput()->withErrors(['update_error' => $exception->getMessage()]); - } - } - - - public function updateNotification(Request $request) - { - $user = auth('users')->user(); - - $request->validate([ - 'email_marketing' => 'boolean', - 'receive_deposit' => 'boolean', - ]); - - $user->update([ - 'email_marketing' => $request->input('email_marketing', false), - 'receive_deposit' => $request->input('receive_deposit', false), - ]); - - return response()->json(['success' => true]); - } - - - public function Email() - { - $title = "Lịch sử Email"; - return view("frontend.email", compact('title')); - } - - public function ViewEmail(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - 'id' => 'required|numeric|not__in:0' - ], - [ - 'id' => [ - 'required' => 'Vui lòng chọn Email' - ] - ] - ); - if ($validator->fails()) { - return back()->withErrors($validator); - } - $template = Mail::find($request->id); - if ($template) { - $title = $template->subject; - return view("emails.render", compact('title', 'template')); - } - return back()->withErrors("Yêu cầu không hợp lệ"); - } - - public function ChangePassword() - { - $title = "Thay đổi mật khẩu"; - $breadcrumbs = $this->crums->add(__('Dashboard'), route('home'))->add(__('Bảo mật tài khoản')); - return view("frontend.user.password", compact('title', 'breadcrumbs')); - } - - public function StoreChangePassword(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - "password" => 'required', - "new_password" => 'required', - 'confirm_password' => 'required' - ], - [ - "password" => [ - 'required' => 'Vui lòng nhập mật khẩu!' - ], - "new_password" => [ - 'required' => 'Vui lòng nhập mật khẩu mới' - ], - 'confirm_password' => [ - 'required' => 'Vui lòng nhập lại mật khẩu mới' - ] - ] - ); - if ($validator->fails()) { - return back()->withInput()->withErrors($validator); - } - - if (!Hash::check($request->password, $request->user('users')->password)) { - return back()->withInput()->withErrors("Mật khẩu cũ không chính xác"); - } - - if ($request->new_password !== $request->confirm_password) { - return back()->withInput()->withErrors("Nhập lại mật khẩu mới không chính xác"); - } - try { - DB::beginTransaction(); - Customer::find($request->user('users')->id)->update([ - 'password' => Hash::make($request->new_password) - ]); - DB::commit(); - } catch (Exception $exception) { - DB::rollBack(); - return back()->withInput()->withErrors($exception->getMessage()); - } - - return back()->with('success', 'Thay đổi mật khẩu thành công!'); - } - - public function Register() - { - return view("frontend.register", [ - 'title' => 'Đăng ký tài khoản' - ]); - } - - public function RegisterStore(UserRegisterRequest $request) - { - try { - $validator = $request->validated(); - DB::beginTransaction(); - $code = Helper::generateUniqueCode(); - $id = Customer::insertGetId([ - 'firstname' => $validator['firstname'], - 'lastname' => $validator['lastname'], - 'email' => $validator['email'], - 'phone' => $validator['phone'], - 'avatar' => '/images/avatar.png', - 'email_hash' => $code, - 'status' => 2, - 'password' => Hash::make($validator['password']), - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now(), - ]); - - $jobs = new CustomerRegister(1, $code, $validator['email'], $id); - dispatch($jobs)->delay(now()->addSeconds(10)); - - DB::commit(); - - } catch (Exception $exception) { - DB::rollBack(); - return back()->withInput()->withErrors($exception->getMessage()); - } - return redirect()->route('user.login')->with('success', 'Đăng ký thành công. Vui lòng kiểm tra xác thực Email!'); - } - - public function Verify(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - 'verify' => 'required', - ], - ['verify.required' => 'Yêu cầu không hợp lệ'] - ); - if ($validator->fails()) { - return redirect()->route('user.login')->withErrors($validator); - } - $code = base64_decode(SignRSA::DecryptFunc($request->verify)); - - $customer = Customer::where('email_hash', 'LIKE', "%$code%")->first(); - - if (!$customer) { - return redirect()->route('user.login')->withErrors('Yêu cầu không hợp lệ'); - } - - if ($customer->status == 1 || $customer->status == 0) { - return redirect()->route('user.login')->withErrors('Yêu cầu không hợp lệ'); - } - - try { - DB::beginTransaction(); - $customer->update([ - 'status' => 1, - 'email_verify' => Carbon::now() - ]); - ModuleSnapshotLimit::create([ - 'customer_id' => $customer->id, - 'limit' => 1, - 'used' => 0 - ]); - DB::commit(); - } catch (Exception $exception) { - DB::rollBack(); - return redirect()->route('user.login')->withErrors($exception->getMessage()); - } - return redirect()->route('user.login')->with('success', 'Kích hoạt tài khoản thành công'); - } - - public function Reset() - { - $title = "Quên mật khẩu"; - - return view("frontend.reset", compact('title')); - } - - public function ResetStore(Request $request) - { - - $request->validate( - [ - 'email' => 'required|email' - ], - [ - 'email' => [ - 'required' => 'Vui lòng nhập địa chỉ email!', - 'email' => 'Định dạng email không hợp lệ' - ] - ] - ); - - $user = Customer::where('email', $request->email)->first(); - if (!$user) { - return back()->withErrors('Không tìm thấy thông tin người dùng'); - } - - $password = Str::random(10); - - try { - DB::beginTransaction(); - $user->update([ - 'password' => Hash::make($password) - ]); - - $data = [ - 'company_name' => setting('setting.company'), - 'company_logo' => setting('setting.logo'), - 'password' => $password, - ]; - - $jobs = new PasswordRecoverJob($data, $user); - - dispatch($jobs)->delay(now()->addSeconds(10)); - DB::commit(); - } catch (Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('user.login')->with('success', "Thiết lập đặt lại mật khẩu thành công. Vui lòng kiểm tra email và thực hiện xác thực theo hướng dẫn."); - - } -} diff --git a/app/Http/Controllers/frontend/Whois/WhoisController.php b/app/Http/Controllers/frontend/Whois/WhoisController.php deleted file mode 100644 index 2fa64e1..0000000 --- a/app/Http/Controllers/frontend/Whois/WhoisController.php +++ /dev/null @@ -1,88 +0,0 @@ -whoisService = $whoisService; - } - - public function index() - { - return view('frontend.whois.index'); - } - - /** - * Get domain whois information - * - * @param WhoisRequest $request - * @return JsonResponse - */ - public function getDomainInfo(WhoisRequest $request): JsonResponse - { - try { - $domain = $request->validated()['domain']; - $data = $this->whoisService->getDomainInfo($domain); - - return response()->json([ - 'success' => true, - 'message' => __('Domain information retrieved successfully.'), - 'data' => $data - ], 200); - - } catch (Exception $e) { - return response()->json([ - 'success' => false, - 'message' => __('Failed to retrieve domain information.'), - 'errors' => [ - 'domain' => [$e->getMessage()] - ] - ], 400); - } - } - - /** - * Check if domain is available - * - * @param WhoisRequest $request - * @return JsonResponse - */ - public function checkAvailability(WhoisRequest $request): JsonResponse - { - try { - $domain = $request->validated()['domain']; - $isAvailable = $this->whoisService->isDomainAvailable($domain); - $status = $this->whoisService->getDomainStatus($domain); - - return response()->json([ - 'success' => true, - 'message' => __('Domain availability checked successfully.'), - 'data' => [ - 'domain' => $domain, - 'available' => $isAvailable, - 'status' => $status - ] - ], 200); - - } catch (Exception $e) { - return response()->json([ - 'success' => false, - 'message' => __('Failed to check domain availability.'), - 'errors' => [ - 'domain' => [$e->getMessage()] - ] - ], 400); - } - } -} diff --git a/app/Http/Requests/CreateCustomerRequest.php b/app/Http/Requests/CreateCustomerRequest.php index 281c25a..74b8df7 100644 --- a/app/Http/Requests/CreateCustomerRequest.php +++ b/app/Http/Requests/CreateCustomerRequest.php @@ -22,8 +22,7 @@ class CreateCustomerRequest extends FormRequest public function rules(): array { return [ - 'firstname' => 'required', - 'lastname' => 'required', + 'fullname' => 'required', 'email' => 'required|email', 'password' => 'required', 'company' => 'nullable', diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 1b77cd6..b18b149 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -54,8 +54,6 @@ class Customer extends Authenticatable * Appends for API */ protected $appends = [ - 'first_name', - 'last_name', 'point', 'gender_label', ]; diff --git a/app/Repositories/Customer/CustomerCachedRepository.php b/app/Repositories/Customer/CustomerCachedRepository.php index fec6716..53c90e1 100644 --- a/app/Repositories/Customer/CustomerCachedRepository.php +++ b/app/Repositories/Customer/CustomerCachedRepository.php @@ -16,19 +16,32 @@ class CustomerCachedRepository extends BaseCachedRepository implements CustomerR { return $this->customerRepository->getModel(); } - public function filter(array $data = [], array $relationships = [], array $withCount = [], array $select = ['*']) - { + public function filter( + array $data = [], + array $relationships = [], + array $withCount = [], + array $select = ['*'], + bool $returnQuery = false + ) { + if ( + $returnQuery || + !empty($data['paginate']) + ) { + return $this->customerRepository + ->filter($data, $relationships, $withCount, $select, $returnQuery); + } + $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filterItem' => [ - 'data' => $data, - 'relationships' => $relationships, - 'select' => $select, - 'withCount' => $withCount - ] + 'filter' => compact('data', 'relationships', 'withCount', 'select') ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($data, $relationships, $select, $withCount) { - return $this->customerRepository->filter($data, $relationships, $withCount, $select); - }); + + return Cache::remember( + $cacheKey, + $this->cacheTime, + fn() => + $this->customerRepository + ->filter($data, $relationships, $withCount, $select, $returnQuery) + ); } public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) { diff --git a/app/Repositories/Customer/CustomerRepository.php b/app/Repositories/Customer/CustomerRepository.php index 5f4c0ba..3d0e8e7 100644 --- a/app/Repositories/Customer/CustomerRepository.php +++ b/app/Repositories/Customer/CustomerRepository.php @@ -11,14 +11,18 @@ class CustomerRepository extends BaseRepository implements CustomerRepositoryInt { return Customer::class; } - public function filter(array $data = [], array $relationships = [], array $withCount = [], array $select = ['*']) - { + public function filter( + array $data = [], + array $relationships = [], + array $withCount = [], + array $select = ['*'], + bool $returnQuery = false + ) { $model = $this->model->with($relationships)->select($select); if (isset($data['name']) && ! empty($data['name'])) { $model = $model->where(function ($query) use ($data) { $name = trim($data['name']); - $query->where("firstname", "LIKE", "%$name%"); - $query->orWhere("lastname", "LIKE", "%$name%"); + $query->where("fullname", "LIKE", "%$name%"); $query->orWhere("company", "LIKE", "%$name%"); }); } @@ -59,6 +63,9 @@ class CustomerRepository extends BaseRepository implements CustomerRepositoryInt if (count($withCount) > 0) { $model = $model->withCount($withCount); } + if ($returnQuery) { + return $model; + } if (isset($data['page'])) { return $model->paginate(15); } diff --git a/app/Repositories/Customer/CustomerRepositoryInterface.php b/app/Repositories/Customer/CustomerRepositoryInterface.php index 6926635..83631d2 100644 --- a/app/Repositories/Customer/CustomerRepositoryInterface.php +++ b/app/Repositories/Customer/CustomerRepositoryInterface.php @@ -6,7 +6,13 @@ use App\Repositories\BaseRepositoryInterface; interface CustomerRepositoryInterface extends BaseRepositoryInterface { - public function filter(array $data = [],array $relationships = [],array $withCount = [],array $select = ['*']); + public function filter( + array $data = [], + array $relationships = [], + array $withCount = [], + array $select = ['*'], + bool $returnQuery = false + ); public function findWithRelationships(int $id = 0, array $relationships = [],array $withCount = [], array $withSum = []); public function findBy(string $key = '',string $value = ''); } \ No newline at end of file diff --git a/config/cors.php b/config/cors.php index 0966399..d8e09b8 100644 --- a/config/cors.php +++ b/config/cors.php @@ -19,7 +19,7 @@ return [ 'allowed_methods' => ['*'], - 'allowed_origins' => ['*', 'http://localhost:5173'], + 'allowed_origins' => ['*', 'http://localhost:5173', 'http://localhost:3000', 'http://localhost:3001'], 'allowed_origins_patterns' => [], diff --git a/database/migrations/2026_02_16_153732_create_cache_table.php b/database/migrations/2026_02_16_153732_create_cache_table.php new file mode 100644 index 0000000..b9c106b --- /dev/null +++ b/database/migrations/2026_02_16_153732_create_cache_table.php @@ -0,0 +1,35 @@ +string('key')->primary(); + $table->mediumText('value'); + $table->integer('expiration'); + }); + + Schema::create('cache_locks', function (Blueprint $table) { + $table->string('key')->primary(); + $table->string('owner'); + $table->integer('expiration'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('cache'); + Schema::dropIfExists('cache_locks'); + } +}; diff --git a/resources/views/administrator/pages/customer/add.blade.php b/resources/views/administrator/pages/customer/add.blade.php index 3a2851d..00a4a20 100644 --- a/resources/views/administrator/pages/customer/add.blade.php +++ b/resources/views/administrator/pages/customer/add.blade.php @@ -7,20 +7,12 @@
| - - | -First Name | -Last Name | -Company | -Email Address | -Services | -Created | -Status | -
|---|---|---|---|---|---|---|---|
| - - | -- $item->id]) }}" - title="View Profile"> {{ $item->firstname }} - | -- {{ $item->lastname }} - | -- {{ $item->company }} - | -- {{ $item->email }} - | -- {!! Helpers::get_count_service($item) !!} - | -- @time($item->created_at) - | -- {!! $item->getStatus() !!} - | -
| First Name | -{{ $customer->firstname }} | - -|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Last Name | -{{ $customer->lastname }} | +Fullname | +{{ $customer->fullname }} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Company | diff --git a/resources/views/administrator/pages/customer/profile.blade.php b/resources/views/administrator/pages/customer/profile.blade.php index 0b6af28..096817c 100644 --- a/resources/views/administrator/pages/customer/profile.blade.php +++ b/resources/views/administrator/pages/customer/profile.blade.php @@ -11,20 +11,12 @@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Số dư tài khoản - | -- {{ number_format(auth('users')->user()->credit,0,',','.') }} đ - | -
| - Tổng tiền đã chi - | -- {{ number_format(((int)auth('users')->user()->total - (int)auth('users')->user()->credit),0,',','.') }} đ - | -
| - Tổng tiền đã nạp - | -- {{ number_format((int)auth('users')->user()->total ,0,',','.') }} đ - | -
- @switch($item->type) - @case(1) - Giảm {{$item->discount}} % trên giá trị đơn - hàng trong lần thanh toán đầu tiên. - @break - @case(2) - Giảm - {{number_format($item->discount,0,',','.')}} đ - trên giá trị đơn hàng trong lần thanh toán đầu tiên. - @break - @endswitch -
-
- Đã chi
- {{ format_price((int) customer()?->total - (int) customer()?->credit) }}
-
- Đã nạp
- {{ format_price((int) customer()?->total) }}
-
- Points
- {{ format_point(0) }}
-
- Tổng tích điểm
- {{ format_point(0) }}
-
- Hạng
- Cơ bản
- {{ __('The invoice you are looking for could not be found or you do not have permission to view it.') }}
- -| - Mô tả - | -- Số lượng - | -- Giá - | -
|---|---|---|
|
- Sản phẩm
- {{ $item->product->name ?? 'Không xác định' }}
-
-
- Loại:
- @if ($item->product_type == 1)
- Shared Hosting
- @elseif($item->product_type == 4)
- VPS
- @elseif($item->product_type == 3)
- Dedicated Server
- @else
- Khác
- @endif
-
-
- Hệ điều hành:
-
-
-
-
- {{ $item->os->osGroup->name }}
- {{ $item->os->version }}
-
-
- |
- - {{ $item->quantity }} - | -
- - {{ format_price($item->price) }} - /{{ $item->months }}tháng - - |
-
|
-
-
- Addon
-
- {{ $addon->addonItems->name }} đơn giá:
- ({{ format_price($addon->addonItems->price) }})
- / {{ $addon->addonItems->step . $addon->addonItems->prefix }}
-
-
- |
- - {{ $addon->qty }} / {{ $addon->addonItems->prefix }} - ({{ $item->months }} tháng) - | -- {{ number_format($addon->price * $item->months) }} VNĐ - | -
| Giá trị sản phẩm | -- | - {{ number_format($productTotal) }} VNĐ - | -
| Tổng addon | -- | - {{ number_format($addonTotal) }} VNĐ - | -
| Giảm giá | -- | - -{{ number_format($invoice->discount_value) }} VNĐ - | -
| Tổng cộng | -- | - {{ number_format($invoice->total) }} VNĐ - | -
{{ $invoice->note }}
-Nếu bạn gặp bất kỳ vấn đề nào khi thanh toán, vui lòng liên hệ với - chúng tôi thông qua. Hotline {{ setting('setting.tel') }} hoặc - thông qua Email {{ setting('setting.support') }}
-| # | -Dịch vụ | -Thời hạn | -Số tiền | -
|---|---|---|---|
| {{ $key + 1 }} | -- {{ $item->description }} - - | -- {!! Helper::ViewBillingCycle($item->billing) !!} - | -- {{ number_format($item->amount * Helper::BillingFormat($item->billing),0,',','.') }} - đ - | -
| - Giảm giá - | -- {{ number_format($invoice->Order->discount_value,0,',','.') }} đ - @php( $discount = $invoice->Order->discount_value) - | -||
| - Thành tiền (Chưa VAT) - | -- {{ number_format($invoice->InvoiceItem->sum('amount') * Helper::BillingFormat($item->billing) - $discount,0,',','.') }} - đ - | -||
| - VAT (10%) - | -- {{ number_format(($invoice->InvoiceItem->sum('amount') * Helper::BillingFormat($item->billing) - $discount) * (10/100),0,',','.') }} - đ - | -||
| - Tổng cộng - | -- {{ number_format($invoice->total,0,',','.') }} đ - | -||
| Client IP | -Hoạt động | -User Agent | -Thời gian | -
|---|---|---|---|
| - {{ $item->client_ip }} - | -- {{ $item->action }} - | -- {{ $item->agent }} - | -- {{ $item->created_at }} - | -
| - Mô tả - | -- Số lượng - | -- Giá - | -
|---|---|---|
|
- Sản phẩm
- {{ $item->product->name ?? 'Không xác định' }}
-
-
- Loại:
- @if ($item->product_type == 1)
- Shared Hosting
- @elseif($item->product_type == 2)
- VPS
- @elseif($item->product_type == 3)
- Dedicated Server
- @else
- Khác
- @endif
-
-
- Hệ điều hành:
-
-
-
-
- {{ $item->os->osGroup->name }}
- {{ $item->os->version }}
-
-
- |
- - {{ $item->quantity }} - | -
- - {{ format_price($item->price) }} - /{{ $item->months }}tháng - - |
-
|
-
-
- Addon
-
- {{ $addon->addonItems->name }} đơn giá:
- ({{ format_price($addon->addonItems->price) }})
- / {{ $addon->addonItems->step . $addon->addonItems->prefix }}
-
-
- |
- - {{ $addon->qty }} / {{ $addon->addonItems->prefix }} - ({{ $item->months }} tháng) - | -- {{ number_format($addon->price * $item->months) }} VNĐ - | -
| Giá trị sản phẩm | -- | - {{ number_format($productTotal) }} VNĐ - | -
| Tổng addon | -- | - {{ number_format($addonTotal) }} VNĐ - | -
| Giảm giá | -- | - -{{ number_format($orders->discount_value) }} VNĐ - | -
| Tổng cộng | -- | - {{ number_format($orders->amount) }} VNĐ - | -
{{ $orders->note }}
-Nếu bạn gặp bất kỳ vấn đề nào khi thanh toán, vui lòng liên hệ với - chúng tôi thông qua. Hotline {{ setting('setting.tel') }} - hoặc - thông qua Email {{ setting('setting.support') }}
-{{ customer()?->email }}
-- - -
- -- Số tiền tối thiểu cho mỗi lần nạp là 200.000đ. - Giao dịch sẽ được xử lý trong vòng 5-10 phút. -
-- Thông tin thanh toán của bạn được mã hóa và bảo vệ an toàn. - Chúng tôi không lưu trữ thông tin thẻ tín dụng. -
-- {{__('bankName')}}: {{ $payment->name }}
-- {{__('accountName')}}: {{ $payment->bankName }}
-- {{__('accountNumber')}}: {{ $payment->accountNo }}
-- Số tiền thanh toán: {{ number_format($amount,0,',','.') }} đ
-- Nội dung thanh toán: {{ $memo }}
-{{ $product->name ?? 'Virtual Machine' }}
-Vui lòng chờ...
-Danh mục này hiện chưa có sản phẩm nào.
-| Tên | -Mô tả | -Ngày tạo - | -Trạng thái - | -Dung lượng - | -Thao tác - | -
|---|---|---|---|---|---|
| - | - | - | - - | -- | -
-
-
-
-
-
- |
-
{{ __('Dịch vụ này chưa có thông tin VM hoặc đang trong quá trình khởi tạo.') }}
-- {{ $serverName }} -
-| - {{ __('Tên') }} | -- {{ __('Ngày tạo') }} | -- {{ __('Trạng thái') }} | -- {{ __('Dung lượng') }} | -- {{ __('Thao tác') }} | -
|---|---|---|---|---|
| - | - | - - | -- |
-
-
-
-
- |
-
Đơn vị: CPU - vCore | RAM - GB | SSD - GB
-Người yêu cầu
-{{ $ticket->first()->Customer->firstname .' '.$ticket->first()->Customer->lastname }}
-Phòng ban
-{{ $ticket->first()->room }}
-Đã Gửi
-{{ $ticket->last()->created_at }}
-Lần cập nhật cuối
-{{ $ticket->first()->created_at }}
-Tình trạng
- {!! Helper::get_ticket_status($ticket->first()->status,$ticket->first()->Reply->first()) !!} -{{ __('Đang tải...') }}
-| - {{ __('Property') }} | -- {{ __('Value') }} | -
|---|---|
| - {{ __('Domain Name') }} | -- |
| - {{ __('Registration Date') }} | -- |
| - {{ __('Last Updated') }} | -- |
| - {{ __('Expiration Date') }} | -- |
| - {{ __('Domain Owner') }} | -- |
| - {{ __('Status') }} | -- - | -
| - {{ __('Registrar') }} | -- |
| - {{ __('Whois Server') }} | -- |
| - {{ __('Nameserver') }} | -- - | -
| - {{ __('DNSSEC') }} | -- - | -