From 82080b5b8458755b33967755a2340808f3ab6fd8 Mon Sep 17 00:00:00 2001 From: baodev0 Date: Mon, 28 Sep 2026 08:26:29 +0700 Subject: [PATCH] update fix deadcode, security --- .env.example | 3 - .phpunit.cache/test-results | 2 +- app/Console/Commands/CheckOrderItemAddons.php | 114 --- .../Commands/EncryptVCenterPasswords.php | 53 - app/Console/Commands/FreshExcept.php | 30 - app/Console/Commands/ListVCenters.php | 51 - app/Console/Commands/LoginServerCheck.php | 33 - app/Enums/AddonInputType.php | 28 - app/Enums/Domain/DomainProvisioningStatus.php | 13 - app/Enums/Invoice/InvoiceStep.php | 23 - app/Enums/Status.php | 33 - app/Events/Create_VirtualMachine_Success.php | 50 - app/Events/NotificationStatus.php | 2 - app/Events/Notification_Expiry_Time.php | 50 - app/Events/Notification_Pay_Success.php | 51 - app/Events/VirtualMachineStatusChanged.php | 55 -- app/Events/VirtualMachine_Panel_Status.php | 46 - app/Exports/ProductCategoriesExport.php | 44 - app/Exports/ProductsExport.php | 72 -- .../Concerns/ManagesCustomerAccount.php | 89 ++ .../Concerns/ManagesCustomerBilling.php | 75 ++ .../Concerns/ManagesCustomerBulkActions.php | 91 ++ .../Concerns/ManagesCustomerDomains.php | 75 ++ .../Concerns/ManagesCustomerNotes.php | 52 + .../Concerns/ManagesCustomerServices.php | 127 +++ .../Api/Admin/Customer/CustomerController.php | 471 +-------- .../EmailTemplate/EmailTemplateController.php | 16 - .../Concerns/HandlesInvoiceAttachments.php | 76 ++ .../Concerns/HandlesInvoicePayments.php | 166 ++++ .../Concerns/SendsInvoiceNotifications.php | 118 +++ .../Api/Admin/Invoice/InvoiceController.php | 360 +------ .../Concerns/ManagesIpPoolBulkActions.php | 182 ++++ .../IpPool/Concerns/ReadsIpPoolUsage.php | 45 + .../Api/Admin/IpPool/IpPoolController.php | 208 +--- .../Api/Admin/OsManager/OsController.php | 1 - .../Api/Admin/Ticket/TicketController.php | 2 - .../Api/Client/ClientController.php | 460 +-------- .../Concerns/HandlesClientAuthentication.php | 296 ++++++ .../Concerns/HandlesClientPasswordReset.php | 88 ++ .../Client/Concerns/ManagesClientProfile.php | 94 ++ .../Api/Client/Service/ServiceController.php | 1 - .../Api/Client/Ticket/TicketController.php | 1 - .../Api/Service/Concerns/ManagesVmAccess.php | 98 ++ .../Api/Service/Concerns/ManagesVmNetwork.php | 242 +++++ .../Api/Service/Concerns/ManagesVmPower.php | 173 ++++ .../Api/Service/OrderControllerRefactor.php | 16 +- .../Api/Service/ProductCategoryController.php | 1 - .../Api/Service/ServiceController.php | 11 - .../Controllers/Api/Service/VmController.php | 488 +--------- .../Api/System/CompressionTestController.php | 89 -- .../Api/Transaction/TransactionController.php | 22 - .../Controllers/Api/VpsActionController.php | 71 -- .../Controllers/admin/AddonController.php | 160 --- .../Controllers/admin/CouponController.php | 139 --- .../Controllers/admin/CustomerController.php | 908 ------------------ .../Controllers/admin/DomainController.php | 47 - .../Controllers/admin/GroupController.php | 176 ---- .../Controllers/admin/InvoiceController.php | 178 ---- .../Controllers/admin/LoginController.php | 57 -- app/Http/Controllers/admin/MailController.php | 13 - app/Http/Controllers/admin/MainController.php | 161 ---- .../Controllers/admin/MediaController.php | 40 - app/Http/Controllers/admin/MenuController.php | 132 --- .../Controllers/admin/ModuleDNSController.php | 176 ---- .../admin/ModuleHostingController.php | 170 ---- .../admin/ModuleIPPoolController.php | 77 -- .../admin/ModulevCenterController.php | 184 ---- .../Controllers/admin/OrderController.php | 448 --------- .../Controllers/admin/PaymentController.php | 99 -- app/Http/Controllers/admin/PostController.php | 111 --- .../Controllers/admin/RolesController.php | 127 --- .../Controllers/admin/SettingController.php | 72 -- .../Controllers/admin/SupportController.php | 87 -- app/Http/Controllers/admin/TaskController.php | 225 ----- .../Controllers/admin/TemplateController.php | 140 --- app/Http/Controllers/admin/UserController.php | 235 ----- .../admin/VCenterManagerController.php | 430 --------- .../Controllers/admin/package/Datacenter.php | 145 --- .../Controllers/admin/package/Datastore.php | 153 --- app/Http/Controllers/admin/package/Folder.php | 147 --- app/Http/Controllers/admin/package/Group.php | 184 ---- app/Http/Controllers/admin/package/Host.php | 173 ---- .../Controllers/admin/package/IPMapping.php | 182 ---- .../Controllers/admin/package/Network.php | 145 --- .../Controllers/admin/package/OsVersion.php | 141 --- .../admin/package/SyncController.php | 17 - .../product/ProductCategoryController.php | 174 ---- .../admin/product/ProductController.php | 256 ----- .../admin/vm/VirtualMachineController.php | 160 --- app/Http/Helpers/Helper.php | 405 -------- app/Http/Kernel.php | 7 - .../AdvancedCompressionMiddleware.php | 131 --- app/Http/Middleware/CompressionMiddleware.php | 90 -- app/Http/Middleware/CustomCKFinderAuth.php | 24 - app/Http/Middleware/CustomerLoginCheck.php | 29 - app/Http/Middleware/IsLoginCheck.php | 24 - app/Http/Middleware/LoginCheck.php | 28 - app/Http/Middleware/StaffCheck.php | 56 -- app/Http/Middleware/TrustHosts.php | 20 - app/Http/Middleware/VerifyCsrfToken.php | 1 - app/Http/Requests/AddOnRequest.php | 34 - app/Http/Requests/AddPayRequest.php | 30 - app/Http/Requests/CouponRequest.php | 34 - .../Requests/Domain/DomainRemoveRequest.php | 21 - app/Http/Requests/Domain/DomainRequest.php | 23 - app/Http/Requests/DomainRequest.php | 34 - app/Http/Requests/Group/GroupRequest.php | 31 - app/Http/Requests/HostRequest.php | 28 - app/Http/Requests/HostingRequest.php | 35 - app/Http/Requests/MenuRequest.php | 34 - .../Requests/ModuleDNS/CreateDNSRequest.php | 31 - .../Requests/ModuleDNS/UpdateDNSRequest.php | 31 - .../ModuleHosting/CreateHostingRequest.php | 31 - .../ModuleHosting/UpdateHostingRequest.php | 31 - app/Http/Requests/Order/OrderRequest.php | 55 -- .../Requests/Package/DatacenterRequest.php | 30 - .../Requests/Package/DatastoreRequest.php | 33 - app/Http/Requests/Package/FolderRequest.php | 31 - app/Http/Requests/Package/HostRequest.php | 32 - app/Http/Requests/Package/IPRequest.php | 38 - app/Http/Requests/Package/NetworkRequest.php | 31 - app/Http/Requests/Package/OsGroupRequest.php | 31 - .../Requests/Package/OsVersionRequest.php | 31 - app/Http/Requests/PostRequest.php | 30 - app/Http/Requests/Product/ProductRequest.php | 208 ---- .../ProductCategoryRequest.php | 87 -- .../Requests/Recharge/RechargeRequest.php | 39 - .../Requests/Record/RecordRemoveRequest.php | 21 - .../Requests/Server/ServerRemoveRequest.php | 21 - app/Http/Requests/Server/ServerRequest.php | 24 - app/Http/Requests/Shop/AddToCartRequest.php | 48 - app/Http/Requests/TicketReplyRequest.php | 30 - app/Http/Requests/TicketRequest.php | 28 - .../Requests/User/UserRegisterRequest.php | 53 - .../Requests/User/UserStoreInformation.php | 60 -- app/Http/Requests/VM/SnapShotRequest.php | 37 - app/Http/Requests/VmwareRequest.php | 39 - app/Http/Requests/WhoisRequest.php | 62 -- .../Requests/vCenter/CreatevCenterRequest.php | 31 - .../Requests/vCenter/UpdatevCenterRequest.php | 31 - .../Client/Service/ServiceClientResource.php | 43 - .../Service/Order/ServiceDetailResource.php | 55 -- app/Http/Services/Cart.php | 149 --- app/Http/Services/Discord.php | 344 ------- app/Http/Services/Hosting.php | 56 -- app/Http/Services/Media.php | 30 - app/Http/Services/ProductService.php | 174 ---- app/Http/Services/Telegram.php | 77 -- app/Http/Services/Upload.php | 80 -- app/Http/Services/VNPayService.php | 78 -- app/Http/Services/vCenter.php | 56 -- app/Http/Services/vCenter/Client.php | 2 - .../Services/vCenter/Concerns/ManagesSsh.php | 13 - .../Concerns/ReadsVirtualMachineInfo.php | 190 ++++ .../Concerns/UpgradesVirtualMachine.php | 212 ++++ app/Http/Services/vCenter/DeleteVMService.php | 94 -- .../vCenter/PowerManagementService.php | 2 - .../Services/vCenter/PowerShellService.php | 64 -- app/Http/Services/vCenter/ResourceService.php | 6 - app/Http/Services/vCenter/SnapshotService.php | 7 +- app/Http/Services/vCenter/TemplateService.php | 73 -- .../vCenter/VMConfigurationService.php | 69 -- .../vCenter/VirtualMachineService.php | 604 +----------- app/Imports/ProductCategoriesImport.php | 46 - app/Imports/ProductsImport.php | 59 -- app/Jobs/Mail/Customer/CustomerPayment.php | 131 --- .../Mail/Customer/OrderPaymentSuccessJob.php | 42 - app/Jobs/Mail/CustomerRegister.php | 136 --- app/Jobs/Mail/PasswordRecoverJob.php | 65 -- app/Jobs/Payment/GetPaymentMessage.php | 35 - app/Jobs/Payment/GetTransactionPayment.php | 101 -- .../Action/ChangeRemotePortJob.php | 121 --- .../Action/ResetVmPasswordJob.php | 101 -- .../VirtualMachine/Create/CheckIPAddress.php | 94 -- .../Create/CheckStatusVirtualMachine.php | 54 -- .../Create/ConfigCLICommand.php | 208 ---- .../VirtualMachine/Create/ConfigDatabase.php | 84 -- .../Create/GetNetworkVirtualMachine.php | 61 -- .../Create/GetVirtualMachine.php | 57 -- .../Handle/WaitStatusVirtualMachine.php | 95 -- app/Jobs/VirtualMachine/Power/RebootVmJob.php | 86 -- app/Jobs/VirtualMachine/Power/StartVmJob.php | 97 -- app/Jobs/VirtualMachine/Power/StopVmJob.php | 84 -- .../Provision/AssignIpToGuestJob.php | 181 +--- .../Provision/ChangePasswordJob.php | 138 --- .../Concerns/ManagesIpReservation.php | 189 ++++ .../Provision/WaitVmReadyJob.php | 136 --- .../Rebuild/RebuildFinalizeJob.php | 1 - .../VirtualMachine/Sync/UpdateVMInfoJob.php | 157 --- app/Mail/ChangePortRemote.php | 42 - app/Mail/Customer/CustomerPayment.php | 45 - app/Mail/Customer/OrderPaymentSuccess.php | 81 -- app/Mail/CustomerRegister.php | 46 - app/Mail/PasswordReset.php | 45 - app/Mail/VmPasswordResetSuccess.php | 42 - app/Models/ActivityLog.php | 9 - app/Models/Banned.php | 18 - app/Models/Billing.php | 21 - app/Models/Cart.php | 85 -- app/Models/CartAddon.php | 36 - app/Models/Customer.php | 40 - app/Models/CustomerLog.php | 20 - app/Models/Device.php | 11 - app/Models/DnsManagerNS.php | 5 - app/Models/DnsManagerUser.php | 28 - app/Models/DnsServer.php | 9 - app/Models/Domain.php | 4 - app/Models/Group.php | 37 - app/Models/Invoice.php | 16 - app/Models/InvoiceAddons.php | 25 - app/Models/IpPool.php | 5 - app/Models/IpPoolContact.php | 5 - app/Models/Mail.php | 5 - app/Models/ModuleDatacenter.php | 5 - app/Models/ModuleDnsServer.php | 7 - app/Models/ModuleHosting.php | 9 - app/Models/ModuleHostingServer.php | 8 - app/Models/ModuleIpAddress.php | 7 - app/Models/ModuleNetwork.php | 9 - app/Models/ModuleVmware.php | 9 - app/Models/OrderAddon.php | 25 - app/Models/OrderItem.php | 1 - app/Models/Os.php | 7 - app/Models/OsGroup.php | 7 - app/Models/Payment.php | 7 - app/Models/Product.php | 25 - app/Models/ProductCategories.php | 5 - app/Models/ProductVMTemplates.php | 11 - app/Models/Province.php | 8 - app/Models/Service.php | 22 - app/Models/ServicePlanPlesk.php | 5 - app/Models/VirtualMachines.php | 5 - app/Models/VmProvisionLock.php | 5 - app/Models/Ward.php | 8 - app/Models/vCenter.php | 7 - app/Notifications/JobsSuccess.php | 61 -- app/Providers/AppServiceProvider.php | 19 - app/Providers/EventServiceProvider.php | 1 - app/Providers/RepositoryProvider.php | 141 +-- app/Providers/ViewServiceProvider.php | 34 - .../Customer/AdminCustomerRepository.php | 38 - .../AdminCustomerRepositoryInterface.php | 10 - .../Admin/Invoice/AdminInvoiceRepository.php | 30 - .../AdminInvoiceRepositoryInterface.php | 10 - .../Admin/Order/AdminOrderRepository.php | 30 - .../Order/AdminOrderRepositoryInterface.php | 10 - .../Admin/Product/ProductRepository.php | 63 -- .../Product/ProductRepositoryInterface.php | 16 - app/Repositories/Api/BaseApiRepository.php | 10 - .../Api/Client/ClientRepository.php | 8 - .../Api/Client/ClientRepositoryInterface.php | 4 - .../Api/Invoice/InvoiceRepository.php | 14 - .../Invoice/InvoiceRepositoryInterface.php | 6 - .../Api/Order/OrderRepository.php | 23 - .../Api/Order/OrderRepositoryInterface.php | 7 +- .../Banned/BannedCachedRepository.php | 19 - app/Repositories/Banned/BannedRepository.php | 14 - .../Banned/BannedRepositoryInterface.php | 6 - app/Repositories/BaseCachedRepository.php | 14 - .../BaseCachedRepositoryInterface.php | 25 - app/Repositories/BaseRepository.php | 9 - app/Repositories/BaseRepositoryInterface.php | 2 - .../Customer/CustomerLogCachedRepository.php | 19 - .../Customer/CustomerLogRepository.php | 14 - .../CustomerLogRepositoryInterface.php | 9 - .../Discount/DiscountCachedRepository.php | 36 - .../Discount/DiscountRepository.php | 72 -- .../Discount/DiscountRepositoryInterface.php | 9 - .../DnsManager/Domain/DnsDomainRepository.php | 15 - .../Domain/DnsDomainRepositoryInterface.php | 6 - .../DnsManager/Record/DnsRecordRepository.php | 10 - .../Record/DnsRecordRepositoryInterface.php | 4 - .../DnsManager/Server/DnsServerRepository.php | 5 - .../Server/DnsServerRepositoryInterface.php | 2 - .../Domain/DomainCachedRepository.php | 2 - .../Domain/DomainTypeCachedRepository.php | 18 - .../Domain/DomainTypeRepository.php | 14 - .../Domain/DomainTypeRepositoryInterface.php | 7 - .../File/FileCachedRepository.php | 18 - app/Repositories/File/FileRepository.php | 14 - .../File/FileRepositoryInterface.php | 8 - .../Group/GroupCachedRepository.php | 42 - app/Repositories/Group/GroupRepository.php | 87 -- .../Group/GroupRepositoryInterface.php | 16 - .../Invoice/InvoiceRepository.php | 9 - .../Invoice/InvoiceRepositoryInterface.php | 4 - .../InvoiceItemCachedRepository.php | 27 - .../InvoiceItem/InvoiceItemRepository.php | 15 - .../InvoiceItemRepositoryInterface.php | 3 - .../Mail/MailCachedRepository.php | 33 - app/Repositories/Mail/MailRepository.php | 30 - .../Mail/MailRepositoryInterface.php | 8 - .../Menu/MenuCachedRepository.php | 64 -- app/Repositories/Menu/MenuRepository.php | 57 -- .../Menu/MenuRepositoryInterface.php | 15 - .../Datacenter/DatacenterCachedRepository.php | 58 -- .../Datacenter/DatacenterRepository.php | 55 -- .../DatacenterRepositoryInterface.php | 17 - .../Datastore/DatastoreCachedRepository.php | 74 -- .../Module/Datastore/DatastoreRepository.php | 98 -- .../DatastoreRepositoryInterface.php | 18 - .../Module/Folder/FolderCachedRepository.php | 70 -- .../Module/Folder/FolderRepository.php | 88 -- .../Folder/FolderRepositoryInterface.php | 18 - .../Module/Host/HostCachedRepository.php | 62 -- .../Module/Host/HostRepository.php | 97 -- .../Module/Host/HostRepositoryInterface.php | 17 - .../HostingServerCachedRepository.php | 1 - .../Module/IPPool/IPPoolCachedRepository.php | 44 - .../Module/IPPool/IPPoolRepository.php | 54 -- .../IPPool/IPPoolRepositoryInterface.php | 10 - .../IpAddress/IpAddressCachedRepository.php | 76 -- .../Module/IpAddress/IpAddressRepository.php | 107 --- .../IpAddressRepositoryInterface.php | 20 - .../Network/NetworkCachedRepository.php | 69 -- .../Module/Network/NetworkRepository.php | 90 -- .../Network/NetworkRepositoryInterface.php | 17 - .../Snapshot/SnapshotCachedRepository.php | 18 - .../SnapshotLimitCachedRepository.php | 18 - .../Snapshot/SnapshotLimitRepository.php | 12 - .../SnapshotLimitRepositoryInterface.php | 6 - .../Module/Snapshot/SnapshotRepository.php | 13 - .../Snapshot/SnapshotRepositoryInterface.php | 6 - .../VirtualMachineRepository.php | 62 -- .../VirtualMachineRepositoryInterface.php | 15 - .../Note/NoteCachedRepository.php | 19 - app/Repositories/Note/NoteRepository.php | 14 - .../Note/NoteRepositoryInterface.php | 7 - app/Repositories/Order/OrderRepository.php | 7 - .../Order/OrderRepositoryInterface.php | 1 - app/Repositories/Os/OsCachedRepository.php | 57 -- app/Repositories/Os/OsRepository.php | 91 -- app/Repositories/Os/OsRepositoryInterface.php | 17 - .../OsGroup/OsGroupCachedRepository.php | 57 -- .../OsGroup/OsGroupRepository.php | 91 -- .../OsGroup/OsGroupRepositoryInterface.php | 18 - .../Post/PostCachedRepository.php | 44 - app/Repositories/Post/PostRepository.php | 47 - .../Post/PostRepositoryInterface.php | 9 - .../Staff/StaffCachedRepository.php | 41 - .../Template/TemplateCachedRepository.php | 18 - .../Template/TemplateRepository.php | 14 - .../Template/TemplateRepositoryInterface.php | 7 - .../Ticket/TicketCachedRepository.php | 21 - app/Repositories/Ticket/TicketRepository.php | 59 -- .../Ticket/TicketRepositoryInterface.php | 2 - .../User/UserCachedRepository.php | 18 - app/Repositories/User/UserRepository.php | 14 - .../User/UserRepositoryInterface.php | 6 - .../Whois/CachedWhoisRepository.php | 66 -- .../Whois/WhoisRepositoryInterface.php | 38 - .../vCenter/vCenterCachedRepository.php | 46 - .../vCenter/vCenterRepository.php | 52 - .../vCenter/vCenterRepositoryInterface.php | 16 - app/Services/Api/Client/ClientService.php | 7 - app/Services/Api/Order/OrderQueryService.php | 25 +- app/Services/Api/Order/OrderService.php | 8 +- app/Services/CompressionService.php | 84 -- .../Concerns/ActivatesPaidInvoices.php | 245 +++++ .../Customer/CustomerBillingService.php | 2 - .../Customer/CustomerImpersonationService.php | 2 - app/Services/Customer/CustomerNoteService.php | 5 - .../CustomerServiceProvisioningService.php | 36 - .../Imports/AdminZoneDnsSqlImporter.php | 483 +--------- .../Concerns/ImportsAdminZoneCustomers.php | 114 +++ .../Imports/Concerns/ImportsAdminZoneDns.php | 182 ++++ .../Imports/Concerns/ParsesSqlDump.php | 207 ++++ app/Services/InvoicePaymentService.php | 240 +---- app/Services/OrderService.php | 2 - app/Services/Plesk/PleskDomainService.php | 74 -- app/Services/ServerService.php | 30 - app/Services/StudentService.php | 100 -- app/Services/VMHelperService.php | 117 --- .../Rebuild/VmRebuildSwapService.php | 37 - .../VirtualMachine/VmPowerService.php | 35 - .../VmProviderSettingsService.php | 5 - app/Services/VmProvisionService.php | 61 -- app/Services/WhoisService.php | 92 -- app/Support/Breadcrumb/Breadcrumb.php | 15 - app/Traits/GetConfig.php | 12 - app/View/.DS_Store | Bin 6148 -> 0 bytes app/View/Components/AdminBadge.php | 73 -- app/View/Components/AdminBreadcrumb.php | 26 - app/View/Components/AdminTab.php | 27 - app/View/Components/ButtonCRM.php | 56 -- app/View/Components/Card/CardBody.php | 26 - app/View/Components/Card/CardHeader.php | 26 - app/View/Components/Card/CardLink.php | 24 - app/View/Components/Card/CardService.php | 26 - app/View/Components/Card/CardWrapper.php | 26 - app/View/Components/DataTable.php | 46 - app/View/Components/FallingEffect.php | 26 - app/View/Components/FixedActionBar.php | 26 - app/View/Components/Form/Customer.php | 31 - app/View/Components/Form/Payment.php | 31 - app/View/Components/ImagePicker.php | 26 - app/View/Components/Input/InputSwitch.php | 29 - app/View/Components/LivewireAssets.php | 26 - app/View/Components/Module/Datacenter.php | 43 - app/View/Components/Module/Host.php | 48 - app/View/Components/Module/IpAddress.php | 40 - app/View/Components/Module/Network.php | 45 - app/View/Components/Module/Os.php | 45 - .../Components/Module/OsServerVersion.php | 52 - app/View/Components/Module/OsVersion.php | 53 - app/View/Components/Module/VCenter.php | 40 - app/View/Components/Order/Customer.php | 30 - app/View/Components/Order/Discount.php | 30 - app/View/Components/Order/Payment.php | 30 - app/View/Components/Order/Type.php | 29 - app/View/Components/SiderbarUser.php | 39 - app/View/Components/Status.php | 28 - app/View/Composers/MenuComposer.php | 21 - app/View/Composers/Posts/PostComposer.php | 25 - config/admin-sidebar.php | 139 --- config/app.php | 1 - config/assets.php | 37 - config/ckfinder.php | 191 ---- config/debugbar.php | 329 ------- config/livewire.php | 160 --- config/sidebar.php | 204 ---- config/user_sidebar.php | 86 -- database/seeders/AdminSeeder.php | 1 - database/seeders/ControlPanelSeeder.php | 1 - database/seeders/CustomerSeeder.php | 1 - database/seeders/DatabaseSeeder.php | 1 - database/seeders/DomainTypeSeeder.php | 1 - database/seeders/GroupSeeder.php | 35 - database/seeders/MenuSeeder.php | 1 - database/seeders/Payment.php | 1 - database/seeders/SnapshotLimitedSeeder.php | 1 - database/seeders/TemplateSeeder.php | 1 - .../App/Models/DomainFluctuationName.php | 15 - resources/views/pdf/invoice-pdf.blade.php | 549 ----------- resources/views/pdf/invoice.blade.php | 566 +---------- resources/views/pdf/invoice/buyer.blade.php | 25 + resources/views/pdf/invoice/footer.blade.php | 26 + resources/views/pdf/invoice/header.blade.php | 38 + resources/views/pdf/invoice/items.blade.php | 83 ++ resources/views/pdf/invoice/seller.blade.php | 39 + .../views/pdf/invoice/signature.blade.php | 35 + resources/views/pdf/invoice/styles.blade.php | 313 ++++++ routes/api.php | 754 +-------------- routes/api/admin/auth.php | 43 + routes/api/admin/billing.php | 92 ++ routes/api/admin/catalog.php | 97 ++ routes/api/admin/content.php | 65 ++ routes/api/admin/customers.php | 72 ++ routes/api/admin/domains.php | 86 ++ routes/api/admin/hosting.php | 31 + routes/api/admin/infrastructure.php | 140 +++ routes/api/admin/system.php | 144 +++ 452 files changed, 4857 insertions(+), 26289 deletions(-) delete mode 100644 app/Console/Commands/CheckOrderItemAddons.php delete mode 100644 app/Console/Commands/EncryptVCenterPasswords.php delete mode 100644 app/Console/Commands/FreshExcept.php delete mode 100644 app/Console/Commands/ListVCenters.php delete mode 100644 app/Console/Commands/LoginServerCheck.php delete mode 100644 app/Enums/AddonInputType.php delete mode 100644 app/Enums/Invoice/InvoiceStep.php delete mode 100644 app/Enums/Status.php delete mode 100644 app/Events/Create_VirtualMachine_Success.php delete mode 100644 app/Events/Notification_Expiry_Time.php delete mode 100644 app/Events/Notification_Pay_Success.php delete mode 100644 app/Events/VirtualMachineStatusChanged.php delete mode 100644 app/Events/VirtualMachine_Panel_Status.php delete mode 100644 app/Exports/ProductCategoriesExport.php delete mode 100644 app/Exports/ProductsExport.php create mode 100644 app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerAccount.php create mode 100644 app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerBilling.php create mode 100644 app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerBulkActions.php create mode 100644 app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerDomains.php create mode 100644 app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerNotes.php create mode 100644 app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerServices.php create mode 100644 app/Http/Controllers/Api/Admin/Invoice/Concerns/HandlesInvoiceAttachments.php create mode 100644 app/Http/Controllers/Api/Admin/Invoice/Concerns/HandlesInvoicePayments.php create mode 100644 app/Http/Controllers/Api/Admin/Invoice/Concerns/SendsInvoiceNotifications.php create mode 100644 app/Http/Controllers/Api/Admin/IpPool/Concerns/ManagesIpPoolBulkActions.php create mode 100644 app/Http/Controllers/Api/Admin/IpPool/Concerns/ReadsIpPoolUsage.php create mode 100644 app/Http/Controllers/Api/Client/Concerns/HandlesClientAuthentication.php create mode 100644 app/Http/Controllers/Api/Client/Concerns/HandlesClientPasswordReset.php create mode 100644 app/Http/Controllers/Api/Client/Concerns/ManagesClientProfile.php create mode 100644 app/Http/Controllers/Api/Service/Concerns/ManagesVmAccess.php create mode 100644 app/Http/Controllers/Api/Service/Concerns/ManagesVmNetwork.php create mode 100644 app/Http/Controllers/Api/Service/Concerns/ManagesVmPower.php delete mode 100644 app/Http/Controllers/Api/System/CompressionTestController.php delete mode 100644 app/Http/Controllers/Api/VpsActionController.php delete mode 100644 app/Http/Controllers/admin/AddonController.php delete mode 100644 app/Http/Controllers/admin/CouponController.php delete mode 100644 app/Http/Controllers/admin/CustomerController.php delete mode 100644 app/Http/Controllers/admin/DomainController.php delete mode 100644 app/Http/Controllers/admin/GroupController.php delete mode 100644 app/Http/Controllers/admin/InvoiceController.php delete mode 100644 app/Http/Controllers/admin/LoginController.php delete mode 100644 app/Http/Controllers/admin/MailController.php delete mode 100644 app/Http/Controllers/admin/MainController.php delete mode 100644 app/Http/Controllers/admin/MediaController.php delete mode 100644 app/Http/Controllers/admin/MenuController.php delete mode 100644 app/Http/Controllers/admin/ModuleDNSController.php delete mode 100644 app/Http/Controllers/admin/ModuleHostingController.php delete mode 100644 app/Http/Controllers/admin/ModuleIPPoolController.php delete mode 100644 app/Http/Controllers/admin/ModulevCenterController.php delete mode 100644 app/Http/Controllers/admin/OrderController.php delete mode 100644 app/Http/Controllers/admin/PaymentController.php delete mode 100644 app/Http/Controllers/admin/PostController.php delete mode 100644 app/Http/Controllers/admin/RolesController.php delete mode 100644 app/Http/Controllers/admin/SettingController.php delete mode 100644 app/Http/Controllers/admin/SupportController.php delete mode 100644 app/Http/Controllers/admin/TaskController.php delete mode 100644 app/Http/Controllers/admin/TemplateController.php delete mode 100644 app/Http/Controllers/admin/UserController.php delete mode 100644 app/Http/Controllers/admin/package/Datacenter.php delete mode 100644 app/Http/Controllers/admin/package/Datastore.php delete mode 100644 app/Http/Controllers/admin/package/Folder.php delete mode 100644 app/Http/Controllers/admin/package/Group.php delete mode 100644 app/Http/Controllers/admin/package/Host.php delete mode 100644 app/Http/Controllers/admin/package/IPMapping.php delete mode 100644 app/Http/Controllers/admin/package/Network.php delete mode 100644 app/Http/Controllers/admin/package/OsVersion.php delete mode 100644 app/Http/Controllers/admin/package/SyncController.php delete mode 100644 app/Http/Controllers/admin/product/ProductCategoryController.php delete mode 100644 app/Http/Controllers/admin/product/ProductController.php delete mode 100644 app/Http/Controllers/admin/vm/VirtualMachineController.php delete mode 100644 app/Http/Middleware/AdvancedCompressionMiddleware.php delete mode 100644 app/Http/Middleware/CompressionMiddleware.php delete mode 100644 app/Http/Middleware/CustomCKFinderAuth.php delete mode 100644 app/Http/Middleware/CustomerLoginCheck.php delete mode 100644 app/Http/Middleware/IsLoginCheck.php delete mode 100644 app/Http/Middleware/LoginCheck.php delete mode 100644 app/Http/Middleware/StaffCheck.php delete mode 100644 app/Http/Middleware/TrustHosts.php delete mode 100644 app/Http/Requests/AddOnRequest.php delete mode 100644 app/Http/Requests/AddPayRequest.php delete mode 100644 app/Http/Requests/CouponRequest.php delete mode 100644 app/Http/Requests/Domain/DomainRemoveRequest.php delete mode 100644 app/Http/Requests/Domain/DomainRequest.php delete mode 100644 app/Http/Requests/DomainRequest.php delete mode 100644 app/Http/Requests/Group/GroupRequest.php delete mode 100644 app/Http/Requests/HostRequest.php delete mode 100644 app/Http/Requests/HostingRequest.php delete mode 100644 app/Http/Requests/MenuRequest.php delete mode 100644 app/Http/Requests/ModuleDNS/CreateDNSRequest.php delete mode 100644 app/Http/Requests/ModuleDNS/UpdateDNSRequest.php delete mode 100644 app/Http/Requests/ModuleHosting/CreateHostingRequest.php delete mode 100644 app/Http/Requests/ModuleHosting/UpdateHostingRequest.php delete mode 100644 app/Http/Requests/Order/OrderRequest.php delete mode 100644 app/Http/Requests/Package/DatacenterRequest.php delete mode 100644 app/Http/Requests/Package/DatastoreRequest.php delete mode 100644 app/Http/Requests/Package/FolderRequest.php delete mode 100644 app/Http/Requests/Package/HostRequest.php delete mode 100644 app/Http/Requests/Package/IPRequest.php delete mode 100644 app/Http/Requests/Package/NetworkRequest.php delete mode 100644 app/Http/Requests/Package/OsGroupRequest.php delete mode 100644 app/Http/Requests/Package/OsVersionRequest.php delete mode 100644 app/Http/Requests/PostRequest.php delete mode 100644 app/Http/Requests/Product/ProductRequest.php delete mode 100644 app/Http/Requests/ProductCategories/ProductCategoryRequest.php delete mode 100644 app/Http/Requests/Recharge/RechargeRequest.php delete mode 100644 app/Http/Requests/Record/RecordRemoveRequest.php delete mode 100644 app/Http/Requests/Server/ServerRemoveRequest.php delete mode 100644 app/Http/Requests/Server/ServerRequest.php delete mode 100644 app/Http/Requests/Shop/AddToCartRequest.php delete mode 100644 app/Http/Requests/TicketReplyRequest.php delete mode 100644 app/Http/Requests/TicketRequest.php delete mode 100644 app/Http/Requests/User/UserRegisterRequest.php delete mode 100644 app/Http/Requests/User/UserStoreInformation.php delete mode 100644 app/Http/Requests/VM/SnapShotRequest.php delete mode 100644 app/Http/Requests/VmwareRequest.php delete mode 100644 app/Http/Requests/WhoisRequest.php delete mode 100644 app/Http/Requests/vCenter/CreatevCenterRequest.php delete mode 100644 app/Http/Requests/vCenter/UpdatevCenterRequest.php delete mode 100644 app/Http/Resources/Api/Client/Service/ServiceClientResource.php delete mode 100644 app/Http/Resources/Api/Service/Order/ServiceDetailResource.php delete mode 100644 app/Http/Services/Cart.php delete mode 100644 app/Http/Services/Media.php delete mode 100644 app/Http/Services/ProductService.php delete mode 100644 app/Http/Services/Upload.php create mode 100644 app/Http/Services/vCenter/Concerns/ReadsVirtualMachineInfo.php create mode 100644 app/Http/Services/vCenter/Concerns/UpgradesVirtualMachine.php delete mode 100644 app/Http/Services/vCenter/DeleteVMService.php delete mode 100644 app/Imports/ProductCategoriesImport.php delete mode 100644 app/Imports/ProductsImport.php delete mode 100644 app/Jobs/Mail/Customer/CustomerPayment.php delete mode 100644 app/Jobs/Mail/Customer/OrderPaymentSuccessJob.php delete mode 100644 app/Jobs/Mail/CustomerRegister.php delete mode 100644 app/Jobs/Mail/PasswordRecoverJob.php delete mode 100644 app/Jobs/Payment/GetPaymentMessage.php delete mode 100644 app/Jobs/Payment/GetTransactionPayment.php delete mode 100644 app/Jobs/VirtualMachine/Action/ChangeRemotePortJob.php delete mode 100644 app/Jobs/VirtualMachine/Action/ResetVmPasswordJob.php delete mode 100644 app/Jobs/VirtualMachine/Create/CheckIPAddress.php delete mode 100644 app/Jobs/VirtualMachine/Create/CheckStatusVirtualMachine.php delete mode 100644 app/Jobs/VirtualMachine/Create/ConfigCLICommand.php delete mode 100644 app/Jobs/VirtualMachine/Create/ConfigDatabase.php delete mode 100644 app/Jobs/VirtualMachine/Create/GetNetworkVirtualMachine.php delete mode 100644 app/Jobs/VirtualMachine/Create/GetVirtualMachine.php delete mode 100644 app/Jobs/VirtualMachine/Handle/WaitStatusVirtualMachine.php delete mode 100644 app/Jobs/VirtualMachine/Power/RebootVmJob.php delete mode 100644 app/Jobs/VirtualMachine/Power/StartVmJob.php delete mode 100644 app/Jobs/VirtualMachine/Power/StopVmJob.php delete mode 100644 app/Jobs/VirtualMachine/Provision/ChangePasswordJob.php create mode 100644 app/Jobs/VirtualMachine/Provision/Concerns/ManagesIpReservation.php delete mode 100644 app/Jobs/VirtualMachine/Provision/WaitVmReadyJob.php delete mode 100644 app/Jobs/VirtualMachine/Sync/UpdateVMInfoJob.php delete mode 100644 app/Mail/ChangePortRemote.php delete mode 100644 app/Mail/Customer/CustomerPayment.php delete mode 100644 app/Mail/Customer/OrderPaymentSuccess.php delete mode 100644 app/Mail/CustomerRegister.php delete mode 100644 app/Mail/PasswordReset.php delete mode 100644 app/Mail/VmPasswordResetSuccess.php delete mode 100644 app/Models/Banned.php delete mode 100644 app/Models/Billing.php delete mode 100644 app/Models/Cart.php delete mode 100644 app/Models/CartAddon.php delete mode 100644 app/Models/CustomerLog.php delete mode 100644 app/Models/Device.php delete mode 100644 app/Models/DnsManagerUser.php delete mode 100644 app/Models/Group.php delete mode 100644 app/Models/InvoiceAddons.php delete mode 100644 app/Models/OrderAddon.php delete mode 100644 app/Models/ProductVMTemplates.php delete mode 100644 app/Notifications/JobsSuccess.php delete mode 100644 app/Providers/ViewServiceProvider.php delete mode 100644 app/Repositories/Admin/Customer/AdminCustomerRepository.php delete mode 100644 app/Repositories/Admin/Customer/AdminCustomerRepositoryInterface.php delete mode 100644 app/Repositories/Admin/Invoice/AdminInvoiceRepository.php delete mode 100644 app/Repositories/Admin/Invoice/AdminInvoiceRepositoryInterface.php delete mode 100644 app/Repositories/Admin/Order/AdminOrderRepository.php delete mode 100644 app/Repositories/Admin/Order/AdminOrderRepositoryInterface.php delete mode 100644 app/Repositories/Admin/Product/ProductRepository.php delete mode 100644 app/Repositories/Admin/Product/ProductRepositoryInterface.php delete mode 100644 app/Repositories/Banned/BannedCachedRepository.php delete mode 100644 app/Repositories/Banned/BannedRepository.php delete mode 100644 app/Repositories/Banned/BannedRepositoryInterface.php delete mode 100644 app/Repositories/BaseCachedRepositoryInterface.php delete mode 100644 app/Repositories/Customer/CustomerLogCachedRepository.php delete mode 100644 app/Repositories/Customer/CustomerLogRepository.php delete mode 100644 app/Repositories/Customer/CustomerLogRepositoryInterface.php delete mode 100644 app/Repositories/Discount/DiscountCachedRepository.php delete mode 100644 app/Repositories/Discount/DiscountRepository.php delete mode 100644 app/Repositories/Discount/DiscountRepositoryInterface.php delete mode 100644 app/Repositories/Domain/DomainTypeCachedRepository.php delete mode 100644 app/Repositories/Domain/DomainTypeRepository.php delete mode 100644 app/Repositories/Domain/DomainTypeRepositoryInterface.php delete mode 100644 app/Repositories/File/FileCachedRepository.php delete mode 100644 app/Repositories/File/FileRepository.php delete mode 100644 app/Repositories/File/FileRepositoryInterface.php delete mode 100644 app/Repositories/Group/GroupCachedRepository.php delete mode 100644 app/Repositories/Group/GroupRepository.php delete mode 100644 app/Repositories/Group/GroupRepositoryInterface.php delete mode 100644 app/Repositories/Mail/MailCachedRepository.php delete mode 100644 app/Repositories/Mail/MailRepository.php delete mode 100644 app/Repositories/Mail/MailRepositoryInterface.php delete mode 100644 app/Repositories/Menu/MenuCachedRepository.php delete mode 100644 app/Repositories/Menu/MenuRepository.php delete mode 100644 app/Repositories/Menu/MenuRepositoryInterface.php delete mode 100644 app/Repositories/Module/Datacenter/DatacenterCachedRepository.php delete mode 100644 app/Repositories/Module/Datacenter/DatacenterRepository.php delete mode 100644 app/Repositories/Module/Datacenter/DatacenterRepositoryInterface.php delete mode 100644 app/Repositories/Module/Datastore/DatastoreCachedRepository.php delete mode 100644 app/Repositories/Module/Datastore/DatastoreRepository.php delete mode 100644 app/Repositories/Module/Datastore/DatastoreRepositoryInterface.php delete mode 100644 app/Repositories/Module/Folder/FolderCachedRepository.php delete mode 100644 app/Repositories/Module/Folder/FolderRepository.php delete mode 100644 app/Repositories/Module/Folder/FolderRepositoryInterface.php delete mode 100644 app/Repositories/Module/Host/HostCachedRepository.php delete mode 100644 app/Repositories/Module/Host/HostRepository.php delete mode 100644 app/Repositories/Module/Host/HostRepositoryInterface.php delete mode 100644 app/Repositories/Module/IPPool/IPPoolCachedRepository.php delete mode 100644 app/Repositories/Module/IPPool/IPPoolRepository.php delete mode 100644 app/Repositories/Module/IPPool/IPPoolRepositoryInterface.php delete mode 100644 app/Repositories/Module/IpAddress/IpAddressCachedRepository.php delete mode 100644 app/Repositories/Module/IpAddress/IpAddressRepository.php delete mode 100644 app/Repositories/Module/IpAddress/IpAddressRepositoryInterface.php delete mode 100644 app/Repositories/Module/Network/NetworkCachedRepository.php delete mode 100644 app/Repositories/Module/Network/NetworkRepository.php delete mode 100644 app/Repositories/Module/Network/NetworkRepositoryInterface.php delete mode 100644 app/Repositories/Module/Snapshot/SnapshotCachedRepository.php delete mode 100644 app/Repositories/Module/Snapshot/SnapshotLimitCachedRepository.php delete mode 100644 app/Repositories/Module/Snapshot/SnapshotLimitRepository.php delete mode 100644 app/Repositories/Module/Snapshot/SnapshotLimitRepositoryInterface.php delete mode 100644 app/Repositories/Module/Snapshot/SnapshotRepository.php delete mode 100644 app/Repositories/Module/Snapshot/SnapshotRepositoryInterface.php delete mode 100644 app/Repositories/Module/VirtualMachine/VirtualMachineRepository.php delete mode 100644 app/Repositories/Module/VirtualMachine/VirtualMachineRepositoryInterface.php delete mode 100644 app/Repositories/Note/NoteCachedRepository.php delete mode 100644 app/Repositories/Note/NoteRepository.php delete mode 100644 app/Repositories/Note/NoteRepositoryInterface.php delete mode 100644 app/Repositories/Os/OsCachedRepository.php delete mode 100644 app/Repositories/Os/OsRepository.php delete mode 100644 app/Repositories/Os/OsRepositoryInterface.php delete mode 100644 app/Repositories/OsGroup/OsGroupCachedRepository.php delete mode 100644 app/Repositories/OsGroup/OsGroupRepository.php delete mode 100644 app/Repositories/OsGroup/OsGroupRepositoryInterface.php delete mode 100644 app/Repositories/Post/PostCachedRepository.php delete mode 100644 app/Repositories/Post/PostRepository.php delete mode 100644 app/Repositories/Post/PostRepositoryInterface.php delete mode 100644 app/Repositories/Staff/StaffCachedRepository.php delete mode 100644 app/Repositories/Template/TemplateCachedRepository.php delete mode 100644 app/Repositories/Template/TemplateRepository.php delete mode 100644 app/Repositories/Template/TemplateRepositoryInterface.php delete mode 100644 app/Repositories/User/UserCachedRepository.php delete mode 100644 app/Repositories/User/UserRepository.php delete mode 100644 app/Repositories/User/UserRepositoryInterface.php delete mode 100644 app/Repositories/Whois/CachedWhoisRepository.php delete mode 100644 app/Repositories/Whois/WhoisRepositoryInterface.php delete mode 100644 app/Repositories/vCenter/vCenterCachedRepository.php delete mode 100644 app/Repositories/vCenter/vCenterRepository.php delete mode 100644 app/Repositories/vCenter/vCenterRepositoryInterface.php delete mode 100644 app/Services/Api/Client/ClientService.php delete mode 100644 app/Services/CompressionService.php create mode 100644 app/Services/Concerns/ActivatesPaidInvoices.php create mode 100644 app/Services/Imports/Concerns/ImportsAdminZoneCustomers.php create mode 100644 app/Services/Imports/Concerns/ImportsAdminZoneDns.php create mode 100644 app/Services/Imports/Concerns/ParsesSqlDump.php delete mode 100644 app/Services/StudentService.php delete mode 100644 app/Services/VMHelperService.php delete mode 100644 app/Services/VirtualMachine/VmPowerService.php delete mode 100644 app/Services/WhoisService.php delete mode 100644 app/Traits/GetConfig.php delete mode 100644 app/View/.DS_Store delete mode 100644 app/View/Components/AdminBadge.php delete mode 100644 app/View/Components/AdminBreadcrumb.php delete mode 100644 app/View/Components/AdminTab.php delete mode 100644 app/View/Components/ButtonCRM.php delete mode 100644 app/View/Components/Card/CardBody.php delete mode 100644 app/View/Components/Card/CardHeader.php delete mode 100644 app/View/Components/Card/CardLink.php delete mode 100644 app/View/Components/Card/CardService.php delete mode 100644 app/View/Components/Card/CardWrapper.php delete mode 100644 app/View/Components/DataTable.php delete mode 100644 app/View/Components/FallingEffect.php delete mode 100644 app/View/Components/FixedActionBar.php delete mode 100644 app/View/Components/Form/Customer.php delete mode 100644 app/View/Components/Form/Payment.php delete mode 100644 app/View/Components/ImagePicker.php delete mode 100644 app/View/Components/Input/InputSwitch.php delete mode 100644 app/View/Components/LivewireAssets.php delete mode 100644 app/View/Components/Module/Datacenter.php delete mode 100644 app/View/Components/Module/Host.php delete mode 100644 app/View/Components/Module/IpAddress.php delete mode 100644 app/View/Components/Module/Network.php delete mode 100644 app/View/Components/Module/Os.php delete mode 100644 app/View/Components/Module/OsServerVersion.php delete mode 100644 app/View/Components/Module/OsVersion.php delete mode 100644 app/View/Components/Module/VCenter.php delete mode 100644 app/View/Components/Order/Customer.php delete mode 100644 app/View/Components/Order/Discount.php delete mode 100644 app/View/Components/Order/Payment.php delete mode 100644 app/View/Components/Order/Type.php delete mode 100644 app/View/Components/SiderbarUser.php delete mode 100644 app/View/Components/Status.php delete mode 100644 app/View/Composers/MenuComposer.php delete mode 100644 app/View/Composers/Posts/PostComposer.php delete mode 100644 config/admin-sidebar.php delete mode 100644 config/assets.php delete mode 100644 config/ckfinder.php delete mode 100644 config/debugbar.php delete mode 100644 config/livewire.php delete mode 100644 config/sidebar.php delete mode 100644 config/user_sidebar.php delete mode 100644 database/seeders/GroupSeeder.php delete mode 100644 modules/DomainFluctuation/App/Models/DomainFluctuationName.php delete mode 100644 resources/views/pdf/invoice-pdf.blade.php create mode 100644 resources/views/pdf/invoice/buyer.blade.php create mode 100644 resources/views/pdf/invoice/footer.blade.php create mode 100644 resources/views/pdf/invoice/header.blade.php create mode 100644 resources/views/pdf/invoice/items.blade.php create mode 100644 resources/views/pdf/invoice/seller.blade.php create mode 100644 resources/views/pdf/invoice/signature.blade.php create mode 100644 resources/views/pdf/invoice/styles.blade.php create mode 100644 routes/api/admin/auth.php create mode 100644 routes/api/admin/billing.php create mode 100644 routes/api/admin/catalog.php create mode 100644 routes/api/admin/content.php create mode 100644 routes/api/admin/customers.php create mode 100644 routes/api/admin/domains.php create mode 100644 routes/api/admin/hosting.php create mode 100644 routes/api/admin/infrastructure.php create mode 100644 routes/api/admin/system.php diff --git a/.env.example b/.env.example index 6efbf0b..959655f 100644 --- a/.env.example +++ b/.env.example @@ -36,9 +36,6 @@ VCENTER_SSH_KNOWN_HOSTS= PLESK_TLS_VERIFY=true PLESK_CA_BUNDLE= -#CONFIG CKFINDER -CKFINDER_DOMAIN=client.test -CKFINDER_LICENSE= BROADCAST_DRIVER=pusher CACHE_DRIVER=redis diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results index 6160f12..e9f9305 100644 --- a/.phpunit.cache/test-results +++ b/.phpunit.cache/test-results @@ -1 +1 @@ -{"version":2,"defects":{"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_reused_rotated_refresh_token_revokes_all_tokens_of_customer":7,"Tests\\Feature\\Console\\ReencryptSecretsTest::test_rotating_keys_re_encrypts_rsa_and_app_key_columns":7,"Tests\\Feature\\Console\\ReencryptSecretsTest::test_dry_run_changes_nothing_and_undecryptable_rows_fail_the_command":7},"times":{"Tests\\Unit\\BillingPricingTest::test_cycle_to_months_covers_every_enum_case":0.001,"Tests\\Unit\\BillingPricingTest::test_cycle_to_months_throws_on_unknown_value":0,"Tests\\Unit\\BillingPricingTest::test_cycle_to_months_throws_on_null":0,"Tests\\Unit\\BillingPricingTest::test_domain_price_formula with data set \"years=1, no setup fee\"":0.004,"Tests\\Unit\\BillingPricingTest::test_domain_price_formula with data set \"years=1, with setup fee\"":0,"Tests\\Unit\\BillingPricingTest::test_domain_price_formula with data set \"years=3, no setup fee\"":0,"Tests\\Unit\\BillingPricingTest::test_domain_price_formula with data set \"years=3, with setup fee\"":0,"Tests\\Unit\\EmailTemplateRendererTest::test_it_replaces_known_placeholders_in_subject_and_html":0.006,"Tests\\Unit\\EmailTemplateRendererTest::test_it_falls_back_to_example_value_when_data_missing":0,"Tests\\Unit\\EmailTemplateRendererTest::test_it_html_escapes_string_type_by_default":0,"Tests\\Unit\\EmailTemplateRendererTest::test_it_rejects_invalid_url_type":0,"Tests\\Unit\\EmailTemplateRendererTest::test_it_throws_for_undeclared_variable":0.001,"Tests\\Unit\\EmailTemplateRendererTest::test_it_never_executes_placeholder_content_as_code":0,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0,"Tests\\Unit\\Models\\ReinstallLogPlainPasswordTest::test_plain_password_is_stored_encrypted_and_reads_back_transparently":0.003,"Tests\\Unit\\Models\\ReinstallLogPlainPasswordTest::test_backfill_migration_is_idempotent":0.001,"Tests\\Unit\\ServiceFillableTest::test_service_ignores_fields_not_declared_in_fillable":0.031,"Tests\\Unit\\Services\\VCenter\\PowerShellServiceSecretLoggingTest::test_change_ip_vm_linux_never_logs_the_guest_password":0.002,"Tests\\Unit\\Services\\VCenter\\PowerShellServiceSecretLoggingTest::test_change_ip_vm_windows_never_logs_the_guest_password":0,"Tests\\Unit\\Support\\VCenterHttpClientFactoryTest::test_verify_true_and_no_ca_bundle_uses_default_verification":0.002,"Tests\\Unit\\Support\\VCenterHttpClientFactoryTest::test_verify_true_with_ca_bundle_pins_the_bundle_path":0,"Tests\\Unit\\Support\\VCenterHttpClientFactoryTest::test_verify_false_disables_verification":0,"Tests\\Feature\\Api\\Admin\\Customer\\CustomerMassAssignmentTest::test_store_ignores_fields_not_declared_in_validation_rules":0.025,"Tests\\Feature\\Api\\Admin\\Customer\\CustomerMassAssignmentTest::test_update_ignores_fields_not_declared_in_validation_rules":0.023,"Tests\\Feature\\Api\\Admin\\Customer\\ResetPasswordTest::test_reset_password_sends_email_and_does_not_leak_password_in_response":0.014,"Tests\\Feature\\Api\\Admin\\Employee\\EmployeeRoleGuardTest::test_non_full_administrator_cannot_edit_a_full_administrator":0.012,"Tests\\Feature\\Api\\Admin\\Employee\\EmployeeRoleGuardTest::test_non_full_administrator_cannot_delete_a_full_administrator":0.01,"Tests\\Feature\\Api\\Admin\\Employee\\EmployeeRoleGuardTest::test_cannot_change_own_role":0.008,"Tests\\Feature\\Api\\Admin\\Employee\\EmployeeRoleGuardTest::test_non_full_administrator_cannot_assign_administrator_role":0.012,"Tests\\Feature\\Api\\Admin\\Employee\\EmployeeRoleGuardTest::test_full_administrator_can_assign_administrator_role":0.015,"Tests\\Feature\\Api\\Admin\\InvoiceTestRouteRemovedTest::test_unauthenticated_invoice_test_routes_no_longer_exist":0.003,"Tests\\Feature\\Api\\Admin\\LoginThrottleTest::test_admin_login_is_throttled_after_5_attempts_per_minute":0.008,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"settings index\"":0.017,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"banks index\"":0.013,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"domain-pricing index\"":0.014,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"email-templates index\"":0.017,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"tickets index\"":0.018,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"activity-logs index\"":0.017,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"email-logs index\"":0.014,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"post-categories index\"":0.017,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"posts index\"":0.018,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"media index\"":0.099,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"dashboard stats\"":0.021,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"queue jobs recent\"":0.02,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_force_delete_post_requires_post_destroy_permission":0.017,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_dashboard_requires_dashboard_view_permission":0.017,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_full_administrator_bypasses_all_permission_gates":0.043,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_access_token_older_than_skew_window_is_rejected":0.002,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_verify_email_code_is_invalidated_after_too_many_wrong_attempts":0.014,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_forgot_password_sends_link_without_changing_password_and_reset_revokes_sessions":0.445,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_cccd_images_are_private_and_served_only_via_signed_url":0.015,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_access_token_is_revoked_when_password_changes":0.02,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_throttle_uses_client_ip_from_encrypted_payload_not_next_server_ip":0.072,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_logout_revokes_refresh_token_so_it_cannot_be_used_again":0.011,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_reused_rotated_refresh_token_revokes_all_tokens_of_customer":0.011,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_legacy_cccd_path_is_not_exposed":0.009,"Tests\\Feature\\Api\\Client\\SecurityHardeningTest::test_register_rejects_short_password":0.004,"Tests\\Feature\\Api\\Client\\SecurityHardeningTest::test_register_accepts_password_with_at_least_ten_characters":0.009,"Tests\\Feature\\Api\\Client\\SecurityHardeningTest::test_cccd_upload_rejects_svg":0.012,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_admin_can_list_domain_orders":0.013,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_non_admin_cannot_access_domain_orders":0.002,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_confirm_registration_creates_dns_domain_and_attaches_customer":0.018,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_cannot_confirm_already_confirmed_order":0.012,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_cannot_create_duplicate_dns_domain":0.013,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_update_kyc_url_sets_pending_kyc_status":0.014,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_activate_transitions_from_pending_kyc_to_active":0.019,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_cannot_activate_pending_order_directly":0.012,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_reject_and_refund_restores_customer_balance":0.026,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_reject_and_refund_is_idempotent":0.031,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_cannot_reject_non_pending_order":0.012,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_refund_uses_invoice_amount_not_current_pricing":0.022,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_transaction_rollback_on_error":0.01,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_authenticated_customer_can_place_domain_order":0.017,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_insufficient_balance_returns_422":0.006,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_duplicate_pending_domain_is_rejected":0.017,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_unauthenticated_cannot_place_order":0.002,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_invalid_product_type_rejected":0.004,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_domain_normalized_to_lowercase":0.015,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_unknown_tld_returns_422":0.003,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_years_defaults_to_1_when_not_provided":0.015,"Tests\\Feature\\Api\\Service\\OrderErrorMessageTest::test_pay_order_hides_unknown_exception_message":0.009,"Tests\\Feature\\Api\\Service\\OrderErrorMessageTest::test_pay_order_keeps_business_message_for_invalid_argument":0.007,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_annual_service_for_exactly_one_cycle_charges_the_annual_price":0.013,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_annual_service_with_months_not_matching_the_cycle_is_rejected":0.007,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_charges_the_recurring_addon_price_on_top_of_the_service_price":0.017,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_uses_current_catalog_price_not_the_original_order_price":0.011,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_fails_when_product_pricing_is_no_longer_active":0.008,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_reuses_original_order_instead_of_creating_a_new_one":0.012,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renewal_invoice_has_a_billing_period_and_is_not_duplicated_for_the_same_period":0.015,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_while_existing_invoice_is_overdue_returns_the_same_invoice":0.02,"Tests\\Feature\\Api\\Service\\ServiceOwnershipTest::test_client_cannot_fetch_another_customers_service_by_uuid":0.012,"Tests\\Feature\\Api\\Service\\ServiceOwnershipTest::test_client_can_fetch_own_service_by_uuid":0.012,"Tests\\Feature\\Api\\Service\\StoreOrderAddonTest::test_addon_belonging_to_another_product_type_is_rejected":0.007,"Tests\\Feature\\Api\\Service\\StoreOrderAddonTest::test_inactive_addon_is_rejected":0.006,"Tests\\Feature\\Api\\Service\\StoreOrderAddonTest::test_qty_above_max_is_rejected":0.007,"Tests\\Feature\\Api\\Service\\StoreOrderAddonTest::test_valid_addon_within_bounds_is_accepted":0.018,"Tests\\Feature\\Api\\Service\\VmChangeIpTest::test_change_ip_outside_pool_returns_422":0.025,"Tests\\Feature\\Api\\Service\\VmChangeIpTest::test_change_ip_already_in_use_returns_422":0.023,"Tests\\Feature\\Api\\Service\\VmChangeIpTest::test_change_ip_to_free_pool_ip_dispatches_job_with_pool_gateway":0.021,"Tests\\Feature\\Api\\Service\\VmChangeIpTest::test_power_on_for_suspended_service_returns_403":0.023,"Tests\\Feature\\Api\\Service\\VmOwnershipTest::test_customer_cannot_read_power_state_of_another_customers_vm_by_numeric_id":0.007,"Tests\\Feature\\Api\\Service\\VmUpgradeAddonsTest::test_upgrade_request_creates_unpaid_invoice_without_touching_vcenter":0.01,"Tests\\Feature\\Api\\Service\\VmUpgradeAddonsTest::test_qty_over_max_is_rejected":0.006,"Tests\\Feature\\Api\\Service\\VmUpgradeAddonsTest::test_paying_the_invoice_dispatches_upgrade_job_and_updates_addons_meta":0.019,"Tests\\Feature\\Api\\Transaction\\ManualDepositProofTest::test_confirm_rejects_proof_path_belonging_to_another_customer":0.009,"Tests\\Feature\\Api\\Transaction\\ManualDepositProofTest::test_confirm_rejects_traversal_and_reused_proof_paths":0.013,"Tests\\Feature\\Api\\Transaction\\ManualDepositProofTest::test_confirm_without_proof_is_rejected":0.006,"Tests\\Feature\\Api\\Transaction\\ManualDepositProofTest::test_upload_then_confirm_binds_proof_to_owner":0.013,"Tests\\Feature\\Console\\InvoiceReminderTest::test_no_reminder_is_sent_before_the_first_offset":0.006,"Tests\\Feature\\Console\\InvoiceReminderTest::test_reminder_step_advances_once_per_offset_reached":0.009,"Tests\\Feature\\Console\\InvoiceReminderTest::test_an_overdue_invoice_reaches_the_later_offsets":0.01,"Tests\\Feature\\Console\\InvoiceReminderTest::test_a_paid_invoice_is_never_reminded":0.006,"Tests\\Feature\\Console\\InvoiceReminderTest::test_manual_reminder_sends_even_when_the_offset_was_already_used":0.007,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_reminder_chain_does_not_reopen_for_long_overdue_legacy_invoices":0.007,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_recently_overdue_invoice_is_still_reminded":0.008,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_manual_reminder_still_works_on_an_old_invoice":0.005,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_legacy_pending_order_does_not_block_new_orders":0.005,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_local_pending_order_still_blocks_new_orders":0.004,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_expiry_does_not_duplicate_an_invoice_already_synced_on_the_order":0.009,"Tests\\Feature\\Console\\ServiceExpiryTest::test_expiry_command_creates_renewal_invoice_on_the_original_order":0.015,"Tests\\Feature\\Console\\ServiceExpiryTest::test_running_expiry_command_twice_does_not_duplicate_the_renewal_invoice":0.021,"Tests\\Feature\\Console\\ServiceExpiryTest::test_cancelled_service_does_not_get_a_renewal_invoice":0.012,"Tests\\Feature\\Console\\ServiceExpiryTest::test_expiry_command_does_not_duplicate_invoice_after_it_became_overdue":0.021,"Tests\\Feature\\Console\\ServiceExpiryTest::test_overdue_renewal_invoice_does_not_reset_an_active_order":0.022,"Tests\\Feature\\Console\\ServiceExpiryTest::test_renewal_invoice_uses_the_current_price_list":0.018,"Tests\\Feature\\Console\\ServiceExpiryTest::test_renewal_falls_back_to_the_original_price_when_no_current_price_exists":0.017,"Tests\\Feature\\EmailTemplateServiceTest::test_send_dispatches_job_to_queue":0.001,"Tests\\Feature\\EmailTemplateServiceTest::test_job_renders_and_sends_mail_with_template_sender_config":0.002,"Tests\\Feature\\EmailTemplateServiceTest::test_job_falls_back_to_system_default_sender_when_template_has_no_mail_config":0.002,"Tests\\Feature\\EmailTemplateServiceTest::test_preview_throws_for_undeclared_variable":0.002,"Tests\\Feature\\EmailTemplateServiceTest::test_load_template_is_cached_by_scenario":0.003,"Tests\\Feature\\Payment\\AdminTransactionApproveTest::test_approving_the_same_manual_deposit_twice_credits_the_customer_only_once":0.012,"Tests\\Feature\\Payment\\AdminTransactionApproveTest::test_approving_credits_the_received_amount_not_the_declared_one":0.01,"Tests\\Feature\\Payment\\AdminTransactionApproveTest::test_approving_without_received_amount_credits_the_declared_amount":0.011,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_paying_an_already_paid_invoice_does_not_double_deduct_balance":0.02,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_a_cancelled_invoice_cannot_be_paid_and_is_never_counted_as_paid":0.007,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_admin_cannot_mark_a_cancelled_invoice_as_paid":0.007,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_partial_payment_records_amount_paid_and_keeps_invoice_unpaid":0.014,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_paying_the_remainder_settles_the_invoice":0.025,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_overpaying_only_charges_the_outstanding_amount":0.018,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_refund_returns_balance_and_records_a_negative_transaction":0.024,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_concurrent_refresh_within_grace_window_does_not_revoke_sessions":0.014,"Tests\\Unit\\Support\\TlsHttpClientFactoryTest::test_verify_true_and_no_ca_bundle_uses_default_verification":0.001,"Tests\\Unit\\Support\\TlsHttpClientFactoryTest::test_verify_true_with_ca_bundle_pins_the_bundle_path":0,"Tests\\Unit\\Support\\TlsHttpClientFactoryTest::test_verify_false_disables_verification":0,"Tests\\Unit\\Support\\TlsHttpClientFactoryTest::test_plesk_reads_its_own_config":0,"Tests\\Unit\\Support\\TlsHttpClientFactoryTest::test_curl_options_follow_vcenter_config":0,"Tests\\Feature\\Console\\ReencryptSecretsTest::test_rotating_keys_re_encrypts_rsa_and_app_key_columns":0.078,"Tests\\Feature\\Console\\ReencryptSecretsTest::test_dry_run_changes_nothing_and_undecryptable_rows_fail_the_command":0.082,"Tests\\Unit\\Support\\SshHostKeyVerifierTest::test_missing_known_hosts_is_rejected_while_tls_verification_is_on":0.006,"Tests\\Unit\\Support\\SshHostKeyVerifierTest::test_missing_known_hosts_is_allowed_when_tls_verification_is_off":0,"Tests\\Unit\\Support\\SshHostKeyVerifierTest::test_host_key_must_match_known_hosts_entry":0.001}} \ No newline at end of file +{"version":2,"defects":{"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_reused_rotated_refresh_token_revokes_all_tokens_of_customer":7,"Tests\\Feature\\Console\\ReencryptSecretsTest::test_rotating_keys_re_encrypts_rsa_and_app_key_columns":7,"Tests\\Feature\\Console\\ReencryptSecretsTest::test_dry_run_changes_nothing_and_undecryptable_rows_fail_the_command":7,"Tests\\Feature\\Api\\Admin\\Customer\\CustomerMassAssignmentTest::test_store_ignores_fields_not_declared_in_validation_rules":7,"Tests\\Feature\\Api\\Admin\\Customer\\CustomerMassAssignmentTest::test_update_ignores_fields_not_declared_in_validation_rules":7,"Tests\\Feature\\Api\\Admin\\Customer\\ResetPasswordTest::test_reset_password_sends_email_and_does_not_leak_password_in_response":7},"times":{"Tests\\Unit\\BillingPricingTest::test_cycle_to_months_covers_every_enum_case":0.003,"Tests\\Unit\\BillingPricingTest::test_cycle_to_months_throws_on_unknown_value":0,"Tests\\Unit\\BillingPricingTest::test_cycle_to_months_throws_on_null":0,"Tests\\Unit\\BillingPricingTest::test_domain_price_formula with data set \"years=1, no setup fee\"":0.004,"Tests\\Unit\\BillingPricingTest::test_domain_price_formula with data set \"years=1, with setup fee\"":0,"Tests\\Unit\\BillingPricingTest::test_domain_price_formula with data set \"years=3, no setup fee\"":0,"Tests\\Unit\\BillingPricingTest::test_domain_price_formula with data set \"years=3, with setup fee\"":0,"Tests\\Unit\\EmailTemplateRendererTest::test_it_replaces_known_placeholders_in_subject_and_html":0.012,"Tests\\Unit\\EmailTemplateRendererTest::test_it_falls_back_to_example_value_when_data_missing":0,"Tests\\Unit\\EmailTemplateRendererTest::test_it_html_escapes_string_type_by_default":0.001,"Tests\\Unit\\EmailTemplateRendererTest::test_it_rejects_invalid_url_type":0,"Tests\\Unit\\EmailTemplateRendererTest::test_it_throws_for_undeclared_variable":0.001,"Tests\\Unit\\EmailTemplateRendererTest::test_it_never_executes_placeholder_content_as_code":0,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0,"Tests\\Unit\\Models\\ReinstallLogPlainPasswordTest::test_plain_password_is_stored_encrypted_and_reads_back_transparently":0.002,"Tests\\Unit\\Models\\ReinstallLogPlainPasswordTest::test_backfill_migration_is_idempotent":0.001,"Tests\\Unit\\ServiceFillableTest::test_service_ignores_fields_not_declared_in_fillable":0.039,"Tests\\Unit\\Services\\VCenter\\PowerShellServiceSecretLoggingTest::test_change_ip_vm_linux_never_logs_the_guest_password":0.004,"Tests\\Unit\\Services\\VCenter\\PowerShellServiceSecretLoggingTest::test_change_ip_vm_windows_never_logs_the_guest_password":0.001,"Tests\\Unit\\Support\\VCenterHttpClientFactoryTest::test_verify_true_and_no_ca_bundle_uses_default_verification":0.002,"Tests\\Unit\\Support\\VCenterHttpClientFactoryTest::test_verify_true_with_ca_bundle_pins_the_bundle_path":0,"Tests\\Unit\\Support\\VCenterHttpClientFactoryTest::test_verify_false_disables_verification":0,"Tests\\Feature\\Api\\Admin\\Customer\\CustomerMassAssignmentTest::test_store_ignores_fields_not_declared_in_validation_rules":0.032,"Tests\\Feature\\Api\\Admin\\Customer\\CustomerMassAssignmentTest::test_update_ignores_fields_not_declared_in_validation_rules":0.009,"Tests\\Feature\\Api\\Admin\\Customer\\ResetPasswordTest::test_reset_password_sends_email_and_does_not_leak_password_in_response":0.018,"Tests\\Feature\\Api\\Admin\\Employee\\EmployeeRoleGuardTest::test_non_full_administrator_cannot_edit_a_full_administrator":0.01,"Tests\\Feature\\Api\\Admin\\Employee\\EmployeeRoleGuardTest::test_non_full_administrator_cannot_delete_a_full_administrator":0.009,"Tests\\Feature\\Api\\Admin\\Employee\\EmployeeRoleGuardTest::test_cannot_change_own_role":0.008,"Tests\\Feature\\Api\\Admin\\Employee\\EmployeeRoleGuardTest::test_non_full_administrator_cannot_assign_administrator_role":0.011,"Tests\\Feature\\Api\\Admin\\Employee\\EmployeeRoleGuardTest::test_full_administrator_can_assign_administrator_role":0.014,"Tests\\Feature\\Api\\Admin\\InvoiceTestRouteRemovedTest::test_unauthenticated_invoice_test_routes_no_longer_exist":0.003,"Tests\\Feature\\Api\\Admin\\LoginThrottleTest::test_admin_login_is_throttled_after_5_attempts_per_minute":0.007,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"settings index\"":0.015,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"banks index\"":0.015,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"domain-pricing index\"":0.015,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"email-templates index\"":0.018,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"tickets index\"":0.019,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"activity-logs index\"":0.018,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"email-logs index\"":0.016,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"post-categories index\"":0.016,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"posts index\"":0.014,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"media index\"":0.086,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"dashboard stats\"":0.019,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_route_requires_expected_permission with data set \"queue jobs recent\"":0.017,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_force_delete_post_requires_post_destroy_permission":0.014,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_dashboard_requires_dashboard_view_permission":0.018,"Tests\\Feature\\Api\\Admin\\PermissionGatingTest::test_full_administrator_bypasses_all_permission_gates":0.043,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_access_token_older_than_skew_window_is_rejected":0.002,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_verify_email_code_is_invalidated_after_too_many_wrong_attempts":0.014,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_forgot_password_sends_link_without_changing_password_and_reset_revokes_sessions":0.449,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_cccd_images_are_private_and_served_only_via_signed_url":0.019,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_access_token_is_revoked_when_password_changes":0.02,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_throttle_uses_client_ip_from_encrypted_payload_not_next_server_ip":0.068,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_logout_revokes_refresh_token_so_it_cannot_be_used_again":0.008,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_reused_rotated_refresh_token_revokes_all_tokens_of_customer":0.012,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_legacy_cccd_path_is_not_exposed":0.007,"Tests\\Feature\\Api\\Client\\SecurityHardeningTest::test_register_rejects_short_password":0.004,"Tests\\Feature\\Api\\Client\\SecurityHardeningTest::test_register_accepts_password_with_at_least_ten_characters":0.009,"Tests\\Feature\\Api\\Client\\SecurityHardeningTest::test_cccd_upload_rejects_svg":0.01,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_admin_can_list_domain_orders":0.012,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_non_admin_cannot_access_domain_orders":0.002,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_confirm_registration_creates_dns_domain_and_attaches_customer":0.014,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_cannot_confirm_already_confirmed_order":0.011,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_cannot_create_duplicate_dns_domain":0.012,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_update_kyc_url_sets_pending_kyc_status":0.012,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_activate_transitions_from_pending_kyc_to_active":0.019,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_cannot_activate_pending_order_directly":0.011,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_reject_and_refund_restores_customer_balance":0.024,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_reject_and_refund_is_idempotent":0.03,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_cannot_reject_non_pending_order":0.012,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_refund_uses_invoice_amount_not_current_pricing":0.021,"Tests\\Feature\\Api\\DomainOrder\\AdminDomainOrderTest::test_transaction_rollback_on_error":0.01,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_authenticated_customer_can_place_domain_order":0.016,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_insufficient_balance_returns_422":0.006,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_duplicate_pending_domain_is_rejected":0.016,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_unauthenticated_cannot_place_order":0.002,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_invalid_product_type_rejected":0.005,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_domain_normalized_to_lowercase":0.017,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_unknown_tld_returns_422":0.005,"Tests\\Feature\\Api\\DomainOrder\\ClientDomainOrderTest::test_years_defaults_to_1_when_not_provided":0.015,"Tests\\Feature\\Api\\Service\\OrderErrorMessageTest::test_pay_order_hides_unknown_exception_message":0.009,"Tests\\Feature\\Api\\Service\\OrderErrorMessageTest::test_pay_order_keeps_business_message_for_invalid_argument":0.009,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_annual_service_for_exactly_one_cycle_charges_the_annual_price":0.015,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_annual_service_with_months_not_matching_the_cycle_is_rejected":0.007,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_charges_the_recurring_addon_price_on_top_of_the_service_price":0.017,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_uses_current_catalog_price_not_the_original_order_price":0.009,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_fails_when_product_pricing_is_no_longer_active":0.008,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_reuses_original_order_instead_of_creating_a_new_one":0.011,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renewal_invoice_has_a_billing_period_and_is_not_duplicated_for_the_same_period":0.014,"Tests\\Feature\\Api\\Service\\RenewServiceTest::test_renew_while_existing_invoice_is_overdue_returns_the_same_invoice":0.018,"Tests\\Feature\\Api\\Service\\ServiceOwnershipTest::test_client_cannot_fetch_another_customers_service_by_uuid":0.013,"Tests\\Feature\\Api\\Service\\ServiceOwnershipTest::test_client_can_fetch_own_service_by_uuid":0.012,"Tests\\Feature\\Api\\Service\\StoreOrderAddonTest::test_addon_belonging_to_another_product_type_is_rejected":0.01,"Tests\\Feature\\Api\\Service\\StoreOrderAddonTest::test_inactive_addon_is_rejected":0.005,"Tests\\Feature\\Api\\Service\\StoreOrderAddonTest::test_qty_above_max_is_rejected":0.006,"Tests\\Feature\\Api\\Service\\StoreOrderAddonTest::test_valid_addon_within_bounds_is_accepted":0.014,"Tests\\Feature\\Api\\Service\\VmChangeIpTest::test_change_ip_outside_pool_returns_422":0.037,"Tests\\Feature\\Api\\Service\\VmChangeIpTest::test_change_ip_already_in_use_returns_422":0.019,"Tests\\Feature\\Api\\Service\\VmChangeIpTest::test_change_ip_to_free_pool_ip_dispatches_job_with_pool_gateway":0.017,"Tests\\Feature\\Api\\Service\\VmChangeIpTest::test_power_on_for_suspended_service_returns_403":0.018,"Tests\\Feature\\Api\\Service\\VmOwnershipTest::test_customer_cannot_read_power_state_of_another_customers_vm_by_numeric_id":0.007,"Tests\\Feature\\Api\\Service\\VmUpgradeAddonsTest::test_upgrade_request_creates_unpaid_invoice_without_touching_vcenter":0.013,"Tests\\Feature\\Api\\Service\\VmUpgradeAddonsTest::test_qty_over_max_is_rejected":0.008,"Tests\\Feature\\Api\\Service\\VmUpgradeAddonsTest::test_paying_the_invoice_dispatches_upgrade_job_and_updates_addons_meta":0.025,"Tests\\Feature\\Api\\Transaction\\ManualDepositProofTest::test_confirm_rejects_proof_path_belonging_to_another_customer":0.01,"Tests\\Feature\\Api\\Transaction\\ManualDepositProofTest::test_confirm_rejects_traversal_and_reused_proof_paths":0.011,"Tests\\Feature\\Api\\Transaction\\ManualDepositProofTest::test_confirm_without_proof_is_rejected":0.006,"Tests\\Feature\\Api\\Transaction\\ManualDepositProofTest::test_upload_then_confirm_binds_proof_to_owner":0.014,"Tests\\Feature\\Console\\InvoiceReminderTest::test_no_reminder_is_sent_before_the_first_offset":0.006,"Tests\\Feature\\Console\\InvoiceReminderTest::test_reminder_step_advances_once_per_offset_reached":0.009,"Tests\\Feature\\Console\\InvoiceReminderTest::test_an_overdue_invoice_reaches_the_later_offsets":0.009,"Tests\\Feature\\Console\\InvoiceReminderTest::test_a_paid_invoice_is_never_reminded":0.006,"Tests\\Feature\\Console\\InvoiceReminderTest::test_manual_reminder_sends_even_when_the_offset_was_already_used":0.005,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_reminder_chain_does_not_reopen_for_long_overdue_legacy_invoices":0.009,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_recently_overdue_invoice_is_still_reminded":0.008,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_manual_reminder_still_works_on_an_old_invoice":0.005,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_legacy_pending_order_does_not_block_new_orders":0.004,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_local_pending_order_still_blocks_new_orders":0.007,"Tests\\Feature\\Console\\PostWhmcsSyncSafetyTest::test_expiry_does_not_duplicate_an_invoice_already_synced_on_the_order":0.011,"Tests\\Feature\\Console\\ServiceExpiryTest::test_expiry_command_creates_renewal_invoice_on_the_original_order":0.015,"Tests\\Feature\\Console\\ServiceExpiryTest::test_running_expiry_command_twice_does_not_duplicate_the_renewal_invoice":0.016,"Tests\\Feature\\Console\\ServiceExpiryTest::test_cancelled_service_does_not_get_a_renewal_invoice":0.015,"Tests\\Feature\\Console\\ServiceExpiryTest::test_expiry_command_does_not_duplicate_invoice_after_it_became_overdue":0.021,"Tests\\Feature\\Console\\ServiceExpiryTest::test_overdue_renewal_invoice_does_not_reset_an_active_order":0.018,"Tests\\Feature\\Console\\ServiceExpiryTest::test_renewal_invoice_uses_the_current_price_list":0.018,"Tests\\Feature\\Console\\ServiceExpiryTest::test_renewal_falls_back_to_the_original_price_when_no_current_price_exists":0.015,"Tests\\Feature\\EmailTemplateServiceTest::test_send_dispatches_job_to_queue":0.001,"Tests\\Feature\\EmailTemplateServiceTest::test_job_renders_and_sends_mail_with_template_sender_config":0.004,"Tests\\Feature\\EmailTemplateServiceTest::test_job_falls_back_to_system_default_sender_when_template_has_no_mail_config":0.002,"Tests\\Feature\\EmailTemplateServiceTest::test_preview_throws_for_undeclared_variable":0.001,"Tests\\Feature\\EmailTemplateServiceTest::test_load_template_is_cached_by_scenario":0.002,"Tests\\Feature\\Payment\\AdminTransactionApproveTest::test_approving_the_same_manual_deposit_twice_credits_the_customer_only_once":0.01,"Tests\\Feature\\Payment\\AdminTransactionApproveTest::test_approving_credits_the_received_amount_not_the_declared_one":0.013,"Tests\\Feature\\Payment\\AdminTransactionApproveTest::test_approving_without_received_amount_credits_the_declared_amount":0.009,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_paying_an_already_paid_invoice_does_not_double_deduct_balance":0.017,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_a_cancelled_invoice_cannot_be_paid_and_is_never_counted_as_paid":0.006,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_admin_cannot_mark_a_cancelled_invoice_as_paid":0.008,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_partial_payment_records_amount_paid_and_keeps_invoice_unpaid":0.015,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_paying_the_remainder_settles_the_invoice":0.021,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_overpaying_only_charges_the_outstanding_amount":0.015,"Tests\\Feature\\Payment\\InvoicePaymentServiceTest::test_refund_returns_balance_and_records_a_negative_transaction":0.02,"Tests\\Feature\\Api\\Client\\ClientAuthSecurityTest::test_concurrent_refresh_within_grace_window_does_not_revoke_sessions":0.016,"Tests\\Unit\\Support\\TlsHttpClientFactoryTest::test_verify_true_and_no_ca_bundle_uses_default_verification":0.002,"Tests\\Unit\\Support\\TlsHttpClientFactoryTest::test_verify_true_with_ca_bundle_pins_the_bundle_path":0,"Tests\\Unit\\Support\\TlsHttpClientFactoryTest::test_verify_false_disables_verification":0,"Tests\\Unit\\Support\\TlsHttpClientFactoryTest::test_plesk_reads_its_own_config":0,"Tests\\Unit\\Support\\TlsHttpClientFactoryTest::test_curl_options_follow_vcenter_config":0,"Tests\\Feature\\Console\\ReencryptSecretsTest::test_rotating_keys_re_encrypts_rsa_and_app_key_columns":0.107,"Tests\\Feature\\Console\\ReencryptSecretsTest::test_dry_run_changes_nothing_and_undecryptable_rows_fail_the_command":0.069,"Tests\\Unit\\Support\\SshHostKeyVerifierTest::test_missing_known_hosts_is_rejected_while_tls_verification_is_on":0.01,"Tests\\Unit\\Support\\SshHostKeyVerifierTest::test_missing_known_hosts_is_allowed_when_tls_verification_is_off":0,"Tests\\Unit\\Support\\SshHostKeyVerifierTest::test_host_key_must_match_known_hosts_entry":0.002,"Tests\\Feature\\ZzInvoiceBladeCompareTest::test_split_invoice_template_renders_same_html":0.04}} \ No newline at end of file diff --git a/app/Console/Commands/CheckOrderItemAddons.php b/app/Console/Commands/CheckOrderItemAddons.php deleted file mode 100644 index c246bda..0000000 --- a/app/Console/Commands/CheckOrderItemAddons.php +++ /dev/null @@ -1,114 +0,0 @@ -argument('order_item_id'); - - $this->info("🔍 Checking addons for Order Item ID: {$orderItemId}"); - - try { - $orderItem = OrderItem::with(['addons.addonItems'])->find($orderItemId); - - if (!$orderItem) { - $this->error("❌ Order Item not found with ID: {$orderItemId}"); - return 1; - } - - $this->info("📋 Order Item Details:"); - $this->info(" - ID: {$orderItem->id}"); - $this->info(" - Product ID: {$orderItem->product_id}"); - $this->info(" - Product Type: {$orderItem->product_type}"); - $this->info(" - Server Name: {$orderItem->server_name}"); - - $this->info("\n🔍 Addons Information:"); - - if ($orderItem->addons && $orderItem->addons->count() > 0) { - $this->info(" - Total Addons: {$orderItem->addons->count()}"); - - foreach ($orderItem->addons as $index => $addon) { - $this->info("\n 📦 Addon #" . ($index + 1) . ":"); - $this->info(" - Order Addon ID: {$addon->id}"); - $this->info(" - Addon ID: {$addon->addon_id}"); - $this->info(" - Quantity (qty): {$addon->qty}"); - $this->info(" - Price: {$addon->price}"); - - if ($addon->addonItems) { - $this->info(" - Addon Name: {$addon->addonItems->name}"); - $this->info(" - Addon Price: {$addon->addonItems->price}"); - $this->info(" - Addon Step: {$addon->addonItems->step}"); - $this->info(" - Addon Prefix: {$addon->addonItems->prefix}"); - } else { - $this->warn(" ⚠️ AddonItems relationship not found!"); - } - } - - // Process addons like in the job - $addonData = [ - 'cpu' => 0, - 'ram' => 0, - 'disk' => 0 - ]; - - foreach ($orderItem->addons as $orderAddon) { - $addon = $orderAddon->addonItems; - if (!$addon) continue; - - $addonName = strtolower(trim($addon->name)); - $quantity = (int) $orderAddon->qty; - - switch ($addonName) { - case 'cpu': - $addonData['cpu'] += $quantity; - break; - case 'ram': - $addonData['ram'] += $quantity; - break; - case 'disk': - case 'ssd': - $addonData['disk'] += $quantity; - break; - } - } - - $this->info("\n📊 Processed Addon Data:"); - $this->info(" - CPU: {$addonData['cpu']}"); - $this->info(" - RAM: {$addonData['ram']}GB"); - $this->info(" - Disk: {$addonData['disk']}GB"); - $this->info(" - Has Addons: " . (array_sum($addonData) > 0 ? 'Yes' : 'No')); - - } else { - $this->warn("⚠️ No addons found for this order item"); - } - - return 0; - - } catch (\Exception $e) { - $this->error("❌ Exception: " . $e->getMessage()); - return 1; - } - } -} \ No newline at end of file diff --git a/app/Console/Commands/EncryptVCenterPasswords.php b/app/Console/Commands/EncryptVCenterPasswords.php deleted file mode 100644 index 1bf17f1..0000000 --- a/app/Console/Commands/EncryptVCenterPasswords.php +++ /dev/null @@ -1,53 +0,0 @@ -get(['id', 'password']); - $fixed = 0; - - foreach ($rows as $row) { - if (!$row->password) continue; - - // Try to decrypt as Laravel encrypted cast (from previous migration) - $plainPassword = null; - try { - $plainPassword = Crypt::decryptString($row->password); - $this->info("vCenter #{$row->id}: detected Laravel encrypted password, will re-encrypt with SignRSA"); - } catch (\Exception) { - // Not Laravel encrypted — check if it's already SignRSA or plain text - $decrypted = SignRSA::DecryptFunc($row->password); - if ($decrypted) { - $this->info("vCenter #{$row->id}: already SignRSA encrypted, skipping"); - continue; - } - // Plain text password - $plainPassword = $row->password; - $this->info("vCenter #{$row->id}: detected plain text password, will encrypt with SignRSA"); - } - - if ($plainPassword) { - $encrypted = SignRSA::EncryptFunc($plainPassword); - DB::table('module_vcenter') - ->where('id', $row->id) - ->update(['password' => $encrypted]); - $fixed++; - $this->info("vCenter #{$row->id}: re-encrypted successfully"); - } - } - - $this->info("Done. Fixed {$fixed} password(s)."); - return 0; - } -} diff --git a/app/Console/Commands/FreshExcept.php b/app/Console/Commands/FreshExcept.php deleted file mode 100644 index 2bc0e20..0000000 --- a/app/Console/Commands/FreshExcept.php +++ /dev/null @@ -1,30 +0,0 @@ -info("🔍 Listing all vCenters:"); - - try { - $vcenters = vCenter::all(); - - if ($vcenters->count() === 0) { - $this->warn("⚠️ No vCenters found in database"); - return 0; - } - - foreach ($vcenters as $vcenter) { - $status = $vcenter->status == 1 ? '✅ Active' : '❌ Inactive'; - $this->info(" ID: {$vcenter->id} | Host: {$vcenter->host} | User: {$vcenter->user} | Status: {$status}"); - } - - return 0; - - } catch (\Exception $e) { - $this->error("❌ Exception: " . $e->getMessage()); - return 1; - } - } -} \ No newline at end of file diff --git a/app/Console/Commands/LoginServerCheck.php b/app/Console/Commands/LoginServerCheck.php deleted file mode 100644 index ac6cfa3..0000000 --- a/app/Console/Commands/LoginServerCheck.php +++ /dev/null @@ -1,33 +0,0 @@ - 'Số', - self::SLIDER => 'Thanh trượt', - self::SELECT => 'Chọn', - }; - } - - public static function getOptions(): array - { - return [ - self::NUMBER->value => self::NUMBER->label(), - self::SLIDER->value => self::SLIDER->label(), - self::SELECT->value => self::SELECT->label(), - ]; - } -} diff --git a/app/Enums/Domain/DomainProvisioningStatus.php b/app/Enums/Domain/DomainProvisioningStatus.php index df6bfad..6d109da 100644 --- a/app/Enums/Domain/DomainProvisioningStatus.php +++ b/app/Enums/Domain/DomainProvisioningStatus.php @@ -22,17 +22,4 @@ enum DomainProvisioningStatus: string self::REFUNDED => 'Đã hoàn tiền', }; } - - /** Valid forward transitions. */ - public function canTransitionTo(self $next): bool - { - return match ($this) { - self::PENDING => in_array($next, [self::REGISTRAR_CREATED, self::PENDING_KYC, self::REFUNDED]), - self::REGISTRAR_CREATED => in_array($next, [self::PENDING_KYC, self::ACTIVE, self::CANCELLED]), - self::PENDING_KYC => in_array($next, [self::ACTIVE, self::CANCELLED]), - self::ACTIVE => in_array($next, [self::CANCELLED]), - self::CANCELLED, - self::REFUNDED => false, - }; - } } diff --git a/app/Enums/Invoice/InvoiceStep.php b/app/Enums/Invoice/InvoiceStep.php deleted file mode 100644 index 424a844..0000000 --- a/app/Enums/Invoice/InvoiceStep.php +++ /dev/null @@ -1,23 +0,0 @@ - '1 ngày trước khi hết hạn', - self::STEP_2 => '3 ngày trước khi hết hạn', - self::STEP_3 => '7 ngày trước khi hết hạn', - self::STEP_4 => '14 ngày trước khi hết hạn', - self::STEP_5 => '30 ngày trước khi hết hạn', - }; - } -} diff --git a/app/Enums/Status.php b/app/Enums/Status.php deleted file mode 100644 index 7a8379a..0000000 --- a/app/Enums/Status.php +++ /dev/null @@ -1,33 +0,0 @@ - 'Không hoạt động', - self::ACTIVE => 'Hoạt động', - }; - } - - public function badge(): string - { - return match ($this) { - self::DEACTIVE => 'Không hoạt động', - self::ACTIVE => 'Hoạt động', - }; - } - - public static function getOptions(): array - { - return [ - self::DEACTIVE->value => self::DEACTIVE->label(), - self::ACTIVE->value => self::ACTIVE->label(), - ]; - } -} \ No newline at end of file diff --git a/app/Events/Create_VirtualMachine_Success.php b/app/Events/Create_VirtualMachine_Success.php deleted file mode 100644 index 7defc60..0000000 --- a/app/Events/Create_VirtualMachine_Success.php +++ /dev/null @@ -1,50 +0,0 @@ -message = $message; - $this->user = $user; - } - - /** - * Get the channels the event should broadcast on. - * - * @return \Illuminate\Broadcasting\Channel|array - */ - public function broadcastOn() - { - return new PrivateChannel('Created.Success.' . $this->user); - } - - public function broadcastAs() - { - return 'Created.Success.Event'; - } - public function broadcastWith() - { - return [ - 'message' => $this->message - ]; - } -} diff --git a/app/Events/NotificationStatus.php b/app/Events/NotificationStatus.php index 1b05ee5..ff320e5 100644 --- a/app/Events/NotificationStatus.php +++ b/app/Events/NotificationStatus.php @@ -4,8 +4,6 @@ namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; -use Illuminate\Broadcasting\PresenceChannel; -use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; diff --git a/app/Events/Notification_Expiry_Time.php b/app/Events/Notification_Expiry_Time.php deleted file mode 100644 index 52f74fd..0000000 --- a/app/Events/Notification_Expiry_Time.php +++ /dev/null @@ -1,50 +0,0 @@ -message = $message; - $this->user = $user; - } - - /** - * Get the channels the event should broadcast on. - * - * @return \Illuminate\Broadcasting\Channel|array - */ - public function broadcastOn() - { - return new PrivateChannel('Expiry-Time.' . $this->user); - } - - public function broadcastAs() - { - return 'Expiry-Time-Event'; - } - public function broadcastWith() - { - return [ - 'message' => $this->message - ]; - } -} diff --git a/app/Events/Notification_Pay_Success.php b/app/Events/Notification_Pay_Success.php deleted file mode 100644 index e16dadb..0000000 --- a/app/Events/Notification_Pay_Success.php +++ /dev/null @@ -1,51 +0,0 @@ -message = $message; - $this->user = $user; - } - - /** - * Get the channels the event should broadcast on. - * - * @return \Illuminate\Broadcasting\Channel|array - */ - public function broadcastOn() - { - return new PrivateChannel('Payment-Success.' . $this->user); - } - - public function broadcastAs() - { - return 'Payment.Success.Event'; - } - public function broadcastWith() - { - return [ - 'message' => $this->message - ]; - } -} diff --git a/app/Events/VirtualMachineStatusChanged.php b/app/Events/VirtualMachineStatusChanged.php deleted file mode 100644 index 1f4ced1..0000000 --- a/app/Events/VirtualMachineStatusChanged.php +++ /dev/null @@ -1,55 +0,0 @@ -userId = $userId; - $this->vmId = $vmId; - $this->status = $status; - $this->message = $message; - } - - /** - * Get the channels the event should broadcast on. - * - * @return \Illuminate\Broadcasting\Channel|array - */ - public function broadcastOn() - { - // Broadcast on a private channel for the user - return new PrivateChannel('user.' . $this->userId); - } - - /** - * The event's broadcast name. - * - * @return string - */ - public function broadcastAs() - { - return 'vm.status.changed'; - } -} diff --git a/app/Events/VirtualMachine_Panel_Status.php b/app/Events/VirtualMachine_Panel_Status.php deleted file mode 100644 index cf9ba49..0000000 --- a/app/Events/VirtualMachine_Panel_Status.php +++ /dev/null @@ -1,46 +0,0 @@ -message = $message; - $this->user = $user; - } - - - public function broadcastOn() - { - return new PrivateChannel('Panel.Status.' . $this->user); - } - - public function broadcastAs() - { - return 'Panel.Status.Event'; - } - public function broadcastWith() - { - return [ - 'message' => $this->message - ]; - } -} diff --git a/app/Exports/ProductCategoriesExport.php b/app/Exports/ProductCategoriesExport.php deleted file mode 100644 index 88bdf48..0000000 --- a/app/Exports/ProductCategoriesExport.php +++ /dev/null @@ -1,44 +0,0 @@ -get(); - } - - public function headings(): array - { - return [ - 'ID', - 'Name', - 'Slug', - 'Parent Name', - 'Parent ID', - 'Type', - 'Is Active', - 'Sort Order', - ]; - } - - public function map($category): array - { - return [ - $category->id, - $category->name, - $category->slug, - $category->parent?->name, - $category->parent_id, - $category->type, - $category->is_active ? 'Yes' : 'No', - $category->sort_order, - ]; - } -} diff --git a/app/Exports/ProductsExport.php b/app/Exports/ProductsExport.php deleted file mode 100644 index 418d654..0000000 --- a/app/Exports/ProductsExport.php +++ /dev/null @@ -1,72 +0,0 @@ -get(); - } - - public function headings(): array - { - return [ - 'ID', - 'Name', - 'Slug', - 'Category', - 'Category ID', - 'Short Description', - 'Description', - 'Setup Fee', - 'Trial Days', - 'CPU', - 'RAM', - 'GPU', - 'Disk', - 'Bandwidth', - 'Stock', - 'Is Unlimited', - 'Is Active', - 'Sort Order', - 'Product Type', - 'Published At', - 'Monthly Price', - 'Sale Price', - ]; - } - - public function map($product): array - { - return [ - $product->id, - $product->name, - $product->slug, - $product->category?->name, - $product->product_categories_id, - $product->short_description, - $product->description, - $product->setup_fee, - $product->trial_days, - $product->cpu, - $product->ram, - $product->gpu, - $product->disk, - $product->bandwidth, - $product->stock, - $product->is_unlimited ? 'Yes' : 'No', - $product->is_active ? 'Yes' : 'No', - $product->sort_order, - $product->product_type, - $product->published_at?->format('Y-m-d'), - $product->pricings->where('billing_cycle', 'monthly')->where('is_active', 1)->first()?->price ?? '', - $product->pricings->where('billing_cycle', 'monthly')->where('is_active', 1)->first()?->price_sale ?? '', - ]; - } -} diff --git a/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerAccount.php b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerAccount.php new file mode 100644 index 0000000..0495806 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerAccount.php @@ -0,0 +1,89 @@ +impersonationService->createLoginCode($uuid, Auth::guard('admin')->user(), $request); + + return response()->json(['error' => false, 'data' => ['code' => $code]]); + } + + public function resetPassword(string $uuid): JsonResponse + { + $this->mailService->resetPassword($uuid); + + return response()->json([ + 'error' => false, + 'message' => 'Password reset successfully, new password has been sent to customer email', + ]); + } + + public function sendPasswordEmail(Request $request, string $uuid): JsonResponse + { + $data = $request->validate(['password' => 'required|string|min:8|max:64']); + + $this->mailService->sendPasswordEmail($uuid, $data['password'], $request->ip()); + + return response()->json(['error' => false, 'message' => 'Đã gửi mật khẩu mới cho khách hàng.']); + } + + public function sendEmail(Request $request, string $uuid): JsonResponse + { + $data = $request->validate([ + 'template_id' => 'nullable|integer|exists:template,id', + 'subject' => 'required_without:template_id|nullable|string|max:255', + 'message' => 'required_without:template_id|nullable|string', + 'to' => 'nullable|string|max:1000', + 'cc' => 'nullable|string|max:1000', + 'bcc' => 'nullable|string|max:1000', + ]); + + $this->mailService->sendCustomEmail($uuid, $data, Auth::guard('admin')->user(), $request->ip()); + + return response()->json(['error' => false, 'message' => 'Đã gửi email cho khách hàng.']); + } + + public function addCredit(Request $request, string $uuid): JsonResponse + { + $request->validate(['amount' => 'required|numeric|min:1', 'action' => 'required|in:+,-']); + + $this->walletService->adjustCredit( + $uuid, + Auth::guard('admin')->user(), + (int) $request->amount, + $request->action === '+', + ); + + return response()->json(['error' => false, 'message' => 'Credit updated successfully']); + } + + public function addTotal(Request $request, string $uuid): JsonResponse + { + $data = $request->validate(['amount' => 'required|numeric|min:1', 'note' => 'required|string|max:1000']); + + $admin = Auth::guard('admin')->user(); + abort_if(!$admin, 401, 'Unauthorized'); + + $transaction = $this->walletService->addTotal($uuid, $admin, (int) $data['amount'], trim($data['note']), $request->ip()); + + return response()->json([ + 'error' => false, + 'message' => 'Total updated successfully', + 'data' => [ + 'transaction' => [ + 'uuid' => $transaction->uuid, + 'amount' => $transaction->amount, + 'note' => $transaction->note, + 'admin' => $admin->username ?? $admin->email, + ], + ], + ]); + } +} diff --git a/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerBilling.php b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerBilling.php new file mode 100644 index 0000000..7411f73 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerBilling.php @@ -0,0 +1,75 @@ +json(['error' => false, 'data' => $this->billingService->getInvoices($uuid)]); + } + + public function updateInvoice(string $uuid, int $invoiceId, Request $request): JsonResponse + { + $data = $request->validate([ + 'invoice_number' => 'nullable|string|max:50', + 'status' => 'nullable|integer|in:1,2,3,4,5', + 'payment_method' => 'nullable|in:transfer,balance', + 'date' => 'nullable|date', + 'due_date' => 'nullable|date', + 'date_paid' => 'nullable|date', + 'note' => 'nullable|string', + 'sub_total' => 'nullable|integer|min:0', + 'tax' => 'nullable|integer|min:0', + 'total' => 'nullable|integer|min:0', + ]); + + $invoice = $this->billingService->updateInvoice($uuid, $invoiceId, $data); + + return response()->json(['error' => false, 'message' => 'Đã cập nhật hóa đơn', 'data' => $invoice]); + } + + public function getTransactions(string $uuid): JsonResponse + { + return response()->json(['error' => false, 'data' => $this->billingService->getTransactions($uuid)]); + } + + public function updateTransaction(string $uuid, int $transactionId, Request $request): JsonResponse + { + $data = $request->validate([ + 'trans' => 'nullable|string|max:255', + 'note' => 'nullable|string', + 'type' => 'nullable|string|max:50', + 'amount' => 'nullable|integer', + ]); + + $transaction = $this->billingService->updateTransaction($uuid, $transactionId, $data); + + return response()->json(['error' => false, 'message' => 'Đã cập nhật giao dịch', 'data' => $transaction]); + } + + public function getOrders(string $uuid): JsonResponse + { + return response()->json(['error' => false, 'data' => $this->billingService->getOrders($uuid)]); + } + + public function updateOrder(string $uuid, int $orderId, Request $request): JsonResponse + { + $data = $request->validate([ + 'order_num' => 'nullable|string|max:255', + 'status' => 'nullable|integer|in:0,1,2,3', + 'payment_method' => 'nullable|in:transfer,balance', + 'date' => 'nullable|date', + 'sub_total' => 'nullable|integer|min:0', + 'discount_amount' => 'nullable|integer|min:0', + 'total' => 'nullable|integer|min:0', + ]); + + $order = $this->billingService->updateOrder($uuid, $orderId, $data); + + return response()->json(['error' => false, 'message' => 'Đã cập nhật đơn hàng', 'data' => $order]); + } +} diff --git a/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerBulkActions.php b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerBulkActions.php new file mode 100644 index 0000000..6cca119 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerBulkActions.php @@ -0,0 +1,91 @@ +get('per_page', 15), 500); + + $query = Customer::onlyTrashed()->with(['hosting', 'vps', 'domain']); + + if ($filter = $request->get('filter')) { + $filter = trim($filter); + $query->where(function ($q) use ($filter) { + $q->where('fullname', 'LIKE', "%{$filter}%") + ->orWhere('email', 'LIKE', "%{$filter}%") + ->orWhere('phone', 'LIKE', "%{$filter}%"); + }); + } + + return $this->paginatedResponse($query->paginate($perPage)); + } + + public function restore(string $uuid): JsonResponse + { + $this->accountService->restore($uuid); + + return response()->json(['error' => false, 'message' => 'Customer restored successfully']); + } + + public function forceDelete(string $uuid): JsonResponse + { + $this->accountService->forceDelete($uuid); + + return response()->json(['error' => false, 'message' => 'Customer permanently deleted']); + } + + public function updateStatus(string $uuid, Request $request): JsonResponse + { + $request->validate(['status' => ['required', 'integer', 'in:0,1,2,3']]); + + $this->accountService->updateStatus($uuid, (int) $request->input('status')); + + return response()->json(['error' => false, 'message' => 'Cập nhật trạng thái thành công']); + } + + public function bulkDelete(Request $request): JsonResponse + { + $request->validate(['uuids' => ['required', 'array', 'min:1'], 'uuids.*' => ['string']]); + + $count = $this->accountService->bulkDelete($request->input('uuids')); + + return response()->json(['error' => false, 'message' => "Đã xoá {$count} khách hàng"]); + } + + public function bulkRestore(Request $request): JsonResponse + { + $request->validate(['uuids' => ['required', 'array', 'min:1'], 'uuids.*' => ['string']]); + + $count = $this->accountService->bulkRestore($request->input('uuids')); + + return response()->json(['error' => false, 'message' => "Đã khôi phục {$count} khách hàng"]); + } + + public function bulkForceDelete(Request $request): JsonResponse + { + $request->validate(['uuids' => ['required', 'array', 'min:1'], 'uuids.*' => ['string']]); + + $count = $this->accountService->bulkForceDelete($request->input('uuids')); + + return response()->json(['error' => false, 'message' => "Đã xoá vĩnh viễn {$count} khách hàng"]); + } + + public function bulkStatus(Request $request): JsonResponse + { + $data = $request->validate([ + 'uuids' => ['required', 'array', 'min:1'], + 'uuids.*' => ['string'], + 'status' => ['required', 'integer', 'in:0,1,2,3'], + ]); + + $count = $this->accountService->bulkStatus($data['uuids'], $data['status']); + + return response()->json(['error' => false, 'message' => "Đã cập nhật trạng thái {$count} khách hàng"]); + } +} diff --git a/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerDomains.php b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerDomains.php new file mode 100644 index 0000000..77a6a02 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerDomains.php @@ -0,0 +1,75 @@ +resolveCustomer($uuid); + + $domains = $this->moduleDomainRepository->filter(['customer_id' => $customer->id], ['customer']); + + return response()->json(['error' => false, 'data' => $domains]); + } + + public function getDnsDomains(string $uuid): JsonResponse + { + $customer = $this->resolveCustomer($uuid); + + $domains = DnsDomain::query() + ->with(['server:id,name,ip_address', 'registrar:id,name,short_name,website']) + ->withCount(['ns', 'record']) + ->whereHas('customer', fn($q) => $q->where('customers.id', $customer->id)) + ->orderByDesc('id') + ->get() + ->map(fn($d) => [ + 'id' => $d->id, + 'domain_name' => $d->domain_name, + 'is_created' => (int) $d->is_created, + 'plesk_id' => $d->plesk_id, + 'ns_count' => (int) ($d->ns_count ?? 0), + 'record_count' => (int) ($d->record_count ?? 0), + 'server' => $d->server ? [ + 'id' => $d->server->id, + 'name' => $d->server->name, + 'ip_address' => $d->server->ip_address, + ] : null, + 'registrar' => $d->registrar ? [ + 'id' => $d->registrar->id, + 'name' => $d->registrar->name, + 'short_name' => $d->registrar->short_name, + 'website' => $d->registrar->website, + ] : null, + 'created_at' => $d->created_at, + 'updated_at' => $d->updated_at, + ]); + + return response()->json(['error' => false, 'data' => $domains]); + } + + public function getDnsDomainRecords(string $uuid, int $domainId): JsonResponse + { + $customer = $this->resolveCustomer($uuid); + + $owns = DB::table('dns_domain_user') + ->where('domain_id', $domainId) + ->where('user_id', $customer->id) + ->exists(); + + abort_if(!$owns, 403, 'Domain không thuộc về khách hàng này'); + + $records = DnsRecord::query() + ->where('dns_manager_domain_id', $domainId) + ->orderBy('type') + ->orderBy('name') + ->get(); + + return response()->json(['error' => false, 'data' => $records]); + } +} diff --git a/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerNotes.php b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerNotes.php new file mode 100644 index 0000000..fb1d0ce --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerNotes.php @@ -0,0 +1,52 @@ +json(['error' => false, 'data' => $this->noteService->getCustomerNotes($uuid)]); + } + + public function storeCustomerNote(string $uuid, Request $request): JsonResponse + { + $data = $request->validate(['note' => 'required|string']); + + $note = $this->noteService->storeCustomerNote($uuid, $data['note'], Auth::id()); + + return response()->json(['error' => false, 'message' => 'Đã thêm ghi chú', 'data' => $note]); + } + + public function destroyCustomerNote(string $uuid, int $noteId): JsonResponse + { + $this->noteService->destroyNote($uuid, $noteId); + + return response()->json(['error' => false, 'message' => 'Đã xoá ghi chú']); + } + + public function getServiceNotes(string $uuid, int $serviceId): JsonResponse + { + return response()->json(['error' => false, 'data' => $this->noteService->getServiceNotes($uuid, $serviceId)]); + } + + public function storeServiceNote(string $uuid, int $serviceId, Request $request): JsonResponse + { + $data = $request->validate(['note' => 'required|string']); + + $note = $this->noteService->storeServiceNote($uuid, $serviceId, $data['note'], Auth::id()); + + return response()->json(['error' => false, 'message' => 'Đã thêm ghi chú', 'data' => $note]); + } + + public function destroyServiceNote(string $uuid, int $serviceId, int $noteId): JsonResponse + { + $this->noteService->destroyNote($uuid, $noteId); + + return response()->json(['error' => false, 'message' => 'Đã xoá ghi chú']); + } +} diff --git a/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerServices.php b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerServices.php new file mode 100644 index 0000000..d2333ef --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Customer/Concerns/ManagesCustomerServices.php @@ -0,0 +1,127 @@ +json(['error' => false, 'data' => $this->serviceProvisioning->getServices($uuid)]); + } + + public function bulkDeleteServices(string $uuid, Request $request): JsonResponse + { + $data = $request->validate(['ids' => 'required|array|min:1', 'ids.*' => 'integer']); + + $deleted = $this->serviceProvisioning->bulkDeleteServices($uuid, $data['ids']); + + return response()->json(['error' => false, 'message' => "Đã xoá {$deleted} dịch vụ", 'data' => ['deleted' => $deleted]]); + } + + public function updateService(string $uuid, int $serviceId, Request $request): JsonResponse + { + $data = $request->validate([ + 'server_name' => 'nullable|string|max:255', + 'status' => 'nullable|in:pending,active,suspended,expired,cancelled,terminated', + 'auto_renew' => 'nullable|boolean', + 'started_at' => 'nullable|date', + 'expires_at' => 'nullable|date', + 'note' => 'nullable|string', + 'requirements' => 'nullable|string', + 'feedback' => 'nullable|string', + 'price' => 'nullable|numeric|min:0', + 'os_id' => 'nullable|integer|exists:os,id', + 'vm_cpu' => 'nullable|integer|min:1', + 'vm_ram_mb' => 'nullable|integer|min:1', + 'vm_disk_gb' => 'nullable|integer|min:1', + 'vm_ip' => 'nullable|string|max:45', + 'vm_dns' => 'nullable|string|max:100', + 'vm_user' => 'nullable|string|max:255', + 'vm_password' => 'nullable|string|max:255', + 'vm_port' => 'nullable|integer|min:1|max:65535', + ]); + + $service = $this->serviceProvisioning->updateService($uuid, $serviceId, $data); + + return response()->json(['error' => false, 'message' => 'Cập nhật dịch vụ thành công', 'data' => $service]); + } + + public function changeServiceIp(string $uuid, int $serviceId, Request $request): JsonResponse + { + $data = $request->validate(['ip' => 'required|ip']); + + abort_if(!Service::where('id', $serviceId)->value('vm_id'), 422, 'Dịch vụ chưa gắn VM.'); + + $service = $this->serviceProvisioning->updateService($uuid, $serviceId, ['vm_ip' => $data['ip']]); + + return response()->json(['error' => false, 'message' => 'Đã đổi IP thành công', 'data' => $service]); + } + + public function changeServicePort(string $uuid, int $serviceId, Request $request): JsonResponse + { + $data = $request->validate(['port' => 'required|integer|min:1|max:65535']); + + abort_if(!Service::where('id', $serviceId)->value('vm_id'), 422, 'Dịch vụ chưa gắn VM.'); + + $service = $this->serviceProvisioning->updateService($uuid, $serviceId, ['vm_port' => $data['port']]); + + return response()->json(['error' => false, 'message' => 'Đã đổi port thành công', 'data' => $service]); + } + + public function notifyServiceRenewal(string $uuid, int $serviceId): JsonResponse + { + $this->serviceProvisioning->notifyRenewal($uuid, $serviceId); + + return response()->json(['error' => false, 'message' => 'Đã gửi thông báo gia hạn đến khách hàng.']); + } + + public function createService(string $uuid, Request $request): JsonResponse + { + $data = $request->validate([ + 'product_id' => 'required|integer|exists:products,id', + 'virtual_machine_id' => 'nullable|integer|exists:virtual_machines,id', + 'server_name' => 'nullable|string|max:255', + 'status' => 'nullable|in:pending,active,suspended,expired,cancelled,terminated', + 'auto_renew' => 'nullable|boolean', + 'started_at' => 'nullable|date', + 'expires_at' => 'nullable|date', + 'note' => 'nullable|string', + 'billing_cycle' => 'nullable|in:monthly,quarterly,semi_annually,annually,biennially,triennially', + 'payment_method' => 'required|in:transfer,balance', + 'amount_paid' => 'nullable|numeric|min:0', + 'proof_images' => 'nullable|array', + 'proof_images.*' => 'file|image|max:5120', + 'os_id' => 'nullable|integer|exists:os,id', + 'vm_cpu' => 'nullable|integer|min:1', + 'vm_ram_mb' => 'nullable|integer|min:1', + 'vm_disk_gb' => 'nullable|integer|min:1', + 'vm_ip' => 'nullable|string|max:45', + 'vm_dns' => 'nullable|string|max:100', + 'vm_user' => 'nullable|string|max:255', + 'vm_password' => 'nullable|string|max:255', + 'vm_port' => 'nullable|integer|min:1|max:65535', + ]); + + // Upload proof images (chuyển khoản) — I/O ở tầng HTTP, service chỉ nhận đường dẫn đã lưu. + if ($data['payment_method'] === 'transfer' && $request->hasFile('proof_images')) { + $data['_proof_paths'] = collect($request->file('proof_images')) + ->map(fn($file) => $file->store('payment-proofs', 'public')) + ->all(); + } + + $service = $this->serviceProvisioning->createService($uuid, $data, $request->ip()); + + return response()->json(['error' => false, 'message' => 'Service created', 'data' => $service], 201); + } + + public function listAvailableVms(Request $request): JsonResponse + { + $vms = $this->serviceProvisioning->listAvailableVms(trim((string) $request->get('q', ''))); + + return response()->json(['error' => false, 'data' => $vms]); + } +} diff --git a/app/Http/Controllers/Api/Admin/Customer/CustomerController.php b/app/Http/Controllers/Api/Admin/Customer/CustomerController.php index 581883e..40c0bbb 100644 --- a/app/Http/Controllers/Api/Admin/Customer/CustomerController.php +++ b/app/Http/Controllers/Api/Admin/Customer/CustomerController.php @@ -6,9 +6,6 @@ use App\Http\Controllers\Controller; use App\Http\Requests\CreateCustomerRequest; use App\Http\Resources\Api\Admin\CustomerResource; use App\Models\Customer; -use App\Models\DnsDomain; -use App\Models\DnsRecord; -use App\Models\Service; use App\Repositories\Customer\CustomerRepositoryInterface; use App\Repositories\Module\Domain\DomainRepositoryInterface; use App\Services\Customer\Concerns\ResolvesCustomer; @@ -21,12 +18,22 @@ use App\Services\Customer\CustomerServiceProvisioningService; use App\Services\Customer\CustomerWalletService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\DB; +use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerAccount; +use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerBilling; +use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerDomains; +use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerServices; +use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerBulkActions; +use App\Http\Controllers\Api\Admin\Customer\Concerns\ManagesCustomerNotes; class CustomerController extends Controller { use ResolvesCustomer; + use ManagesCustomerAccount; + use ManagesCustomerBilling; + use ManagesCustomerDomains; + use ManagesCustomerServices; + use ManagesCustomerBulkActions; + use ManagesCustomerNotes; public function __construct( private readonly CustomerRepositoryInterface $customerRepository, @@ -115,460 +122,6 @@ class CustomerController extends Controller return response()->json(['error' => false, 'message' => 'Customer deleted successfully']); } - public function trash(Request $request): JsonResponse - { - $perPage = min((int) $request->get('per_page', 15), 500); - - $query = Customer::onlyTrashed()->with(['hosting', 'vps', 'domain']); - - if ($filter = $request->get('filter')) { - $filter = trim($filter); - $query->where(function ($q) use ($filter) { - $q->where('fullname', 'LIKE', "%{$filter}%") - ->orWhere('email', 'LIKE', "%{$filter}%") - ->orWhere('phone', 'LIKE', "%{$filter}%"); - }); - } - - return $this->paginatedResponse($query->paginate($perPage)); - } - - public function restore(string $uuid): JsonResponse - { - $this->accountService->restore($uuid); - - return response()->json(['error' => false, 'message' => 'Customer restored successfully']); - } - - public function forceDelete(string $uuid): JsonResponse - { - $this->accountService->forceDelete($uuid); - - return response()->json(['error' => false, 'message' => 'Customer permanently deleted']); - } - - public function loginAs(Request $request, string $uuid): JsonResponse - { - $code = $this->impersonationService->createLoginCode($uuid, Auth::guard('admin')->user(), $request); - - return response()->json(['error' => false, 'data' => ['code' => $code]]); - } - - public function resetPassword(string $uuid): JsonResponse - { - $this->mailService->resetPassword($uuid); - - return response()->json([ - 'error' => false, - 'message' => 'Password reset successfully, new password has been sent to customer email', - ]); - } - - public function sendPasswordEmail(Request $request, string $uuid): JsonResponse - { - $data = $request->validate(['password' => 'required|string|min:8|max:64']); - - $this->mailService->sendPasswordEmail($uuid, $data['password'], $request->ip()); - - return response()->json(['error' => false, 'message' => 'Đã gửi mật khẩu mới cho khách hàng.']); - } - - public function sendEmail(Request $request, string $uuid): JsonResponse - { - $data = $request->validate([ - 'template_id' => 'nullable|integer|exists:template,id', - 'subject' => 'required_without:template_id|nullable|string|max:255', - 'message' => 'required_without:template_id|nullable|string', - 'to' => 'nullable|string|max:1000', - 'cc' => 'nullable|string|max:1000', - 'bcc' => 'nullable|string|max:1000', - ]); - - $this->mailService->sendCustomEmail($uuid, $data, Auth::guard('admin')->user(), $request->ip()); - - return response()->json(['error' => false, 'message' => 'Đã gửi email cho khách hàng.']); - } - - public function addCredit(Request $request, string $uuid): JsonResponse - { - $request->validate(['amount' => 'required|numeric|min:1', 'action' => 'required|in:+,-']); - - $this->walletService->adjustCredit( - $uuid, - Auth::guard('admin')->user(), - (int) $request->amount, - $request->action === '+', - ); - - return response()->json(['error' => false, 'message' => 'Credit updated successfully']); - } - - public function addTotal(Request $request, string $uuid): JsonResponse - { - $data = $request->validate(['amount' => 'required|numeric|min:1', 'note' => 'required|string|max:1000']); - - $admin = Auth::guard('admin')->user(); - abort_if(!$admin, 401, 'Unauthorized'); - - $transaction = $this->walletService->addTotal($uuid, $admin, (int) $data['amount'], trim($data['note']), $request->ip()); - - return response()->json([ - 'error' => false, - 'message' => 'Total updated successfully', - 'data' => [ - 'transaction' => [ - 'uuid' => $transaction->uuid, - 'amount' => $transaction->amount, - 'note' => $transaction->note, - 'admin' => $admin->username ?? $admin->email, - ], - ], - ]); - } - - public function getProducts(string $uuid): JsonResponse - { - return response()->json(['error' => false, 'data' => $this->serviceProvisioning->getProducts($uuid)]); - } - - public function getInvoices(string $uuid): JsonResponse - { - return response()->json(['error' => false, 'data' => $this->billingService->getInvoices($uuid)]); - } - - public function updateInvoice(string $uuid, int $invoiceId, Request $request): JsonResponse - { - $data = $request->validate([ - 'invoice_number' => 'nullable|string|max:50', - 'status' => 'nullable|integer|in:1,2,3,4,5', - 'payment_method' => 'nullable|in:transfer,balance', - 'date' => 'nullable|date', - 'due_date' => 'nullable|date', - 'date_paid' => 'nullable|date', - 'note' => 'nullable|string', - 'sub_total' => 'nullable|integer|min:0', - 'tax' => 'nullable|integer|min:0', - 'total' => 'nullable|integer|min:0', - ]); - - $invoice = $this->billingService->updateInvoice($uuid, $invoiceId, $data); - - return response()->json(['error' => false, 'message' => 'Đã cập nhật hóa đơn', 'data' => $invoice]); - } - - public function getTransactions(string $uuid): JsonResponse - { - return response()->json(['error' => false, 'data' => $this->billingService->getTransactions($uuid)]); - } - - public function updateTransaction(string $uuid, int $transactionId, Request $request): JsonResponse - { - $data = $request->validate([ - 'trans' => 'nullable|string|max:255', - 'note' => 'nullable|string', - 'type' => 'nullable|string|max:50', - 'amount' => 'nullable|integer', - ]); - - $transaction = $this->billingService->updateTransaction($uuid, $transactionId, $data); - - return response()->json(['error' => false, 'message' => 'Đã cập nhật giao dịch', 'data' => $transaction]); - } - - public function getDomains(string $uuid): JsonResponse - { - $customer = $this->resolveCustomer($uuid); - - $domains = $this->moduleDomainRepository->filter(['customer_id' => $customer->id], ['customer']); - - return response()->json(['error' => false, 'data' => $domains]); - } - - public function getDnsDomains(string $uuid): JsonResponse - { - $customer = $this->resolveCustomer($uuid); - - $domains = DnsDomain::query() - ->with(['server:id,name,ip_address', 'registrar:id,name,short_name,website']) - ->withCount(['ns', 'record']) - ->whereHas('customer', fn($q) => $q->where('customers.id', $customer->id)) - ->orderByDesc('id') - ->get() - ->map(fn($d) => [ - 'id' => $d->id, - 'domain_name' => $d->domain_name, - 'is_created' => (int) $d->is_created, - 'plesk_id' => $d->plesk_id, - 'ns_count' => (int) ($d->ns_count ?? 0), - 'record_count' => (int) ($d->record_count ?? 0), - 'server' => $d->server ? [ - 'id' => $d->server->id, - 'name' => $d->server->name, - 'ip_address' => $d->server->ip_address, - ] : null, - 'registrar' => $d->registrar ? [ - 'id' => $d->registrar->id, - 'name' => $d->registrar->name, - 'short_name' => $d->registrar->short_name, - 'website' => $d->registrar->website, - ] : null, - 'created_at' => $d->created_at, - 'updated_at' => $d->updated_at, - ]); - - return response()->json(['error' => false, 'data' => $domains]); - } - - public function getDnsDomainRecords(string $uuid, int $domainId): JsonResponse - { - $customer = $this->resolveCustomer($uuid); - - $owns = DB::table('dns_domain_user') - ->where('domain_id', $domainId) - ->where('user_id', $customer->id) - ->exists(); - - abort_if(!$owns, 403, 'Domain không thuộc về khách hàng này'); - - $records = DnsRecord::query() - ->where('dns_manager_domain_id', $domainId) - ->orderBy('type') - ->orderBy('name') - ->get(); - - return response()->json(['error' => false, 'data' => $records]); - } - - public function getServices(string $uuid): JsonResponse - { - return response()->json(['error' => false, 'data' => $this->serviceProvisioning->getServices($uuid)]); - } - - public function bulkDeleteServices(string $uuid, Request $request): JsonResponse - { - $data = $request->validate(['ids' => 'required|array|min:1', 'ids.*' => 'integer']); - - $deleted = $this->serviceProvisioning->bulkDeleteServices($uuid, $data['ids']); - - return response()->json(['error' => false, 'message' => "Đã xoá {$deleted} dịch vụ", 'data' => ['deleted' => $deleted]]); - } - - public function updateService(string $uuid, int $serviceId, Request $request): JsonResponse - { - $data = $request->validate([ - 'server_name' => 'nullable|string|max:255', - 'status' => 'nullable|in:pending,active,suspended,expired,cancelled,terminated', - 'auto_renew' => 'nullable|boolean', - 'started_at' => 'nullable|date', - 'expires_at' => 'nullable|date', - 'note' => 'nullable|string', - 'requirements' => 'nullable|string', - 'feedback' => 'nullable|string', - 'price' => 'nullable|numeric|min:0', - 'os_id' => 'nullable|integer|exists:os,id', - 'vm_cpu' => 'nullable|integer|min:1', - 'vm_ram_mb' => 'nullable|integer|min:1', - 'vm_disk_gb' => 'nullable|integer|min:1', - 'vm_ip' => 'nullable|string|max:45', - 'vm_dns' => 'nullable|string|max:100', - 'vm_user' => 'nullable|string|max:255', - 'vm_password' => 'nullable|string|max:255', - 'vm_port' => 'nullable|integer|min:1|max:65535', - ]); - - $service = $this->serviceProvisioning->updateService($uuid, $serviceId, $data); - - return response()->json(['error' => false, 'message' => 'Cập nhật dịch vụ thành công', 'data' => $service]); - } - - public function changeServiceIp(string $uuid, int $serviceId, Request $request): JsonResponse - { - $data = $request->validate(['ip' => 'required|ip']); - - abort_if(!Service::where('id', $serviceId)->value('vm_id'), 422, 'Dịch vụ chưa gắn VM.'); - - $service = $this->serviceProvisioning->updateService($uuid, $serviceId, ['vm_ip' => $data['ip']]); - - return response()->json(['error' => false, 'message' => 'Đã đổi IP thành công', 'data' => $service]); - } - - public function changeServicePort(string $uuid, int $serviceId, Request $request): JsonResponse - { - $data = $request->validate(['port' => 'required|integer|min:1|max:65535']); - - abort_if(!Service::where('id', $serviceId)->value('vm_id'), 422, 'Dịch vụ chưa gắn VM.'); - - $service = $this->serviceProvisioning->updateService($uuid, $serviceId, ['vm_port' => $data['port']]); - - return response()->json(['error' => false, 'message' => 'Đã đổi port thành công', 'data' => $service]); - } - - public function notifyServiceRenewal(string $uuid, int $serviceId): JsonResponse - { - $this->serviceProvisioning->notifyRenewal($uuid, $serviceId); - - return response()->json(['error' => false, 'message' => 'Đã gửi thông báo gia hạn đến khách hàng.']); - } - - public function createService(string $uuid, Request $request): JsonResponse - { - $data = $request->validate([ - 'product_id' => 'required|integer|exists:products,id', - 'virtual_machine_id' => 'nullable|integer|exists:virtual_machines,id', - 'server_name' => 'nullable|string|max:255', - 'status' => 'nullable|in:pending,active,suspended,expired,cancelled,terminated', - 'auto_renew' => 'nullable|boolean', - 'started_at' => 'nullable|date', - 'expires_at' => 'nullable|date', - 'note' => 'nullable|string', - 'billing_cycle' => 'nullable|in:monthly,quarterly,semi_annually,annually,biennially,triennially', - 'payment_method' => 'required|in:transfer,balance', - 'amount_paid' => 'nullable|numeric|min:0', - 'proof_images' => 'nullable|array', - 'proof_images.*' => 'file|image|max:5120', - 'os_id' => 'nullable|integer|exists:os,id', - 'vm_cpu' => 'nullable|integer|min:1', - 'vm_ram_mb' => 'nullable|integer|min:1', - 'vm_disk_gb' => 'nullable|integer|min:1', - 'vm_ip' => 'nullable|string|max:45', - 'vm_dns' => 'nullable|string|max:100', - 'vm_user' => 'nullable|string|max:255', - 'vm_password' => 'nullable|string|max:255', - 'vm_port' => 'nullable|integer|min:1|max:65535', - ]); - - // Upload proof images (chuyển khoản) — I/O ở tầng HTTP, service chỉ nhận đường dẫn đã lưu. - if ($data['payment_method'] === 'transfer' && $request->hasFile('proof_images')) { - $data['_proof_paths'] = collect($request->file('proof_images')) - ->map(fn($file) => $file->store('payment-proofs', 'public')) - ->all(); - } - - $service = $this->serviceProvisioning->createService($uuid, $data, $request->ip()); - - return response()->json(['error' => false, 'message' => 'Service created', 'data' => $service], 201); - } - - public function listAvailableVms(Request $request): JsonResponse - { - $vms = $this->serviceProvisioning->listAvailableVms(trim((string) $request->get('q', ''))); - - return response()->json(['error' => false, 'data' => $vms]); - } - - public function getOrders(string $uuid): JsonResponse - { - return response()->json(['error' => false, 'data' => $this->billingService->getOrders($uuid)]); - } - - public function updateOrder(string $uuid, int $orderId, Request $request): JsonResponse - { - $data = $request->validate([ - 'order_num' => 'nullable|string|max:255', - 'status' => 'nullable|integer|in:0,1,2,3', - 'payment_method' => 'nullable|in:transfer,balance', - 'date' => 'nullable|date', - 'sub_total' => 'nullable|integer|min:0', - 'discount_amount' => 'nullable|integer|min:0', - 'total' => 'nullable|integer|min:0', - ]); - - $order = $this->billingService->updateOrder($uuid, $orderId, $data); - - return response()->json(['error' => false, 'message' => 'Đã cập nhật đơn hàng', 'data' => $order]); - } - - public function updateStatus(string $uuid, Request $request): JsonResponse - { - $request->validate(['status' => ['required', 'integer', 'in:0,1,2,3']]); - - $this->accountService->updateStatus($uuid, (int) $request->input('status')); - - return response()->json(['error' => false, 'message' => 'Cập nhật trạng thái thành công']); - } - - public function bulkDelete(Request $request): JsonResponse - { - $request->validate(['uuids' => ['required', 'array', 'min:1'], 'uuids.*' => ['string']]); - - $count = $this->accountService->bulkDelete($request->input('uuids')); - - return response()->json(['error' => false, 'message' => "Đã xoá {$count} khách hàng"]); - } - - public function bulkRestore(Request $request): JsonResponse - { - $request->validate(['uuids' => ['required', 'array', 'min:1'], 'uuids.*' => ['string']]); - - $count = $this->accountService->bulkRestore($request->input('uuids')); - - return response()->json(['error' => false, 'message' => "Đã khôi phục {$count} khách hàng"]); - } - - public function bulkForceDelete(Request $request): JsonResponse - { - $request->validate(['uuids' => ['required', 'array', 'min:1'], 'uuids.*' => ['string']]); - - $count = $this->accountService->bulkForceDelete($request->input('uuids')); - - return response()->json(['error' => false, 'message' => "Đã xoá vĩnh viễn {$count} khách hàng"]); - } - - public function bulkStatus(Request $request): JsonResponse - { - $data = $request->validate([ - 'uuids' => ['required', 'array', 'min:1'], - 'uuids.*' => ['string'], - 'status' => ['required', 'integer', 'in:0,1,2,3'], - ]); - - $count = $this->accountService->bulkStatus($data['uuids'], $data['status']); - - return response()->json(['error' => false, 'message' => "Đã cập nhật trạng thái {$count} khách hàng"]); - } - - public function getCustomerNotes(string $uuid): JsonResponse - { - return response()->json(['error' => false, 'data' => $this->noteService->getCustomerNotes($uuid)]); - } - - public function storeCustomerNote(string $uuid, Request $request): JsonResponse - { - $data = $request->validate(['note' => 'required|string']); - - $note = $this->noteService->storeCustomerNote($uuid, $data['note'], Auth::id()); - - return response()->json(['error' => false, 'message' => 'Đã thêm ghi chú', 'data' => $note]); - } - - public function destroyCustomerNote(string $uuid, int $noteId): JsonResponse - { - $this->noteService->destroyNote($uuid, $noteId); - - return response()->json(['error' => false, 'message' => 'Đã xoá ghi chú']); - } - - public function getServiceNotes(string $uuid, int $serviceId): JsonResponse - { - return response()->json(['error' => false, 'data' => $this->noteService->getServiceNotes($uuid, $serviceId)]); - } - - public function storeServiceNote(string $uuid, int $serviceId, Request $request): JsonResponse - { - $data = $request->validate(['note' => 'required|string']); - - $note = $this->noteService->storeServiceNote($uuid, $serviceId, $data['note'], Auth::id()); - - return response()->json(['error' => false, 'message' => 'Đã thêm ghi chú', 'data' => $note]); - } - - public function destroyServiceNote(string $uuid, int $serviceId, int $noteId): JsonResponse - { - $this->noteService->destroyNote($uuid, $noteId); - - return response()->json(['error' => false, 'message' => 'Đã xoá ghi chú']); - } - private function paginatedResponse(\Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator): JsonResponse { return response()->json([ diff --git a/app/Http/Controllers/Api/Admin/EmailTemplate/EmailTemplateController.php b/app/Http/Controllers/Api/Admin/EmailTemplate/EmailTemplateController.php index 0864802..0f6c4e5 100644 --- a/app/Http/Controllers/Api/Admin/EmailTemplate/EmailTemplateController.php +++ b/app/Http/Controllers/Api/Admin/EmailTemplate/EmailTemplateController.php @@ -325,22 +325,6 @@ class EmailTemplateController extends Controller ->all(); } - // ── Invoice PDF preview ─────────────────────────────────────────────────── - - public function previewInvoiceJson(int $id): JsonResponse - { - $template = Template::findOrFail($id); - if ($template->scenario !== 'invoice_pdf' || $template->format !== 'json') { - return response()->json(['error' => true, 'message' => 'Template này không phải invoice JSON.'], 422); - } - - $config = json_decode($template->message ?? '{}', true); - // Return the config + blade HTML preview - $html = view('pdf.invoice-preview', compact('config'))->render(); - - return response()->json(['error' => false, 'html' => $html]); - } - // ── Helpers ─────────────────────────────────────────────────────────────── private function format(Template $t): array diff --git a/app/Http/Controllers/Api/Admin/Invoice/Concerns/HandlesInvoiceAttachments.php b/app/Http/Controllers/Api/Admin/Invoice/Concerns/HandlesInvoiceAttachments.php new file mode 100644 index 0000000..6c1f6a4 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Invoice/Concerns/HandlesInvoiceAttachments.php @@ -0,0 +1,76 @@ +invoiceRepository->find($id); + if (!$invoice) { + return response()->json(['error' => true, 'message' => "Can't find invoice #$id"], 404); + } + + $attachments = $this->attachmentRepository->getByInvoice($id); + + return response()->json([ + 'error' => false, + 'data' => $attachments, + ]); + } + + public function uploadAttachment(Request $request, int $id): JsonResponse + { + $invoice = $this->invoiceRepository->find($id); + if (!$invoice) { + return response()->json(['error' => true, 'message' => "Can't find invoice #$id"], 404); + } + + $request->validate([ + 'file' => 'required|file|mimes:jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx|max:10240', + ]); + + $file = $request->file('file'); + $path = $file->store("invoice-attachments/{$id}", 'public'); + $uploader = $request->user()?->username ?? $request->user()?->name ?? 'admin'; + + $attachment = $this->attachmentRepository->create([ + 'invoice_id' => $id, + 'filename' => $file->getClientOriginalName(), + 'path' => $path, + 'disk' => 'public', + 'mime_type' => $file->getMimeType(), + 'size' => $file->getSize(), + 'uploaded_by' => $uploader, + ]); + + return response()->json([ + 'error' => false, + 'message' => 'File uploaded successfully', + 'data' => $attachment, + ], 201); + } + + public function deleteAttachment(int $id, int $attachmentId): JsonResponse + { + $invoice = $this->invoiceRepository->find($id); + if (!$invoice) { + return response()->json(['error' => true, 'message' => "Can't find invoice #$id"], 404); + } + + $attachment = $this->attachmentRepository->find($attachmentId); + if (!$attachment || $attachment->invoice_id !== $id) { + return response()->json(['error' => true, 'message' => 'Attachment not found'], 404); + } + + $this->attachmentRepository->deleteWithFile($attachmentId); + + return response()->json([ + 'error' => false, + 'message' => 'Attachment deleted', + ]); + } +} diff --git a/app/Http/Controllers/Api/Admin/Invoice/Concerns/HandlesInvoicePayments.php b/app/Http/Controllers/Api/Admin/Invoice/Concerns/HandlesInvoicePayments.php new file mode 100644 index 0000000..c2d2907 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Invoice/Concerns/HandlesInvoicePayments.php @@ -0,0 +1,166 @@ +invoiceRepository->find($id); + + if (!$invoice) { + return response()->json([ + 'error' => true, + 'message' => "Can't find invoice by ID #" . $id + ], 404); + } + + // Bỏ trống = thu đủ hoá đơn. Nhập số nhỏ hơn khi khách mới chuyển được một phần — + // hoá đơn ghi nhận trả một phần và chưa kích hoạt dịch vụ. + $validated = $request->validate([ + 'received_amount' => 'nullable|integer|min:1', + ]); + + try { + DB::beginTransaction(); + + $invoice->load(['order', 'customer']); + $invoice = $this->invoicePaymentService->markPaidByAdmin( + $invoice, + auth()->id(), + receivedAmount: $validated['received_amount'] ?? null, + ); + + DB::commit(); + + $outstanding = InvoicePaymentService::outstanding($invoice); + + return response()->json([ + 'error' => false, + 'message' => $outstanding > 0 + ? 'Đã ghi nhận thanh toán một phần. Còn thiếu ' . number_format($outstanding) . ' VNĐ.' + : 'Invoice marked as paid', + 'data' => [ + 'amount_paid' => (int) $invoice->amount_paid, + 'outstanding' => $outstanding, + ], + ]); + } catch (\InvalidArgumentException $e) { + DB::rollBack(); + return response()->json([ + 'error' => true, + 'message' => $e->getMessage() + ], 422); + } catch (\Exception $e) { + DB::rollBack(); + return response()->json([ + 'error' => true, + 'message' => $e->getMessage() + ], 500); + } + } + + public function cancel(int $id): JsonResponse + { + $invoice = $this->invoiceRepository->find($id); + + if (!$invoice) { + return response()->json([ + 'error' => true, + 'message' => "Can't find invoice by ID #" . $id + ], 404); + } + + try { + $this->assertCancellable($invoice); + + DB::beginTransaction(); + $invoice->update([ + 'status' => InvoiceStatus::CANCELLED->value, + 'paid_at' => null + ]); + DB::commit(); + + return response()->json([ + 'error' => false, + 'message' => 'Invoice cancelled' + ]); + } catch (\InvalidArgumentException $e) { + DB::rollBack(); + return response()->json([ + 'error' => true, + 'message' => $e->getMessage() + ], 422); + } catch (\Exception $e) { + DB::rollBack(); + return response()->json([ + 'error' => true, + 'message' => $e->getMessage() + ], 500); + } + } + + /** + * Hoàn tiền hoá đơn đã thu — cộng lại số dư khách và ghi Transaction âm, thay cho việc + * huỷ hoá đơn đã thanh toán (nay đã bị chặn) hoặc xử lý tay ngoài hệ thống. + */ + public function refund(Request $request, int $id): JsonResponse + { + $invoice = $this->invoiceRepository->find($id); + + if (!$invoice) { + return response()->json([ + 'error' => true, + 'message' => "Can't find invoice by ID #" . $id + ], 404); + } + + $validated = $request->validate([ + 'amount' => 'nullable|integer|min:1', + 'reason' => 'nullable|string|max:500', + ]); + + try { + $invoice = $this->invoicePaymentService->refund( + $invoice, + auth()->id(), + $validated['amount'] ?? null, + $validated['reason'] ?? '', + ); + } catch (\InvalidArgumentException $e) { + return response()->json(['error' => true, 'message' => $e->getMessage()], 422); + } catch (\Throwable $e) { + Log::error('InvoiceController::refund error: ' . $e->getMessage(), ['invoice_id' => $id]); + return response()->json(['error' => true, 'message' => 'Không thể hoàn tiền, vui lòng thử lại.'], 500); + } + + return response()->json([ + 'error' => false, + 'message' => 'Đã hoàn tiền vào số dư khách hàng.', + 'data' => ['invoice' => new InvoiceResource($invoice)], + ]); + } + + /** + * Huỷ hoá đơn đã thu tiền sẽ xoá paid_at nhưng không hoàn số dư, không revert order/service, + * trong khi Transaction vẫn còn — sổ sách lệch im lặng. Chặn ở đây, đảo giao dịch phải đi + * qua luồng hoàn tiền. + */ + private function assertCancellable(Invoice $invoice): void + { + if (in_array((int) $invoice->status, [InvoiceStatus::PAID->value, InvoiceStatus::REFUNDED->value], true)) { + throw new \InvalidArgumentException( + 'Không thể huỷ hoá đơn đã thanh toán hoặc đã hoàn tiền. Hãy dùng chức năng hoàn tiền nếu cần đảo giao dịch.' + ); + } + } +} diff --git a/app/Http/Controllers/Api/Admin/Invoice/Concerns/SendsInvoiceNotifications.php b/app/Http/Controllers/Api/Admin/Invoice/Concerns/SendsInvoiceNotifications.php new file mode 100644 index 0000000..981e147 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Invoice/Concerns/SendsInvoiceNotifications.php @@ -0,0 +1,118 @@ +invoiceRepository->find($id); + + if (!$invoice) { + return response()->json([ + 'error' => true, + 'message' => "Can't find invoice by ID #" . $id + ], 404); + } + + if (!$reminders->isRemindable($invoice)) { + return response()->json([ + 'error' => true, + 'message' => 'Chỉ nhắc được hoá đơn chưa thanh toán hoặc quá hạn.', + ], 422); + } + + try { + $reminders->remind($invoice->load('customer'), force: true); + } catch (\Throwable $e) { + return response()->json([ + 'error' => true, + 'message' => 'Không gửi được email nhắc: ' . $e->getMessage(), + ], 500); + } + + return response()->json([ + 'error' => false, + 'message' => 'Đã gửi email nhắc thanh toán tới ' . $invoice->customer?->email, + ]); + } + + public function sendMail(Request $request, int $id): JsonResponse + { + $request->validate([ + 'message' => 'nullable|string|max:1000', + 'type' => 'nullable|string|in:invoice,renewal', + ]); + + $invoice = $this->invoiceRepository->find($id); + if (!$invoice) { + return response()->json(['error' => true, 'message' => "Can't find invoice #$id"], 404); + } + + $invoice->load('customer'); + $customer = $invoice->customer; + if (!$customer || !$customer->email) { + return response()->json(['error' => true, 'message' => 'Customer email not found'], 422); + } + + $attachments = $this->attachmentRepository->getByInvoice($id); + $companyName = setting('general.company_name') ?: config('app.name'); + $type = $request->get('type', 'invoice'); + $message = $request->get( + 'message', + $type === 'renewal' + ? "Dịch vụ của quý khách sắp đến hạn gia hạn. Vui lòng kiểm tra hóa đơn #{$invoice->order_num} được đính kèm và hoàn tất thanh toán để dịch vụ không bị gián đoạn." + : "Vui lòng xem thông tin hóa đơn #{$invoice->order_num} của bạn." + ); + $paymentUrl = url('/client/invoices/' . $invoice->invoice_number . '/payment'); + try { + $invoicePdf = $this->invoicePdfService->generate($invoice); + $invoicePdfFileName = $this->invoicePdfService->generateFileName($invoice); + + Mail::to($customer->email)->send( + new InvoiceNotificationMail( + $invoice, + $attachments, + $customer->fullname, + $message, + $companyName, + $type, + $invoicePdf, + $invoicePdfFileName, + $paymentUrl + ) + ); + } catch (Throwable $e) { + Log::error('InvoiceController@sendMail failed', [ + 'invoice_id' => $id, + 'invoice_number' => $invoice->invoice_number ?? null, + 'customer_email' => $customer->email, + 'type' => $type, + 'error' => $e->getMessage(), + ]); + + return response()->json([ + 'error' => true, + 'message' => 'Không thể gửi email: ' . $e->getMessage(), + ], 500); + } + + return response()->json([ + 'error' => false, + 'message' => "Email đã được gửi tới {$customer->email}", + ]); + } +} diff --git a/app/Http/Controllers/Api/Admin/Invoice/InvoiceController.php b/app/Http/Controllers/Api/Admin/Invoice/InvoiceController.php index b99e60b..4dc9789 100644 --- a/app/Http/Controllers/Api/Admin/Invoice/InvoiceController.php +++ b/app/Http/Controllers/Api/Admin/Invoice/InvoiceController.php @@ -7,32 +7,29 @@ use App\Helpers\OrderHelper; use App\Http\Controllers\Controller; use App\Http\Requests\InvoiceRequest; use App\Http\Resources\Api\Admin\InvoiceResource; -use App\Mail\InvoiceNotificationMail; -use App\Models\Customer; use App\Models\Invoice; use App\Models\InvoiceItem; use App\Models\Order; -use App\Models\Payment; use App\Repositories\Invoice\InvoiceRepositoryInterface; use App\Repositories\InvoiceAttachment\InvoiceAttachmentRepositoryInterface; -use App\Repositories\InvoiceItem\InvoiceItemRepositoryInterface; use App\Services\InvoicePaymentService; use App\Services\InvoicePdfService; -use App\Services\InvoiceReminderService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; -use Illuminate\Support\Facades\Storage; -use Throwable; -use Yajra\DataTables\Facades\DataTables; +use App\Http\Controllers\Api\Admin\Invoice\Concerns\HandlesInvoicePayments; +use App\Http\Controllers\Api\Admin\Invoice\Concerns\HandlesInvoiceAttachments; +use App\Http\Controllers\Api\Admin\Invoice\Concerns\SendsInvoiceNotifications; class InvoiceController extends Controller { + use HandlesInvoicePayments; + use HandlesInvoiceAttachments; + use SendsInvoiceNotifications; + public function __construct( private readonly InvoiceRepositoryInterface $invoiceRepository, - private readonly InvoiceItemRepositoryInterface $invoiceItemRepository, private readonly InvoiceAttachmentRepositoryInterface $attachmentRepository, private readonly InvoicePdfService $invoicePdfService, private readonly InvoicePaymentService $invoicePaymentService, @@ -57,28 +54,6 @@ class InvoiceController extends Controller ]); } - public function datatable(Request $request): JsonResponse - { - $query = $this->invoiceRepository->filter( - $request->all(), - ['payment', 'customer', 'order'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn('invoice_number', fn($d) => $d->invoice_number) - ->addColumn('date', fn($d) => $d->date) - ->addColumn('customer', fn($d) => $d->customer?->fullname) - ->addColumn('payment', fn($d) => $d->payment?->name) - ->addColumn('total', fn($d) => $d->total) - ->addColumn('status', fn($d) => $d->status) - ->addColumn('action', fn($d) => $d->id) - ->rawColumns(['invoice_number', 'date', 'customer', 'payment', 'total', 'status', 'action']) - ->make(true); - } - /** * Admin tạo thêm 1 invoice thủ công cho 1 order đã tồn tại — chỉ dùng cho phụ * phí/điều chỉnh thuộc CHÍNH dịch vụ trong order đó (vd. phạt trễ hạn, phí xử lý). @@ -261,328 +236,7 @@ class InvoiceController extends Controller } } - public function markAsPaid(Request $request, int $id): JsonResponse - { - $invoice = $this->invoiceRepository->find($id); - - if (!$invoice) { - return response()->json([ - 'error' => true, - 'message' => "Can't find invoice by ID #" . $id - ], 404); - } - - // Bỏ trống = thu đủ hoá đơn. Nhập số nhỏ hơn khi khách mới chuyển được một phần — - // hoá đơn ghi nhận trả một phần và chưa kích hoạt dịch vụ. - $validated = $request->validate([ - 'received_amount' => 'nullable|integer|min:1', - ]); - - try { - DB::beginTransaction(); - - $invoice->load(['order', 'customer']); - $invoice = $this->invoicePaymentService->markPaidByAdmin( - $invoice, - auth()->id(), - receivedAmount: $validated['received_amount'] ?? null, - ); - - DB::commit(); - - $outstanding = InvoicePaymentService::outstanding($invoice); - - return response()->json([ - 'error' => false, - 'message' => $outstanding > 0 - ? 'Đã ghi nhận thanh toán một phần. Còn thiếu ' . number_format($outstanding) . ' VNĐ.' - : 'Invoice marked as paid', - 'data' => [ - 'amount_paid' => (int) $invoice->amount_paid, - 'outstanding' => $outstanding, - ], - ]); - } catch (\InvalidArgumentException $e) { - DB::rollBack(); - return response()->json([ - 'error' => true, - 'message' => $e->getMessage() - ], 422); - } catch (\Exception $e) { - DB::rollBack(); - return response()->json([ - 'error' => true, - 'message' => $e->getMessage() - ], 500); - } - } - - public function cancel(int $id): JsonResponse - { - $invoice = $this->invoiceRepository->find($id); - - if (!$invoice) { - return response()->json([ - 'error' => true, - 'message' => "Can't find invoice by ID #" . $id - ], 404); - } - - try { - $this->assertCancellable($invoice); - - DB::beginTransaction(); - $invoice->update([ - 'status' => InvoiceStatus::CANCELLED->value, - 'paid_at' => null - ]); - DB::commit(); - - return response()->json([ - 'error' => false, - 'message' => 'Invoice cancelled' - ]); - } catch (\InvalidArgumentException $e) { - DB::rollBack(); - return response()->json([ - 'error' => true, - 'message' => $e->getMessage() - ], 422); - } catch (\Exception $e) { - DB::rollBack(); - return response()->json([ - 'error' => true, - 'message' => $e->getMessage() - ], 500); - } - } - - /** - * Hoàn tiền hoá đơn đã thu — cộng lại số dư khách và ghi Transaction âm, thay cho việc - * huỷ hoá đơn đã thanh toán (nay đã bị chặn) hoặc xử lý tay ngoài hệ thống. - */ - public function refund(Request $request, int $id): JsonResponse - { - $invoice = $this->invoiceRepository->find($id); - - if (!$invoice) { - return response()->json([ - 'error' => true, - 'message' => "Can't find invoice by ID #" . $id - ], 404); - } - - $validated = $request->validate([ - 'amount' => 'nullable|integer|min:1', - 'reason' => 'nullable|string|max:500', - ]); - - try { - $invoice = $this->invoicePaymentService->refund( - $invoice, - auth()->id(), - $validated['amount'] ?? null, - $validated['reason'] ?? '', - ); - } catch (\InvalidArgumentException $e) { - return response()->json(['error' => true, 'message' => $e->getMessage()], 422); - } catch (\Throwable $e) { - Log::error('InvoiceController::refund error: ' . $e->getMessage(), ['invoice_id' => $id]); - return response()->json(['error' => true, 'message' => 'Không thể hoàn tiền, vui lòng thử lại.'], 500); - } - - return response()->json([ - 'error' => false, - 'message' => 'Đã hoàn tiền vào số dư khách hàng.', - 'data' => ['invoice' => new InvoiceResource($invoice)], - ]); - } - - /** - * Nhắc thanh toán thủ công — dùng khi kế toán muốn thúc riêng một khách mà không chờ - * mốc tự động trong billing.reminder_offsets. - */ - public function remind(int $id, InvoiceReminderService $reminders): JsonResponse - { - $invoice = $this->invoiceRepository->find($id); - - if (!$invoice) { - return response()->json([ - 'error' => true, - 'message' => "Can't find invoice by ID #" . $id - ], 404); - } - - if (!$reminders->isRemindable($invoice)) { - return response()->json([ - 'error' => true, - 'message' => 'Chỉ nhắc được hoá đơn chưa thanh toán hoặc quá hạn.', - ], 422); - } - - try { - $reminders->remind($invoice->load('customer'), force: true); - } catch (\Throwable $e) { - return response()->json([ - 'error' => true, - 'message' => 'Không gửi được email nhắc: ' . $e->getMessage(), - ], 500); - } - - return response()->json([ - 'error' => false, - 'message' => 'Đã gửi email nhắc thanh toán tới ' . $invoice->customer?->email, - ]); - } - - /** - * Huỷ hoá đơn đã thu tiền sẽ xoá paid_at nhưng không hoàn số dư, không revert order/service, - * trong khi Transaction vẫn còn — sổ sách lệch im lặng. Chặn ở đây, đảo giao dịch phải đi - * qua luồng hoàn tiền. - */ - private function assertCancellable(Invoice $invoice): void - { - if (in_array((int) $invoice->status, [InvoiceStatus::PAID->value, InvoiceStatus::REFUNDED->value], true)) { - throw new \InvalidArgumentException( - 'Không thể huỷ hoá đơn đã thanh toán hoặc đã hoàn tiền. Hãy dùng chức năng hoàn tiền nếu cần đảo giao dịch.' - ); - } - } - // ─── Attachments ──────────────────────────────────────────────────────────── - public function listAttachments(int $id): JsonResponse - { - $invoice = $this->invoiceRepository->find($id); - if (!$invoice) { - return response()->json(['error' => true, 'message' => "Can't find invoice #$id"], 404); - } - - $attachments = $this->attachmentRepository->getByInvoice($id); - - return response()->json([ - 'error' => false, - 'data' => $attachments, - ]); - } - - public function uploadAttachment(Request $request, int $id): JsonResponse - { - $invoice = $this->invoiceRepository->find($id); - if (!$invoice) { - return response()->json(['error' => true, 'message' => "Can't find invoice #$id"], 404); - } - - $request->validate([ - 'file' => 'required|file|mimes:jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx|max:10240', - ]); - - $file = $request->file('file'); - $path = $file->store("invoice-attachments/{$id}", 'public'); - $uploader = $request->user()?->username ?? $request->user()?->name ?? 'admin'; - - $attachment = $this->attachmentRepository->create([ - 'invoice_id' => $id, - 'filename' => $file->getClientOriginalName(), - 'path' => $path, - 'disk' => 'public', - 'mime_type' => $file->getMimeType(), - 'size' => $file->getSize(), - 'uploaded_by' => $uploader, - ]); - - return response()->json([ - 'error' => false, - 'message' => 'File uploaded successfully', - 'data' => $attachment, - ], 201); - } - - public function deleteAttachment(int $id, int $attachmentId): JsonResponse - { - $invoice = $this->invoiceRepository->find($id); - if (!$invoice) { - return response()->json(['error' => true, 'message' => "Can't find invoice #$id"], 404); - } - - $attachment = $this->attachmentRepository->find($attachmentId); - if (!$attachment || $attachment->invoice_id !== $id) { - return response()->json(['error' => true, 'message' => 'Attachment not found'], 404); - } - - $this->attachmentRepository->deleteWithFile($attachmentId); - - return response()->json([ - 'error' => false, - 'message' => 'Attachment deleted', - ]); - } - // ─── Send Mail ─────────────────────────────────────────────────────────────── - - public function sendMail(Request $request, int $id): JsonResponse - { - $request->validate([ - 'message' => 'nullable|string|max:1000', - 'type' => 'nullable|string|in:invoice,renewal', - ]); - - $invoice = $this->invoiceRepository->find($id); - if (!$invoice) { - return response()->json(['error' => true, 'message' => "Can't find invoice #$id"], 404); - } - - $invoice->load('customer'); - $customer = $invoice->customer; - if (!$customer || !$customer->email) { - return response()->json(['error' => true, 'message' => 'Customer email not found'], 422); - } - - $attachments = $this->attachmentRepository->getByInvoice($id); - $companyName = setting('general.company_name') ?: config('app.name'); - $type = $request->get('type', 'invoice'); - $message = $request->get( - 'message', - $type === 'renewal' - ? "Dịch vụ của quý khách sắp đến hạn gia hạn. Vui lòng kiểm tra hóa đơn #{$invoice->order_num} được đính kèm và hoàn tất thanh toán để dịch vụ không bị gián đoạn." - : "Vui lòng xem thông tin hóa đơn #{$invoice->order_num} của bạn." - ); - $paymentUrl = url('/client/invoices/' . $invoice->invoice_number . '/payment'); - try { - $invoicePdf = $this->invoicePdfService->generate($invoice); - $invoicePdfFileName = $this->invoicePdfService->generateFileName($invoice); - - Mail::to($customer->email)->send( - new InvoiceNotificationMail( - $invoice, - $attachments, - $customer->fullname, - $message, - $companyName, - $type, - $invoicePdf, - $invoicePdfFileName, - $paymentUrl - ) - ); - } catch (Throwable $e) { - Log::error('InvoiceController@sendMail failed', [ - 'invoice_id' => $id, - 'invoice_number' => $invoice->invoice_number ?? null, - 'customer_email' => $customer->email, - 'type' => $type, - 'error' => $e->getMessage(), - ]); - - return response()->json([ - 'error' => true, - 'message' => 'Không thể gửi email: ' . $e->getMessage(), - ], 500); - } - - return response()->json([ - 'error' => false, - 'message' => "Email đã được gửi tới {$customer->email}", - ]); - } } diff --git a/app/Http/Controllers/Api/Admin/IpPool/Concerns/ManagesIpPoolBulkActions.php b/app/Http/Controllers/Api/Admin/IpPool/Concerns/ManagesIpPoolBulkActions.php new file mode 100644 index 0000000..dcec2ac --- /dev/null +++ b/app/Http/Controllers/Api/Admin/IpPool/Concerns/ManagesIpPoolBulkActions.php @@ -0,0 +1,182 @@ +validate([ + 'ids' => 'required|array|min:1', + 'ids.*' => 'integer', + 'gateway' => 'required|string|max:45', + ]); + + $pools = IpPool::whereIn('id', $request->ids)->get(['id', 'ip_address', 'module_network_id']); + + if ($pools->isEmpty()) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy IP Pool nào'], 404); + } + + DB::beginTransaction(); + try { + IpPool::whereIn('id', $pools->pluck('id'))->update(['gateway' => $request->gateway]); + + // Mirror into module_ip_address, grouped by network to keep this to a handful of queries. + foreach ($pools->groupBy('module_network_id') as $networkId => $group) { + ModuleIpAddress::whereIn('ip', $group->pluck('ip_address')) + ->where('module_network_id', $networkId) + ->update(['gateway' => $request->gateway]); + } + + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + return response()->json(['error' => true, 'message' => $e->getMessage()], 500); + } + + return response()->json([ + 'error' => false, + 'message' => "Đã cập nhật gateway cho {$pools->count()} IP", + 'updated' => $pools->count(), + ]); + } + + public function bulkDelete(Request $request): JsonResponse + { + $request->validate([ + 'ids' => 'required|array|min:1', + 'ids.*' => 'integer', + ]); + + $ids = $request->ids; + $pools = IpPool::whereIn('id', $ids)->get(); + + if ($pools->isEmpty()) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy IP Pool nào'], 404); + } + + // Block deletion if any IP is currently assigned + $inUseIps = ModuleIpAddress::whereIn('ip', $pools->pluck('ip_address')) + ->where('status', 1) + ->pluck('ip') + ->toArray(); + + if (!empty($inUseIps)) { + return response()->json([ + 'error' => true, + 'message' => 'Không thể xoá — các IP sau đang được gán cho máy chủ: ' . implode(', ', $inUseIps), + ], 422); + } + + foreach ($pools as $p) { + ModuleIpAddress::where('ip', $p->ip_address) + ->where('module_network_id', $p->module_network_id) + ->delete(); + } + + $deleted = IpPool::whereIn('id', $ids)->delete(); + + return response()->json([ + 'error' => false, + 'message' => "Đã xoá {$deleted} IP Pool thành công", + 'deleted' => $deleted, + ]); + } + + public function import(Request $request): JsonResponse + { + $request->validate([ + 'file' => 'required|file|mimes:csv,txt|max:10240', + ]); + + $countBefore = IpPool::withTrashed()->count(); + + try { + Excel::import(new IpPoolImport(), $request->file('file')); + } catch (ValidationException $e) { + $errors = []; + foreach ($e->failures() as $failure) { + $errors[] = 'Dòng ' . $failure->row() . ': ' . implode(', ', $failure->errors()); + } + + return response()->json([ + 'error' => true, + 'message' => 'Import thất bại', + 'errors' => $errors, + ], 422); + } catch (\Exception $e) { + return response()->json(['error' => true, 'message' => 'Import thất bại: ' . $e->getMessage()], 500); + } + + $countAfter = IpPool::withTrashed()->count(); + + IpPool::resyncAllContacts(); + + return response()->json([ + 'error' => false, + 'message' => 'Import thành công', + 'created' => max(0, $countAfter - $countBefore), + ]); + } + + /** + * Backfill/repair the module_ip_address mirror for every ip_pools row. + * Existing mirror rows only get gateway/netmask refreshed — status/vm_name + * (assigned VM state) is left untouched. Missing rows are created with + * status derived from the current ip_pools.status. + */ + public function sync(): JsonResponse + { + $created = 0; + $updated = 0; + $hostByNet = ModuleNetwork::pluck('module_host_id', 'id'); + + IpPool::chunkById(500, function ($pools) use (&$created, &$updated, $hostByNet) { + foreach ($pools as $pool) { + $hostId = $hostByNet[$pool->module_network_id] ?? null; + $netmask = $pool->netmask ?: '255.255.255.0'; + + $mia = ModuleIpAddress::where('ip', $pool->ip_address) + ->where('module_network_id', $pool->module_network_id) + ->first(); + + if ($mia) { + $mia->update([ + 'module_host_id' => $hostId, + 'gateway' => $pool->gateway, + 'netmask' => $netmask, + ]); + $updated++; + } else { + ModuleIpAddress::create([ + 'ip' => $pool->ip_address, + 'module_network_id' => $pool->module_network_id, + 'module_host_id' => $hostId, + 'gateway' => $pool->gateway, + 'netmask' => $netmask, + 'status' => $pool->status === 'assigned' ? 1 : 0, + ]); + $created++; + } + } + }); + + return response()->json([ + 'error' => false, + 'message' => "Đã đồng bộ: tạo mới {$created}, cập nhật {$updated}", + 'created' => $created, + 'updated' => $updated, + ]); + } +} diff --git a/app/Http/Controllers/Api/Admin/IpPool/Concerns/ReadsIpPoolUsage.php b/app/Http/Controllers/Api/Admin/IpPool/Concerns/ReadsIpPoolUsage.php new file mode 100644 index 0000000..0a37288 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/IpPool/Concerns/ReadsIpPoolUsage.php @@ -0,0 +1,45 @@ +select(['id as ip_pool_id', 'service_id']); + + $viaVm = DB::table('ip_pools as ip') + ->join('module_ip_address as mia', function ($join) { + $join->on('mia.ip', '=', 'ip.ip_address') + ->on('mia.module_network_id', '=', 'ip.module_network_id'); + }) + ->join('module_vmware as mv', 'mv.module_ip_address', '=', 'mia.id') + ->whereNotNull('mv.service_id') + ->select(['ip.id as ip_pool_id', 'mv.service_id']); + + $used = $direct->toBase()->union($viaVm)->get()->unique(fn ($row) => "{$row->service_id}-{$row->ip_pool_id}")->values(); + + return response()->json(['error' => false, 'data' => $used]); + } + + public function networks(): JsonResponse + { + $networks = ModuleNetwork::select(['id', 'name', 'type', 'network', 'module_host_id']) + ->with(['host:id,name,module_datacenter_id', 'host.datacenter:id,name']) + ->orderBy('name') + ->get(); + + return response()->json(['error' => false, 'data' => $networks]); + } +} diff --git a/app/Http/Controllers/Api/Admin/IpPool/IpPoolController.php b/app/Http/Controllers/Api/Admin/IpPool/IpPoolController.php index cc399da..20c7255 100644 --- a/app/Http/Controllers/Api/Admin/IpPool/IpPoolController.php +++ b/app/Http/Controllers/Api/Admin/IpPool/IpPoolController.php @@ -3,18 +3,20 @@ namespace App\Http\Controllers\Api\Admin\IpPool; use App\Http\Controllers\Controller; -use App\Imports\IpPoolImport; use App\Models\IpPool; use App\Models\ModuleIpAddress; use App\Models\ModuleNetwork; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; -use Maatwebsite\Excel\Facades\Excel; -use Maatwebsite\Excel\Validators\ValidationException; +use App\Http\Controllers\Api\Admin\IpPool\Concerns\ManagesIpPoolBulkActions; +use App\Http\Controllers\Api\Admin\IpPool\Concerns\ReadsIpPoolUsage; class IpPoolController extends Controller { + use ManagesIpPoolBulkActions; + use ReadsIpPoolUsage; + /** * Descriptive/CSV-import fields — plain pass-through, unlike gateway/cidr/netmask/ * status/service_id which have defaulting + module_ip_address mirror logic below. @@ -341,204 +343,4 @@ class IpPoolController extends Controller return response()->json(['error' => false, 'message' => 'Đã xoá IP Pool thành công']); } - - public function bulkUpdateGateway(Request $request): JsonResponse - { - $request->validate([ - 'ids' => 'required|array|min:1', - 'ids.*' => 'integer', - 'gateway' => 'required|string|max:45', - ]); - - $pools = IpPool::whereIn('id', $request->ids)->get(['id', 'ip_address', 'module_network_id']); - - if ($pools->isEmpty()) { - return response()->json(['error' => true, 'message' => 'Không tìm thấy IP Pool nào'], 404); - } - - DB::beginTransaction(); - try { - IpPool::whereIn('id', $pools->pluck('id'))->update(['gateway' => $request->gateway]); - - // Mirror into module_ip_address, grouped by network to keep this to a handful of queries. - foreach ($pools->groupBy('module_network_id') as $networkId => $group) { - ModuleIpAddress::whereIn('ip', $group->pluck('ip_address')) - ->where('module_network_id', $networkId) - ->update(['gateway' => $request->gateway]); - } - - DB::commit(); - } catch (\Exception $e) { - DB::rollBack(); - return response()->json(['error' => true, 'message' => $e->getMessage()], 500); - } - - return response()->json([ - 'error' => false, - 'message' => "Đã cập nhật gateway cho {$pools->count()} IP", - 'updated' => $pools->count(), - ]); - } - - public function bulkDelete(Request $request): JsonResponse - { - $request->validate([ - 'ids' => 'required|array|min:1', - 'ids.*' => 'integer', - ]); - - $ids = $request->ids; - $pools = IpPool::whereIn('id', $ids)->get(); - - if ($pools->isEmpty()) { - return response()->json(['error' => true, 'message' => 'Không tìm thấy IP Pool nào'], 404); - } - - // Block deletion if any IP is currently assigned - $inUseIps = ModuleIpAddress::whereIn('ip', $pools->pluck('ip_address')) - ->where('status', 1) - ->pluck('ip') - ->toArray(); - - if (!empty($inUseIps)) { - return response()->json([ - 'error' => true, - 'message' => 'Không thể xoá — các IP sau đang được gán cho máy chủ: ' . implode(', ', $inUseIps), - ], 422); - } - - foreach ($pools as $p) { - ModuleIpAddress::where('ip', $p->ip_address) - ->where('module_network_id', $p->module_network_id) - ->delete(); - } - - $deleted = IpPool::whereIn('id', $ids)->delete(); - - return response()->json([ - 'error' => false, - 'message' => "Đã xoá {$deleted} IP Pool thành công", - 'deleted' => $deleted, - ]); - } - - public function import(Request $request): JsonResponse - { - $request->validate([ - 'file' => 'required|file|mimes:csv,txt|max:10240', - ]); - - $countBefore = IpPool::withTrashed()->count(); - - try { - Excel::import(new IpPoolImport(), $request->file('file')); - } catch (ValidationException $e) { - $errors = []; - foreach ($e->failures() as $failure) { - $errors[] = 'Dòng ' . $failure->row() . ': ' . implode(', ', $failure->errors()); - } - - return response()->json([ - 'error' => true, - 'message' => 'Import thất bại', - 'errors' => $errors, - ], 422); - } catch (\Exception $e) { - return response()->json(['error' => true, 'message' => 'Import thất bại: ' . $e->getMessage()], 500); - } - - $countAfter = IpPool::withTrashed()->count(); - - IpPool::resyncAllContacts(); - - return response()->json([ - 'error' => false, - 'message' => 'Import thành công', - 'created' => max(0, $countAfter - $countBefore), - ]); - } - - /** - * Backfill/repair the module_ip_address mirror for every ip_pools row. - * Existing mirror rows only get gateway/netmask refreshed — status/vm_name - * (assigned VM state) is left untouched. Missing rows are created with - * status derived from the current ip_pools.status. - */ - public function sync(): JsonResponse - { - $created = 0; - $updated = 0; - $hostByNet = ModuleNetwork::pluck('module_host_id', 'id'); - - IpPool::chunkById(500, function ($pools) use (&$created, &$updated, $hostByNet) { - foreach ($pools as $pool) { - $hostId = $hostByNet[$pool->module_network_id] ?? null; - $netmask = $pool->netmask ?: '255.255.255.0'; - - $mia = ModuleIpAddress::where('ip', $pool->ip_address) - ->where('module_network_id', $pool->module_network_id) - ->first(); - - if ($mia) { - $mia->update([ - 'module_host_id' => $hostId, - 'gateway' => $pool->gateway, - 'netmask' => $netmask, - ]); - $updated++; - } else { - ModuleIpAddress::create([ - 'ip' => $pool->ip_address, - 'module_network_id' => $pool->module_network_id, - 'module_host_id' => $hostId, - 'gateway' => $pool->gateway, - 'netmask' => $netmask, - 'status' => $pool->status === 'assigned' ? 1 : 0, - ]); - $created++; - } - } - }); - - return response()->json([ - 'error' => false, - 'message' => "Đã đồng bộ: tạo mới {$created}, cập nhật {$updated}", - 'created' => $created, - 'updated' => $updated, - ]); - } - - /** - * Service IDs already tied to an IP — either directly via ip_pools.service_id, - * or indirectly via a VM (module_vmware) sitting on that IP. Used by the admin - * form to disable services that are already in use on another IP. - */ - public function usedServices(): JsonResponse - { - $direct = IpPool::whereNotNull('service_id') - ->select(['id as ip_pool_id', 'service_id']); - - $viaVm = DB::table('ip_pools as ip') - ->join('module_ip_address as mia', function ($join) { - $join->on('mia.ip', '=', 'ip.ip_address') - ->on('mia.module_network_id', '=', 'ip.module_network_id'); - }) - ->join('module_vmware as mv', 'mv.module_ip_address', '=', 'mia.id') - ->whereNotNull('mv.service_id') - ->select(['ip.id as ip_pool_id', 'mv.service_id']); - - $used = $direct->toBase()->union($viaVm)->get()->unique(fn ($row) => "{$row->service_id}-{$row->ip_pool_id}")->values(); - - return response()->json(['error' => false, 'data' => $used]); - } - - public function networks(): JsonResponse - { - $networks = ModuleNetwork::select(['id', 'name', 'type', 'network', 'module_host_id']) - ->with(['host:id,name,module_datacenter_id', 'host.datacenter:id,name']) - ->orderBy('name') - ->get(); - - return response()->json(['error' => false, 'data' => $networks]); - } } diff --git a/app/Http/Controllers/Api/Admin/OsManager/OsController.php b/app/Http/Controllers/Api/Admin/OsManager/OsController.php index 9db7274..7ccf953 100644 --- a/app/Http/Controllers/Api/Admin/OsManager/OsController.php +++ b/app/Http/Controllers/Api/Admin/OsManager/OsController.php @@ -4,7 +4,6 @@ namespace App\Http\Controllers\Api\Admin\OsManager; use App\Http\Controllers\Controller; use App\Models\Os; -use App\Models\OsGroup; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Validation\Rule; diff --git a/app/Http/Controllers/Api/Admin/Ticket/TicketController.php b/app/Http/Controllers/Api/Admin/Ticket/TicketController.php index b7c2825..d93589a 100644 --- a/app/Http/Controllers/Api/Admin/Ticket/TicketController.php +++ b/app/Http/Controllers/Api/Admin/Ticket/TicketController.php @@ -8,14 +8,12 @@ use App\Http\Resources\Api\Ticket\TicketResource; use App\Http\Resources\Api\Ticket\TicketReplyResource; use App\Models\Notification; use App\Models\Ticket; -use App\Repositories\Ticket\TicketRepositoryInterface; use App\Repositories\TicketReply\TicketReplyRepositoryInterface; use Illuminate\Http\Request; class TicketController extends Controller { public function __construct( - protected TicketRepositoryInterface $ticketRepository, protected TicketReplyRepositoryInterface $ticketReplyRepository, ) {} diff --git a/app/Http/Controllers/Api/Client/ClientController.php b/app/Http/Controllers/Api/Client/ClientController.php index 271349e..6717472 100644 --- a/app/Http/Controllers/Api/Client/ClientController.php +++ b/app/Http/Controllers/Api/Client/ClientController.php @@ -3,19 +3,13 @@ namespace App\Http\Controllers\Api\Client; use App\Http\Controllers\Controller; -use App\Http\Requests\Api\Client\ClientAuthRequest; -use App\Http\Requests\Api\Client\ClientChangePasswordRequest; -use App\Http\Requests\Api\Client\ClientRegisterRequest; -use App\Http\Requests\Api\Client\ClientUpdateCccdRequest; -use App\Http\Requests\Api\Client\ClientUpdateRequest; use App\Http\Resources\Api\Client\ClientResource; -use App\Models\Customer; use App\Services\ActivityLogger; use App\Services\Api\Client\ClientAuthService; -use Illuminate\Http\Request; -use Illuminate\Support\Facades\Storage; -use Illuminate\Validation\Rules\Password; +use App\Http\Controllers\Api\Client\Concerns\HandlesClientAuthentication; +use App\Http\Controllers\Api\Client\Concerns\HandlesClientPasswordReset; +use App\Http\Controllers\Api\Client\Concerns\ManagesClientProfile; /** * @OA\Tag( * name="Client Authentication", @@ -24,6 +18,10 @@ use Illuminate\Validation\Rules\Password; */ class ClientController extends Controller { + use HandlesClientAuthentication; + use HandlesClientPasswordReset; + use ManagesClientProfile; + protected $authService; protected ActivityLogger $activityLogger; @@ -97,448 +95,4 @@ class ClientController extends Controller ], ]); } - - /** - * @OA\Post( - * path="/client/login", - * operationId="clientLogin", - * tags={"Client Authentication"}, - * summary="Client login", - * description="Authenticate a client and return access token + refresh token", - * @OA\RequestBody( - * required=true, - * @OA\JsonContent( - * required={"email","password"}, - * @OA\Property(property="email", type="string", format="email", example="client@example.com"), - * @OA\Property(property="password", type="string", format="password", example="password123") - * ) - * ), - * @OA\Response( - * response=200, - * description="Login successful", - * @OA\JsonContent( - * @OA\Property(property="error", type="boolean", example=false), - * @OA\Property(property="data", type="object", - * @OA\Property(property="client", type="object"), - * @OA\Property(property="token", type="string", example="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."), - * @OA\Property(property="refresh_token", type="string", example="random64charstring..."), - * @OA\Property(property="expires_in", type="integer", example=900) - * ) - * ) - * ), - * @OA\Response( - * response=422, - * description="Validation error" - * ) - * ) - */ - public function login(ClientAuthRequest $request) - { - $result = $this->authService->authenticateClient($request->validated()); - - $client = $result['client']; - $this->activityLogger->logAuth('login', 'customer', $client->id, $client->email); - - return response()->json([ - 'error' => false, - 'data' => [ - 'client' => new ClientResource($client->loadCount(['vps', 'hosting', 'domain'])), - 'token' => $result['token'], - 'refresh_token' => $result['refresh_token'], - 'expires_in' => config('jwt.ttl', 60) * 60, // seconds - ], - ]); - } - - /** - * @OA\Post( - * path="/client/register", - * operationId="clientRegister", - * tags={"Client Authentication"}, - * summary="Client registration", - * description="Register a new client account", - * @OA\RequestBody( - * required=true, - * @OA\JsonContent( - * required={"fullname","phone","email","password","password_confirmation"}, - * @OA\Property(property="fullname", type="string", example="Nguyen Van A"), - * @OA\Property(property="phone", type="string", example="0987654321"), - * @OA\Property(property="email", type="string", format="email", example="client@example.com"), - * @OA\Property(property="password", type="string", format="password", example="password123"), - * @OA\Property(property="password_confirmation", type="string", format="password", example="password123") - * ) - * ), - * @OA\Response( - * response=200, - * description="Registration successful", - * @OA\JsonContent( - * @OA\Property(property="error", type="boolean", example=false), - * @OA\Property(property="data", type="object", - * @OA\Property(property="client", type="object"), - * @OA\Property(property="token", type="string", example="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."), - * @OA\Property(property="refresh_token", type="string", example="random64charstring..."), - * @OA\Property(property="expires_in", type="integer", example=900) - * ) - * ) - * ), - * @OA\Response( - * response=422, - * description="Validation error" - * ) - * ) - */ - public function register(ClientRegisterRequest $request) - { - $data = $request->validated(); - - if (!empty($data['trial_config'])) { - $decoded = json_decode((string) $data['trial_config'], true); - $data['trial_config'] = is_array($decoded) ? $decoded : null; - } - - $result = $this->authService->registerClient($data); - - $client = $result['client']; - $this->activityLogger->logAuth('register', 'customer', $client->id, $client->email); - - return response()->json([ - 'error' => false, - 'message' => 'Đăng ký thành công. Vui lòng kiểm tra email và nhập mã xác thực để kích hoạt tài khoản.', - ]); - } - - public function verifyEmail(Request $request) - { - $request->validate([ - 'email' => ['required', 'email', 'max:255'], - 'code' => ['required', 'string', 'size:6'], - ]); - - $result = $this->authService->verifyEmail( - $request->input('email'), - $request->input('code'), - ); - - $client = $result['client']; - $this->activityLogger->logAuth('email_verified', 'customer', $client->id, $client->email); - - return response()->json([ - 'error' => false, - 'data' => [ - 'client' => new ClientResource($client->loadCount(['vps', 'hosting', 'domain'])), - 'token' => $result['token'], - 'refresh_token' => $result['refresh_token'], - 'expires_in' => config('jwt.ttl', 60) * 60, - ], - ]); - } - - public function resendVerifyCode(Request $request) - { - $request->validate([ - 'email' => ['required', 'email', 'max:255'], - ]); - - $this->authService->resendVerifyCode($request->input('email')); - - return response()->json([ - 'error' => false, - 'message' => 'Nếu email tồn tại và chưa xác thực, mã xác thực mới đã được gửi.', - ]); - } - - /** - * @OA\Post( - * path="/client/logout", - * operationId="clientLogout", - * tags={"Client Authentication"}, - * summary="Client logout", - * description="Logout the currently authenticated client and revoke refresh token", - * security={{"bearerAuth":{}}}, - * @OA\RequestBody( - * @OA\JsonContent( - * @OA\Property(property="refresh_token", type="string", example="random64charstring...") - * ) - * ), - * @OA\Response( - * response=200, - * description="Logout successful", - * @OA\JsonContent( - * @OA\Property(property="error", type="boolean", example=false), - * @OA\Property(property="message", type="string", example="Đã đăng xuất thành công.") - * ) - * ), - * @OA\Response( - * response=401, - * description="Unauthenticated" - * ) - * ) - */ - public function logout(Request $request) - { - $customer = auth('client')->user(); - if ($customer) { - $this->activityLogger->logAuth('logout', 'customer', $customer->id, $customer->email); - } - - if ($rawToken = $request->input('refresh_token')) { - $this->authService->revokeRefreshToken($rawToken); - } - - auth('client')->logout(); - - return response()->json([ - 'error' => false, - 'message' => 'Đã đăng xuất thành công.', - ]); - } - - /** - * @OA\Post( - * path="/client/refresh", - * operationId="clientRefresh", - * tags={"Client Authentication"}, - * summary="Refresh access token", - * description="Exchange a valid refresh token for a new access token + rotated refresh token", - * @OA\RequestBody( - * required=true, - * @OA\JsonContent( - * required={"refresh_token"}, - * @OA\Property(property="refresh_token", type="string", example="random64charstring...") - * ) - * ), - * @OA\Response( - * response=200, - * description="Token refreshed", - * @OA\JsonContent( - * @OA\Property(property="error", type="boolean", example=false), - * @OA\Property(property="data", type="object", - * @OA\Property(property="token", type="string"), - * @OA\Property(property="refresh_token", type="string"), - * @OA\Property(property="expires_in", type="integer", example=900), - * @OA\Property(property="client", type="object") - * ) - * ) - * ), - * @OA\Response( - * response=401, - * description="Invalid or expired refresh token" - * ) - * ) - */ - public function refresh(Request $request) - { - $request->validate([ - 'refresh_token' => ['required', 'string'], - ]); - - try { - $result = $this->authService->rotateRefreshToken($request->input('refresh_token')); - - return response()->json([ - 'error' => false, - 'data' => [ - 'token' => $result['token'], - 'refresh_token' => $result['refresh_token'], - 'expires_in' => config('jwt.ttl', 60) * 60, - 'client' => new ClientResource($result['client']->loadCount(['vps', 'hosting', 'domain'])), - ], - ]); - } catch (\Illuminate\Validation\ValidationException $e) { - return response()->json([ - 'error' => true, - 'message' => collect($e->errors())->flatten()->first(), - ], 401); - } - } - - /** - * Đổi mã impersonate (do admin cấp) lấy access token + refresh token. - * Endpoint public — code chỉ dùng được 1 lần và hết hạn sau 120s. - */ - public function impersonateExchange(Request $request) - { - $request->validate([ - 'code' => ['required', 'string', 'max:128'], - ]); - - $code = (string) $request->input('code'); - $payload = \Illuminate\Support\Facades\Cache::pull("impersonate:{$code}"); - - if (!$payload) { - return response()->json([ - 'error' => true, - 'message' => 'Mã đăng nhập không hợp lệ hoặc đã hết hạn.', - ], 401); - } - - return response()->json([ - 'error' => false, - 'data' => [ - 'token' => $payload['token'], - 'refresh_token' => $payload['refresh_token'], - 'client' => $payload['client'], - 'expires_in' => config('jwt.ttl', 60) * 60, - ], - ]); - } - - public function forgotPassword(Request $request) - { - $request->validate([ - 'email' => ['required', 'email', 'max:255'], - ]); - - $customer = $this->authService->sendPasswordResetLink($request->input('email')); - - if ($customer) { - $this->activityLogger->log( - event: 'password_forgot', - logName: 'Customer', - description: "Customer {$customer->email} yêu cầu link đặt lại mật khẩu", - subject: $customer, - ); - } - - // Do not disclose whether the email exists. - return response()->json([ - 'error' => false, - 'message' => 'Nếu email tồn tại, link đặt lại mật khẩu đã được gửi đến hộp thư của bạn.', - ]); - } - - public function resetPassword(Request $request) - { - $data = $request->validate([ - 'email' => ['required', 'email', 'max:255'], - 'token' => ['required', 'string', 'max:255'], - 'password' => ['required', Password::min(10), 'confirmed'], - ]); - - $customer = $this->authService->resetPassword($data); - - $this->activityLogger->log( - event: 'password_reset', - logName: 'Customer', - description: "Customer {$customer->email} đặt lại mật khẩu qua email", - subject: $customer, - ); - - return response()->json([ - 'error' => false, - 'message' => 'Đặt lại mật khẩu thành công, vui lòng đăng nhập lại.', - ]); - } - - /** - * Trả ảnh CCCD từ disk private. Route yêu cầu chữ ký (signed:relative) nên không cần - * Bearer — chữ ký do ClientResource cấp cho đúng chủ tài khoản, hết hạn sau 60 phút. - */ - public function cccdImage(int $customer, string $side) - { - $path = Customer::findOrFail($customer)->{$side === 'front' ? 'front_identity_number' : 'back_identity_number'}; - - abort_unless($path && str_starts_with($path, 'cccd/') && Storage::disk('local')->exists($path), 404); - - $mime = match (strtolower(pathinfo($path, PATHINFO_EXTENSION))) { - 'png' => 'image/png', - 'webp' => 'image/webp', - default => 'image/jpeg', - }; - - return Storage::disk('local')->response($path, "cccd-{$side}", [ - 'Content-Type' => $mime, - 'Content-Disposition' => "inline; filename=\"cccd-{$side}\"", - 'Cache-Control' => 'private, no-store', - ]); - } - - public function update(ClientUpdateRequest $request) - { - $result = $this->authService->updateProfile(auth('client')->user(), $request->validated()); - $user = $result['client']; - - if (!empty($result['dirty'])) { - $this->activityLogger->log( - event: 'profile_update', - logName: 'Customer', - description: "Customer {$user->email} cập nhật thông tin cá nhân: " . implode(', ', array_keys($result['dirty'])), - subject: $user, - oldValues: $result['old'], - newValues: array_diff_key($result['dirty'], array_flip(['password', 'remember_token'])), - ); - } - - return response()->json([ - 'error' => false, - 'message' => 'Cập nhật thông tin thành công', - 'data' => [ - 'client' => new ClientResource($user->loadCount(['vps', 'hosting', 'domain'])), - ], - ]); - } - - public function updateCccd(ClientUpdateCccdRequest $request) - { - // Disk private: ảnh chỉ đọc được qua route signed `api.client.cccd`. - $frontUrl = $request->file('cmnd_before')->store('cccd', 'local'); - $backUrl = $request->file('cmnd_after')->store('cccd', 'local'); - - $result = $this->authService->updateCccd( - auth('client')->user(), - $request->validated('cmnd'), - $frontUrl, - $backUrl, - ); - $user = $result['client']; - - if (!empty($result['dirty'])) { - $this->activityLogger->log( - event: 'cccd_update', - logName: 'Customer', - description: "Customer {$user->email} cập nhật chứng minh thư", - subject: $user, - oldValues: $result['old'], - newValues: $result['dirty'], - ); - } - - return response()->json([ - 'error' => false, - 'message' => 'Cập nhật chứng minh thư thành công', - 'data' => [ - 'client' => new ClientResource($user->loadCount(['vps', 'hosting', 'domain'])), - ], - ]); - } - - public function changePassword(ClientChangePasswordRequest $request) - { - $user = auth('client')->user(); - - try { - $this->authService->changePassword( - $user, - $request->input('current_password'), - $request->input('password'), - ); - } catch (\Illuminate\Validation\ValidationException $e) { - return response()->json([ - 'error' => true, - 'message' => collect($e->errors())->flatten()->first(), - ], 422); - } - - $this->activityLogger->log( - event: 'password_change', - logName: 'Customer', - description: "Customer {$user->email} đã đổi mật khẩu", - subject: $user, - ); - - return response()->json([ - 'error' => false, - 'message' => 'Đổi mật khẩu thành công', - ]); - } } diff --git a/app/Http/Controllers/Api/Client/Concerns/HandlesClientAuthentication.php b/app/Http/Controllers/Api/Client/Concerns/HandlesClientAuthentication.php new file mode 100644 index 0000000..e422dc5 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Concerns/HandlesClientAuthentication.php @@ -0,0 +1,296 @@ +authService->authenticateClient($request->validated()); + + $client = $result['client']; + $this->activityLogger->logAuth('login', 'customer', $client->id, $client->email); + + return response()->json([ + 'error' => false, + 'data' => [ + 'client' => new ClientResource($client->loadCount(['vps', 'hosting', 'domain'])), + 'token' => $result['token'], + 'refresh_token' => $result['refresh_token'], + 'expires_in' => config('jwt.ttl', 60) * 60, // seconds + ], + ]); + } + + /** + * @OA\Post( + * path="/client/register", + * operationId="clientRegister", + * tags={"Client Authentication"}, + * summary="Client registration", + * description="Register a new client account", + * @OA\RequestBody( + * required=true, + * @OA\JsonContent( + * required={"fullname","phone","email","password","password_confirmation"}, + * @OA\Property(property="fullname", type="string", example="Nguyen Van A"), + * @OA\Property(property="phone", type="string", example="0987654321"), + * @OA\Property(property="email", type="string", format="email", example="client@example.com"), + * @OA\Property(property="password", type="string", format="password", example="password123"), + * @OA\Property(property="password_confirmation", type="string", format="password", example="password123") + * ) + * ), + * @OA\Response( + * response=200, + * description="Registration successful", + * @OA\JsonContent( + * @OA\Property(property="error", type="boolean", example=false), + * @OA\Property(property="data", type="object", + * @OA\Property(property="client", type="object"), + * @OA\Property(property="token", type="string", example="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."), + * @OA\Property(property="refresh_token", type="string", example="random64charstring..."), + * @OA\Property(property="expires_in", type="integer", example=900) + * ) + * ) + * ), + * @OA\Response( + * response=422, + * description="Validation error" + * ) + * ) + */ + public function register(ClientRegisterRequest $request) + { + $data = $request->validated(); + + if (!empty($data['trial_config'])) { + $decoded = json_decode((string) $data['trial_config'], true); + $data['trial_config'] = is_array($decoded) ? $decoded : null; + } + + $result = $this->authService->registerClient($data); + + $client = $result['client']; + $this->activityLogger->logAuth('register', 'customer', $client->id, $client->email); + + return response()->json([ + 'error' => false, + 'message' => 'Đăng ký thành công. Vui lòng kiểm tra email và nhập mã xác thực để kích hoạt tài khoản.', + ]); + } + + public function verifyEmail(Request $request) + { + $request->validate([ + 'email' => ['required', 'email', 'max:255'], + 'code' => ['required', 'string', 'size:6'], + ]); + + $result = $this->authService->verifyEmail( + $request->input('email'), + $request->input('code'), + ); + + $client = $result['client']; + $this->activityLogger->logAuth('email_verified', 'customer', $client->id, $client->email); + + return response()->json([ + 'error' => false, + 'data' => [ + 'client' => new ClientResource($client->loadCount(['vps', 'hosting', 'domain'])), + 'token' => $result['token'], + 'refresh_token' => $result['refresh_token'], + 'expires_in' => config('jwt.ttl', 60) * 60, + ], + ]); + } + + public function resendVerifyCode(Request $request) + { + $request->validate([ + 'email' => ['required', 'email', 'max:255'], + ]); + + $this->authService->resendVerifyCode($request->input('email')); + + return response()->json([ + 'error' => false, + 'message' => 'Nếu email tồn tại và chưa xác thực, mã xác thực mới đã được gửi.', + ]); + } + + /** + * @OA\Post( + * path="/client/logout", + * operationId="clientLogout", + * tags={"Client Authentication"}, + * summary="Client logout", + * description="Logout the currently authenticated client and revoke refresh token", + * security={{"bearerAuth":{}}}, + * @OA\RequestBody( + * @OA\JsonContent( + * @OA\Property(property="refresh_token", type="string", example="random64charstring...") + * ) + * ), + * @OA\Response( + * response=200, + * description="Logout successful", + * @OA\JsonContent( + * @OA\Property(property="error", type="boolean", example=false), + * @OA\Property(property="message", type="string", example="Đã đăng xuất thành công.") + * ) + * ), + * @OA\Response( + * response=401, + * description="Unauthenticated" + * ) + * ) + */ + public function logout(Request $request) + { + $customer = auth('client')->user(); + if ($customer) { + $this->activityLogger->logAuth('logout', 'customer', $customer->id, $customer->email); + } + + if ($rawToken = $request->input('refresh_token')) { + $this->authService->revokeRefreshToken($rawToken); + } + + auth('client')->logout(); + + return response()->json([ + 'error' => false, + 'message' => 'Đã đăng xuất thành công.', + ]); + } + + /** + * @OA\Post( + * path="/client/refresh", + * operationId="clientRefresh", + * tags={"Client Authentication"}, + * summary="Refresh access token", + * description="Exchange a valid refresh token for a new access token + rotated refresh token", + * @OA\RequestBody( + * required=true, + * @OA\JsonContent( + * required={"refresh_token"}, + * @OA\Property(property="refresh_token", type="string", example="random64charstring...") + * ) + * ), + * @OA\Response( + * response=200, + * description="Token refreshed", + * @OA\JsonContent( + * @OA\Property(property="error", type="boolean", example=false), + * @OA\Property(property="data", type="object", + * @OA\Property(property="token", type="string"), + * @OA\Property(property="refresh_token", type="string"), + * @OA\Property(property="expires_in", type="integer", example=900), + * @OA\Property(property="client", type="object") + * ) + * ) + * ), + * @OA\Response( + * response=401, + * description="Invalid or expired refresh token" + * ) + * ) + */ + public function refresh(Request $request) + { + $request->validate([ + 'refresh_token' => ['required', 'string'], + ]); + + try { + $result = $this->authService->rotateRefreshToken($request->input('refresh_token')); + + return response()->json([ + 'error' => false, + 'data' => [ + 'token' => $result['token'], + 'refresh_token' => $result['refresh_token'], + 'expires_in' => config('jwt.ttl', 60) * 60, + 'client' => new ClientResource($result['client']->loadCount(['vps', 'hosting', 'domain'])), + ], + ]); + } catch (\Illuminate\Validation\ValidationException $e) { + return response()->json([ + 'error' => true, + 'message' => collect($e->errors())->flatten()->first(), + ], 401); + } + } + + /** + * Đổi mã impersonate (do admin cấp) lấy access token + refresh token. + * Endpoint public — code chỉ dùng được 1 lần và hết hạn sau 120s. + */ + public function impersonateExchange(Request $request) + { + $request->validate([ + 'code' => ['required', 'string', 'max:128'], + ]); + + $code = (string) $request->input('code'); + $payload = \Illuminate\Support\Facades\Cache::pull("impersonate:{$code}"); + + if (!$payload) { + return response()->json([ + 'error' => true, + 'message' => 'Mã đăng nhập không hợp lệ hoặc đã hết hạn.', + ], 401); + } + + return response()->json([ + 'error' => false, + 'data' => [ + 'token' => $payload['token'], + 'refresh_token' => $payload['refresh_token'], + 'client' => $payload['client'], + 'expires_in' => config('jwt.ttl', 60) * 60, + ], + ]); + } +} diff --git a/app/Http/Controllers/Api/Client/Concerns/HandlesClientPasswordReset.php b/app/Http/Controllers/Api/Client/Concerns/HandlesClientPasswordReset.php new file mode 100644 index 0000000..10ab29b --- /dev/null +++ b/app/Http/Controllers/Api/Client/Concerns/HandlesClientPasswordReset.php @@ -0,0 +1,88 @@ +validate([ + 'email' => ['required', 'email', 'max:255'], + ]); + + $customer = $this->authService->sendPasswordResetLink($request->input('email')); + + if ($customer) { + $this->activityLogger->log( + event: 'password_forgot', + logName: 'Customer', + description: "Customer {$customer->email} yêu cầu link đặt lại mật khẩu", + subject: $customer, + ); + } + + // Do not disclose whether the email exists. + return response()->json([ + 'error' => false, + 'message' => 'Nếu email tồn tại, link đặt lại mật khẩu đã được gửi đến hộp thư của bạn.', + ]); + } + + public function resetPassword(Request $request) + { + $data = $request->validate([ + 'email' => ['required', 'email', 'max:255'], + 'token' => ['required', 'string', 'max:255'], + 'password' => ['required', Password::min(10), 'confirmed'], + ]); + + $customer = $this->authService->resetPassword($data); + + $this->activityLogger->log( + event: 'password_reset', + logName: 'Customer', + description: "Customer {$customer->email} đặt lại mật khẩu qua email", + subject: $customer, + ); + + return response()->json([ + 'error' => false, + 'message' => 'Đặt lại mật khẩu thành công, vui lòng đăng nhập lại.', + ]); + } + + public function changePassword(ClientChangePasswordRequest $request) + { + $user = auth('client')->user(); + + try { + $this->authService->changePassword( + $user, + $request->input('current_password'), + $request->input('password'), + ); + } catch (\Illuminate\Validation\ValidationException $e) { + return response()->json([ + 'error' => true, + 'message' => collect($e->errors())->flatten()->first(), + ], 422); + } + + $this->activityLogger->log( + event: 'password_change', + logName: 'Customer', + description: "Customer {$user->email} đã đổi mật khẩu", + subject: $user, + ); + + return response()->json([ + 'error' => false, + 'message' => 'Đổi mật khẩu thành công', + ]); + } +} diff --git a/app/Http/Controllers/Api/Client/Concerns/ManagesClientProfile.php b/app/Http/Controllers/Api/Client/Concerns/ManagesClientProfile.php new file mode 100644 index 0000000..38b6444 --- /dev/null +++ b/app/Http/Controllers/Api/Client/Concerns/ManagesClientProfile.php @@ -0,0 +1,94 @@ +authService->updateProfile(auth('client')->user(), $request->validated()); + $user = $result['client']; + + if (!empty($result['dirty'])) { + $this->activityLogger->log( + event: 'profile_update', + logName: 'Customer', + description: "Customer {$user->email} cập nhật thông tin cá nhân: " . implode(', ', array_keys($result['dirty'])), + subject: $user, + oldValues: $result['old'], + newValues: array_diff_key($result['dirty'], array_flip(['password', 'remember_token'])), + ); + } + + return response()->json([ + 'error' => false, + 'message' => 'Cập nhật thông tin thành công', + 'data' => [ + 'client' => new ClientResource($user->loadCount(['vps', 'hosting', 'domain'])), + ], + ]); + } + + public function updateCccd(ClientUpdateCccdRequest $request) + { + // Disk private: ảnh chỉ đọc được qua route signed `api.client.cccd`. + $frontUrl = $request->file('cmnd_before')->store('cccd', 'local'); + $backUrl = $request->file('cmnd_after')->store('cccd', 'local'); + + $result = $this->authService->updateCccd( + auth('client')->user(), + $request->validated('cmnd'), + $frontUrl, + $backUrl, + ); + $user = $result['client']; + + if (!empty($result['dirty'])) { + $this->activityLogger->log( + event: 'cccd_update', + logName: 'Customer', + description: "Customer {$user->email} cập nhật chứng minh thư", + subject: $user, + oldValues: $result['old'], + newValues: $result['dirty'], + ); + } + + return response()->json([ + 'error' => false, + 'message' => 'Cập nhật chứng minh thư thành công', + 'data' => [ + 'client' => new ClientResource($user->loadCount(['vps', 'hosting', 'domain'])), + ], + ]); + } + + /** + * Trả ảnh CCCD từ disk private. Route yêu cầu chữ ký (signed:relative) nên không cần + * Bearer — chữ ký do ClientResource cấp cho đúng chủ tài khoản, hết hạn sau 60 phút. + */ + public function cccdImage(int $customer, string $side) + { + $path = Customer::findOrFail($customer)->{$side === 'front' ? 'front_identity_number' : 'back_identity_number'}; + + abort_unless($path && str_starts_with($path, 'cccd/') && Storage::disk('local')->exists($path), 404); + + $mime = match (strtolower(pathinfo($path, PATHINFO_EXTENSION))) { + 'png' => 'image/png', + 'webp' => 'image/webp', + default => 'image/jpeg', + }; + + return Storage::disk('local')->response($path, "cccd-{$side}", [ + 'Content-Type' => $mime, + 'Content-Disposition' => "inline; filename=\"cccd-{$side}\"", + 'Cache-Control' => 'private, no-store', + ]); + } +} diff --git a/app/Http/Controllers/Api/Client/Service/ServiceController.php b/app/Http/Controllers/Api/Client/Service/ServiceController.php index 46c7810..ed0ba16 100644 --- a/app/Http/Controllers/Api/Client/Service/ServiceController.php +++ b/app/Http/Controllers/Api/Client/Service/ServiceController.php @@ -16,7 +16,6 @@ use App\Services\Api\ServiceClient\ServiceClientService; use App\Services\VirtualMachine\Rebuild\VmRebuildService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Throwable; diff --git a/app/Http/Controllers/Api/Client/Ticket/TicketController.php b/app/Http/Controllers/Api/Client/Ticket/TicketController.php index 3631ef5..16ff470 100644 --- a/app/Http/Controllers/Api/Client/Ticket/TicketController.php +++ b/app/Http/Controllers/Api/Client/Ticket/TicketController.php @@ -11,7 +11,6 @@ use App\Mail\Admin\TicketAdminNotification; use App\Models\Setting; use App\Models\Ticket; use App\Models\Notification; -use App\Models\TicketReply; use App\Events\TicketReplyCreated; use App\Repositories\Ticket\TicketRepositoryInterface; use App\Repositories\TicketReply\TicketReplyRepositoryInterface; diff --git a/app/Http/Controllers/Api/Service/Concerns/ManagesVmAccess.php b/app/Http/Controllers/Api/Service/Concerns/ManagesVmAccess.php new file mode 100644 index 0000000..c38e018 --- /dev/null +++ b/app/Http/Controllers/Api/Service/Concerns/ManagesVmAccess.php @@ -0,0 +1,98 @@ +get('product'); + if (!$productId) { + return response()->json(['error' => true, 'message' => 'Please choose product']); + } + + $vm = $this->resolveVmById($productId, $request); + if ($vm instanceof JsonResponse) { + return $vm; + } + + $result = $this->getConsoleTicket($serviceVCenter, $vm); + if ($result instanceof JsonResponse) { + return $result; + } + + $this->activityLogger->log( + event: 'console_access', + logName: 'VM', + description: "Customer {$request->user()->email} truy cập console VM: {$vm->name} (ID: {$vm->vmware_id})", + subject: $vm, + newValues: ['product_id' => $productId, 'vm_name' => $vm->name], + ); + + return response()->json([ + 'error' => false, + 'data' => $result['data'], + ]); + } + + /** + * POST /v1/system/vm-change-password + * + * Body: { product, current_password, new_password } + * Dispatches ChangeVmPasswordJob — returns immediately, runs async. + */ + public function changePassword(Request $request): JsonResponse + { + $validated = $request->validate([ + 'product' => ['required'], + 'current_password' => ['required', 'string'], + 'new_password' => ['required', 'string', 'min:8'], + ]); + + $vm = $this->resolveVmById($validated['product'], $request); + if ($vm instanceof JsonResponse) { + return $vm; + } + + // ponytail: don't release — lock expires after 5 min while the async job runs + $lock = $this->acquireVmLock($vm, 300); + if (!$lock) { + return $this->vmLocked(); + } + + // Detect OS family from VM's os_id record + $os = $vm->os_id ? Os::with('osGroup')->find($vm->os_id) : null; + $osGroupName = strtolower($os?->osGroup?->name ?? ''); + $osFamily = str_contains($osGroupName, 'windows') + ? VmProvisionService::OS_WINDOWS + : VmProvisionService::OS_LINUX; + + ChangeVmPasswordJob::dispatch( + $vm->id, + $vm->vmware_id, + $osFamily, + $validated['new_password'], + $validated['current_password'], + ); + + $this->activityLogger->log( + event: 'change_password', + logName: 'VM', + description: "Customer {$request->user()->email} yêu cầu đổi mật khẩu VM: {$vm->name}", + subject: $vm, + newValues: ['os_family' => $osFamily], + ); + + return response()->json([ + 'success' => true, + 'message' => 'Yêu cầu đổi mật khẩu đã được ghi nhận. Hệ thống sẽ xử lý trong vài phút.', + ]); + } +} diff --git a/app/Http/Controllers/Api/Service/Concerns/ManagesVmNetwork.php b/app/Http/Controllers/Api/Service/Concerns/ManagesVmNetwork.php new file mode 100644 index 0000000..ef4608a --- /dev/null +++ b/app/Http/Controllers/Api/Service/Concerns/ManagesVmNetwork.php @@ -0,0 +1,242 @@ +validate([ + 'server_name' => ['required', 'string', 'max:255'], + ]); + + $service = $this->findOwnedServiceByServerName($validated['server_name'], $request); + if ($service instanceof JsonResponse) { + return $service; + } + + $vm = $service->orderItem?->order?->vmWare?->first(); + if (!$vm) { + return response()->json(['success' => false, 'message' => 'Không tìm thấy VM cho dịch vụ này.'], 404); + } + + $networkId = $vm->ipAddress?->module_network_id; + if (!$networkId) { + return response()->json(['success' => false, 'message' => 'Không xác định được mạng của VM.'], 422); + } + + $ips = app(VmIpReservationService::class)->freeIpsInNetwork($networkId); + + return response()->json([ + 'success' => true, + 'data' => $ips->map(fn (IpPool $ip) => [ + 'ip_address' => $ip->ip_address, + 'gateway' => $ip->gateway, + ])->values(), + ]); + } + + /** + * POST /v1/system/vm-change-ip + * + * Body: + * server_name string Service.server_name (e.g. "xasdf.com.vnx") + * ip_address string New IP to assign — must be a free IP from the VM's network pool + */ + /** + * Dispatches ChangeIpVMJob — returns immediately, runs async. + */ + public function changeIp(Request $request, VmIpReservationService $ipReservation): JsonResponse + { + $validated = $request->validate([ + 'server_name' => ['required', 'string', 'max:255'], + 'ip_address' => ['required', 'ip'], + 'dns' => ['nullable', 'ip'], + 'guest_user' => ['required', 'string', 'max:255'], + 'guest_password' => ['required', 'string', 'max:255'], + ]); + + $service = $this->findOwnedServiceByServerName($validated['server_name'], $request); + if ($service instanceof JsonResponse) { + return $service; + } + + $vm = $service->orderItem?->order?->vmWare?->first(); + + if (!$vm) { + return response()->json(['success' => false, 'message' => 'Không tìm thấy VM cho dịch vụ này.'], 404); + } + + $networkId = $vm->ipAddress?->module_network_id; + if (!$networkId) { + return response()->json(['success' => false, 'message' => 'Không xác định được mạng của VM.'], 422); + } + + $reservation = $ipReservation->reserveInNetwork($networkId, $validated['ip_address'], $service->id); + if (!$reservation) { + return response()->json(['success' => false, 'message' => 'IP không thuộc pool khả dụng hoặc đã được sử dụng.'], 422); + } + + $oldIpPoolId = IpPool::where('module_network_id', $networkId) + ->where('service_id', $service->id) + ->where('status', 'assigned') + ->where('id', '!=', $reservation['ip_pool_id']) + ->value('id'); + + // Auto-detect OS type from the service's order item OS group + $osGroup = strtolower($service->orderItem?->os?->osGroup?->name ?? ''); + $osType = (str_contains($osGroup, 'linux') || str_contains($osGroup, 'ubuntu') || str_contains($osGroup, 'centos')) + ? 'linux' + : 'windows'; + + $dns = $validated['dns'] ?? '8.8.8.8'; + + // ponytail: don't release — lock expires after 5 min while the async job runs + $lock = $this->acquireVmLock($vm, 300); + if (!$lock) { + $ipReservation->releaseByPoolId($reservation['ip_pool_id']); + return $this->vmLocked(); + } + + ChangeIpVMJob::dispatch( + $vm->id, + $validated['ip_address'], + $reservation['gateway'], + $osType, + $dns, + $validated['guest_user'], + $validated['guest_password'], + $reservation['ip_pool_id'], + $oldIpPoolId, + ); + + $this->activityLogger->log( + event: 'change_ip', + logName: 'VM', + description: "Customer {$request->user()->email} yêu cầu đổi IP VM: {$vm->name} → {$validated['ip_address']}", + subject: $vm, + newValues: ['ip_address' => $validated['ip_address'], 'gateway' => $reservation['gateway'], 'os_type' => $osType], + ); + + return response()->json([ + 'success' => true, + 'message' => 'Yêu cầu đổi IP đã được ghi nhận. Hệ thống sẽ xử lý trong vài phút.', + ]); + } + + /** + * POST /v1/system/vm-change-port + * + * Body: + * product int|string ModuleVmware.id|vmware_id|service_uuid + * new_port int New RDP port (1024–65535) + */ + public function changePort(Request $request, ChangePortVMAction $action): JsonResponse + { + $validated = $request->validate([ + 'product' => ['required'], + 'new_port' => ['required', 'integer', 'min:1024', 'max:65535'], + ]); + + $vm = $this->resolveVmById($validated['product'], $request); + if ($vm instanceof JsonResponse) { + return $vm; + } + + $lock = $this->acquireVmLock($vm); + if (!$lock) { + return $this->vmLocked(); + } + + try { + $result = $action->execute($vm, (string) $validated['new_port']); + } finally { + $lock->release(); + } + + // changeRDPPortWindows returns array on success/failure or string on login fail + if ($result === 'Login_Again' || $result === 'Login_Fail') { + return response()->json([ + 'success' => false, + 'message' => 'Không thể đăng nhập vào vCenter.', + ], 400); + } + + if (is_array($result)) { + $ok = isset($result['success']) + ? (bool) $result['success'] + : !($result['error'] ?? true); + + if ($ok) { + $this->activityLogger->log( + event: 'change_port', + logName: 'VM', + description: "Customer {$request->user()->email} đổi cổng RDP VM: {$vm->name} → port {$validated['new_port']}", + subject: $vm, + newValues: ['new_port' => $validated['new_port']], + ); + } + + return response()->json([ + 'success' => $ok, + 'message' => $ok ? 'Đổi cổng RDP thành công.' : ($result['message'] ?? 'Đổi cổng thất bại.'), + 'data' => $result, + ], $ok ? 200 : 400); + } + + \Illuminate\Support\Facades\Log::error('changePort: unexpected vCenter response', [ + 'vm_id' => $vm->id, + 'result' => $result, + ]); + + return response()->json([ + 'success' => false, + 'message' => 'Phản hồi không hợp lệ từ vCenter.', + ], 400); + } + + /** + * Look up a Service by server_name, verify ownership and active status. + * Returns JsonResponse on error, Service on success. + */ + private function findOwnedServiceByServerName(string $serverName, Request $request): Service|JsonResponse + { + $customer = $request->user(); + + // VM chain: Service → orderItem → order → vmWare (same as ServiceClientDetailResource) + $service = Service::with([ + 'orderItem.order.vmWare.vCenter', + 'orderItem.order.vmWare.ipAddress.host.datacenter.vCenter', + 'orderItem.os.osGroup', + ]) + ->where('server_name', $serverName) + ->where('customer_id', $customer->id) + ->first(); + + if (!$service) { + return response()->json(['success' => false, 'message' => 'Không tìm thấy dịch vụ.'], 404); + } + + if ($service->status !== 'active') { + return response()->json(['success' => false, 'message' => 'Dịch vụ không ở trạng thái hoạt động, không thể thao tác.'], 403); + } + + return $service; + } +} diff --git a/app/Http/Controllers/Api/Service/Concerns/ManagesVmPower.php b/app/Http/Controllers/Api/Service/Concerns/ManagesVmPower.php new file mode 100644 index 0000000..957ed26 --- /dev/null +++ b/app/Http/Controllers/Api/Service/Concerns/ManagesVmPower.php @@ -0,0 +1,173 @@ + } + */ + public function powerOn(Request $request, PowerOnVMAction $action): JsonResponse + { + $productId = $request->input('product'); + if (!$productId) { + return response()->json(['success' => false, 'message' => 'Thiếu product id.'], 422); + } + + $vm = $this->resolveVmById($productId, $request); + if ($vm instanceof JsonResponse) { + return $vm; + } + + $lock = $this->acquireVmLock($vm); + if (!$lock) { + return $this->vmLocked(); + } + + try { + $result = $action->execute($vm); + } finally { + $lock->release(); + } + + $this->activityLogger->log( + event: 'power_on', + logName: 'VM', + description: "Customer {$request->user()->email} bật VM: {$vm->name} (ID: {$vm->vmware_id})", + subject: $vm, + ); + + return $this->vmResponse($result, 'Đã gửi lệnh bật VM.'); + } + + /** + * POST /v1/system/vm-power-off + * + * Body: { "product": } + */ + public function powerOff(Request $request, PowerOffVMAction $action): JsonResponse + { + $productId = $request->input('product'); + if (!$productId) { + return response()->json(['success' => false, 'message' => 'Thiếu product id.'], 422); + } + + $vm = $this->resolveVmById($productId, $request); + if ($vm instanceof JsonResponse) { + return $vm; + } + + $lock = $this->acquireVmLock($vm); + if (!$lock) { + return $this->vmLocked(); + } + + try { + $result = $action->execute($vm); + } finally { + $lock->release(); + } + + $this->activityLogger->log( + event: 'power_off', + logName: 'VM', + description: "Customer {$request->user()->email} tắt VM: {$vm->name} (ID: {$vm->vmware_id})", + subject: $vm, + ); + + return $this->vmResponse($result, 'Đã gửi lệnh tắt VM.'); + } + + public function restart(Request $request, vCenter $serviceVCenter): JsonResponse + { + $productId = $request->input('product'); + if (!$productId) { + return response()->json(['error' => true, 'message' => 'Thiếu product id'], 422); + } + + $vm = $this->resolveVmById($productId, $request); + if ($vm instanceof JsonResponse) { + return $vm; + } + + $lock = $this->acquireVmLock($vm); + if (!$lock) { + return $this->vmLocked(); + } + + try { + $result = $serviceVCenter->restartVM($vm, $vm->vmware_id); + } finally { + $lock->release(); + } + + if (is_string($result) || ($result['error'] ?? true)) { + return response()->json([ + 'error' => true, + 'message' => is_string($result) ? $result : ($result['data'] ?? 'Không thể restart VM'), + ], 400); + } + + $this->activityLogger->log( + event: 'restart', + logName: 'VM', + description: "Customer {$request->user()->email} restart VM: {$vm->name} (ID: {$vm->vmware_id})", + subject: $vm, + ); + + return response()->json([ + 'error' => false, + 'message' => 'Đang restart máy ảo vui lòng chờ!', + 'data' => $result['data'] ?? 'success', + ]); + } + + /** + * GET /v1/system/vm-power-state?product={id|uuid|vmware_id} + * + * Calls vSphere REST API directly: GET /rest/vcenter/vm/{vmware_id}/power + * Returns: { success: true, data: { state: "POWERED_ON"|"POWERED_OFF"|"SUSPENDED", clean_power_off: bool } } + */ + public function powerState(Request $request, vCenter $serviceVCenter): JsonResponse + { + $productId = $request->input('product'); + if (!$productId) { + return response()->json(['success' => false, 'message' => 'Thiếu product id.'], 422); + } + + $vm = $this->resolveVmById($productId, $request, requireActiveService: false); + if ($vm instanceof JsonResponse) { + return $vm; + } + + $result = $serviceVCenter->getVmPowerState($vm, $vm->vmware_id); + + if ($result === 'Login_Again') { + return response()->json(['success' => false, 'message' => 'Không thể đăng nhập vào vCenter.'], 400); + } + + if (is_array($result) && ($result['error'] ?? true)) { + return response()->json([ + 'success' => false, + 'message' => $result['data'] ?? 'Không thể lấy trạng thái VM.', + ], 400); + } + + return response()->json([ + 'success' => true, + 'data' => [ + 'state' => $result['data']['state'] ?? null, + 'clean_power_off' => $result['data']['clean_power_off'] ?? null, + ], + ]); + } +} diff --git a/app/Http/Controllers/Api/Service/OrderControllerRefactor.php b/app/Http/Controllers/Api/Service/OrderControllerRefactor.php index ad7b57e..1435b2f 100644 --- a/app/Http/Controllers/Api/Service/OrderControllerRefactor.php +++ b/app/Http/Controllers/Api/Service/OrderControllerRefactor.php @@ -13,9 +13,8 @@ use App\Http\Resources\Api\Service\Order\InvoiceSummaryResource; use App\Http\Resources\Api\Service\Order\OrderDetailResource; use App\Http\Resources\Api\Service\Order\OrderItemResource; use App\Http\Resources\Api\Service\Order\OrderSummaryResource; -use App\Http\Resources\Api\Service\Order\ServiceDetailResource; use App\Http\Resources\Api\Service\Order\ServiceSummaryResource; -use App\Repositories\Api\Order\OrderRepository; +use App\Repositories\Api\Order\OrderRepositoryInterface; use App\Services\ActivityLogger; use App\Services\Api\Order\OrderQueryService; use App\Services\Api\Order\OrderService; @@ -29,7 +28,7 @@ class OrderControllerRefactor extends Controller { public function __construct( private readonly OrderService $orderService, - private readonly OrderRepository $orderRepository, + private readonly OrderRepositoryInterface $orderRepository, private readonly OrderQueryService $orderQueryService, private readonly ActivityLogger $activityLogger, private readonly InvoicePaymentService $invoicePaymentService, @@ -305,17 +304,6 @@ class OrderControllerRefactor extends Controller return response()->json(['success' => true, 'data' => ServiceSummaryResource::collection($services)]); } - public function getClientServiceDetail(Request $request, string $uuid): JsonResponse - { - $service = $this->orderQueryService->findCustomerService((int) $request->user()->id, $uuid); - - if (!$service) { - return response()->json(['success' => false, 'message' => 'Không tìm thấy dịch vụ.'], 404); - } - - return response()->json(['success' => true, 'data' => new ServiceDetailResource($service)]); - } - public function updateClientServiceAutoRenew(Request $request, int $id): JsonResponse { $validated = $request->validate(['auto_renew' => ['required', 'boolean']]); diff --git a/app/Http/Controllers/Api/Service/ProductCategoryController.php b/app/Http/Controllers/Api/Service/ProductCategoryController.php index 1d6741f..7b02af6 100644 --- a/app/Http/Controllers/Api/Service/ProductCategoryController.php +++ b/app/Http/Controllers/Api/Service/ProductCategoryController.php @@ -5,7 +5,6 @@ namespace App\Http\Controllers\Api\Service; use App\Enums\Product\ProductTypeEnums; use App\Http\Controllers\Controller; use App\Http\Resources\Api\Service\ProductCategoryResource; -use App\Http\Resources\Api\Service\ServiceResource; use App\Models\Product; use App\Models\ProductCategories; use Illuminate\Http\Request; diff --git a/app/Http/Controllers/Api/Service/ServiceController.php b/app/Http/Controllers/Api/Service/ServiceController.php index ec32b1f..86ee71d 100644 --- a/app/Http/Controllers/Api/Service/ServiceController.php +++ b/app/Http/Controllers/Api/Service/ServiceController.php @@ -5,22 +5,11 @@ namespace App\Http\Controllers\Api\Service; use App\Http\Controllers\Controller; use App\Http\Resources\Api\Service\ServiceListResource; use App\Http\Resources\Api\Service\ServiceResource; -use App\Models\Addon; -use App\Models\Cart; -use App\Models\CartAddon; -use App\Models\Discount; -use App\Models\Order; -use App\Models\OrderAddon; -use App\Models\OrderItem; use App\Models\Product; -use App\Models\ProductPricings; use App\Models\Service; use App\Repositories\Api\Service\ServiceRepository; use App\Services\Api\Service\ServiceService; use Illuminate\Http\Request; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Validator; /** * @OA\Tag( diff --git a/app/Http/Controllers/Api/Service/VmController.php b/app/Http/Controllers/Api/Service/VmController.php index 0974090..fc7aeba 100644 --- a/app/Http/Controllers/Api/Service/VmController.php +++ b/app/Http/Controllers/Api/Service/VmController.php @@ -2,533 +2,57 @@ namespace App\Http\Controllers\Api\Service; -use App\Actions\VM\ChangePortVMAction; -use App\Actions\VM\PowerOffVMAction; -use App\Actions\VM\PowerOnVMAction; use App\Http\Controllers\Api\Service\Concerns\ResolvesVm; use App\Http\Controllers\Controller; use App\Http\Services\vCenter; -use App\Jobs\VirtualMachine\ChangeIpVMJob; -use App\Jobs\VirtualMachine\ChangeVmPasswordJob; -use App\Models\IpPool; use App\Models\ModuleVmware; -use App\Models\Os; -use App\Models\Service; use App\Services\ActivityLogger; -use App\Services\VirtualMachine\VmIpReservationService; -use App\Services\VmProvisionService; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; +use App\Http\Controllers\Api\Service\Concerns\ManagesVmPower; +use App\Http\Controllers\Api\Service\Concerns\ManagesVmNetwork; +use App\Http\Controllers\Api\Service\Concerns\ManagesVmAccess; class VmController extends Controller { use ResolvesVm; + use ManagesVmPower; + use ManagesVmNetwork; + use ManagesVmAccess; public function __construct(protected ActivityLogger $activityLogger) {} // ------------------------------------------------------------------------- // Power ON // ------------------------------------------------------------------------- - /** - * POST /v1/system/vm-power-on - * - * Body: { "product": } - */ - public function powerOn(Request $request, PowerOnVMAction $action): JsonResponse - { - $productId = $request->input('product'); - if (!$productId) { - return response()->json(['success' => false, 'message' => 'Thiếu product id.'], 422); - } - - $vm = $this->resolveVmById($productId, $request); - if ($vm instanceof JsonResponse) { - return $vm; - } - - $lock = $this->acquireVmLock($vm); - if (!$lock) { - return $this->vmLocked(); - } - - try { - $result = $action->execute($vm); - } finally { - $lock->release(); - } - - $this->activityLogger->log( - event: 'power_on', - logName: 'VM', - description: "Customer {$request->user()->email} bật VM: {$vm->name} (ID: {$vm->vmware_id})", - subject: $vm, - ); - - return $this->vmResponse($result, 'Đã gửi lệnh bật VM.'); - } - // ------------------------------------------------------------------------- // Power OFF // ------------------------------------------------------------------------- - /** - * POST /v1/system/vm-power-off - * - * Body: { "product": } - */ - public function powerOff(Request $request, PowerOffVMAction $action): JsonResponse - { - $productId = $request->input('product'); - if (!$productId) { - return response()->json(['success' => false, 'message' => 'Thiếu product id.'], 422); - } - - $vm = $this->resolveVmById($productId, $request); - if ($vm instanceof JsonResponse) { - return $vm; - } - - $lock = $this->acquireVmLock($vm); - if (!$lock) { - return $this->vmLocked(); - } - - try { - $result = $action->execute($vm); - } finally { - $lock->release(); - } - - $this->activityLogger->log( - event: 'power_off', - logName: 'VM', - description: "Customer {$request->user()->email} tắt VM: {$vm->name} (ID: {$vm->vmware_id})", - subject: $vm, - ); - - return $this->vmResponse($result, 'Đã gửi lệnh tắt VM.'); - } - // ------------------------------------------------------------------------- // Change IP // ------------------------------------------------------------------------- - /** - * GET /v1/system/vm-ip-options - * - * Query: server_name string Service.server_name - * Lists free IP pool entries in the same network as the VM's current IP. - */ - public function ipOptions(Request $request): JsonResponse - { - $validated = $request->validate([ - 'server_name' => ['required', 'string', 'max:255'], - ]); - - $service = $this->findOwnedServiceByServerName($validated['server_name'], $request); - if ($service instanceof JsonResponse) { - return $service; - } - - $vm = $service->orderItem?->order?->vmWare?->first(); - if (!$vm) { - return response()->json(['success' => false, 'message' => 'Không tìm thấy VM cho dịch vụ này.'], 404); - } - - $networkId = $vm->ipAddress?->module_network_id; - if (!$networkId) { - return response()->json(['success' => false, 'message' => 'Không xác định được mạng của VM.'], 422); - } - - $ips = app(VmIpReservationService::class)->freeIpsInNetwork($networkId); - - return response()->json([ - 'success' => true, - 'data' => $ips->map(fn (IpPool $ip) => [ - 'ip_address' => $ip->ip_address, - 'gateway' => $ip->gateway, - ])->values(), - ]); - } - - /** - * POST /v1/system/vm-change-ip - * - * Body: - * server_name string Service.server_name (e.g. "xasdf.com.vnx") - * ip_address string New IP to assign — must be a free IP from the VM's network pool - */ - /** - * Dispatches ChangeIpVMJob — returns immediately, runs async. - */ - public function changeIp(Request $request, VmIpReservationService $ipReservation): JsonResponse - { - $validated = $request->validate([ - 'server_name' => ['required', 'string', 'max:255'], - 'ip_address' => ['required', 'ip'], - 'dns' => ['nullable', 'ip'], - 'guest_user' => ['required', 'string', 'max:255'], - 'guest_password' => ['required', 'string', 'max:255'], - ]); - - $service = $this->findOwnedServiceByServerName($validated['server_name'], $request); - if ($service instanceof JsonResponse) { - return $service; - } - - $vm = $service->orderItem?->order?->vmWare?->first(); - - if (!$vm) { - return response()->json(['success' => false, 'message' => 'Không tìm thấy VM cho dịch vụ này.'], 404); - } - - $networkId = $vm->ipAddress?->module_network_id; - if (!$networkId) { - return response()->json(['success' => false, 'message' => 'Không xác định được mạng của VM.'], 422); - } - - $reservation = $ipReservation->reserveInNetwork($networkId, $validated['ip_address'], $service->id); - if (!$reservation) { - return response()->json(['success' => false, 'message' => 'IP không thuộc pool khả dụng hoặc đã được sử dụng.'], 422); - } - - $oldIpPoolId = IpPool::where('module_network_id', $networkId) - ->where('service_id', $service->id) - ->where('status', 'assigned') - ->where('id', '!=', $reservation['ip_pool_id']) - ->value('id'); - - // Auto-detect OS type from the service's order item OS group - $osGroup = strtolower($service->orderItem?->os?->osGroup?->name ?? ''); - $osType = (str_contains($osGroup, 'linux') || str_contains($osGroup, 'ubuntu') || str_contains($osGroup, 'centos')) - ? 'linux' - : 'windows'; - - $dns = $validated['dns'] ?? '8.8.8.8'; - - // ponytail: don't release — lock expires after 5 min while the async job runs - $lock = $this->acquireVmLock($vm, 300); - if (!$lock) { - $ipReservation->releaseByPoolId($reservation['ip_pool_id']); - return $this->vmLocked(); - } - - ChangeIpVMJob::dispatch( - $vm->id, - $validated['ip_address'], - $reservation['gateway'], - $osType, - $dns, - $validated['guest_user'], - $validated['guest_password'], - $reservation['ip_pool_id'], - $oldIpPoolId, - ); - - $this->activityLogger->log( - event: 'change_ip', - logName: 'VM', - description: "Customer {$request->user()->email} yêu cầu đổi IP VM: {$vm->name} → {$validated['ip_address']}", - subject: $vm, - newValues: ['ip_address' => $validated['ip_address'], 'gateway' => $reservation['gateway'], 'os_type' => $osType], - ); - - return response()->json([ - 'success' => true, - 'message' => 'Yêu cầu đổi IP đã được ghi nhận. Hệ thống sẽ xử lý trong vài phút.', - ]); - } - - /** - * Look up a Service by server_name, verify ownership and active status. - * Returns JsonResponse on error, Service on success. - */ - private function findOwnedServiceByServerName(string $serverName, Request $request): Service|JsonResponse - { - $customer = $request->user(); - - // VM chain: Service → orderItem → order → vmWare (same as ServiceClientDetailResource) - $service = Service::with([ - 'orderItem.order.vmWare.vCenter', - 'orderItem.order.vmWare.ipAddress.host.datacenter.vCenter', - 'orderItem.os.osGroup', - ]) - ->where('server_name', $serverName) - ->where('customer_id', $customer->id) - ->first(); - - if (!$service) { - return response()->json(['success' => false, 'message' => 'Không tìm thấy dịch vụ.'], 404); - } - - if ($service->status !== 'active') { - return response()->json(['success' => false, 'message' => 'Dịch vụ không ở trạng thái hoạt động, không thể thao tác.'], 403); - } - - return $service; - } - // ------------------------------------------------------------------------- // Change Port (RDP) // ------------------------------------------------------------------------- - /** - * POST /v1/system/vm-change-port - * - * Body: - * product int|string ModuleVmware.id|vmware_id|service_uuid - * new_port int New RDP port (1024–65535) - */ - public function changePort(Request $request, ChangePortVMAction $action): JsonResponse - { - $validated = $request->validate([ - 'product' => ['required'], - 'new_port' => ['required', 'integer', 'min:1024', 'max:65535'], - ]); - - $vm = $this->resolveVmById($validated['product'], $request); - if ($vm instanceof JsonResponse) { - return $vm; - } - - $lock = $this->acquireVmLock($vm); - if (!$lock) { - return $this->vmLocked(); - } - - try { - $result = $action->execute($vm, (string) $validated['new_port']); - } finally { - $lock->release(); - } - - // changeRDPPortWindows returns array on success/failure or string on login fail - if ($result === 'Login_Again' || $result === 'Login_Fail') { - return response()->json([ - 'success' => false, - 'message' => 'Không thể đăng nhập vào vCenter.', - ], 400); - } - - if (is_array($result)) { - $ok = isset($result['success']) - ? (bool) $result['success'] - : !($result['error'] ?? true); - - if ($ok) { - $this->activityLogger->log( - event: 'change_port', - logName: 'VM', - description: "Customer {$request->user()->email} đổi cổng RDP VM: {$vm->name} → port {$validated['new_port']}", - subject: $vm, - newValues: ['new_port' => $validated['new_port']], - ); - } - - return response()->json([ - 'success' => $ok, - 'message' => $ok ? 'Đổi cổng RDP thành công.' : ($result['message'] ?? 'Đổi cổng thất bại.'), - 'data' => $result, - ], $ok ? 200 : 400); - } - - \Illuminate\Support\Facades\Log::error('changePort: unexpected vCenter response', [ - 'vm_id' => $vm->id, - 'result' => $result, - ]); - - return response()->json([ - 'success' => false, - 'message' => 'Phản hồi không hợp lệ từ vCenter.', - ], 400); - } - // ------------------------------------------------------------------------- // Console Ticket // ------------------------------------------------------------------------- - public function consoleTicket(Request $request, vCenter $serviceVCenter): JsonResponse - { - $productId = $request->get('product'); - if (!$productId) { - return response()->json(['error' => true, 'message' => 'Please choose product']); - } - - $vm = $this->resolveVmById($productId, $request); - if ($vm instanceof JsonResponse) { - return $vm; - } - - $result = $this->getConsoleTicket($serviceVCenter, $vm); - if ($result instanceof JsonResponse) { - return $result; - } - - $this->activityLogger->log( - event: 'console_access', - logName: 'VM', - description: "Customer {$request->user()->email} truy cập console VM: {$vm->name} (ID: {$vm->vmware_id})", - subject: $vm, - newValues: ['product_id' => $productId, 'vm_name' => $vm->name], - ); - - return response()->json([ - 'error' => false, - 'data' => $result['data'], - ]); - } - // ------------------------------------------------------------------------- // Restart VM // ------------------------------------------------------------------------- - public function restart(Request $request, vCenter $serviceVCenter): JsonResponse - { - $productId = $request->input('product'); - if (!$productId) { - return response()->json(['error' => true, 'message' => 'Thiếu product id'], 422); - } - - $vm = $this->resolveVmById($productId, $request); - if ($vm instanceof JsonResponse) { - return $vm; - } - - $lock = $this->acquireVmLock($vm); - if (!$lock) { - return $this->vmLocked(); - } - - try { - $result = $serviceVCenter->restartVM($vm, $vm->vmware_id); - } finally { - $lock->release(); - } - - if (is_string($result) || ($result['error'] ?? true)) { - return response()->json([ - 'error' => true, - 'message' => is_string($result) ? $result : ($result['data'] ?? 'Không thể restart VM'), - ], 400); - } - - $this->activityLogger->log( - event: 'restart', - logName: 'VM', - description: "Customer {$request->user()->email} restart VM: {$vm->name} (ID: {$vm->vmware_id})", - subject: $vm, - ); - - return response()->json([ - 'error' => false, - 'message' => 'Đang restart máy ảo vui lòng chờ!', - 'data' => $result['data'] ?? 'success', - ]); - } - // ------------------------------------------------------------------------- // Change Password // ------------------------------------------------------------------------- - /** - * POST /v1/system/vm-change-password - * - * Body: { product, current_password, new_password } - * Dispatches ChangeVmPasswordJob — returns immediately, runs async. - */ - public function changePassword(Request $request): JsonResponse - { - $validated = $request->validate([ - 'product' => ['required'], - 'current_password' => ['required', 'string'], - 'new_password' => ['required', 'string', 'min:8'], - ]); - - $vm = $this->resolveVmById($validated['product'], $request); - if ($vm instanceof JsonResponse) { - return $vm; - } - - // ponytail: don't release — lock expires after 5 min while the async job runs - $lock = $this->acquireVmLock($vm, 300); - if (!$lock) { - return $this->vmLocked(); - } - - // Detect OS family from VM's os_id record - $os = $vm->os_id ? Os::with('osGroup')->find($vm->os_id) : null; - $osGroupName = strtolower($os?->osGroup?->name ?? ''); - $osFamily = str_contains($osGroupName, 'windows') - ? VmProvisionService::OS_WINDOWS - : VmProvisionService::OS_LINUX; - - ChangeVmPasswordJob::dispatch( - $vm->id, - $vm->vmware_id, - $osFamily, - $validated['new_password'], - $validated['current_password'], - ); - - $this->activityLogger->log( - event: 'change_password', - logName: 'VM', - description: "Customer {$request->user()->email} yêu cầu đổi mật khẩu VM: {$vm->name}", - subject: $vm, - newValues: ['os_family' => $osFamily], - ); - - return response()->json([ - 'success' => true, - 'message' => 'Yêu cầu đổi mật khẩu đã được ghi nhận. Hệ thống sẽ xử lý trong vài phút.', - ]); - } - // ------------------------------------------------------------------------- // Power State // ------------------------------------------------------------------------- - /** - * GET /v1/system/vm-power-state?product={id|uuid|vmware_id} - * - * Calls vSphere REST API directly: GET /rest/vcenter/vm/{vmware_id}/power - * Returns: { success: true, data: { state: "POWERED_ON"|"POWERED_OFF"|"SUSPENDED", clean_power_off: bool } } - */ - public function powerState(Request $request, vCenter $serviceVCenter): JsonResponse - { - $productId = $request->input('product'); - if (!$productId) { - return response()->json(['success' => false, 'message' => 'Thiếu product id.'], 422); - } - - $vm = $this->resolveVmById($productId, $request, requireActiveService: false); - if ($vm instanceof JsonResponse) { - return $vm; - } - - $result = $serviceVCenter->getVmPowerState($vm, $vm->vmware_id); - - if ($result === 'Login_Again') { - return response()->json(['success' => false, 'message' => 'Không thể đăng nhập vào vCenter.'], 400); - } - - if (is_array($result) && ($result['error'] ?? true)) { - return response()->json([ - 'success' => false, - 'message' => $result['data'] ?? 'Không thể lấy trạng thái VM.', - ], 400); - } - - return response()->json([ - 'success' => true, - 'data' => [ - 'state' => $result['data']['state'] ?? null, - 'clean_power_off' => $result['data']['clean_power_off'] ?? null, - ], - ]); - } - // ------------------------------------------------------------------------- // Shared helpers // ------------------------------------------------------------------------- diff --git a/app/Http/Controllers/Api/System/CompressionTestController.php b/app/Http/Controllers/Api/System/CompressionTestController.php deleted file mode 100644 index 9d2b72a..0000000 --- a/app/Http/Controllers/Api/System/CompressionTestController.php +++ /dev/null @@ -1,89 +0,0 @@ - 'Compression test response', - 'timestamp' => now()->toISOString(), - 'client_capabilities' => CompressionService::getClientCapabilities($request), - 'large_data' => $this->generateTestData(), - 'meta' => [ - 'total_items' => 1000, - 'page' => 1, - 'per_page' => 50, - 'compression_enabled' => true, - ] - ]; - - $content = json_encode($testData); - $compressionInfo = CompressionService::getCompressionInfo($content); - - return response()->json([ - 'success' => true, - 'data' => $testData, - 'compression_analysis' => $compressionInfo, - 'response_size' => CompressionService::formatBytes(strlen($content)) - ]); - } - - /** - * Get compression statistics - */ - public function stats(Request $request): JsonResponse - { - return response()->json([ - 'client_capabilities' => CompressionService::getClientCapabilities($request), - 'server_capabilities' => [ - 'gzip' => function_exists('gzencode'), - 'deflate' => function_exists('gzdeflate'), - 'brotli' => function_exists('brotli_compress'), - ], - 'middleware_enabled' => true, - 'min_compression_size' => '1KB', - 'compression_level' => 6, - ]); - } - - /** - * Generate large test data for compression testing - */ - private function generateTestData(): array - { - $data = []; - - // Generate repeated patterns that compress well - for ($i = 1; $i <= 100; $i++) { - $data[] = [ - 'id' => $i, - 'name' => "Test Item Number {$i}", - 'description' => 'This is a test description that contains repetitive text to demonstrate compression effectiveness. Lorem ipsum dolor sit amet, consectetur adipiscing elit.', - 'status' => $i % 2 === 0 ? 'active' : 'inactive', - 'created_at' => now()->subDays($i)->toISOString(), - 'metadata' => [ - 'category' => 'test-category-' . ($i % 5), - 'tags' => ['test', 'compression', 'api', 'laravel'], - 'properties' => [ - 'property_1' => 'Repeated property value', - 'property_2' => 'Another repeated property value', - 'property_3' => 'Yet another repeated property value', - ] - ] - ]; - } - - return $data; - } -} \ No newline at end of file diff --git a/app/Http/Controllers/Api/Transaction/TransactionController.php b/app/Http/Controllers/Api/Transaction/TransactionController.php index 184ed25..b038b1f 100644 --- a/app/Http/Controllers/Api/Transaction/TransactionController.php +++ b/app/Http/Controllers/Api/Transaction/TransactionController.php @@ -215,28 +215,6 @@ class TransactionController extends Controller ]); } - public function getDetailTransaction(Request $request, $slug) - { - $user = $request->user(); - - $transaction = Transaction::where('trans', $slug) - ->where('customer_id', $user->id) - ->first(); - - if (!$transaction) { - return response()->json([ - 'error' => true, - 'message' => 'Giao dịch không tồn tại', - ], 404); - } - - return response()->json([ - 'error' => false, - 'message' => 'Lấy chi tiết giao dịch thành công', - 'data' => $transaction, - ]); - } - public function verifyPayment(Request $request) { $transRef = $request->query('vnp_TxnRef'); diff --git a/app/Http/Controllers/Api/VpsActionController.php b/app/Http/Controllers/Api/VpsActionController.php deleted file mode 100644 index b4b50dd..0000000 --- a/app/Http/Controllers/Api/VpsActionController.php +++ /dev/null @@ -1,71 +0,0 @@ -vCenterService = $vCenterService; - } - - public function rebuildVps(Request $request) - { - set_time_limit(300); // Increase timeout to 5 minutes for slow vCenter connection - $request->validate([ - 'vm_name' => 'required|string', - 'vcenter_id' => 'required|integer' // We need to know which vCenter to connect to - ]); - - $vmName = $request->input('vm_name'); - $vcenterId = $request->input('vcenter_id'); - - // Retrieve vCenter details from DB to authenticate - $vcenterModel = \App\Models\vCenter::find($vcenterId); - - if (!$vcenterModel) { - return response()->json([ - 'error' => true, - 'message' => 'vCenter ID not found' - ], 404); - } - - // Authenticate with vCenter - $token = $this->vCenterService->loginServer($vcenterModel); - if (!$token) { - return response()->json([ - 'error' => true, - 'message' => 'Failed to login to vCenter' - ], 401); - } - - Log::info("API Rebuild VPS requested for VM: {$vmName} on vCenter: {$vcenterModel->host}"); - - // Call the service function - $result = $this->vCenterService->reBuildVPS($vmName); - - if (is_array($result) && isset($result['error']) && $result['error']) { - return response()->json($result, 500); - } - - if ($result === "Login_Again") { // Double check handled in service but good to catch - return response()->json([ - 'error' => true, - 'message' => 'Session expired, login again' - ], 401); - } - - return response()->json([ - 'error' => false, - 'message' => 'Rebuild command executed', - 'data' => $result - ]); - } -} diff --git a/app/Http/Controllers/admin/AddonController.php b/app/Http/Controllers/admin/AddonController.php deleted file mode 100644 index 2c69241..0000000 --- a/app/Http/Controllers/admin/AddonController.php +++ /dev/null @@ -1,160 +0,0 @@ -addonRepository->filter( - [ - 'page' => $request->get('page', 1), - ] - ); - $title = 'List Add-On'; - - return view('administrator.pages.addon.index', compact('addOns', 'title')); - } - - public function create() - { - $title = 'Add new addon'; - return view('administrator.pages.addon.add', compact('title')); - } - - public function store(AddOnRequest $request) - { - $request->validated(); - - DB::beginTransaction(); - try { - $this->addonRepository->create($request->all()); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return redirect()->route('addon.index')->with('success', 'Added successfully'); - } - public function edit($id = 0) - { - - $addon = $this->addonRepository->find($id); - if (!$addon) { - return back()->withErrors('Invalid request'); - } - - $title = 'Edit Addon'; - - return view('administrator.pages.addon.edit', compact('title', 'addon')); - } - public function update(int $id = 0, AddOnRequest $request) - { - $addon = $this->addonRepository->find($id); - if (!$addon) { - return back()->withErrors('Invalid request'); - } - $request->validated(); - - DB::beginTransaction(); - try { - $this->addonRepository->update($id, $request->all()); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return redirect()->route('addon.index')->with('success', 'Updated successfully'); - } - public function destroy($id) - { - $addon = $this->addonRepository->find($id); - if (!$addon) { - return back()->withErrors('Invalid request'); - } - DB::beginTransaction(); - try { - $this->addonRepository->delete($id); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return redirect()->route('addon.index')->with('success', 'Deleted successfully'); - } - - - public function datatable(Request $request) - { - $addons = Addon::orderBy('sort_order')->get(); - - $grouped = $addons->groupBy(function ($item) { - return $item->type instanceof AddonType - ? $item->type->value - : $item->type; - }); - - $types = AddonType::values(); - $data = []; - - foreach ($types as $type) { - $typeEnum = AddonType::tryFrom($type); - $typeLabel = $typeEnum ? $typeEnum->label() : $type; - $typeAddons = $grouped->get($type, collect()); - - $childrenHtml = view('administrator.pages.addon.datatable.children', [ - 'children' => $typeAddons - ])->render(); - - $data[] = [ - 'id' => $type, - 'type' => $type, - 'name' => $typeLabel, - 'code' => $type, - 'children' => $typeAddons->toArray(), - 'children_html' => $childrenHtml, - 'status' => true, - ]; - } - - return DataTables::of(collect($data)) - ->addColumn('details_control', function ($d) { - $hasChildren = !empty($d['children']) && count($d['children']) > 0; - if ($hasChildren) { - return ''; - } - return ''; - }) - ->addColumn('name', function ($d) { - return $d['name']; - }) - ->addColumn('slug', function ($d) { - return $d['code']; - }) - ->addColumn('is_active', function ($d) { - return 'Active'; - }) - ->addColumn('action', function ($d) { - return ''; - }) - ->rawColumns(['details_control', 'is_active', 'children_html']) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/CouponController.php b/app/Http/Controllers/admin/CouponController.php deleted file mode 100644 index a1dd0b1..0000000 --- a/app/Http/Controllers/admin/CouponController.php +++ /dev/null @@ -1,139 +0,0 @@ -discountRepository->filter($request->merge(['page' => $request->query('page') ?? 1])->all()); - - return view("administrator.pages.coupon.index", compact('title', 'coupons')); - } - public function create() - { - $title = "Create new Coupon"; - - return view("administrator.pages.coupon.add", compact('title')); - } - public function store(CouponRequest $request) - { - - $request->validated(); - - $isExits = $this->discountRepository->findBy('code', $request->code); - - if ($isExits) { - return back()->withInput()->withErrors('Coupon code already exists'); - } - if (Carbon::createFromFormat('d/m/Y', $request->start)->timestamp > Carbon::createFromFormat('d/m/Y', $request->end)->timestamp) { - return back()->withInput()->withErrors('Coupon code expiration date is invalid'); - } - - if (Carbon::createFromFormat('d/m/Y', $request->end)->timestamp < Carbon::now()->timestamp) { - - return back()->withInput()->withErrors('Coupon code expiration date is invalid'); - } - - DB::beginTransaction(); - try { - $data = $request->all(); - $data['start'] = Carbon::createFromFormat('d/m/Y', $request->start)->timestamp; - $data['end'] = Carbon::createFromFormat('d/m/Y', $request->end)->timestamp; - $this->discountRepository->create($data); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withInput()->withErrors($error->getMessage()); - } - - return redirect()->route('coupon.index')->with('success', 'Added successfully'); - } - public function edit(int $id = 0) - { - $coupon = $this->discountRepository->find($id); - - if (! $coupon) { - return back()->withErrors("Invalid request"); - } - - $title = "Edit coupon #" . $coupon->code; - - return view("administrator.pages.coupon.edit", compact('title', 'coupon')); - } - - public function update(int $id = 0, CouponRequest $request) - { - $coupon = $this->discountRepository->find($id); - - if (! $coupon) { - return back()->withErrors("Invalid request"); - } - $request->validated(); - if ($request->code !== $coupon->code) { - - $isExits = $this->discountRepository->findBy('code', $request->code); - if ($isExits) { - return back()->withInput()->withErrors('Coupon code already exists'); - } - } - - if (Carbon::createFromFormat('d/m/Y', $request->start)->timestamp > Carbon::createFromFormat('d/m/Y', $request->end)->timestamp) { - return back()->withInput()->withErrors('Coupon code expiration date is invalid'); - } - - if (Carbon::createFromFormat('d/m/Y', $request->end)->timestamp < Carbon::now()->timestamp) { - - return back()->withInput()->withErrors('Coupon code expiration date is invalid'); - } - DB::beginTransaction(); - try { - $data = $request->all(); - $data['start'] = Carbon::createFromFormat('d/m/Y', $request->start)->timestamp; - $data['end'] = Carbon::createFromFormat('d/m/Y', $request->end)->timestamp; - $this->discountRepository->update($id,$data); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withInput()->withErrors($error->getMessage()); - } - - return redirect()->route('coupon.index')->with('success', 'Updated successfully'); - } - public function destroy(int $id = 0) - { - $coupon = $this->discountRepository->find($id); - - if (! $coupon) { - return back()->withErrors("Invalid request"); - } - DB::beginTransaction(); - try { - $this->discountRepository->delete($id); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withInput()->withErrors($error->getMessage()); - } - - return back()->with('success', 'Deleted successfully'); - } -} diff --git a/app/Http/Controllers/admin/CustomerController.php b/app/Http/Controllers/admin/CustomerController.php deleted file mode 100644 index 0ee1af8..0000000 --- a/app/Http/Controllers/admin/CustomerController.php +++ /dev/null @@ -1,908 +0,0 @@ -customerRepository->filter( - $request->merge(['page' => $request->get('page') ?? 1])->all(), - [], - ['hosting', 'vps', 'domain'], - ); - - $title = 'View All Customer'; - - return view('administrator.pages.customer.index', compact('customers', 'title')); - } - - //Validate userid - - /** - * Get customer Information - */ - public function getCustomerInfo(Request $request) - { - //Validate userid - $userid = $this->validateUserid(); - - if (!$userid) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - //find data after validate - - $customer = $this->customerRepository->findWithRelationships( - $userid, - [ - 'mail', - 'file' - ], - [ - 'invoice' => [ - 'invoices_status_count_1' => [ - 'key' => 'status', - 'condition' => '=', - 'value' => 1 - ], - 'invoices_status_count_0' => [ - 'key' => 'status', - 'condition' => '=', - 'value' => 0 - ] - ], - 'hosting' => 'hosting_count', - 'vps' => 'vps_count', - 'domain' => 'domain_count', - 'ticket' => 'ticket_count' - ], - [ - 'invoice' => [ - 'invoices_status_1' => [ - 'key' => 'status', - 'condition' => '=', - 'value' => 1, - 'sum' => 'total' - ], - 'invoices_status_0' => [ - 'key' => 'status', - 'condition' => '=', - 'value' => 0, - 'sum' => 'total' - ] - ] - ] - ); - if (!$customer) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - - $title = "#" . $customer->id . " " . $customer->fullname; - - return view("administrator.pages.customer.info", compact('customer', 'title', 'userid')); - } - - protected function validateUserid() - { - $validate = Validator::make(\request()->all(), [ - 'userid' => 'numeric|required' - ]); - if ($validate->fails()) { - return false; - } - return \request()->userid; - } - - /** - * Get customer Profile - */ - public function getCustomerProfile(Request $request) - { - //Validate userid - $userid = $this->validateUserid(); - - if (!$userid) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - - //find data after validate - - $customer = $this->customerRepository->findWithRelationships($userid, ['snapshot']); - - if (!$customer) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - - $title = "#" . $customer->id . " " . $customer->fullname; - return view("administrator.pages.customer.profile", compact('customer', 'title', 'userid')); - } - - /** - * Save profile update - */ - public function storeCustomerProfile(Request $request) - { - //Validate userid - $userid = $this->validateUserid(); - - if (!$userid) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - - //validate data after validate userid - $request->validate([ - 'fullname' => 'required', - 'email' => 'required' - ]); - //find user - $customer = $this->customerRepository->findWithRelationships($userid, ['snapshot']); - - if (!$customer) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - try { - DB::beginTransaction(); - - $data = $request->except(['_token', 'userid', 'password', 'email_marketing', 'snapshot_limit']); - - $request->email_marketing === "on" ? $data['email_marketing'] = 1 : $data['email_marketing'] = 0; - - if (!empty($request->password)) { - $data['password'] = password_hash($request->password, PASSWORD_BCRYPT); - } - - $this->customerRepository->update($userid, $data); - - $customer->snapShot->limit = $request->snapshot_limit; - - $customer->snapShot->save(); - - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return back()->with('success', 'Updated successfully'); - } - - - /** - * Get all product for customer - */ - public function getCustomerProduct(Request $request) - { - //Validate userid - $userid = $this->validateUserid(); - - if (!$userid) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - $customer = $this->customerRepository->find($userid); - if (!$customer) { - return redirect()->route('customer.index')->withErrors('Invalidated Request'); - } - - // Lấy tất cả hosting và VPS của customer - $hostingServices = $this->moduleHostingRepository->filter( - ['customer_id' => $userid], - ['server'] - ); - - $vpsServices = $this->vmwareRepository->filter( - ['customer_id' => $userid], - ['ipaddress.host.datacenter.vCenter', 'os.osGroup'] - ); - - // Kết hợp và format dữ liệu - $allService = collect(); - - foreach ($hostingServices as $hosting) { - $allService->push((object) [ - 'product_id' => $hosting->id, - 'product_type' => 3, - 'name' => $hosting->name, - 'description' => $hosting->name . ' (Hosting)' - ]); - } - - foreach ($vpsServices as $vps) { - $allService->push((object) [ - 'product_id' => $vps->id, - 'product_type' => 4, - 'name' => $vps->name, - 'description' => $vps->name . ' (VPS)' - ]); - } - - // Lấy invoice_item cụ thể cho sản phẩm - $invoice_item = $this->invoiceItemRepository->findBy( - $userid, - [ - 'key' => 'customer_id', - 'condition' => '=', - 'value' => $userid - ] - ); - - $type = null; - $product = null; - $productName = ''; - - if ($invoice_item) { - $product_id = $invoice_item->product_id ?? null; - $product_module_id = $request->get('product') ?? $invoice_item->product_id ?? null; - $type = $request->get('type') ?? $invoice_item->product_type ?? null; - } else { - $product_id = null; - $product_module_id = $request->get('product'); - $type = $request->get('type'); - } - - switch ($type) { - case 3: - $product = $this->moduleHostingRepository->findWithRelationships( - $product_module_id, - [ - 'order.payment', - 'server' - ] - ); - // Tìm template hosting dựa trên product_id - $productName = $this->hostingRepository->find($product_id); - break; - case 4: - $product = $this->vmwareRepository->findWithRelationships( - $product_module_id, - [ - 'order.payment', - 'ipaddress.host.datacenter.vCenter', - 'os.osGroup' - ] - ); - // Tìm template VPS dựa trên product_id - $productName = $this->vPSRepositoryInterface->find($product_id); - break; - } - - $title = "#" . $customer->id . " " . $customer->fullname; - return view( - 'administrator.pages.customer.product', - compact( - 'title', - 'userid', - 'type', - 'product', - 'allService', - 'productName', - 'invoice_item' - ) - ); - } - - public function storeCustomerProduct(Request $request) - { - - if (!$request->product_type) { - return back()->with($request->except(['_token', '_method']))->withErrors('Invalid Request'); - } - - $product = null; - $data = null; - $rules = []; - switch ($request->product_type) { - case 3: - $rules = [ - 'product_id' => 'required', - 'next_due_date' => 'required', - 'next_payment' => 'required', - 'status' => 'required' - ]; - $product = $this->moduleHostingRepository->find($request->product_id); - $data = $this->handleHosting($request); - break; - case 4: - $rules = [ - 'product_id' => 'required', - 'next_due_date' => 'required', - 'next_payment' => 'required', - 'status' => 'required' - ]; - $product = $this->vmwareRepository->find($request->product_id); - - $data = $this->handleVmware($request); - break; - } - $validator = Validator::make($request->all(), $rules); - if ($validator->fails()) { - return back()->withErrors($validator->errors()->first()); - } - if (!$product) { - return back()->withErrors('Product not found'); - } - try { - DB::beginTransaction(); - $product->update($data); - $this->ipaddressRepository->updateBy(['ip' => $request->ip], ['status' => 1, 'vm_name' => $request->name]); - DB::commit(); - } catch (Exception $exception) { - DB::rollBack(); - return back()->with($request->except(['_token', '_method']))->withErrors($exception->getMessage()); - } - return back()->with('success', 'Update information successfully'); - } - protected function handleHosting($request) - { - return [ - 'name' => $request->name, - 'module_server_id' => $request->module_server_id, - 'next_payment' => $request->next_payment, - 'next_due_date' => Carbon::createFromFormat('d/m/Y', $request->next_due_date)->timestamp, - 'next_invoice' => Carbon::createFromFormat('d/m/Y', $request->next_due_date)->timestamp, - 'username' => $request->user, - 'password' => $request->password ? SignRSA::EncryptFunc($request->password) : '', - 'status' => $request->status - ]; - } - - protected function handleVmware($request) - { - return [ - 'name' => $request->name, - 'module_ipaddress_id' => $request->ip, - 'port' => $request->port, - 'user' => $request->user, - 'password' => $request->password ? SignRSA::EncryptFunc($request->password) : '', - 'hostname' => $request->hostname, - 'vmware_id' => $request->vmware_id, - 'os_id' => $request->os_version, - 'next_payment' => $request->next_payment, - 'next_due_date' => Carbon::createFromFormat('d/m/Y', $request->next_due_date)->timestamp, - 'next_invoice' => Carbon::createFromFormat('d/m/Y', $request->next_due_date)->timestamp, - 'status' => $request->status - ]; - } - - /** - * Get Domain for customer - */ - public function getCustomerDomain(Request $request) - { - //Validate userid - $userid = $this->validateUserid(); - - if (!$userid) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - $customer = $this->customerRepository->find($userid); - if (!$customer) { - return redirect()->route('customer.index')->withErrors('Invalid Request'); - } - $domains = $this->moduleDomainRepository->filter( - [ - 'customer_id' => $userid, - 'page' => $request->get('page') ?? 1 - ], - ['customer'] - ); - - $title = "#" . $customer->id . " " . $customer->fullname; - - return view('administrator.pages.customer.domain', compact('userid', 'title', 'domains')); - } - - /** - * Get Invoice for customer - */ - public function getCustomerInvoice(Request $request) - { - //Validate userid - $userid = $this->validateUserid(); - - if (!$userid) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - $customer = $this->customerRepository->find($userid); - - if (!$customer) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - - $invoices = $this->invoiceRepository->filter( - [ - 'customer_id' => $userid, - 'page' => $request->get('page') ?? 1 - ], - [ - 'payment' - ] - ); - - - $title = "#" . $customer->id . " " . $customer->fullname; - - return view('administrator.pages.customer.invoice', compact('userid', 'title', 'invoices')); - } - - /** - * Get Transactions for customer - */ - public function getCustomerTransactions(Request $request) - { - //Validate userid - $userid = $this->validateUserid(); - - if (!$userid) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - - $customer = $this->customerRepository->find($userid); - - if (!$customer) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - $transactions = $this->transactionRepository->filter( - [ - 'customer_id' => $userid, - 'page' => $request->get('page') ?? 1 - ] - ); - - $title = "#" . $customer->id . " " . $customer->fullname; - - return view('administrator.pages.customer.transaction', compact('userid', 'title', 'transactions')); - } - - /** - * Get Mail for customer - */ - public function getCustomerEmail(Request $request) - { - $userid = $this->validateUserid(); - if (!$userid) { - return redirect()->route("customer.index")->withErrors("No data found for this user"); - } - - $customer = $this->customerRepository->find($userid); - - if (!$customer) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - $emails = $this->mailRepository->filter( - [ - 'customer_id' => $userid, - 'page' => $request->get('page') ?? 1 - ] - ); - - $title = "#" . $customer->id . " " . $customer->fullname; - - if (isset($request->email_id)) { - $template = $this->mailRepository->find($request->get('email_id')); - if ($template) { - // Blade emails.render đã bị xoá cùng toàn bộ view email — nội dung mail đã là - // HTML hoàn chỉnh render từ tbl.template, trả thẳng ra là đủ. - return response($template->message ?? '', 200, ['Content-Type' => 'text/html; charset=UTF-8']); - } - } - return view('administrator.pages.customer.mail', compact('userid', 'title', 'emails')); - } - - /** - * Get Log for customer - */ - public function getCustomerLog(Request $request) - { - $userid = $this->validateUserid(); - if (!$userid) { - return redirect()->route("customer.index")->withErrors("No data found for this user"); - } - - $customer = $this->customerRepository->find($userid); - - if (!$customer) { - return redirect()->route('customer.index')->withErrors('Invalid Request'); - } - - $logs = $this->logRepository->filter([ - 'customer_id' => $userid, - 'page' => $request->get('page') ?? 1 - ], ['customer']); - - $title = "#" . $customer->id . " " . $customer->fullname; - - return view('administrator.pages.customer.log', compact('userid', 'title', 'logs')); - } - - /** - * Save new customer - */ - public function store(CreateCustomerRequest $request) - { - $request->validated(); - - - $user = $this->customerRepository->findBy('email', $request->get('email')); - - if ($user) { - return back()->withErrors("Email already exists on the system!"); - } - - try { - DB::beginTransaction(); - $data = $request->all(); - $data['password'] = password_hash($request->password, PASSWORD_BCRYPT); - $request->email_marketing === "on" ? $data['email_marketing'] = 1 : $data['email_marketing'] = 0; - $this->customerRepository->create($data); - - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('customer.index')->with('success', 'New user added successfully'); - } - - /** - * add new customer - */ - public function create() - { - return view('administrator.pages.customer.add', ['title' => 'Thêm Customer']); - } - - - public function PasswordReset(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - 'userid' => 'required|numeric|not__in:0' - ], - [ - 'userid' => [ - 'required' => 'Invalid request' - ] - ] - ); - if ($validator->fails()) { - return back()->withErrors($validator); - } - $password = Str::random(10); - - try { - DB::beginTransaction(); - - $this->customerRepository->update( - $request->get('userid'), - [ - 'password' => Hash::make($password) - ] - ); - $data = [ - 'company_name' => setting('company'), - 'company_logo' => setting('logo'), - 'password' => $password, - ]; - - $jobs = new PasswordRecoverJob($data, $this->customerRepository->find($request->get('userid'))); - - dispatch($jobs)->delay(now()->addSeconds(10)); - - DB::commit(); - } catch (Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return back()->with('success', "Change and Send new password to email successfully"); - } - - public function addPay(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - 'userid' => 'required|numeric|not__in:0' - ], - [ - 'userid' => [ - 'required' => 'Invalid request' - ] - ] - ); - if ($validator->fails()) { - return back()->withErrors($validator); - } - $userid = $request->userid; - $customer = $this->customerRepository->find($userid); - - if (!$customer) { - return redirect()->route('customer.index')->withErrors('No data found for this user'); - } - $title = "Add Pay for Customer"; - return view('administrator.pages.customer.addPay', compact('title', 'userid')); - } - - public function addPayStore(AddPayRequest $request) - { - $request->validated(); - - $customer = $this->customerRepository->find($request->get('userid')); - - if (!$customer) { - return redirect()->route('customer.index')->withErrors('Invalid Request'); - } - try { - DB::beginTransaction(); - - match ($request->action) { - "+" => $this->customerRepository->update($request->get('userid'), [ - 'credit' => (int) $customer->credit + (int) $request->get('amount'), - 'total' => (int) $customer->total + (int) $request->get('amount'), - ]), - "-" => $this->customerRepository->update($request->get('userid'), [ - 'credit' => (int) $customer->credit - (int) $request->get('amount'), - 'total' => (int) $customer->total - (int) $request->get('amount'), - ]), - }; - - DB::commit(); - } catch (Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return back()->with('success', 'Recharge for user successfully'); - } - public function LoginWithCustomer(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - 'userid' => 'required|numeric|not__in:0' - ], - [ - 'userid' => [ - 'required' => 'Invalid request' - ] - ] - ); - if ($validator->fails()) { - return back()->withErrors($validator); - } - $customer = $this->customerRepository->find($request->get('userid')); - - if (!$customer) { - return back()->withErrors('Invalid request'); - } - // TODO: impersonation feature needs redesign for JWT — session login does not work with JWT guard - // This is a legacy Livewire admin feature; will be removed when portal-admin migration is complete - Auth::guard('client')->login($customer); - - session()->put('admin_login_for_customer', true); - - return redirect()->route('home'); - } - public function LogoutWithCustomer() - { - Auth::guard('client')->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('status', function ($d) { - return view('administrator.pages.customer.datatable.status', ['row' => $d])->render(); - }) - ->addColumn( - 'action', - function ($d) { - return view('administrator.pages.customer.datatable.action', ['row' => $d])->render(); - } - ) - ->rawColumns(['status', 'action']) - ->make(true); - } - - public function edit(int $id = 0) - { - $customer = $this->customerRepository->find($id); - - if (!$customer) { - return back()->withErrors('Invalid request.'); - } - - $title = 'Edit Customer'; - return view('administrator.pages.customer.edit', compact('title', 'customer')); - } - - public function update(int $id = 0, CreateCustomerRequest $request) - { - $customer = $this->customerRepository->find($id); - - if (!$customer) { - return back()->withErrors('Invalid request.'); - } - - $request->validated(); - - try { - DB::beginTransaction(); - - $data = $request->except(['_token', 'password']); - - if (!empty($request->password)) { - $data['password'] = password_hash($request->password, PASSWORD_BCRYPT); - } - - $this->customerRepository->update($id, $data); - - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return redirect()->route('customer.index')->with('success', 'Update customer successfully!'); - } - - public function destroy(int $id = 0) - { - $customer = $this->customerRepository->find($id); - - if (!$customer) { - return back()->withErrors('Invalid request.'); - } - - DB::beginTransaction(); - try { - Invoice::where('customer_id', $id)->delete(); - Order::where('customer_id', $id)->delete(); - Transaction::where('customer_id', $id)->delete(); - - $customer->delete(); - - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return redirect()->route('customer.index')->with('success', 'Deleted customer successfully!'); - } - - public function trash() - { - $title = 'Trash Customers'; - return view('administrator.pages.customer.trash', compact('title')); - } - - public function datatableTrash(Request $request) - { - $query = $this->customerRepository->onlyTrashed( - [], - ['hosting', 'vps', 'domain'] - ); - - return DataTables::eloquent($query) - ->addColumn('status', function ($d) { - return view('administrator.pages.customer.datatable.status', ['row' => $d])->render(); - }) - ->addColumn( - 'action', - function ($d) { - return view('administrator.pages.customer.datatable.action-trash', ['row' => $d])->render(); - } - ) - ->rawColumns(['status', 'action']) - ->make(true); - } - - public function restore(int $id = 0) - { - $customer = $this->customerRepository->find($id); - - if (!$customer) { - $customer = $this->customerRepository->onlyTrashed()->find($id); - } - - if (!$customer) { - return back()->withErrors('Invalid request.'); - } - - DB::beginTransaction(); - try { - $customer->restore(); - Invoice::withTrashed()->where('customer_id', $id)->restore(); - Order::withTrashed()->where('customer_id', $id)->restore(); - Transaction::withTrashed()->where('customer_id', $id)->restore(); - - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return back()->with('success', 'Restored customer successfully!'); - } - - public function forceDelete(int $id = 0) - { - $customer = $this->customerRepository->onlyTrashed()->find($id); - - if (!$customer) { - return back()->withErrors('Invalid request.'); - } - - DB::beginTransaction(); - try { - Invoice::withTrashed()->where('customer_id', $id)->forceDelete(); - Order::withTrashed()->where('customer_id', $id)->forceDelete(); - Transaction::withTrashed()->where('customer_id', $id)->forceDelete(); - - $customer->forceDelete(); - - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return back()->with('success', 'Permanently deleted customer!'); - } -} diff --git a/app/Http/Controllers/admin/DomainController.php b/app/Http/Controllers/admin/DomainController.php deleted file mode 100644 index e7032e6..0000000 --- a/app/Http/Controllers/admin/DomainController.php +++ /dev/null @@ -1,47 +0,0 @@ -domainRepository->filter( - [ - 'page' => $request->get('page',1), - ], - ['type'] - ); - $title = "List All Domain"; - return view("administrator.pages.product.domain.index", compact('domain', 'title')); - } - - //import domain from file excel - public function import(Request $request,Upload $upload) - { - $filePath = $upload->FileUpload($request->file); - - if (! $filePath) - { - return back()->withErrors("Please choose your file"); - } - Excel::import(new DomainImport, Storage::path($filePath)); - - unlink(Storage::path($filePath)); - - return back()->with('success','Import data from file success'); - } -} diff --git a/app/Http/Controllers/admin/GroupController.php b/app/Http/Controllers/admin/GroupController.php deleted file mode 100644 index 7db2d0c..0000000 --- a/app/Http/Controllers/admin/GroupController.php +++ /dev/null @@ -1,176 +0,0 @@ -all(), ['grid' => 'numeric']); - if ($validate->fails()) { - return false; - } - return \request()->grid; - } - - - public function __construct( - public GroupRepositoryInterface $groupRepository, - public LogRepositoryInterface $logRepository - ) - { - parent::__construct(); - } - //validate Group ID - - /** - * Add new group - */ - public function addGroupInformation() - { - $title = 'Add New Product Group'; - return view('administrator.pages.product.group', compact('title')); - } - - /** - * Store information group - */ - public function storeGroupInformation(Request $request) - { - $request->validate([ - 'name' => 'required', - 'type_id' => 'required', - 'status' => 'required', - ]); - - try { - DB::beginTransaction(); - $this->groupRepository->create($request->only(['name', 'type_id', 'status'])); - - // Clear all product-related caches after group creation - $this->clearGroupCaches($request->type_id); - - DB::commit(); - - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('product.index')->with('success', 'Add New Product Group Success'); - } - - - /** - * View information group - */ - public function getGroupInformation(Request $request) - { - $group_id = $this->validateGrid(); - - if (!$group_id) { - return redirect()->route('product.index')->withErrors('No information data found'); - } - $group = Group::find($group_id); - - if (!$group) { - return redirect()->route('product.index')->withErrors('No information data found'); - } - - $type = Type::select('id', 'name')->get(); - - $title = 'Action Group'; - - return view("administrator.pages.product.group", compact('title', 'type', 'group', 'group_id')); - - } - - /** - * Save information group - */ - public function updateGroupInformation(GroupRequest $request) - { - $data = $request->validated(); - $group = Group::find($data['grid']); - - if (!$group) { - return redirect()->route('product.index')->withErrors('No information data found'); - } - - try { - DB::beginTransaction(); - - Group::where('id', $group->id)->update($request->except(['_token', 'grid', '_method'])); - - // Clear all product-related caches after group update - $this->clearGroupCaches($group->type_id); - - Log::create([ - 'agent' => $request->userAgent(), - 'client_ip' => $request->getClientIp(), - 'username' => $request->user('admins')->username, - 'description' => $request->user('admins')->username . " modify Group Product", - 'customer_id' => 0 - ]); - DB::commit(); - - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('product.index')->with('success', 'Updated Product Group success'); - } - - public function removeGroup(Request $request) - { - $request->validate([ - 'group_id' => 'required' - ]); - - $group = Group::find($request->group_id); - - // Clear caches before deletion - $this->clearGroupCaches($group->type_id); - - $group->delete(); - - return redirect()->route('product.index')->with('success', 'Product group deleted successfully'); - - } - - /** - * Clear all group and product-related caches - */ - private function clearGroupCaches($typeId): void - { - Cache::forget(CacheKey::build('group', 'shop-types')); - - if ($typeId) { - Cache::forget(CacheKey::build('group', 'by-type', ['type_id' => $typeId])); - - $groups = $this->groupRepository->filter(['type_id' => $typeId]); - foreach ($groups as $group) { - Cache::forget(CacheKey::build('group', 'products', ['type_id' => $typeId, 'group_id' => $group->id])); - if ($group->slug) { - for ($page = 1; $page <= 10; $page++) { - Cache::forget(CacheKey::build('group', 'slug-products', ['page' => $page, 'slug' => $group->slug])); - } - } - } - } - } -} diff --git a/app/Http/Controllers/admin/InvoiceController.php b/app/Http/Controllers/admin/InvoiceController.php deleted file mode 100644 index 45deb4e..0000000 --- a/app/Http/Controllers/admin/InvoiceController.php +++ /dev/null @@ -1,178 +0,0 @@ -invoiceRepository->filter( - $request->merge(['page' => $request->get('page') ?? 1])->all(), - [ - 'payment', - 'customer' - ] - ); - $title = 'List all invoices'; - - return view('administrator.pages.invoice.index', compact('title', 'invoices')); - } - - public function create() - { - $title = 'Add new Invoice'; - $customer = Customer::select('id', 'fullname')->where('status', 1)->get(); - $payment = Payment::select('id', 'name')->where('status', 1)->get(); - - return view('administrator.pages.invoice.add', compact('title', 'customer', 'payment')); - } - - public function store(Request $request) - { - $validated = $request->validate([ - 'customer_id' => 'required|numeric', - 'payment_method' => 'required|numeric', - 'sub_total' => 'required|numeric', - 'total' => 'required|numeric', - ]); - - $invoiceData = [ - 'date' => time(), - 'due_date' => time() + (7 * 24 * 60 * 60), - 'sub_total' => $validated['sub_total'], - 'tax' => $validated['sub_total'] * 0.1, - 'total' => $validated['total'], - 'payment_method' => $validated['payment_method'], - 'customer_id' => $validated['customer_id'], - 'status' => 0, - ]; - - DB::beginTransaction(); - try { - $invoice = $this->invoiceRepository->create($invoiceData); - DB::commit(); - return redirect()->route('administrator.pages.invoice.edit', ['id' => $invoice->id])->with('success', 'Invoice created successfully'); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - } - - public function edit(int $id = 0) - { - $invoice = $this->invoiceRepository->findWithRelationships($id, ['payment', 'invoiceItem', 'customer']); - - if (!$invoice) { - return back()->withErrors("Can't find Invoice by ID #" . $id); - } - - $title = "View invoice #" . $id; - - return view("administrator.pages.invoice.edit", compact('title', 'invoice')); - } - - public function update(InvoiceRequest $request, int $id = 0) - { - - $invoice = $this->invoiceRepository->find($id); - if (!$invoice) { - return back()->withErrors("Invalid request"); - } - $request->validated(); - - $status = match (strtolower($request->get('action'))) { - "paid" => 1, - "cancel" => 2, - default => 0 - }; - - DB::beginTransaction(); - try { - - $this->invoiceRepository->update($id, [ - 'status' => $status, - 'date_paid' => $status === 1 ? time() : null - ]); - - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return back()->with('success', "Updated Successfully"); - } - - public function destroy(int $id = 0) - { - - $invoice = $this->invoiceRepository->find($id); - if (!$invoice) { - return back()->withErrors("Invalid request"); - } - - DB::beginTransaction(); - try { - - $this->orderRepository->updateBy('invoice_id', $id, ['invoice_id' => 0]); - $this->invoiceRepository->delete($id); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return redirect()->route('administrator.pages.invoice.index')->with('success', 'Invoice deleted successfully'); - } - - public function datatable(Request $request) - { - $query = $this->invoiceRepository->filter( - $request->all(), - ['payment', 'customer'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn('invoice_number', function ($d) { - return view('administrator.pages.invoice.datatable.invoice_number', ['row' => $d])->render(); - }) - ->addColumn('date', function ($d) { - return view('administrator.pages.invoice.datatable.date', ['row' => $d])->render(); - }) - ->addColumn('customer', function ($d) { - return view('administrator.pages.invoice.datatable.customer', ['row' => $d])->render(); - }) - ->addColumn('payment', function ($d) { - return view('administrator.pages.invoice.datatable.payment', ['row' => $d])->render(); - }) - ->addColumn('total', function ($d) { - return view('administrator.pages.invoice.datatable.total', ['row' => $d])->render(); - }) - ->addColumn('status', function ($d) { - return view('administrator.pages.invoice.datatable.status', ['row' => $d])->render(); - }) - ->addColumn('action', function ($d) { - return view('administrator.pages.invoice.datatable.action', ['row' => $d])->render(); - }) - ->rawColumns(['invoice_number', 'date', 'customer', 'payment', 'total', 'status', 'action']) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/LoginController.php b/app/Http/Controllers/admin/LoginController.php deleted file mode 100644 index 1c77248..0000000 --- a/app/Http/Controllers/admin/LoginController.php +++ /dev/null @@ -1,57 +0,0 @@ -validate([ - 'username' => 'required|min:3', - 'password' => 'required|min:3' - ]); - - $user = User::where('username', $request->username)->first(); - if (!$user) - return back()->withErrors('Không tìm thấy thông tin người dùng'); - - if (Auth::guard('admin')->attempt(['username' => $request->username, 'password' => $request->password], $request->remember)) { - if (\auth()->user()->status) { - $token = \auth()->user()->createToken('access_token', ['*'], Carbon::now()->addDay())->plainTextToken; - Cookie::queue('accessToken', $token, 24 * 60); - - if (Cookie::has('admin_prev_url')) { - Cookie::queue(Cookie::forget('admin_prev_url')); - return redirect(Cookie::get('admin_prev_url')); - } - return redirect()->route('dashboard')->with('success', 'Đã đăng nhập thành công!'); - } - auth('admins')->logout(); - return back()->withErrors('Tài khoản bị khoá, vui lòng liên hệ quản trị viên'); - } - - return back()->withErrors('Thông tin đăng nhập không hợp lệ'); - } catch (\Exception $e) { - // Log the error for debugging - Log::error('Login error: ' . $e->getMessage(), [ - 'username' => $request->username, - 'ip' => $request->ip(), - 'user_agent' => $request->userAgent() - ]); - - return back()->withErrors('Có lỗi xảy ra trong quá trình đăng nhập. Vui lòng thử lại sau.'); - } - } -} diff --git a/app/Http/Controllers/admin/MailController.php b/app/Http/Controllers/admin/MailController.php deleted file mode 100644 index b04acdd..0000000 --- a/app/Http/Controllers/admin/MailController.php +++ /dev/null @@ -1,13 +0,0 @@ -orderRepository->filter([ - 'status' => 0 - ]); - - $invocie_unpaid = $this->invoiceRepository->filter( - [ - 'status' => 0 - ] - ); - - $tickets = $this->ticketRepository->filter( - [ - 'status' => 1 - ] - )->unique('ticket_num'); - - $customers = $this->customerRepository->getCount(); - - $total_amount = $this->invoiceRepository->filter()->sum('total'); - $total_amount_year = $this->invoiceRepository->get_billing_year(date('Y')); - $total_amount_month = $this->invoiceRepository->get_billing_month(date('m')); - $total_amount_day = $this->invoiceRepository->get_billing_day(Carbon::today()); - - $posts = $this->postRepository->filter([ - 'limit' => 6 - ]); - $logs = $this->logRepository->filter( - [ - 'limit' => 6 - ] - ); - - $vmwares = $this->vmwareRepository->filter( - [ - 'limit' => 6, - 'status' => [ - 'key' => 'status', - 'condition' => 'NotIn', - 'value' => [3] - ] - ], - [ - 'ipaddress', - 'customer' - ] - ); - - $hostings = $this->moduleHostingRepository->filter( - [ - 'status' => [ - 'key' => 'status', - 'condition' => 'NotIn', - 'value' => [3] - ], - 'limit' => 6 - ], - [ - 'customer' - ] - ); - - $orders = $this->orderRepository->filter( - [ - 'limit' => 6, - 'invoice_id' => [ - 'key' => 'invoice_id', - 'condition' => 'NotIn', - 'value' => [0] - ] - ], - [ - 'customer' - ] - ); - - $staffs = $this->staffRepository->filter([], ['user']); - - return view( - 'administrator.home', - compact( - 'title', - 'pending_order', - 'invocie_unpaid', - 'tickets', - 'customers', - 'total_amount', - 'total_amount_year', - 'total_amount_month', - 'total_amount_day', - 'posts', - 'logs', - 'vmwares', - 'hostings', - 'orders', - 'staffs' - ) - ); - } - - public function SystemLog(Request $request) - { - $logs = $this->logRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ], - [ - 'customer' - ] - ); - $title = 'System Activity Log'; - - return view('administrator.pages.log', compact('title', 'logs')); - } - - public function ClearCache() - { - Artisan::call('cache:clear'); - Artisan::call('config:clear'); - Artisan::call('route:clear'); - Artisan::call('view:clear'); - return redirect()->back()->with('success', 'Cache cleared successfully'); - } -} diff --git a/app/Http/Controllers/admin/MediaController.php b/app/Http/Controllers/admin/MediaController.php deleted file mode 100644 index 1cee8be..0000000 --- a/app/Http/Controllers/admin/MediaController.php +++ /dev/null @@ -1,40 +0,0 @@ -ImageUpload($request->upload); - - if ($file['error'] === false) - { - return response()->json(['fileName' => $file['filename'], 'uploaded' => 1, 'url' => url('template/' . $file['data'])]); - } - return response()->json($file); - } - public function uploadImage(Request $request,Upload $upload) - { - $file = $upload->ImageUpload($request->file); - - if ($file['error'] === false) - { - return response()->json([ - 'error' => false, - 'message' => 'Upload thành công', - 'url' => $file['data'] - ]); - } - return response()->json($file); - } -} diff --git a/app/Http/Controllers/admin/MenuController.php b/app/Http/Controllers/admin/MenuController.php deleted file mode 100644 index 65cd5cd..0000000 --- a/app/Http/Controllers/admin/MenuController.php +++ /dev/null @@ -1,132 +0,0 @@ - $menu->id, - 'title' => $prefix . ' ' . $menu->title, - 'route_name' => $menu->route_name, - 'is_active' => $menu->is_active, - 'created_at' => $menu->created_at, - ]; - - if ($menu->children && $menu->children->count() > 0) { - $result = array_merge($result, $this->buildMenuOptions($menu->children, $prefix . '--')); - } - } - - return $result; - } - - public function index(Request $request) - { - $menuOptions = $this->buildMenuOptions( - $this->menuRepository->getTree() - ); - $title = 'List Menu'; - - return view('administrator.pages.menu.index', compact('menuOptions', 'title')); - } - - public function create() - { - $title = 'Add new Menu'; - $menus = $this->menuRepository->getTree(); - - $parentMenuOptions = $this->buildMenuOptions($menus); - return view('administrator.pages.menu.add', compact('title', 'parentMenuOptions')); - } - - public function store(MenuRequest $request) - { - $data = $request->validated(); - - DB::beginTransaction(); - try { - $this->menuRepository->store($data); - - DB::commit(); - } catch (\Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - - return redirect()->route('menu.index')->with('success', 'Added successfully'); - } - - public function edit($id) - { - $menu = $this->menuRepository->find($id); - - if (!$menu) { - return back()->withErrors('Invalid request'); - } - - $menus = $this->menuRepository->getTree(); - - $parentMenuOptions = $this->buildMenuOptions($menus); - - $title = 'Edit Menu'; - - return view('administrator.pages.menu.edit', compact('title', 'menu', 'parentMenuOptions')); - } - - public function update(int $id = 0, MenuRequest $request) - { - $menu = $this->menuRepository->find($id); - - if (!$menu) { - return back()->withErrors('Invalid request'); - } - $request->validated(); - - - DB::beginTransaction(); - try { - $this->menuRepository->update($id, $request->all()); - DB::commit(); - } catch (\Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('menu.index')->with('success', 'Updated successfully'); - } - - public function destroy(int $id = 0) - { - $menu = $this->menuRepository->find($id); - - if (!$menu) { - return back()->withErrors('Invalid request'); - } - - DB::beginTransaction(); - try { - $this->menuRepository->delete($id); - DB::commit(); - } catch (\Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('menu.index')->with('success', 'Deleted successfully'); - } -} diff --git a/app/Http/Controllers/admin/ModuleDNSController.php b/app/Http/Controllers/admin/ModuleDNSController.php deleted file mode 100644 index 1c9f9a4..0000000 --- a/app/Http/Controllers/admin/ModuleDNSController.php +++ /dev/null @@ -1,176 +0,0 @@ -dnsServerRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ] - ); - $title = 'List all server dns'; - return view('administrator.pages.module.dns.index', compact('modules', 'title')); - } - public function import(Request $request) - { - $data = Excel::toArray([], $request->file); - if (count($data) > 0) { - $data = array_slice($data[0], 1); - $allData = []; - foreach ($data as $row) { - if (isset($row[1]) && isset($row[2]) && isset($row[3]) && !empty($row[1]) && !empty($row[1]) && !empty($row[1])) { - $allData[] = [ - 'name' => $row[1], - 'user' => $row[2], - 'password' => SignRSA::EncryptFunc($row[3]) - ]; - } - } - } - - if (!isset($allData) || (isset($allData) && count($allData) < 1)) { - return back()->withErrors('No data imported'); - } - DB::beginTransaction(); - try { - $this->dnsServerRepository->insert($allData); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return back()->with('success', 'Imported successfully'); - } - public function create() - { - $title = 'Add new server dns'; - return view('administrator.pages.module.dns.add', compact('title')); - } - public function store(CreateDNSRequest $request) - { - $request->validated(); - - $isExitsHost = $this->dnsServerRepository->findByName($request->get('name')); - if ($isExitsHost) { - return back()->withErrors('The server already exists'); - } - DB::beginTransaction(); - try { - $data = $request->all(); - $data['password'] = SignRSA::EncryptFunc($request->password); - $this->dnsServerRepository->create($data); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('module.dns.index')->with('success', 'Added successfully'); - } - public function edit(int $id = 0) - { - $server = $this->dnsServerRepository->find($id); - if (!$server) - return back()->withErrors('Can\'t find server by id ' . $server); - $title = 'View server dns information'; - return view('administrator.pages.module.dns.edit', compact('title', 'server')); - } - public function update(UpdateDNSRequest $request, int $id = 0) - { - $server = $this->dnsServerRepository->find($id); - - if (!$server) - return back()->withErrors('Invalid Request'); - - $request->validated(); - - if ($request->get('name') != $server->name) { - $isExitsHost = $this->dnsServerRepository->findByName($request->get('name')); - if ($isExitsHost) { - return back()->withErrors('The server already exists'); - } - } - - DB::beginTransaction(); - try { - $data = $request->all(); - if (!empty($request->get('password'))) { - $data['password'] = SignRSA::EncryptFunc($request->password); - } - $this->dnsServerRepository->update($id, $data); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('module.dns.index')->with('success', 'Updated Successfully'); - } - public function destroy(int $id = 0) - { - $server = $this->dnsServerRepository->find($id); - if (!$server) { - return back()->withErrors('Invalid Request'); - } - DB::beginTransaction(); - try { - - $this->dnsServerRepository->delete($id); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('module.dns.index')->with('success', 'Deleted successfully.'); - } - - - public function datatable(Request $request) - { - $query = $this->dnsServerRepository->filter( - $request->all(), - [], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn( - 'action', - function ($d) { - return view('administrator.pages.module.dns.datatable.action', ['row' => $d])->render(); - } - ) - ->filterColumn('name', function ($query, $keyword) { - $query->where('name', 'like', "%{$keyword}%"); - }) - ->filterColumn('user', function ($query, $keyword) { - $query->where('user', 'like', "%{$keyword}%"); - }) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/ModuleHostingController.php b/app/Http/Controllers/admin/ModuleHostingController.php deleted file mode 100644 index c45ad8c..0000000 --- a/app/Http/Controllers/admin/ModuleHostingController.php +++ /dev/null @@ -1,170 +0,0 @@ -hostingServerRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ] - ); - $title = 'List all server hosting'; - return view('administrator.pages.module.hosting.index', compact('modules', 'title')); - } - public function import(Request $request) - { - if (!$request->file) { - return back()->withErrors("Please choose your file"); - } - $data = Excel::toArray([], $request->file); - if (count($data) > 0) { - $data = array_slice($data[0], 1); - $allData = []; - foreach ($data as $row) { - if (isset($row[1]) && isset($row[2]) && isset($row[3]) && !empty($row[1]) && !empty($row[1]) && !empty($row[1])) { - $allData[] = [ - 'name' => $row[1], - 'user' => $row[2], - 'password' => SignRSA::EncryptFunc($row[3]) - ]; - } - } - } - - if (!isset($allData) || (isset($allData) && count($allData) < 1)) { - return back()->withErrors('No data imported'); - } - DB::beginTransaction(); - try { - $this->hostingServerRepository->insert($allData); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return back()->with('success', 'Imported successfully'); - } - public function create() - { - $title = 'Add new server hosting'; - return view('administrator.pages.module.hosting.add', compact('title')); - } - public function store(CreateHostingRequest $request) - { - $request->validated(); - $isExitsHost = $this->hostingServerRepository->findByName($request->get('name')); - if ($isExitsHost) { - return back()->withErrors('The server already exists'); - } - DB::beginTransaction(); - try { - $data = $request->all(); - $data['password'] = SignRSA::EncryptFunc($request->password); - $this->hostingServerRepository->create($data); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('module.hosting.index')->with('success', 'Added successfully'); - } - public function edit(int $id = 0) - { - - $server = $this->hostingServerRepository->find($id); - if (!$server) - return back()->withErrors('Can\'t find server by id ' . $id); - $title = 'View server hosting information'; - return view('administrator.pages.module.hosting.edit', compact('title', 'server')); - } - public function update(UpdateHostingRequest $request, int $id = 0) - { - $server = $this->hostingServerRepository->find($id); - if (!$server) { - return back()->withErrors('Invalid Request'); - } - $request->validated(); - if ($request->get('name') != $server->name) { - $isExitsHost = $this->hostingServerRepository->findByName($request->get('name')); - if ($isExitsHost) { - return back()->withErrors('The server already exists'); - } - } - DB::beginTransaction(); - try { - $data = $request->all(); - if (!empty($request->get('password'))) { - $data['password'] = SignRSA::EncryptFunc($request->password); - } - $this->hostingServerRepository->update($id, $data); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('module.hosting.index')->with('success', 'Updated successfully.'); - } - public function destroy(int $id = 0) - { - $server = $this->hostingServerRepository->find($id); - if (!$server) { - return back()->withErrors('Invalid Request'); - } - DB::beginTransaction(); - try { - - $this->hostingServerRepository->delete($id); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('module.hosting.index')->with('success', 'Deleted successfully.'); - } - - public function datatable(Request $request) - { - $query = $this->hostingServerRepository->filter( - $request->all(), - [], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn( - 'action', - function ($d) { - return view('administrator.pages.module.dns.datatable.action', ['row' => $d])->render(); - } - ) - ->filterColumn('name', function ($query, $keyword) { - $query->where('name', 'like', "%{$keyword}%"); - }) - ->filterColumn('user', function ($query, $keyword) { - $query->where('user', 'like', "%{$keyword}%"); - }) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/ModuleIPPoolController.php b/app/Http/Controllers/admin/ModuleIPPoolController.php deleted file mode 100644 index acb94eb..0000000 --- a/app/Http/Controllers/admin/ModuleIPPoolController.php +++ /dev/null @@ -1,77 +0,0 @@ -ipPoolRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ] - ); - $title = 'List all server ip pool'; - return view('administrator.pages.module.ippool.index', compact('modules', 'title')); - } - - public function create() - { - $title = 'Add new server ip pool'; - $moduleNetworks = $this->moduleNetworkRepository->filter( - [ - 'page' => 1 - ] - ); - return view('administrator.pages.module.ippool.add', compact('title', 'moduleNetworks')); - } - - public function store(Request $request) - { - $data = $request->all(); - $ipAddresses = explode(',', $data['ip_address']); - $moduleNetworkId = $data['module_network_id']; - - foreach ($ipAddresses as $ip) { - $this->ipPoolRepository->create([ - 'ip_address' => trim($ip), - 'module_network_id' => $moduleNetworkId, - ]); - } - - return redirect()->route('module.ippool.index')->with('success', 'Add new server ip pool success'); - } - - public function edit(int $id = 0) - { - $module = $this->ipPoolRepository->find($id); - $moduleNetworks = $this->moduleNetworkRepository->filter( - [ - 'page' => 1 - ] - ); - $title = 'Edit server ip pool'; - return view('administrator.pages.module.ippool.edit', ['module' => $module, 'title' => $title, 'moduleNetworks' => $moduleNetworks]); - } - - public function update(Request $request, int $id = 0) - { - $this->ipPoolRepository->update($id, $request->all()); - } - - public function destroy(int $id = 0) - { - $this->ipPoolRepository->delete($id); - return redirect()->route('module.ippool.index')->with('success', 'Delete server ip pool success'); - } -} diff --git a/app/Http/Controllers/admin/ModulevCenterController.php b/app/Http/Controllers/admin/ModulevCenterController.php deleted file mode 100644 index 360bfad..0000000 --- a/app/Http/Controllers/admin/ModulevCenterController.php +++ /dev/null @@ -1,184 +0,0 @@ -vCenterRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ] - ); - $title = 'List all vCenter'; - return view('administrator.pages.module.vcenter.index', compact('modules', 'title')); - } - public function import(Request $request) - { - if (!$request->file) { - return back()->withErrors("Please choose your file"); - } - $data = Excel::toArray([], $request->file); - if (count($data) > 0) { - $data = array_slice($data[0], 1); - $allData = []; - foreach ($data as $row) { - if (isset($row[1]) && isset($row[2]) && isset($row[3]) && !empty($row[1]) && !empty($row[1]) && !empty($row[1])) { - $allData[] = [ - 'host' => $row[1], - 'user' => $row[2], - 'password' => SignRSA::EncryptFunc($row[3]) - ]; - } - } - } - - if (!isset($allData) || (isset($allData) && count($allData) < 1)) { - return back()->withErrors('No data imported'); - } - - DB::beginTransaction(); - try { - $this->vCenterRepository->insert($allData); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return back()->with('success', 'Imported successfully'); - } - public function create() - { - $title = 'Add new vCenter'; - return view('administrator.pages.module.vcenter.add', compact('title')); - } - public function store(CreatevCenterRequest $request) - { - $request->validated(); - - $isExitsHost = $this->vCenterRepository->findByName($request->get('host')); - if ($isExitsHost) { - return back()->withErrors('The server already exists'); - } - DB::beginTransaction(); - try { - $data = $request->all(); - $data['password'] = SignRSA::EncryptFunc($request->password); - $this->vCenterRepository->create($data); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('module.vcenter.index')->with('success', 'Added Successfully'); - } - public function edit(int $id = 0) - { - $server = $this->vCenterRepository->find($id); - if (!$server) - return back()->withErrors('Can\'t find server by id ' . $id); - $title = 'View vCenter information'; - return view('administrator.pages.module.vcenter.edit', compact('title', 'server')); - } - public function update(UpdatevCenterRequest $request, int $id = 0) - { - $server = $this->vCenterRepository->find($id); - if (!$server) - return back()->withErrors('Invalid Request'); - $request->validated(); - if ($request->get('host') != $server->name) { - $isExitsHost = $this->vCenterRepository->findByName($request->get('host')); - if ($isExitsHost) { - return back()->withErrors('The server already exists'); - } - } - DB::beginTransaction(); - try { - $data = $request->all(); - if (!empty($request->get('password'))) { - $data['password'] = SignRSA::EncryptFunc($request->password); - } - $this->vCenterRepository->update($id, $data); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('module.vcenter.index')->with('success', 'Updated Successfully'); - } - - public function destroy(int $id = 0) - { - $server = $this->vCenterRepository->find($id); - if (!$server) { - return back()->withErrors('Invalid Request'); - } - DB::beginTransaction(); - try { - - $this->vCenterRepository->delete($id); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('module.vcenter.index')->with('success', 'Deleted successfully.'); - } - - public function datatable(Request $request) - { - $query = $this->vCenterRepository->filter( - $request->all(), - [], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn( - 'action', - function ($d) { - return view('administrator.pages.module.vcenter.datatable.action', ['row' => $d])->render(); - } - ) - ->addColumn( - 'status', - function ($d) { - return view('administrator.pages.module.vcenter.datatable.status', ['row' => $d])->render(); - } - ) - ->filterColumn('v_center.host', function ($query, $keyword) { - $query->whereHas('vCenter', function ($q) use ($keyword) { - $q->where('host', 'like', "%{$keyword}%"); - }); - }) - ->rawColumns(['action', 'status']) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/OrderController.php b/app/Http/Controllers/admin/OrderController.php deleted file mode 100644 index 718ada9..0000000 --- a/app/Http/Controllers/admin/OrderController.php +++ /dev/null @@ -1,448 +0,0 @@ - 1, - 3 => 3, - 4 => 6, - 5 => 12, - default => 0 - }; - - return [ - 'next_due_date' => $months ? Carbon::today()->addMonths($months)->timestamp : null, - 'next_invoice' => $months ? Carbon::today()->addMonths($months)->timestamp : null, - 'billing' => $months ?: 1 - ]; - } - - public function store(OrderRequest $request) - { - $request->validated(); - - $total = 0; - - $product_item = []; - $relid_data = []; - - foreach ($request->product as $key => $item) { - $product = null; - - //get product item by type product - $relid_data[$key]['type'] = $item['type']; - - $product = match ((int) $item['type']) { - 3 => $this->hostingRepository->find($item['product_id']), - 4 => $this->vPSRepository->find($item['product_id']), - default => null - }; - - if (!$product) { - throw new Exception("Sản phẩm không hợp lệ."); - } - - - $message = $product->name; - - $billing = 0; - - //Data product item with module auto create - $relid_data[$key]['data'] = [ - 'customer_id' => $request->customer_id, - 'status' => 0, - 'name' => $item['domain'] - ]; - //billing data - $billingDetails = $this->calculateBillingDetails($item['billing']); - $relid_data[$key]['data'] = array_merge($relid_data[$key]['data'], $billingDetails); - - //First pay for bill - $price_select = $item['price_custom'] ? - $item['price_custom'] : - $product->price_sale ?? $product->price; - - $relid_data[$key]['data']['first_payment'] = $price_select * $billingDetails['billing']; - - //next_pay - $relid_data[$key]['data']['next_payment'] = ($item['price_custom'] ?? $product->price) * $billingDetails['billing']; - - //invoice item - $generalCode = Helper::generateUniqueCode(); - - $product_item[$key] = [ - 'customer_id' => $request->customer_id, - 'type_id' => $item['type'], - 'product_id' => $item['product_id'], - 'description' => $message, - 'amount' => $price_select, - 'billing' => $item['billing'], - 'tax' => 1, - 'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp, - 'code' => $generalCode - ]; - - $relid_data[$key]['code'] = $generalCode; - - if ($item['quantity'] > 1) { - for ($i = 1; $i < $item['quantity']; $i++) { - $newCode = Helper::generateUniqueCode(); - $product_item[] = array_merge($product_item[$key], ['code' => $newCode]); - $relid_data[] = array_merge($relid_data[$key], ['code' => $newCode]); - } - } - - if ($item['price_custom']) { - - $total += $item['quantity'] * $item['price_custom']; - } else { - - $price = ($product->price_sale ? $product->price_sale : null) ?? $product->price; - - $total += $item['quantity'] * $price * $billingDetails['billing'];; - } - } - $order_item = [ - 'order_num' => Helper::generateUniqueCode(), - 'customer_id' => $request->customer_id, - 'date' => Carbon::today()->timestamp, - 'invoice_id' => 0, - 'discount_id' => $request->discount_id ?? 0, - 'amount' => $total + ($total * (10 / 100)), - 'payment_method' => $request->payment_method, - 'user_id' => auth()->guard('admin')->user()->id, - 'status' => $request->status, - 'client_ip' => $request->getClientIp() - ]; - - $invoice_data = [ - 'date' => Carbon::today()->timestamp, - 'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp, - 'date_paid' => (int)$request->status === 1 ? Carbon::today()->timestamp : null, - 'sub_total' => $total, - 'tax' => $total * (10 / 100), - 'total' => $total + ($total * (10 / 100)), - 'payment_method' => $request->payment_method, - 'customer_id' => $request->create_invoice ? $request->customer_id : 0, - 'status' => $request->status - ]; - - DB::beginTransaction(); - - try { - - $invoice = $this->invoiceRepository->create($invoice_data); - - $product_item = array_map(function ($array) use ($invoice) { - $array['invoice_id'] = $invoice->id; - return $array; - }, $product_item); - - if ($request->create_invoice) { - $order_item['invoice_id'] = $invoice->id; - } - - $order = $this->orderRepository->create($order_item); - - //append order_id to relid data - $relid_data = array_map(function ($array) use ($order) { - $array['data']['order_id'] = $order->id; - 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 = $this->moduleHostingRepository->create($item['data']); - $relid[$code] = $relid_id->id; - break; - case 4: - $item['data']['status'] = 2; - $relid_id = $this->moduleVmwareRepository->create($item['data']); - $relid[$code] = $relid_id->id; - break; - } - } - - $product_item = array_filter(array_map(function ($array) use ($relid) { - if (isset($relid[$array['code']])) { - $array['relid'] = $relid[$array['code']]; - unset($array['code']); - return $array; - } - return null; - }, $product_item)); - - - $this->invoiceItemRepository->insert($product_item); - - $last_id = $this->invoiceItemRepository->filter(['limit' => count($product_item)], [], [], ['id'])->pluck('id')->toArray(); - - $order->orderItem()->sync($last_id); - - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('administrator.pages.order.edit', ['id' => $order]); - } - - - public function create() - { - $title = 'Add new Order for Customer'; - $customer = Customer::select('id', 'fullname')->where('status', 1)->get(); - $payment = Payment::select('id', 'name')->where('status', 1)->get(); - $discount = Discount::select('id', 'code', 'save', 'type')->where('status', 1)->get(); - // $type = Type::select('id', 'name')->where('status', 1)->get(); - - return view('administrator.pages.order.add', compact('title', 'customer', 'payment', 'discount')); - } - - public function edit(int $id = 0) - { - $order = $this->orderRepository->findWithRelationships($id, ['orderItem', 'user', 'customer.invoiceItem.type', 'payment', 'discount', 'invoice.invoiceItems']); - - if (!$order) { - return redirect()->route('administrator.pages.order.index')->withErrors('Can\'t find a order by order id #' . $id); - } - - $title = 'View Manage Order'; - - $order->orderItem->transform(function ($item) { - $item->status = $this->invoiceItemRepository->findServiceStatus($item->relid, $item->type_id); - return $item; - }); - return view('administrator.pages.order.edit', compact('title', 'order')); - } - - public function update(int $id, UpdateOrderRequest $request) - { - $order = $this->orderRepository->findWithRelationships($id, ['invoice', 'orderItem']); - - if (!$order) { - return redirect()->route('administrator.pages.order.index')->withErrors('Invalid request'); - } - - $status = match ($request->action) { - "accept" => 1, - "cancel" => 2, - default => 0 - }; - - try { - DB::beginTransaction(); - - $this->orderRepository->update($id, ['status' => $status]); - - if ($status === 1 && $request->module == 1) { - $jobs = []; - $addon = []; - $product = 0; - foreach ($order->orderItem as $key => $item) { - switch ((int) $item->type_id) { - case 1: - $itemData = $this->addonRepository->find($item->relid); - if ($itemData) { - $price = match ((int) $item->billing) { - 2 => $item->amount / 1, - 3 => $item->amount / 3, - 4 => $item->amount / 6, - 5 => $item->amount / 12, - default => 0 - }; - $addon[strtolower($itemData->name)] = (int) $price / $itemData->price; - $product = $item->parent_id; - } - break; - case 3: - $hostingServer = $this->moduleHostingServerRepository->get_active(); - if ($hostingServer) { - $this->moduleHostingRepository->update($item->relid, ['module_server_id' => $hostingServer->first()->id]); - $jobs[] = new CreateHostingJobs($item->relid); - } - break; - case 4: - $product = $this->moduleVmwareRepository->find($item->relid); - - if ($product && $product->os_id) { - $os = $this->osRepository->findWithRelationships($product->os_id, ['osGroup']); - if (! $os || ! $os->osGroup->isNotEmpty()) break; - - $ip_address = $this->ipAddressRepository->filter([ - 'module_host_id' => $os->osGroup->module_host_id, - 'status' => 0 - ]); - if (count($ip_address) > 0) { - $this->moduleVmwareRepository->update($item->relid, [ - 'module_ipaddress_id' => $ip_address->first()->id, - ]); - $this->ipAddressRepository->update($ip_address->first()->id, [ - 'status' => 1 - ]); - $jobs[] = new CreateVMJobs($product->id); - } - } - break; - default: - break; - } - } - - if (count($order->orderItem) === count($addon)) { - dispatch(new UpgradeVirtualMachine($product, $addon, $order->id))->delay(now()->addSeconds(10)); - } - if (count($jobs)) { - foreach ($jobs as $item) { - dispatch($item)->delay(now()->addSeconds(10)); - } - } - } - - - - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return back()->with('success', 'Order Set to ' . $request->action . ' success'); - } - - public function destroy(Request $request) - { - $validator = Validator::make($request->all(), [ - 'order' => 'numeric|required' - ]); - if ($validator->fails()) { - return back()->withErrors('Can\'t find order'); - } - - $order = Order::with('Invoice', 'Invoice.InvoiceItem')->find($request->order); - - if (!$order) { - return back()->withErrors('Invalid request'); - } - if ($order->Invoice) { - $order->Invoice->delete(); - - if ($order->Invoice->InvoiceItem->count()) { - foreach ($order->Invoice->InvoiceItem->all() as $item) { - $item->delete(); - } - } - } - - $order->delete(); - - return back()->with('success', 'Delete Success'); - } - - public function datatable(Request $request) - { - $query = $this->orderRepository->filter( - $request->all(), - ['payment', 'customer', 'invoice'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn('order_num', function ($d) { - return view('administrator.pages.order.datatable.order_num', ['row' => $d])->render(); - }) - ->addColumn('date', function ($d) { - return view('administrator.pages.order.datatable.date', ['row' => $d])->render(); - }) - ->addColumn('customer', function ($d) { - return view('administrator.pages.order.datatable.customer', ['row' => $d])->render(); - }) - ->addColumn('payment', function ($d) { - return view('administrator.pages.order.datatable.payment', ['row' => $d])->render(); - }) - ->addColumn('amount', function ($d) { - return view('administrator.pages.order.datatable.amount', ['row' => $d])->render(); - }) - ->addColumn('invoice_status', function ($d) { - return view('administrator.pages.order.datatable.invoice_status', ['row' => $d])->render(); - }) - ->addColumn('status', function ($d) { - return view('administrator.pages.order.datatable.status', ['row' => $d])->render(); - }) - ->addColumn('action', function ($d) { - return view('administrator.pages.order.datatable.action', ['row' => $d])->render(); - }) - ->rawColumns(['order_num', 'date', 'customer', 'payment', 'amount', 'invoice_status', 'status', 'action']) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/PaymentController.php b/app/Http/Controllers/admin/PaymentController.php deleted file mode 100644 index 4cf64bd..0000000 --- a/app/Http/Controllers/admin/PaymentController.php +++ /dev/null @@ -1,99 +0,0 @@ -paymentReppository->filter( - $request->merge(['page' => $request->get('page') ?? 1])->all() - ); - $title = 'List Payment'; - - return view('administrator.pages.payment.index', compact('payments', 'title')); - } - - public function create() - { - $title = 'Add new Payment Method'; - - return view('administrator.pages.payment.add', compact('title')); - } - - public function store(PaymentRequest $request) - { - $request->validated(); - - DB::beginTransaction(); - try { - $this->paymentReppository->create($request->all()); - DB::commit(); - } catch (\Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('payment.index')->with('success', 'Added successfully'); - } - - public function edit(int $id = 0) - { - $payment = $this->paymentReppository->find($id); - - if (! $payment) { - return back()->withErrors('Invalid request'); - } - $title = 'Edit Payment Method'; - - return view('administrator.pages.payment.edit', compact('title', 'payment')); - } - public function update(int $id = 0, PaymentRequest $request) - { - $payment = $this->paymentReppository->find($id); - - if (! $payment) { - return back()->withErrors('Invalid request'); - } - $request->validated(); - - - DB::beginTransaction(); - try { - $this->paymentReppository->update($id, $request->all()); - DB::commit(); - } catch (\Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('payment.index')->with('success', 'Updated successfully'); - } - - public function destroy(int $id = 0) - { - $payment = $this->paymentReppository->find($id); - - if (! $payment) { - return back()->withErrors('Invalid request'); - } - - DB::beginTransaction(); - try { - $this->paymentReppository->delete($id); - DB::commit(); - } catch (\Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('payment.index')->with('success', 'Deleted successfully'); - } -} diff --git a/app/Http/Controllers/admin/PostController.php b/app/Http/Controllers/admin/PostController.php deleted file mode 100644 index fcf90a1..0000000 --- a/app/Http/Controllers/admin/PostController.php +++ /dev/null @@ -1,111 +0,0 @@ -postRepository->filter($request->merge(['page' => $request->get('page') ?? 1])->all()); - - return view('administrator.pages.post.index', compact('title', 'posts')); - } - public function create() - { - $title = 'Add new Post'; - - return view('administrator.pages.post.add', compact('title')); - } - - public function store(PostRequest $request) - { - $request->validated(); - - $isExits = $this->postRepository->findBy('slug', $request->get('slug')); - - if ($isExits) { - return back()->withErrors('The Slug already exits'); - } - - DB::beginTransaction(); - try { - $this->postRepository->create($request->all()); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('post.index')->with('success', 'Added successfully'); - } - - - public function edit(int $id = 0) - { - $post = $this->postRepository->find($id); - if (!$post) { - return back()->withErrors('Invalid request'); - } - $title = 'Edit Post'; - - return view('administrator.pages.post.edit', compact('title', 'post')); - } - - public function update(int $id = 0,PostRequest $request) - { - $post = $this->postRepository->find($id); - if (!$post) { - return back()->withErrors('Invalid request'); - } - - $request->validated(); - - if($request->slug !== $post->slug) - { - $isExits = $this->postRepository->findBy('slug', $request->get('slug')); - - if ($isExits) { - return back()->withErrors('The Slug already exits'); - } - } - DB::beginTransaction(); - try { - $this->postRepository->update($id,$request->all()); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('post.index')->with('success', 'Updated successfully'); - } - - public function destroy(int $id = 0) - { - $post = $this->postRepository->find($id); - - if (!$post) { - return back()->withErrors('Invalid request'); - } - DB::beginTransaction(); - try { - $this->postRepository->delete($id); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return redirect()->route('post.index')->with('success', 'Deleted successfully'); - } -} diff --git a/app/Http/Controllers/admin/RolesController.php b/app/Http/Controllers/admin/RolesController.php deleted file mode 100644 index 25a2b5c..0000000 --- a/app/Http/Controllers/admin/RolesController.php +++ /dev/null @@ -1,127 +0,0 @@ -orderby('id')->get(); - - $title = 'Add new administrator role'; - - return view('administrator.pages.role.add', compact('permission', 'title')); - } - public function store(Request $request) - { - $request->validate( - [ - 'name' => 'required' - ], - [ - 'name.required' => 'Please enter name role' - ] - ); - - try { - - DB::beginTransaction(); - - $role = Role::create([ - 'name' => $request->name, - 'guard_name' => 'admins' - ]); - - $permissions = Permission::whereIn('id',$request->permission)->get(); - - $role->givePermissionTo($permissions); - - DB::commit(); - - }catch (Exception $exception){ - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('role.index')->with('success','Add new administrator role successfully'); - } - public function edit($id = 0) - { - $role = Role::find($id); - - if (! $role) { - return back()->withErrors('Invalid request'); - } - - $permission = Permission::select('id','name')->orderby('id')->get(); - - $title = 'Edit administrator role'; - - return view('administrator.pages.role.edit', compact('permission', 'title', 'role')); - - } - public function update(Request $request, $id = 0) - { - $role = Role::find($id); - - if (! $role) { - return back()->withErrors('Invalid request'); - } - $request->validate( - [ - 'name' => 'required' - ], - [ - 'name.required' => 'Please enter name role' - ] - ); - try { - - DB::beginTransaction(); - - $role->name = $request->name; - $role->save(); - - $permissions = Permission::whereIn('id',$request->permission)->get(); - - $role->syncPermissions($permissions); - - DB::commit(); - - }catch (Exception $exception){ - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('role.index')->with('success','Update new value for administrator role successfully'); - } - public function destroy($id = 0) - { - $role = Role::find($id); - - if (! $role) { - return back()->withErrors('Invalid request'); - } - - $role->revokePermissionTo($role->getAllPermissions()); - - $role->delete(); - - return redirect()->route('role.index')->with('success','Remove administrator role successfully'); - - } -} diff --git a/app/Http/Controllers/admin/SettingController.php b/app/Http/Controllers/admin/SettingController.php deleted file mode 100644 index 66b5c29..0000000 --- a/app/Http/Controllers/admin/SettingController.php +++ /dev/null @@ -1,72 +0,0 @@ -except(['_token', '_method']) as $key => $value) { - if (!empty($key)) { - if (is_array($value)) { - $value = json_encode($value); - } - - // Nếu key là smtp -> kiểm tra và mã hoá password nếu có - if ($key === 'smtp') { - $decoded = is_array($value) ? $value : json_decode($value, true); - - if (isset($decoded['password']) && !empty($decoded['password'])) { - $decoded['password'] = SignRSA::EncryptFunc($decoded['password']); - } - - $value = json_encode($decoded); - } - - $this->settingRepository->updateOrCreateByKey($key, [ - 'key' => $key, - 'value' => $value - ]); - } - } - - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return back()->with('success', 'Updated Successfully'); - } -} diff --git a/app/Http/Controllers/admin/SupportController.php b/app/Http/Controllers/admin/SupportController.php deleted file mode 100644 index 823e585..0000000 --- a/app/Http/Controllers/admin/SupportController.php +++ /dev/null @@ -1,87 +0,0 @@ -ticketRepository->filter($request->merge(['page' => $request->get('1') ?? 1])->all()); - $title = "All Support Ticket"; - return view('administrator.pages.support.index', compact('title', 'tickets')); - } - - public function edit(int $ticket_num = 0) - { - - $ticket = $this->ticketRepository->findWithRelationshipsByKey('ticket_num', $ticket_num, [ - 'reply.user', - 'customer' - ]); - - if (! $ticket) { - return back()->withErrors('Invalid Request'); - } - - $title = "Ticket #" . $ticket_num; - - return view("administrator.pages.support.view", compact('title', 'ticket')); - } - - public function reply(int $id = 0, TicketReplyRequest $request) - { - - $ticket = $this->ticketRepository->find($id); - if (! $ticket) { - return back()->withInput()->withErrors('Invalid Request'); - } - $request->validated(); - - $request->merge(['user_id' => auth()->guard('admin')->id()]); - - DB::beginTransaction(); - try { - $this->ticketReplyRepository->create($request->all()); - $this->ticketRepository->updateBy('ticket_num',$ticket->ticket_num,['status' => 1]); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withInput()->withErrors($error->getMessage()); - } - return back()->with('success', 'Reply successfully'); - } - public function close(int $ticket_num = 0) - { - - $ticket = $this->ticketRepository->findBy('ticket_num', $ticket_num); - - if (! $ticket) { - return back()->withErrors('Invalid Request'); - } - - DB::beginTransaction(); - try { - $this->ticketRepository->updateBy('ticket_num',$ticket_num,['status' => 0]); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withInput()->withErrors($error->getMessage()); - } - return redirect()->route('support.index')->with('success', 'Closed successfully'); - } -} diff --git a/app/Http/Controllers/admin/TaskController.php b/app/Http/Controllers/admin/TaskController.php deleted file mode 100644 index 32cbc13..0000000 --- a/app/Http/Controllers/admin/TaskController.php +++ /dev/null @@ -1,225 +0,0 @@ -all(), [ - 'userid' => 'numeric|required', - 'type' => 'numeric|required', - 'product' => 'numeric|required', - 'action' => 'required' - ]); - - if ($validator->fails()) { - return back()->withErrors($validator); - } - $jobs = null; - - try { - DB::beginTransaction(); - switch ($request->type) { - case 3: - $validate = Validator::make( - $request->all(), - [ - 'domain' => 'required' - ], - [ - 'domain' => ['required' => 'Please enter your domain name before creating Hosting!'] - ] - ); - if ($validate->fails()) { - return back()->withErrors($validate); - } - $regexFilterDomain = '/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/m'; - - $filter = filter_var( - $request->domain, - FILTER_VALIDATE_REGEXP, - [ - 'options' => [ - 'regexp' => $regexFilterDomain - ] - ] - ); - - if ($filter === false) { - return back()->withErrors("The domain name is not in the correct format"); - } - - $server = $this->moduleHostingServerRepository->get_active(); - - if (!$server) { - return back()->withErrors('Invalid request.'); - } - - $this->moduleHostingServerRepository->update($request->product,['name' => $request->domain, 'module_server_id' => $server->first()->id]); - - $jobs = new CreateHostingJobs($request->product); - - break; - case 4: - $validate = Validator::make($request->all(), [ - 'os_group_id' => 'numeric|required|not__in:0', - 'os_version' => 'numeric|required|not__in:0' - ], [ - 'os_group_id.required' => 'Please choose Os', - 'os_version.required' => 'Please choose Os Version' - ]); - if ($validate->fails()) { - return back()->withErrors($validate); - } - $data = []; - - if ($request->os_version) { - $data['os_id'] = $request->os_version; - } - $ipaddress = $this->ipAddressRepository->findBy('module_host_id',$request->module_host_id); - if ($ipaddress) { - $data['module_ipaddress_id'] = $ipaddress->id; - - $this->ipAddressRepository->update($ipaddress->id,[ - 'status' => 1 - ]); - } - $this->vmwareRepository->update($request->product,$data); - $jobs = new CreateVMJobs($request->product); - } - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - if ($jobs) { - - dispatch($jobs)->delay(now()->addSeconds(5)); - - return back()->with('success', 'The system has received the information. Please wait a second'); - } - return back()->withErrors('Invalid request.'); - } - - public function Console(Request $request) - { - $validator = Validator::make( - $request->all(), - [ - 'product' => 'numeric|required|not__in:0', - 'vmware-id' => 'string|required' - ], - [ - 'product' => [ - "required" => "Product can't empty" - ], - 'vmware-id' => [ - "required" => "Product can't empty" - ] - ] - ); - if ($validator->fails()) { - return back()->withErrors($validator); - } - $product = $this->vmwareRepository->findWithRelationships($request->product,['ipaddress.host.datacenter']); - - if (! $product) { - return back()->withErrors("Can't find Product for ID #" . $request->product); - } - $title = 'View Console for VirtualMachine #' . $request->{'vmware-id'}; - - return view('administrator.pages.customer.console', compact('title', 'product')); - } - - public function Action(Request $request) - { - $validator = Validator::make($request->all(), [ - 'product' => 'numeric|required', - 'userid' => 'numeric|required', - 'action' => 'required' - ]); - - if ($validator->fails()) { - return back()->withErrors($validator); - } - - // ponytail: map action → waitFor (operations_ready value to poll WHILE waiting) - $waitMap = [ - 'Suspend' => true, - 'ShutdownGuest' => true, - 'PowerOff' => true, - 'Reboot' => false, - 'Start' => false, - ]; - - dispatch(new VMJobs($request->product, $request->action)); - - if (isset($waitMap[$request->action])) { - dispatch(new WaitStatusVirtualMachine($request->product, $waitMap[$request->action])); - } - - Log::create([ - 'agent' => $request->userAgent(), - 'client_ip' => $request->getClientIp(), - 'username' => auth()->guard('admin')->user()->username, - 'description' => auth()->guard('admin')->user()->username . ": $request->action for VirtualMachine $request->product", - 'customer_id' => $request->userid - ]); - - return back()->with('success', 'The system has received the processing request, please wait a moment.'); - } - - 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 = $this->vmwareRepository->find($request->product); - - if (! $product) { - return back()->withErrors("Can't find Product"); - } - $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'); - } -} diff --git a/app/Http/Controllers/admin/TemplateController.php b/app/Http/Controllers/admin/TemplateController.php deleted file mode 100644 index ea0279e..0000000 --- a/app/Http/Controllers/admin/TemplateController.php +++ /dev/null @@ -1,140 +0,0 @@ -orderBy('type_id')->simplePaginate(20); - $title = "List email template"; - - return view('administrator.pages.template.index', compact('template', 'title')); - } - - public function create() - { - $title = "Add new email template"; - - $type = Type::select('name', 'id')->get(); - - return view('administrator.pages.template.add', compact('title', 'type')); - } - - public function store(Request $request) - { - $request->validate([ - 'type_id' => 'required', - 'name' => 'required', - 'subject' => 'required', - 'message' => 'required' - ]); - try { - DB::beginTransaction(); - Template::create($request->all()); - DB::commit(); - } catch (Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('template.index')->with('success', 'Add hew template success'); - } - - public function edit(Request $request) - { - $validator = Validator::make($request->all(), [ - 'id' => 'numeric|required|not__in:0' - ], [ - 'id' => [ - 'required' => 'Please choose Template' - ] - ]); - - if ($validator->fails()) { - return back()->withErrors($validator); - } - - $template = Template::find($request->id); - - if (!$template) { - return back()->withErrors('Invalidate request'); - } - - $title = "View email template"; - - $type = Type::select('name', 'id')->get(); - - return view('administrator.pages.template.edit', compact('title', 'type', 'template')); - } - public function update(Request $request) - { - $validator = Validator::make($request->all(), - [ - 'type_id' => 'required', - 'name' => 'required', - 'subject' => 'required', - 'status' => 'required', - 'message' => 'required', - 'id' => 'numeric|required|not__in:0' - ], - [ - 'id' => [ - 'required' => 'Please choose Template' - ] - ] - ); - - if ($validator->fails()) { - return back()->withErrors($validator); - } - - $template = Template::find($request->id); - - if (!$template) { - return back()->withErrors('Invalidate request'); - } - try { - DB::beginTransaction(); - $template->update($request->except(['id','_token','_method'])); - DB::commit(); - } catch (\Exception $error) - { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('template.index')->with('success','Update new information for template'); - } - public function destroy(Request $request) - { - $validator = Validator::make($request->all(), [ - 'id' => 'numeric|required|not__in:0' - ], [ - 'id' => [ - 'required' => 'Please choose Template' - ] - ]); - if ($validator->fails()) { - return back()->withErrors($validator); - } - - $template = Template::find($request->id); - - if (!$template) { - return back()->withErrors('Invalidate request'); - } - if (in_array($request->id,[])) - { - return back()->withErrors('Invalidate request'); - } - $template->detele(); - return back()->with('success','Delete success'); - } -} diff --git a/app/Http/Controllers/admin/UserController.php b/app/Http/Controllers/admin/UserController.php deleted file mode 100644 index 6ce3dec..0000000 --- a/app/Http/Controllers/admin/UserController.php +++ /dev/null @@ -1,235 +0,0 @@ -paginate(15); - - return view('administrator.pages.user.index', compact('title', 'users')); - } - - public function store(Request $request) - { - - - $request->validate( - [ - 'username' => 'required', - 'administrator_roles' => 'required', - 'fullname' => 'required', - 'email' => 'required', - 'password' => 'required', - 'password_confirm' => 'required', - ], - [ - 'username' => [ - 'required' => 'Please enter Username' - ], - 'administrator_roles' => [ - 'required' => 'Please select roles' - ], - 'fullname' => [ - 'required' => 'Please enter your fullname' - ], - 'email' => [ - 'required' => 'Please enter your email' - ], - 'password' => [ - 'required' => 'Please enter Password' - ], - 'password_confirm' => [ - 'required' => 'Please enter Confirm Password' - ] - ]); - - - $CheckUsername = User::where('username', $request->username)->first(); - - if ($CheckUsername) { - return back()->withErrors('Username already exists'); - } - - $CheckEmail = User::where('email', $request->email)->first(); - - if ($CheckEmail) { - return back()->withErrors('Email already exists'); - } - - if ($request->password !== $request->password_confirm) { - return back()->withErrors('Incorrect Confirm Password'); - } - - $data = [ - 'fullname' => $request->fullname, - 'username' => $request->username, - 'email' => $request->email, - 'password' => Hash::make($request->password), - 'email_verified_at' => Carbon::now(), - ]; -// dd($data); - try { - - DB::beginTransaction(); - - $user = User::create($data); - - $user->assignRole($request->administrator_roles); - - DB::commit(); - - } catch (Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('administrator.pages.user.index')->with('success', 'New account added successfully'); - } - - public function create() - { - $roles = Role::select('name')->get(); - $title = "Add new Administrator"; - - return view('administrator.pages.user.add', compact('roles', 'title')); - - } - - public function edit(Request $request) - { - $request->validate( - [ - 'id' => 'required|numeric|not__in:0' - ], - [ - 'id' => [ - 'required' => 'Please choose Administrator' - ] - ] - ); - $user = User::with('roles')->find($request->id); - - if (!$user) { - return back()->withErrors("Invalidate Request"); - } - $roles = Role::select('name')->get(); - $title = 'Edit admin information'; - - return view('administrator.pages.user.edit', compact('user', 'title', 'roles')); - } - - public function update(Request $request) - { - $validator = Validator::make($request->all(), - [ - 'username' => 'required', - 'administrator_roles' => 'required', - 'fullname' => 'required', - 'email' => 'required', - 'id' => 'required|numeric|not__in:0' - ], - [ - 'username' => [ - 'required' => 'Please enter Username' - ], - 'administrator_roles' => [ - 'required' => 'Please select roles' - ], - 'fullname' => [ - 'required' => 'Please enter your fullname' - ], - 'email' => [ - 'required' => 'Please enter your email' - ], - 'id' => [ - 'required' => 'Invalidate request' - ], - ] - ); - if ($validator->fails()) { - return back()->withErrors($validator); - } - $user = User::with('roles')->find($request->id); - if (!$user) { - return back()->withErrors('Invalidate request'); - } - $data = $request->except(['_token', '_method', 'password', 'password_confirm_2', 'password_confirm', 'id']); - - if (!empty($request->password_confirm_2)) { - - $validator_2 = Validator::make($request->all(), - [ - 'password' => 'required', - 'password_confirm' => 'required', - ], - [ - 'password.required' => 'Please enter your password', - 'password_confirm.required' => 'Please enter password confirm 2' - ] - ); - - if ($validator_2->fails()) { - return back()->withErrors($validator_2); - } - if (!Hash::check($request->password_confirm_2, $user->password)) { - return back()->withErrors("Incorrect password"); - } - if (!empty($request->password_confirm) && $request->password_confirm !== $request->password) { - return back()->withErrors("Incorrect Confirm Password"); - } - $data['password'] = Hash::make($request->password_confirm); - } - - try { - DB::beginTransaction(); - $user->update($data); - $user->syncRoles($request->administrator_roles); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return back()->with('success', 'Update new information for administrator account success'); - } - - public function destroy(Request $request) - { - $request->validate( - [ - 'id' => 'required' - ], - [ - 'id' => [ - 'required' => 'Please choose Administrator' - ] - ] - ); - $user = User::with('roles')->find($request->id); - - if (!$user) { - return back()->withErrors("Invalidate Request"); - } - - if ((int)$request->id === 1) { - return back()->withErrors("Invalidate Request"); - } - - $user->removeRole($user->roles->first()->name); - $user->delete(); - - return back()->with('success', 'Delete success'); - } - -} diff --git a/app/Http/Controllers/admin/VCenterManagerController.php b/app/Http/Controllers/admin/VCenterManagerController.php index e230251..55749e2 100644 --- a/app/Http/Controllers/admin/VCenterManagerController.php +++ b/app/Http/Controllers/admin/VCenterManagerController.php @@ -4,444 +4,14 @@ namespace App\Http\Controllers\admin; use App\Http\Controllers\Controller; use App\Mail\VmSuspendedMail; -use App\Models\ModuleIpAddress; use App\Models\ModuleVmware; use App\Models\VirtualMachines; -use App\Repositories\Module\Datacenter\DatacenterRepositoryInterface; -use App\Repositories\Module\Datastore\DatastoreRepositoryInterface; -use App\Repositories\Module\Folder\FolderRepositoryInterface; -use App\Repositories\Module\Host\HostRepositoryInterface; -use App\Repositories\Module\IpAddress\IpAddressRepositoryInterface; -use App\Repositories\Module\Network\NetworkRepositoryInterface; -use App\Repositories\vCenter\vCenterRepositoryInterface; use Illuminate\Http\Request; use App\Support\TlsHttpClientFactory; use Illuminate\Support\Facades\Mail; -use Yajra\DataTables\Facades\DataTables; class VCenterManagerController extends Controller { - public function __construct( - private readonly vCenterRepositoryInterface $vCenterRepo, - private readonly DatacenterRepositoryInterface $datacenterRepo, - private readonly HostRepositoryInterface $hostRepo, - private readonly DatastoreRepositoryInterface $datastoreRepo, - private readonly NetworkRepositoryInterface $networkRepo, - private readonly FolderRepositoryInterface $folderRepo, - private readonly IpAddressRepositoryInterface $ipRepo, - ) { - parent::__construct(); - } - - /** - * Main view — server-render vCenters + their datacenters for initial tree. - */ - public function index() - { - $vcenters = $this->vCenterRepo->filter( - [], - ['datacenters'], - ['datacenters'], - ['id', 'host', 'status'] - ); - - return view('administrator.pages.vcenter.manager', compact('vcenters')); - } - - /** - * AJAX — Lazy-load children of a tree node. - * - * GET vcenter-manager/tree/children?type=datacenter&id=1 - * Returns JSON array of { id, type, name, icon, has_children, meta } - */ - public function treeChildren(Request $request) - { - $type = $request->input('type'); - $id = (int) $request->input('id'); - - return match ($type) { - 'vcenter' => $this->vcenterNodes($id), - 'datacenter' => $this->hostNodes($id), - 'host' => $this->hostChildNodes($id), - default => response()->json([]), - }; - } - - private function vcenterNodes(int $vcenterId) - { - $datacenters = $this->datacenterRepo->filter( - ['vcenter_id' => $vcenterId], - [], - [], - ['id', 'name'] - ); - - return response()->json($datacenters->map(fn ($dc) => [ - 'id' => $dc->id, - 'type' => 'datacenter', - 'name' => $dc->name, - 'icon' => 'fas fa-building', - 'has_children' => true, - 'meta' => [], - ])); - } - - private function hostNodes(int $datacenterId) - { - $hosts = $this->hostRepo->filter( - ['module_datacenter_id' => $datacenterId], - [], - ['datastore', 'network'], - ['id', 'name', 'template'] - ); - - return response()->json($hosts->map(fn ($h) => [ - 'id' => $h->id, - 'type' => 'host', - 'name' => $h->name, - 'icon' => 'fas fa-server', - 'has_children' => true, - 'meta' => [ - 'datastores' => $h->datastore_count ?? 0, - 'networks' => $h->network_count ?? 0, - 'template' => (bool) $h->template, - ], - ])); - } - - private function hostChildNodes(int $hostId) - { - $datastores = $this->datastoreRepo->filter( - ['module_host_id' => $hostId], - [], - [], - ['id', 'name', 'type', 'space', 'status'] - ); - - $networks = $this->networkRepo->filter( - ['module_host_id' => $hostId], - [], - ['ipAddresses'], - ['id', 'name', 'type'] - ); - - $children = []; - - foreach ($datastores as $ds) { - $children[] = [ - 'id' => $ds->id, - 'type' => 'datastore', - 'name' => $ds->name, - 'icon' => 'fas fa-hdd', - 'has_children' => false, - 'meta' => ['space' => $ds->space, 'type' => $ds->type, 'status' => $ds->status], - ]; - } - - foreach ($networks as $net) { - $children[] = [ - 'id' => $net->id, - 'type' => 'network', - 'name' => $net->name, - 'icon' => 'fas fa-network-wired', - 'has_children' => false, - 'meta' => ['type' => $net->type, 'ips' => $net->ip_addresses_count ?? 0], - ]; - } - - return response()->json($children); - } - - /** - * AJAX — Return rendered detail panel HTML for a tree node. - * - * GET vcenter-manager/tree/detail?type=host&id=3 - * Returns JSON { html, type, id } - */ - public function nodeDetail(Request $request) - { - $type = $request->input('type'); - $id = (int) $request->input('id'); - - $data = match ($type) { - 'vcenter' => $this->vCenterDetail($id), - 'datacenter' => $this->datacenterDetail($id), - 'host' => $this->hostDetail($id), - 'datastore' => $this->datastoreDetail($id), - 'network' => $this->networkDetail($id), - default => null, - }; - - if (! $data) { - return response()->json(['error' => 'Not found'], 404); - } - - $html = view("administrator.pages.vcenter.partials.detail-{$type}", $data)->render(); - - return response()->json(compact('html', 'type', 'id')); - } - - private function vCenterDetail(int $id): array - { - $item = $this->vCenterRepo->find($id); - $datacenterCount = $this->datacenterRepo->filter(['vcenter_id' => $id])->count(); - $vmCount = ModuleVmware::where('vcenter_id', $id)->count(); - - return compact('item', 'datacenterCount', 'vmCount'); - } - - private function datacenterDetail(int $id): array - { - $item = $this->datacenterRepo->find($id); - $hosts = $this->hostRepo->filter( - ['module_datacenter_id' => $id], - [], - ['datastore', 'network'], - ['id', 'name', 'template'] - ); - $folders = $this->folderRepo->filter( - ['module_datacenter_id' => $id], - [], - [], - ['id', 'name', 'type', 'folder'] - ); - - return compact('item', 'hosts', 'folders'); - } - - private function hostDetail(int $id): array - { - $item = $this->hostRepo->find($id); - $datastores = $this->datastoreRepo->filter(['module_host_id' => $id]); - $networks = $this->networkRepo->filter( - ['module_host_id' => $id], - [], - ['ipAddresses'], - ['id', 'name', 'type'] - ); - $ipTotal = ModuleIpAddress::where('module_host_id', $id)->count(); - $ipFree = ModuleIpAddress::where('module_host_id', $id)->where('status', 0)->count(); - - return compact('item', 'datastores', 'networks', 'ipTotal', 'ipFree'); - } - - private function datastoreDetail(int $id): array - { - $item = $this->datastoreRepo->find($id); - return compact('item'); - } - - private function networkDetail(int $id): array - { - $item = $this->networkRepo->find($id); - $ips = $this->ipRepo->filter( - ['module_network_id' => $id], - [], - [], - ['id', 'ip', 'gateway', 'netmask', 'dns', 'status'] - ); - - return compact('item', 'ips'); - } - - /** - * AJAX — DataTables JSON for Virtual Machines scoped by tree node. - * - * GET vcenter-manager/vms/datatable?scope_type=host&scope_id=2 - */ - public function vmDatatable(Request $request) - { - $scopeType = $request->input('scope_type', 'all'); - $scopeId = (int) $request->input('scope_id', 0); - - $query = ModuleVmware::with(['customer', 'ipAddress', 'vCenter', 'os']); - - if ($scopeType === 'vcenter' && $scopeId) { - $query->where('vcenter_id', $scopeId); - } elseif ($scopeType === 'datacenter' && $scopeId) { - $hostIds = $this->hostRepo - ->filter(['module_datacenter_id' => $scopeId], [], [], ['id']) - ->pluck('id'); - $query->whereHas('ipAddress', fn ($q) => $q->whereIn('module_host_id', $hostIds)); - } elseif ($scopeType === 'host' && $scopeId) { - $query->whereHas('ipAddress', fn ($q) => $q->where('module_host_id', $scopeId)); - } - - return DataTables::of($query) - ->addColumn('customer_name', fn ($vm) => $vm->customer?->fullname ?? 'N/A') - ->addColumn('ip_address', fn ($vm) => $vm->ipAddress?->ip ?? 'N/A') - ->addColumn('vcenter_host', fn ($vm) => $vm->vCenter?->host ?? 'N/A') - ->addColumn('os_name', fn ($vm) => $vm->os?->version ?? 'N/A') - ->addColumn('status_badge', fn ($vm) => $vm->getStatus()) - ->rawColumns(['status_badge']) - ->make(true); - } - - public function wizardVcenters() - { - $vcenters = $this->vCenterRepo->filter(['status' => 1], [], [], ['id', 'host', 'user']); - return response()->json($vcenters); - } - - public function wizardStoreVcenter(Request $request) - { - $data = $request->validate([ - 'host' => 'required|string', - 'user' => 'required|string', - 'password' => 'required|string', - 'status' => 'nullable|integer', - ]); - - try { - $vcenter = \App\Models\Vcenter::create([ - 'host' => $data['host'], - 'user' => $data['user'], - 'password' => $data['password'], - 'status' => $data['status'] ?? 1, - ]); - - return response()->json([ - 'success' => true, - 'vcenter' => ['id' => $vcenter->id, 'host' => $vcenter->host, 'user' => $vcenter->user] - ]); - } catch (\Exception $e) { - return response()->json(['error' => $e->getMessage()], 500); - } - } - - public function wizardDatacenters(Request $request) - { - $vcenterId = $request->input('vcenter_id'); - $vcenter = $this->vCenterRepo->find($vcenterId); - - if (!$vcenter) { - return response()->json(['error' => 'vCenter not found'], 404); - } - - $vcenterService = new \App\Http\Services\vCenter(); - $loginResult = $vcenterService->loginServer($vcenter); - - if (!$loginResult) { - return response()->json(['error' => 'Cannot login to vCenter'], 400); - } - - $datacenters = $vcenterService->getAllDatacenter(); - - if ($datacenters === false || $datacenters === 'Login_Again') { - return response()->json(['error' => 'Cannot get datacenters'], 400); - } - - return response()->json(array_map(fn($dc) => [ - 'datacenter_id' => $dc['datacenter'] ?? $dc['datacenter_id'] ?? '', - 'name' => $dc['name'] ?? $dc['datacenter'] ?? '', - ], $datacenters)); - } - - public function wizardHosts(Request $request) - { - $vcenterId = $request->input('vcenter_id'); - $datacenterId = $request->input('datacenter_id'); - - $vcenter = $this->vCenterRepo->find($vcenterId); - - if (!$vcenter) { - return response()->json(['error' => 'vCenter not found'], 404); - } - - $vcenterService = new \App\Http\Services\vCenter(); - $vcenterService->loginServer($vcenter); - - $hosts = $vcenterService->getAllHost($datacenterId); - - if ($hosts === false || $hosts === 'Login_Again') { - return response()->json(['error' => 'Cannot get hosts'], 400); - } - - return response()->json(array_map(fn($host) => [ - 'host_id' => $host['host'] ?? $host['host_id'] ?? '', - 'name' => $host['name'] ?? $host['host'] ?? '', - ], $hosts)); - } - - public function wizardDatastoreNetwork(Request $request) - { - $vcenterId = $request->input('vcenter_id'); - $hostId = $request->input('host_id'); - - $vcenter = $this->vCenterRepo->find($vcenterId); - - if (!$vcenter) { - return response()->json(['error' => 'vCenter not found'], 404); - } - - $vcenterService = new \App\Http\Services\vCenter(); - $vcenterService->loginServer($vcenter); - - $datastores = $vcenterService->getAllDataStore($hostId); - $networks = $vcenterService->getNetwork($hostId); - - return response()->json([ - 'datastores' => $datastores ? array_map(fn($ds) => [ - 'datastore' => $ds['datastore'] ?? $ds['name'] ?? '', - 'name' => $ds['name'] ?? $ds['datastore'] ?? '', - 'type' => $ds['type'] ?? 'UNKNOWN', - ], $datastores) : [], - 'networks' => $networks ? array_map(fn($net) => [ - 'network_id' => $net['network'] ?? $net['name'] ?? '', - 'name' => $net['name'] ?? $net['network'] ?? '', - 'type' => $net['type'] ?? 'UNKNOWN', - ], $networks) : [], - ]); - } - - public function wizardSave(Request $request) - { - $data = $request->validate([ - 'vcenter_id' => 'required|integer', - 'datacenter' => 'required|array', - 'host' => 'required|array', - 'datastores' => 'nullable|array', - 'networks' => 'nullable|array', - ]); - - try { - $datacenter = \App\Models\ModuleDatacenter::create([ - 'name' => $data['datacenter']['name'], - 'datacenter_id' => $data['datacenter']['datacenter_id'], - 'vcenter_id' => $data['vcenter_id'], - ]); - - $host = \App\Models\ModuleHost::create([ - 'name' => $data['host']['name'], - 'host_id' => $data['host']['host_id'], - 'module_datacenter_id' => $datacenter->id, - ]); - - foreach ($data['datastores'] ?? [] as $ds) { - \App\Models\ModuleDatastore::create([ - 'name' => $ds['name'], - 'datastore' => $ds['datastore'], - 'type' => $ds['type'], - 'module_host_id' => $host->id, - 'status' => 1, - ]); - } - - foreach ($data['networks'] ?? [] as $net) { - \App\Models\ModuleNetwork::create([ - 'name' => $net['name'], - 'network' => $net['network_id'], - 'type' => $net['type'], - 'module_host_id' => $host->id, - ]); - } - - return response()->json(['success' => true, 'message' => 'Thêm mới infrastructure thành công!']); - } catch (\Exception $e) { - return response()->json(['error' => $e->getMessage()], 500); - } - } - public function powerState(Request $request) { $vmId = (int) $request->input('vm_id'); diff --git a/app/Http/Controllers/admin/package/Datacenter.php b/app/Http/Controllers/admin/package/Datacenter.php deleted file mode 100644 index 4613172..0000000 --- a/app/Http/Controllers/admin/package/Datacenter.php +++ /dev/null @@ -1,145 +0,0 @@ -datacenterRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ], - ['vCenter'] - ); - $title = 'List all datacenter'; - - return view('administrator.pages.package.datacenter.index', compact('datacenter', 'title')); - } - public function create() - { - $title = 'Add datacenter'; - return view('administrator.pages.package.datacenter.add', compact('title')); - } - public function store(DatacenterRequest $request) - { - $request->validated(); - - $vcenter = $this->vCenterRepository->find($request->get('vcenter_id')); - if (!$vcenter) { - return back()->withErrors('Invalid request.'); - } - $isCreated = $this->datacenterRepository->findBy('datacenter_id', $request->get('datacenter_id')); - if ($isCreated) { - return back()->withErrors('The Datacenter ID already exists.'); - } - DB::beginTransaction(); - try { - $this->datacenterRepository->create($request->all()); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.datacenter.index')->with('success', 'Add new datacenter successfully!'); - } - public function edit(int $id = 0) - { - $datacenter = $this->datacenterRepository->find($id); - - if (!$datacenter) { - return back()->withErrors('Invalid request.'); - } - $title = 'Edit Datacenter'; - return view('administrator.pages.package.datacenter.edit', compact('title', 'datacenter')); - } - public function update(int $id = 0, DatacenterRequest $request) - { - $datacenter = $this->datacenterRepository->find($id); - - if (!$datacenter) { - return back()->withErrors('Invalid request.'); - } - $request->validated(); - if ($request->get('datacenter_id') !== $datacenter->datacenter_id) { - $isCreated = $this->datacenterRepository->findBy('datacenter_id', $request->get('datacenter_id')); - if ($isCreated) { - return back()->withErrors('The Datacenter ID already exists.'); - } - } - DB::beginTransaction(); - try { - $this->datacenterRepository->update($id, $request->all()); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.datacenter.index')->with('success', 'Update datacenter successfully!'); - } - public function destroy(int $id = 0) - { - $datacenter = $this->datacenterRepository->find($id); - - if (!$datacenter) { - return back()->withErrors('Invalid request.'); - } - - DB::beginTransaction(); - try { - $this->datacenterRepository->delete($id); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.datacenter.index')->with('success', 'Deleted datacenter successfully!'); - } - public function sync(SyncDatacenter $syncDatacenter) - { - dispatch($syncDatacenter)->delay(now()->addSeconds(5)); - return back()->with('success', 'The process is being established, please wait'); - } - - public function datatable(Request $request) - { - $query = $this->datacenterRepository->filter( - $request->all(), - ['vCenter'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn( - 'action', - function ($d) { - return view('administrator.pages.package.datacenter.datatable.action', ['row' => $d])->render(); - } - ) - ->filterColumn('v_center.host', function ($query, $keyword) { - $query->whereHas('vCenter', function ($q) use ($keyword) { - $q->where('host', 'like', "%{$keyword}%"); - }); - }) - ->make(true); - } - -} diff --git a/app/Http/Controllers/admin/package/Datastore.php b/app/Http/Controllers/admin/package/Datastore.php deleted file mode 100644 index 8a8dc5e..0000000 --- a/app/Http/Controllers/admin/package/Datastore.php +++ /dev/null @@ -1,153 +0,0 @@ -datastoreRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ], - [ - 'host.datacenter.vCenter' - ] - ); - $title = 'List all datastore'; - return view('administrator.pages.package.datastore.index', compact('title', 'datastore')); - } - public function sync(SyncDatastore $syncDatastore) - { - - dispatch($syncDatastore)->delay(now()->addSeconds(5)); - return back()->with('success', 'The process is being established, please wait'); - } - public function create() - { - $title = 'Add Datastore'; - return view('administrator.pages.package.datastore.add', compact('title')); - } - public function store(DatastoreRequest $request) - { - $request->validated(); - - $host = $this->hostRepository->find($request->get('module_host_id')); - if (!$host) { - return back()->withErrors('Invalid Request'); - } - $isCreated = $this->datastoreRepository->findBy('datastore', $request->get('datastore')); - - if ($isCreated) { - return back()->withErrors('The Datastore ID already exists'); - } - DB::beginTransaction(); - try { - $this->datastoreRepository->create($request->all()); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.datastore.index')->with('success', 'Add new datastore successfully'); - } - public function edit(int $id = 0) - { - $datastore = $this->datastoreRepository->findWithRelationships($id, ['host.datacenter']); - - if (!$datastore) { - return back()->withErrors('Invalid Request'); - } - $title = 'Edit Datastore'; - - return view('administrator.pages.package.datastore.edit', compact('title', 'datastore')); - } - public function update(int $id, DatastoreRequest $request) - { - $datastore = $this->datastoreRepository->find($id); - if (!$datastore) { - return redirect()->route('package.datastore.index')->withErrors('Invalid request.'); - } - $request->validated(); - - $host = $this->hostRepository->find($request->get('module_host_id')); - if (!$host) { - return back()->withErrors('Invalid Request'); - } - if ($request->get('datastore') !== $datastore->datastore) { - $isCreated = $this->datastoreRepository->findBy('datastore', $request->get('datastore')); - - if ($isCreated) { - return back()->withErrors('The Datastore ID already exists'); - } - } - DB::beginTransaction(); - try { - $this->datastoreRepository->update($id, $request->all()); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.datastore.index')->with('success', 'Updated successfully'); - } - public function destroy(int $id = 0) - { - $datastore = $this->datastoreRepository->find($id); - - if (!$datastore) { - return back()->withErrors('Invalid request.'); - } - - DB::beginTransaction(); - try { - $this->datastoreRepository->delete($id); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.datastore.index')->with('success', 'Deleted datacenter successfully!'); - } - - public function datatable(Request $request) - { - $query = $this->datastoreRepository->filter( - $request->all(), - ['host.datacenter.vCenter'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn( - 'action', - fn($d) => - 'Edit' - ) - ->filterColumn('host.datacenter.v_center.host', function ($query, $keyword) { - $query->whereHas('host.datacenter.vCenter', function ($q) use ($keyword) { - $q->where('host', 'like', "%{$keyword}%"); - }); - }) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/package/Folder.php b/app/Http/Controllers/admin/package/Folder.php deleted file mode 100644 index e0f6cb8..0000000 --- a/app/Http/Controllers/admin/package/Folder.php +++ /dev/null @@ -1,147 +0,0 @@ -folderRepository->filter([ - 'page' => $request->get('page') ?? 1 - ], ['datacenter.vCenter']); - $title = 'List all folder'; - - return view('administrator.pages.package.folder.index', compact('folders', 'title')); - } - public function sync(SyncFolder $syncFolder) - { - dispatch($syncFolder)->delay(now()->addSeconds(5)); - return back()->with('success', 'The process is being established, please wait'); - } - public function create() - { - $title = 'Add Folder'; - return view('administrator.pages.package.folder.add', compact('title')); - } - public function store(FolderRequest $request) - { - $request->validated(); - - $datacenter = $this->datacenterRepository->find($request->get('module_datacenter_id')); - if (!$datacenter) { - return back()->withErrors('Invalid Request'); - } - $isCreated = $this->folderRepository->findBy('folder', $request->get('folder')); - - if ($isCreated) { - return back()->withErrors('The Folder ID already exists'); - } - DB::beginTransaction(); - try { - $this->folderRepository->create($request->all()); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.folder.index')->with('success', 'Add new Folder successfully'); - } - public function edit(int $id = 0) - { - $folder = $this->folderRepository->findWithRelationships($id, ['datacenter']); - if (!$folder) { - return back()->withErrors('Invalid Request'); - } - $title = 'Edit Folder'; - - return view('administrator.pages.package.folder.edit', compact('title', 'folder')); - } - public function update(int $id, FolderRequest $request) - { - $folder = $this->folderRepository->find($id); - if (!$folder) { - return redirect()->route('package.folder.index')->withErrors('Invalid request.'); - } - $request->validated(); - - $datacenter = $this->datacenterRepository->find($request->get('module_datacenter_id')); - if (!$datacenter) { - return back()->withErrors('Invalid Request'); - } - if ($request->get('folder') !== $folder->folder) { - - $isCreated = $this->folderRepository->findBy('folder', $request->get('folder')); - - if ($isCreated) { - return back()->withErrors('The Folder ID already exists'); - } - } - DB::beginTransaction(); - try { - $this->folderRepository->update($id, $request->all()); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.folder.index')->with('success', 'Updated successfully'); - } - public function destroy(int $id = 0) - { - $datastore = $this->folderRepository->find($id); - - if (!$datastore) { - return back()->withErrors('Invalid request.'); - } - - DB::beginTransaction(); - try { - $this->folderRepository->delete($id); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.folder.index')->with('success', 'Deleted successfully!'); - } - - public function datatable(Request $request) - { - $query = $this->folderRepository->filter( - $request->all(), - ['datacenter.vCenter'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn( - 'action', - fn($d) => - 'Edit' - ) - ->filterColumn('datacenter.v_center.host', function ($query, $keyword) { - $query->whereHas('datacenter.vCenter', function ($q) use ($keyword) { - $q->where('host', 'like', "%{$keyword}%"); - }); - }) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/package/Group.php b/app/Http/Controllers/admin/package/Group.php deleted file mode 100644 index c3c5c97..0000000 --- a/app/Http/Controllers/admin/package/Group.php +++ /dev/null @@ -1,184 +0,0 @@ -osGroupRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ], - ['host'] - ); - $title = 'List all group Os for all datastore'; - - return view('administrator.pages.package.group.index', compact('group', 'title')); - } - public function import(Request $request) - { - if (! $request->file) { - return back()->withErrors("Please choose your file"); - } - $data = Excel::toArray([], $request->file); - if (count($data) > 0) { - $data = array_slice($data[0], 1); - $allData = []; - foreach ($data as $row) { - if (isset($row[1]) && isset($row[2]) && isset($row[3]) && !empty($row[1]) && !empty($row[1]) && !empty($row[1])) { - $allData[] = [ - 'name' => $row[1], - 'thumbnail' => $row[2], - 'module_host_id' => $row[3] - ]; - } - } - } - - if (! isset($allData) || (isset($allData) && count($allData) < 1)) { - return back()->withErrors('No data imported'); - } - DB::beginTransaction(); - try { - $this->osGroupRepository->insert($allData); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return back()->with('success', 'Imported successfully'); - } - public function create() - { - $title = 'Add new Group OS'; - - return view('administrator.pages.package.group.add', compact('title')); - } - public function store(OsGroupRequest $request) - { - $request->validated(); - - DB::beginTransaction(); - try { - $this->osGroupRepository->create($request->all()); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.group.index')->with('success', 'Add new Os Group successfully'); - } - public function edit(int $id = 0) - { - - $group = $this->osGroupRepository->findWithRelationships($id, ['host.datacenter']); - if (! $group) { - return back()->withErrors('Can\'t find your group by id ' . $id); - } - $title = 'Edit Group OS'; - - return view('administrator.pages.package.group.edit', compact('title', 'group')); - } - public function update(int $id = 0, OsGroupRequest $request) - { - $group = $this->osGroupRepository->find($id); - if (! $group) { - return redirect()->route('package.group.index')->withErrors('Invalid Request'); - } - $request->validated(); - DB::beginTransaction(); - try { - $this->osGroupRepository->update($id, $request->all()); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.group.index')->with('success', 'Update successfully'); - } - public function destroy(int $id = 0) - { - $group = $this->osGroupRepository->find($id); - if (! $group) { - return redirect()->route('package.group.index')->withErrors('Invalid Request'); - } - DB::beginTransaction(); - try { - $this->osGroupRepository->delete($id); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - - return redirect()->route('package.group.index')->with('success', 'Delete Group success'); - } - - - public function datatable(Request $request) - { - $query = $this->osGroupRepository->filter( - $request->all(), - ['host'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->editColumn('thumbnail', function ($d) { - if (!$d->thumbnail) { - return '-'; - } - - return '' . e($d->name) . ''; - }) - ->editColumn('module_host_id', function ($d) { - if ($d->host) { - return $d->host->name . ' (Host ID: ' . $d->host->host_id . ')'; - } - return '-'; - }) - ->editColumn('status', function ($d) { - return $d->getStatus(); - }) - ->addColumn('action', function ($d) { - return ' - Edit - '; - }) - ->filterColumn('host', function ($query, $keyword) { - $query->whereHas('host', function ($q) use ($keyword) { - $q->where('host', 'like', "%{$keyword}%"); - }); - }) - ->rawColumns(['thumbnail', 'action', 'status']) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/package/Host.php b/app/Http/Controllers/admin/package/Host.php deleted file mode 100644 index a0c436e..0000000 --- a/app/Http/Controllers/admin/package/Host.php +++ /dev/null @@ -1,173 +0,0 @@ -hostRepository->filter( - $request->all(), - ['datacenter.vCenter'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->editColumn('updated_at', function ($d) { - return $d->updated_at ? $d->updated_at->format('d/m/Y') : ''; - }) - ->editColumn('datacenter.name', function ($d) { - return $d->datacenter->name . ' - ' . $d->datacenter->datacenter_id; - }) - ->addColumn('action', function ($d) { - $editUrl = route('package.host.edit', ['id' => $d->id]); - $deleteBtn = ''; - - if (auth()->guard('admin')->user()->can("delete module vcenter")) { - $deleteUrl = route('package.host.destroy', ['id' => $d->id]); - $csrf = csrf_field(); - $method = method_field('delete'); - $deleteBtn = << - $method - $csrf - - -HTML; - } - - return << - - - $deleteBtn -HTML; - }) - ->filterColumn('datacenter.name', function ($query, $keyword) { - $query->whereHas('datacenter', function ($q) use ($keyword) { - $q->where('name', 'like', "%{$keyword}%"); - }); - }) - ->filterColumn('datacenter.v_center.host', function ($query, $keyword) { - $query->whereHas('datacenter.vCenter', function ($q) use ($keyword) { - $q->where('host', 'like', "%{$keyword}%"); - }); - }) - ->rawColumns(['action']) - ->make(true); - } - public function sync(SyncHost $syncHost) - { - dispatch($syncHost)->delay(now()->addSeconds(5)); - return back()->with('success', 'The process is being established, please wait'); - } - public function create() - { - $title = 'Add host'; - - return view('administrator.pages.package.host.add', compact('title')); - } - public function store(HostRequest $request) - { - $request->validated(); - $datacenter = $this->datacenterRepository->find($request->get('module_datacenter_id')); - if (!$datacenter) { - return back()->withErrors('Invalid Request'); - } - $isCreated = $this->hostRepository->findBy('host_id', $request->get('host_id')); - if ($isCreated) { - return back()->withErrors('The Host ID already exists'); - } - DB::beginTransaction(); - try { - $this->hostRepository->create($request->all()); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.host.index')->with('success', 'Add new host successfully'); - } - public function edit(int $id = 0) - { - $host = $this->hostRepository->findWithRelationships($id, ['datacenter']); - if (!$host) { - return back()->withErrors('Invalid Request'); - } - $title = "View Host Information"; - - return view('administrator.pages.package.host.edit', compact('host', 'title')); - } - public function update(int $id = 0, HostRequest $request) - { - - $request->validated(); - $host = $this->hostRepository->find($id); - if (!$host) { - return redirect()->route('package.host.index')->withErrors('Invalid Request'); - } - if ($request->get('host_id') !== $host->host_id) { - $isCreated = $this->hostRepository->findBy('host_id', $request->get('host_id')); - if ($isCreated) { - return back()->withErrors('The Host ID already exists'); - } - } - try { - DB::beginTransaction(); - $this->hostRepository->update($id, $request->all()); - DB::commit(); - } catch (Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('package.host.index')->with('success', 'Update successfully'); - } - public function destroy(int $id = 0) - { - $host = $this->hostRepository->find($id); - - if (!$host) { - return back()->withErrors('Invalid request.'); - } - - DB::beginTransaction(); - try { - $this->hostRepository->delete($id); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.host.index')->with('success', 'Deleted successfully!'); - } -} diff --git a/app/Http/Controllers/admin/package/IPMapping.php b/app/Http/Controllers/admin/package/IPMapping.php deleted file mode 100644 index 2524f3f..0000000 --- a/app/Http/Controllers/admin/package/IPMapping.php +++ /dev/null @@ -1,182 +0,0 @@ -ipAddressRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ], - ['host.datacenter'] - ); - $title = 'List All IP Address'; - - return view('administrator.pages.package.ipaddress.index', compact('listIP', 'title')); - } - - public function create() - { - $title = 'Add new IP Address'; - - return view('administrator.pages.package.ipaddress.add', compact('title')); - } - - public function store(IPRequest $request) - { - $request->validated(); - - $model_data = []; - - if ((int) $request->option_selected === 1) { - if (empty($request->get('list-ip'))) { - return back()->withErrors('IP Address can\'t empty!'); - } - $list = explode("\r\n", $request->get('list-ip')); - if (count($list) > 0) { - foreach ($list as $item) { - $model_data[] = [ - 'module_network_id' => $request->module_network_id, - 'module_host_id' => $request->module_host_id, - 'gateway' => $request->gateway, - 'dns' => $request->dns, - 'netmask' => $request->netmask, - 'ip' => $item - ]; - } - } - } else { - if (empty($request->get('from'))) { - return back()->withErrors('IP Address From can\'t empty!'); - } - if (empty($request->get('to'))) { - return back()->withErrors('IP Address To can\'t empty!'); - } - $ipaddress = explode(".", $request->from); - $from = $ipaddress[3]; - $to = explode(".", $request->to)[3]; - - for ($from; $from <= $to; $from++) { - $ipaddress[3] = $from; - $model_data[] = [ - 'module_network_id' => $request->module_network_id, - 'module_host_id' => $request->module_host_id, - 'gateway' => $request->gateway, - 'dns' => $request->dns, - 'netmask' => $request->netmask, - 'ip' => implode('.', $ipaddress) - ]; - } - } - DB::beginTransaction(); - try { - $this->ipAddressRepository->insert($model_data); - - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.ipaddress.index')->with('success', 'Add new ipaddress successfully'); - } - - public function edit(int $id = 0) - { - $ipaddress = $this->ipAddressRepository->findWithRelationships($id, ['host.datacenter.vCenter']); - - - if (!$ipaddress) { - return back()->withErrors('Can\'t find IP Address by ID #' . $id); - } - - - $title = "View IP Address"; - - return view('administrator.pages.package.ipaddress.edit', compact('title', 'ipaddress')); - } - - public function update(int $id = 0, IPRequest $request) - { - $ipaddress = $this->ipAddressRepository->find($id); - - if (!$ipaddress) { - return back()->withErrors('Can\'t find IP Address by ID #' . $id); - } - - $request->validated(); - - if ($request->get('ip') !== $ipaddress->ip) { - $ip = $this->ipAddressRepository->findBy('ip', $request->get('ip')); - if ($ip) { - return back()->withErrors('IP address already exists'); - } - } - - DB::beginTransaction(); - try { - $this->ipAddressRepository->update($id, $request->all()); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.ipaddress.index')->with('success', 'Update new information for IP Address success'); - } - - public function destroy(int $id = 0) - { - $ipaddress = $this->ipAddressRepository->find($id); - - if (!$ipaddress) { - return back()->withErrors('Can\'t find IP Address by ID #' . $id); - } - - DB::beginTransaction(); - try { - $this->ipAddressRepository->delete($id); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return back()->with('success', 'Deleted Successfully'); - } - - public function datatable(Request $request) - { - $query = $this->ipAddressRepository->filter( - $request->all(), - ['host.datacenter.vCenter'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn('action', function ($d) { - return view('administrator.pages.package.ipaddress.datatable.action', [ - 'id' => $d->id, - 'ip' => $d->ip - ])->render(); - }) - ->filterColumn('host.datacenter.v_center.host', function ($query, $keyword) { - $query->whereHas('host.datacenter.vCenter', function ($q) use ($keyword) { - $q->where('host', 'like', "%{$keyword}%"); - }); - }) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/package/Network.php b/app/Http/Controllers/admin/package/Network.php deleted file mode 100644 index 4d0036e..0000000 --- a/app/Http/Controllers/admin/package/Network.php +++ /dev/null @@ -1,145 +0,0 @@ -networkRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ], - ['host.datacenter.vCenter'] - ); - $title = 'List all network'; - - return view('administrator.pages.package.network.index', compact('networks', 'title')); - } - - public function sync(SyncNetwork $syncNetwork) - { - dispatch($syncNetwork)->delay(now()->addSeconds(5)); - return back()->with('success', 'The process is being established, please wait'); - } - public function create() - { - $title = 'Add Network'; - - return view('administrator.pages.package.network.add', compact('title')); - } - public function store(NetworkRequest $request) - { - $request->validated(); - $host = $this->hostRepository->find($request->get('module_host_id')); - if (!$host) { - return back()->withErrors('Invalid Request'); - } - $isCreated = $this->networkRepository->findBy('network', $request->get('network')); - if ($isCreated) { - return back()->withErrors('The Network ID already exists'); - } - DB::beginTransaction(); - try { - $this->networkRepository->create($request->all()); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.network.index')->with('success', 'Add new Network successfully'); - } - public function edit(int $id = 0) - { - $network = $this->networkRepository->findWithRelationships($id, ['host.datacenter']); - if (!$network) { - return back()->withErrors('Invalid Request'); - } - $title = "View Network Information"; - - return view('administrator.pages.package.network.edit', compact('network', 'title')); - } - public function update(int $id = 0, NetworkRequest $request) - { - - $request->validated(); - $host = $this->networkRepository->find($id); - if (!$host) { - return redirect()->route('package.network.index')->withErrors('Invalid Request'); - } - if ($request->get('network') !== $host->network) { - $isCreated = $this->networkRepository->findBy('network', $request->get('network')); - if ($isCreated) { - return back()->withErrors('The Network ID already exists'); - } - } - try { - DB::beginTransaction(); - $this->networkRepository->update($id, $request->all()); - DB::commit(); - } catch (Exception $exception) { - DB::rollBack(); - return back()->withErrors($exception->getMessage()); - } - return redirect()->route('package.network.index')->with('success', 'Update successfully'); - } - public function destroy(int $id = 0) - { - $host = $this->networkRepository->find($id); - - if (!$host) { - return back()->withErrors('Invalid request.'); - } - - DB::beginTransaction(); - try { - $this->networkRepository->delete($id); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.network.index')->with('success', 'Deleted successfully!'); - } - - public function datatable(Request $request) - { - $query = $this->networkRepository->filter( - $request->all(), - ['host.datacenter.vCenter'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn( - 'action', - fn($d) => - 'Edit' - ) - ->filterColumn('host.datacenter.v_center.host', function ($query, $keyword) { - $query->whereHas('host.datacenter.vCenter', function ($q) use ($keyword) { - $q->where('host', 'like', "%{$keyword}%"); - }); - }) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/package/OsVersion.php b/app/Http/Controllers/admin/package/OsVersion.php deleted file mode 100644 index f8a5851..0000000 --- a/app/Http/Controllers/admin/package/OsVersion.php +++ /dev/null @@ -1,141 +0,0 @@ -osRepository->filter([ - 'page' => $request->get('page') ?? 1 - ], ['osGroup.host']); - $title = 'List all Version OS'; - - return view('administrator.pages.package.version.index', compact('versions', 'title')); - } - public function create() - { - $title = 'Add new Version Os'; - - return view('administrator.pages.package.version.add', compact('title')); - } - public function store(OsVersionRequest $request) - { - $validatedData = $request->validated(); - - unset($validatedData['module_datacenter_id']); - - DB::beginTransaction(); - try { - $this->osRepository->create($validatedData); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.version.index')->with('success', 'Add Os Version successfully'); - } - public function edit(int $id = 0) - { - $version = $this->osRepository->findWithRelationships($id, ['osGroup.host.datacenter.vCenter']); - if (! $version) { - return back()->withErrors('Can\'t find version os by ID #' . $id); - } - - $title = "Edit version OS"; - - return view('administrator.pages.package.version.edit', compact('title', 'version')); - } - public function update(int $id = 0, OsVersionRequest $request) - { - $version = $this->osRepository->find($id); - if (! $version) { - return back()->withErrors('Can\'t find version os by ID #' . $id); - } - - $validatedData = $request->validated(); - - // Remove unnecessary fields - unset($validatedData['vcenter_id'], $validatedData['module_datacenter_id']); - - DB::beginTransaction(); - try { - $this->osRepository->update($id, $validatedData); - DB::commit(); - } catch (\Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.version.index')->with('success', 'Update successfully'); - } - public function destroy($id) - { - try { - $version = $this->osRepository->find($id); - if (!$version) { - return back()->withErrors('Can\'t find version os by ID #' . $id); - } - - $version->delete(); - - return redirect()->route('package.version.index') - ->with('success', 'OS Version deleted successfully'); - } catch (\Exception $error) { - return back()->withErrors('Failed to delete OS Version: ' . $error->getMessage()); - } - } - - public function datatable(Request $request) - { - $query = $this->osRepository->filter( - [], - ['osGroup'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->editColumn('os_group', function ($d) { - return $d->osGroup ? $d->osGroup->name . ' - ' . $d->osGroup->host->name : ''; - }) - ->addColumn('action', function ($d) { - return view('administrator.pages.package.version.datatable.action', [ - 'id' => $d->id, - 'version' => $d->version - ])->render(); - }) - ->addColumn( - 'status', - function ($d) { - return view('administrator.pages.package.version.datatable.status', ['row' => $d])->render(); - } - ) - ->filterColumn('host', function ($query, $keyword) { - $query->whereHas('host', function ($q) use ($keyword) { - $q->where('host', 'like', "%{$keyword}%"); - }); - }) - ->rawColumns(['action', 'status']) - ->make(true); - } -} diff --git a/app/Http/Controllers/admin/package/SyncController.php b/app/Http/Controllers/admin/package/SyncController.php deleted file mode 100644 index 41ac202..0000000 --- a/app/Http/Controllers/admin/package/SyncController.php +++ /dev/null @@ -1,17 +0,0 @@ -delay(now()->addSeconds(5)); - return back()->with('success','Please wait job run in background'); - } -} diff --git a/app/Http/Controllers/admin/product/ProductCategoryController.php b/app/Http/Controllers/admin/product/ProductCategoryController.php deleted file mode 100644 index 2d0cab9..0000000 --- a/app/Http/Controllers/admin/product/ProductCategoryController.php +++ /dev/null @@ -1,174 +0,0 @@ -whereNull('parent_id') - ->orderBy('sort_order') - ->get(); - return view('administrator.pages.product.categories.create', compact('categories')); - } - - public function store(ProductCategoryRequest $request) - { - $category = $this->productCategoryService->store($request->validated()); - return redirect()->route('products.group.edit', ['id' => $category->id])->with('success', 'Create Group Success'); - } - - public function edit(int $id) - { - $category = $this->productCategoryRepository->find($id); - if (!$category) { - return redirect()->route('products.group.index')->with('error', 'Category not found'); - } - $categories = \App\Models\ProductCategories::with('children') - ->whereNull('parent_id') - ->where('id', '!=', $id) - ->orderBy('sort_order') - ->get(); - return view('administrator.pages.product.categories.edit', compact('category', 'categories')); - } - - public function update(ProductCategoryRequest $request, int $id) - { - $this->productCategoryService->update($id, $request->validated()); - return redirect()->route('products.group.edit', ['id' => $id])->with('success', 'Update Group Success'); - } - - public function destroy(int $id) - { - $this->productCategoryService->delete($id); - return redirect()->route('products.group.index')->with('success', 'Delete Group Success'); - } - - public function trash() - { - return view('administrator.pages.product.categories.trash'); - } - - public function restore(int $id) - { - $this->productCategoryService->restore($id); - return redirect()->route('products.group.trash')->with('success', 'Restore Group Success'); - } - - public function forceDelete(int $id) - { - $this->productCategoryService->forceDelete($id); - return redirect()->route('products.group.trash')->with('success', 'Force Delete Group Success'); - } - - public function bulkDelete(Request $request) - { - $request->validate([ - 'ids' => 'required|array', - 'ids.*' => 'exists:product_categories,id' - ]); - - $this->productCategoryService->bulkDelete($request->ids); - - return response()->json(['message' => 'Selected product groups deleted successfully.']); - } - - public function datatable(Request $request) - { - $query = $this->productCategoryRepository->filter( - array_merge($request->all(), ['parent_id' => null]), - ['children'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn('details_control', function ($d) { - return view('administrator.pages.product.categories.datatable.details_control', ['row' => $d])->render(); - }) - ->addColumn( - 'action', - function ($d) use ($request) { - if ($request->has('trashed') && $request->trashed == 1) { - return view('administrator.pages.product.categories.datatable.action_trash', ['row' => $d])->render(); - } - return view('administrator.pages.product.categories.datatable.action', ['row' => $d])->render(); - } - ) - ->addColumn( - 'is_active', - function ($d) { - return view('administrator.pages.product.categories.datatable.status', ['row' => $d])->render(); - } - ) - ->addColumn( - 'checkbox', - function ($d) { - return '
'; - } - ) - ->addColumn( - 'sub_group_html', - function ($d) { - return view('administrator.pages.product.categories.datatable.sub_group', ['children' => $d->children])->render(); - } - ) - ->rawColumns(['details_control', 'action', 'is_active', 'checkbox', 'sub_group_html']) - ->make(true); - } - - public function export(Request $request) - { - if ($request->get('sample') == 1) { - return Excel::download(new ProductCategoriesExport(), 'product_categories_sample.xlsx'); - } - return Excel::download(new ProductCategoriesExport(), 'product_categories_' . date('Y-m-d') . '.xlsx'); - } - - public function import(Request $request) - { - $request->validate([ - 'file' => 'required|mimes:xlsx,xls,csv' - ]); - - try { - Excel::import(new ProductCategoriesImport(), $request->file('file')); - return redirect()->route('products.group.index')->with('success', 'Import product categories successfully'); - } catch (\Maatwebsite\Excel\Validators\ValidationException $e) { - $failures = $e->failures(); - $errorMessage = 'Import failed at row(s): '; - foreach ($failures as $failure) { - $errorMessage .= $failure->row() . ' (' . implode(', ', $failure->errors()) . '); '; - } - return back()->withErrors($errorMessage); - } catch (\Exception $e) { - return back()->withErrors('Import failed: ' . $e->getMessage()); - } - } -} \ No newline at end of file diff --git a/app/Http/Controllers/admin/product/ProductController.php b/app/Http/Controllers/admin/product/ProductController.php deleted file mode 100644 index 93a361f..0000000 --- a/app/Http/Controllers/admin/product/ProductController.php +++ /dev/null @@ -1,256 +0,0 @@ -productRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ] - ); - - $title = "List All Product"; - - return view('administrator.pages.product.index', compact('title', 'products')); - } - - public function create(Request $request) - { - $step = $request->get('step') ?? 1; - $title = 'Add new product'; - // Fetch Product Categories (Roots only, children via relation) - $group = \App\Models\ProductCategories::where('is_active', \App\Enums\Status::ACTIVE->value)->whereNull('parent_id')->get(); - $type = null; - - // Fetch OS versions with their group for the OS selector component - $osVersions = \App\Models\Os::with('osGroup')->where('status', 1)->get(); - - // Fetch Control Panels for Hosting products - $controlPanels = \App\Models\ControlPanel::active()->ordered()->get(); - - if ($step === 1) { - return view("administrator.pages.product.create", compact('group', 'type', 'title', 'osVersions', 'controlPanels')); - } - - if (!$request->has(['step', 'type'])) { - return back()->withErrors('Missing step or type'); - } - - $type_id = (int) $request->type; - $product_name = $request->name; - - $view = "administrator.pages.product.create"; - - $data = compact('type_id', 'title', 'group', 'type', 'product_name', 'osOptions'); - - return view($view, $data); - } - - public function store(ProductRequest $request) - { - try { - $this->productService->store($request->validated()); - - return redirect()->route('products.index')->with('success', 'Add new product success'); - } catch (Exception $e) { - return back()->withErrors($e->getMessage())->withInput(); - } - } - - public function edit(Request $request, $id) - { - $product = $this->productRepository->find($id); - - if (!$product) { - return back()->withErrors("Can't find product."); - } - - // Load relationships - $product->load(['pricings', 'osVersions', 'category']); - - $title = 'Edit product: ' . $product->name; - $group = \App\Models\ProductCategories::where('is_active', \App\Enums\Status::ACTIVE->value)->whereNull('parent_id')->get(); - $type = null; - - // Fetch OS versions for select - $osVersions = \App\Models\Os::with('osGroup')->where('status', 1)->get(); - - $view = "administrator.pages.product.edit"; - - // Fetch Control Panels for Hosting products - $controlPanels = \App\Models\ControlPanel::active()->ordered()->get(); - - $data = compact('product', 'title', 'group', 'type', 'osVersions', 'controlPanels'); - - return view($view, $data); - } - - public function update(ProductRequest $request, $id) - { - try { - $this->productService->update((int) $id, $request->validated()); - return redirect()->route('products.index')->with('success', 'Update product success'); - } catch (Exception $e) { - return back()->withErrors($e->getMessage())->withInput(); - } - } - - public function destroy(Request $request, $id) - { - try { - $this->productService->delete((int) $id); - return redirect()->route('products.index')->with('success', 'Deleted successfully'); - } catch (Exception $e) { - return back()->withErrors($e->getMessage()); - } - } - - public function duplicate($id) - { - try { - $product = $this->productRepository->find($id); - - if (!$product) { - return back()->withErrors("Can't find product."); - } - - $product->load(['pricings', 'osVersions', 'controlPanels']); - - $newData = $product->only([ - 'short_description', 'description', 'setup_fee', 'trial_days', - 'cpu', 'ram', 'gpu', 'disk', 'bandwidth', 'stock', - 'is_unlimited', 'is_active', 'sort_order', 'product_type', - 'product_categories_id', 'published_at', - ]); - $newData['name'] = $product->name . ' (Copy)'; - - $newProduct = $this->productService->store($newData); - - if ($product->osVersions->isNotEmpty()) { - $newProduct->osVersions()->sync($product->osVersions->pluck('id')); - } - - if ($product->controlPanels->isNotEmpty()) { - $syncData = []; - foreach ($product->controlPanels as $panel) { - $syncData[$panel->id] = [ - 'price_modifier' => $panel->pivot->price_modifier, - 'is_default' => $panel->pivot->is_default, - ]; - } - $newProduct->controlPanels()->sync($syncData); - } - - if ($product->pricings->isNotEmpty()) { - foreach ($product->pricings as $pricing) { - $newProduct->pricings()->create([ - 'billing_cycle' => $pricing->billing_cycle, - 'price' => $pricing->price, - 'price_sale' => $pricing->price_sale, - 'currency' => $pricing->currency, - 'is_active' => $pricing->is_active, - ]); - } - } - - return redirect()->route('products.edit', ['id' => $newProduct->id]) - ->with('success', 'Product duplicated successfully'); - } catch (Exception $e) { - return back()->withErrors($e->getMessage()); - } - } - - public function datatable(Request $request) - { - $query = $this->productRepository->filter( - $request->all(), - ['category', 'pricings'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn('category', function ($d) { - return view('administrator.pages.product.datatable.category', ['row' => $d])->render(); - }) - ->addColumn('price', function ($d) { - return view('administrator.pages.product.datatable.price', ['row' => $d])->render(); - }) - ->addColumn('specs', function ($d) { - return view('administrator.pages.product.datatable.specs', ['row' => $d])->render(); - }) - ->addColumn('status', function ($d) { - return view('administrator.pages.product.datatable.status', ['row' => $d])->render(); - }) - ->addColumn('action', function ($d) { - return view('administrator.pages.product.datatable.action', ['row' => $d])->render(); - }) - ->rawColumns(['category', 'price', 'specs', 'status', 'action']) - ->make(true); - } - - private function mapEnumToTypeId(?ProductTypeEnums $enum): int - { - return match ($enum) { - ProductTypeEnums::ADDON => 1, - ProductTypeEnums::DOMAIN => 2, - ProductTypeEnums::HOSTING => 3, - ProductTypeEnums::VPS => 4, - default => 0 - }; - } - - public function export(Request $request) - { - if ($request->get('sample') == 1) { - return Excel::download(new ProductsExport(), 'products_sample.xlsx'); - } - return Excel::download(new ProductsExport(), 'products_' . date('Y-m-d') . '.xlsx'); - } - - public function import(Request $request) - { - $request->validate([ - 'file' => 'required|mimes:xlsx,xls,csv' - ]); - - try { - Excel::import(new ProductsImport(), $request->file('file')); - return redirect()->route('products.index')->with('success', 'Import products successfully'); - } catch (\Maatwebsite\Excel\Validators\ValidationException $e) { - $failures = $e->failures(); - $errorMessage = 'Import failed at row(s): '; - foreach ($failures as $failure) { - $errorMessage .= $failure->row() . ' (' . implode(', ', $failure->errors()) . '); '; - } - return back()->withErrors($errorMessage); - } catch (\Exception $e) { - return back()->withErrors('Import failed: ' . $e->getMessage()); - } - } -} diff --git a/app/Http/Controllers/admin/vm/VirtualMachineController.php b/app/Http/Controllers/admin/vm/VirtualMachineController.php deleted file mode 100644 index adaf2df..0000000 --- a/app/Http/Controllers/admin/vm/VirtualMachineController.php +++ /dev/null @@ -1,160 +0,0 @@ -virtualMachineRepository->filter( - [ - 'page' => $request->get('page') ?? 1 - ] - ); - $title = 'List all Virtual Machine'; - return view('administrator.pages.package.vm.index', compact('vms', 'title')); - } - - public function create() - { - $title = 'Add Virtual Machine'; - $vcenters = \App\Models\Vcenter::where('status', 1)->get(); - return view('administrator.pages.package.vm.add', compact('title', 'vcenters')); - } - - public function store(Request $request) - { - $request->validate([ - 'vm_id' => 'required|unique:virtual_machines,vm_id', - 'name' => 'required', - ]); - - DB::beginTransaction(); - try { - $data = $request->all(); - if (empty($data['uuid'])) { - $data['uuid'] = Str::uuid(); - } - $this->virtualMachineRepository->create($data); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.vm.index')->with('success', 'Add new VM successfully!'); - } - - public function edit($id) - { - $vm = $this->virtualMachineRepository->find($id); - if (!$vm) { - return back()->withErrors('Invalid request.'); - } - $vm->load('datacenter', 'host'); - $vcenters = \App\Models\Vcenter::where('status', 1)->get(); - $title = 'Edit Virtual Machine'; - return view('administrator.pages.package.vm.edit', compact('title', 'vm', 'vcenters')); - } - - public function update(Request $request, $id) - { - $vm = $this->virtualMachineRepository->find($id); - if (!$vm) { - return back()->withErrors('Invalid request.'); - } - - $request->validate([ - 'name' => 'required', - 'vm_id' => 'required|unique:virtual_machines,vm_id,' . $id, - ]); - - DB::beginTransaction(); - try { - $this->virtualMachineRepository->update($id, $request->all()); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.vm.index')->with('success', 'Update VM successfully!'); - } - - public function destroy($id) - { - $vm = $this->virtualMachineRepository->find($id); - if (!$vm) { - return back()->withErrors('Invalid request.'); - } - - DB::beginTransaction(); - try { - $this->virtualMachineRepository->delete($id); - DB::commit(); - } catch (Exception $error) { - DB::rollBack(); - return back()->withErrors($error->getMessage()); - } - return redirect()->route('package.vm.index')->with('success', 'Deleted VM successfully!'); - } - - public function sync(SyncVirtualMachine $syncVM) - { - dispatch($syncVM)->delay(now()->addSeconds(5)); - return back()->with('success', 'The process is being established, please wait'); - } - - public function datatable(Request $request) - { - $query = $this->virtualMachineRepository->filter( - $request->all(), - ['datacenter', 'host'], - [], - ['*'], - true - ); - - return DataTables::eloquent($query) - ->addColumn( - 'action', - function ($d) { - return view('administrator.pages.package.vm.datatable.action', ['row' => $d])->render(); - } - ) - ->editColumn('updated_at', function ($d) { - return $d->updated_at ? $d->updated_at->format('H:i:s d/m/Y') : ''; - }) - ->editColumn('power_state', function ($d) { - if ($d->power_state == 'POWERED_ON') { - return 'Đang bật'; - } elseif ($d->power_state == 'POWERED_OFF') { - return 'Đang tắt'; - } - return $d->power_state ?? 'N/A'; - }) - ->editColumn('datacenter.name', function ($d) { - return $d->datacenter ? $d->datacenter->name . ' - ' . $d->datacenter->datacenter_id : 'N/A'; - }) - ->editColumn('host.name', function ($d) { - return $d->host ? $d->host->name . ' - ' . $d->host->host_id : 'N/A'; - }) - ->rawColumns(['action', 'power_state']) - ->make(true); - } -} \ No newline at end of file diff --git a/app/Http/Helpers/Helper.php b/app/Http/Helpers/Helper.php index e49356e..b93f16e 100644 --- a/app/Http/Helpers/Helper.php +++ b/app/Http/Helpers/Helper.php @@ -2,7 +2,6 @@ namespace App\Http\Helpers; - use App\Models\Order; class Helper @@ -14,7 +13,6 @@ class Helper 'InActive'; } - public function billing_cycle($billing = 0): string { return match ($billing) { @@ -27,13 +25,6 @@ class Helper }; } - public static function ipStatus($status = 0): string - { - return $status === 1 ? - 'Assigned' : - 'Free'; - } - public static function OrderStatus($status = 0): string { switch ($status) { @@ -71,372 +62,6 @@ class Helper return $result; } - public static function get_count_service($user) - { - return $user->hosting_count + $user->vps_count + $user->domain_count; - } - - public static function VirtualMachineStatus($status = 0): string - { - - switch ($status) { - case 1: - $html = 'Active'; - break; - case 2: - $html = 'Suspend'; - break; - case 3: - $html = 'Pending'; - break; - default: - $html = 'Power off'; - break; - } - return $html; - } - - public static function LoadCountry($select = ''): string - { - $select = $select ?? ""; - - $countries = [ - ['name' => 'AFGHANISTAN', 'code' => 'AF'], - ['name' => 'ÅLAND ISLANDS', 'code' => 'AX'], - ['name' => 'ALBANIA', 'code' => 'AL'], - ['name' => 'ALGERIA', 'code' => 'DZ'], - ['name' => 'AMERICAN SAMOA', 'code' => 'AS'], - ['name' => 'ANDORRA', 'code' => 'AD'], - ['name' => 'ANGOLA', 'code' => 'AO'], - ['name' => 'ANGUILLA', 'code' => 'AI'], - ['name' => 'ANTARCTICA', 'code' => 'AQ'], - ['name' => 'ANTIGUA AND BARBUDA', 'code' => 'AG'], - ['name' => 'ARGENTINA', 'code' => 'AR'], - ['name' => 'ARMENIA', 'code' => 'AM'], - ['name' => 'ARUBA', 'code' => 'AW'], - ['name' => 'AUSTRALIA', 'code' => 'AU'], - ['name' => 'AUSTRIA', 'code' => 'AT'], - ['name' => 'AZERBAIJAN', 'code' => 'AZ'], - ['name' => 'BAHAMAS', 'code' => 'BS'], - ['name' => 'BAHRAIN', 'code' => 'BH'], - ['name' => 'BANGLADESH', 'code' => 'BD'], - ['name' => 'BARBADOS', 'code' => 'BB'], - ['name' => 'BELARUS', 'code' => 'BY'], - ['name' => 'BELGIUM', 'code' => 'BE'], - ['name' => 'BELIZE', 'code' => 'BZ'], - ['name' => 'BENIN', 'code' => 'BJ'], - ['name' => 'BERMUDA', 'code' => 'BM'], - ['name' => 'BHUTAN', 'code' => 'BT'], - ['name' => 'BOLIVIA, PLURINATIONAL STATE OF', 'code' => 'BO'], - ['name' => 'BOSNIA AND HERZEGOVINA', 'code' => 'BA'], - ['name' => 'BOTSWANA', 'code' => 'BW'], - ['name' => 'BOUVET ISLAND', 'code' => 'BV'], - ['name' => 'BRAZIL', 'code' => 'BR'], - ['name' => 'BRITISH INDIAN OCEAN TERRITORY', 'code' => 'IO'], - ['name' => 'BRUNEI DARUSSALAM', 'code' => 'BN'], - ['name' => 'BULGARIA', 'code' => 'BG'], - ['name' => 'BURKINA FASO', 'code' => 'BF'], - ['name' => 'BURUNDI', 'code' => 'BI'], - ['name' => 'CAMBODIA', 'code' => 'KH'], - ['name' => 'CAMEROON', 'code' => 'CM'], - ['name' => 'CANADA', 'code' => 'CA'], - ['name' => 'CAPE VERDE', 'code' => 'CV'], - ['name' => 'CAYMAN ISLANDS', 'code' => 'KY'], - ['name' => 'CENTRAL AFRICAN REPUBLIC', 'code' => 'CF'], - ['name' => 'CHAD', 'code' => 'TD'], - ['name' => 'CHILE', 'code' => 'CL'], - ['name' => 'CHINA', 'code' => 'CN'], - ['name' => 'CHRISTMAS ISLAND', 'code' => 'CX'], - ['name' => 'COCOS (KEELING) ISLANDS', 'code' => 'CC'], - ['name' => 'COLOMBIA', 'code' => 'CO'], - ['name' => 'COMOROS', 'code' => 'KM'], - ['name' => 'CONGO', 'code' => 'CG'], - ['name' => 'CONGO, THE DEMOCRATIC REPUBLIC OF THE', 'code' => 'CD'], - ['name' => 'COOK ISLANDS', 'code' => 'CK'], - ['name' => 'COSTA RICA', 'code' => 'CR'], - ['name' => 'CÔTE D\'IVOIRE', 'code' => 'CI'], - ['name' => 'CROATIA', 'code' => 'HR'], - ['name' => 'CUBA', 'code' => 'CU'], - ['name' => 'CYPRUS', 'code' => 'CY'], - ['name' => 'CZECH REPUBLIC', 'code' => 'CZ'], - ['name' => 'DENMARK', 'code' => 'DK'], - ['name' => 'DJIBOUTI', 'code' => 'DJ'], - ['name' => 'DOMINICA', 'code' => 'DM'], - ['name' => 'DOMINICAN REPUBLIC', 'code' => 'DO'], - ['name' => 'ECUADOR', 'code' => 'EC'], - ['name' => 'EGYPT', 'code' => 'EG'], - ['name' => 'EL SALVADOR', 'code' => 'SV'], - ['name' => 'EQUATORIAL GUINEA', 'code' => 'GQ'], - ['name' => 'ERITREA', 'code' => 'ER'], - ['name' => 'ESTONIA', 'code' => 'EE'], - ['name' => 'ETHIOPIA', 'code' => 'ET'], - ['name' => 'FALKLAND ISLANDS (MALVINAS)', 'code' => 'FK'], - ['name' => 'FAROE ISLANDS', 'code' => 'FO'], - ['name' => 'FIJI', 'code' => 'FJ'], - ['name' => 'FINLAND', 'code' => 'FI'], - ['name' => 'FRANCE', 'code' => 'FR'], - ['name' => 'FRENCH GUIANA', 'code' => 'GF'], - ['name' => 'FRENCH POLYNESIA', 'code' => 'PF'], - ['name' => 'FRENCH SOUTHERN TERRITORIES', 'code' => 'TF'], - ['name' => 'GABON', 'code' => 'GA'], - ['name' => 'GAMBIA', 'code' => 'GM'], - ['name' => 'GEORGIA', 'code' => 'GE'], - ['name' => 'GERMANY', 'code' => 'DE'], - ['name' => 'GHANA', 'code' => 'GH'], - ['name' => 'GIBRALTAR', 'code' => 'GI'], - ['name' => 'GREECE', 'code' => 'GR'], - ['name' => 'GREENLAND', 'code' => 'GL'], - ['name' => 'GRENADA', 'code' => 'GD'], - ['name' => 'GUADELOUPE', 'code' => 'GP'], - ['name' => 'GUAM', 'code' => 'GU'], - ['name' => 'GUATEMALA', 'code' => 'GT'], - ['name' => 'GUERNSEY', 'code' => 'GG'], - ['name' => 'GUINEA', 'code' => 'GN'], - ['name' => 'GUINEA-BISSAU', 'code' => 'GW'], - ['name' => 'GUYANA', 'code' => 'GY'], - ['name' => 'HAITI', 'code' => 'HT'], - ['name' => 'HEARD ISLAND AND MCDONALD ISLANDS', 'code' => 'HM'], - ['name' => 'HOLY SEE (VATICAN CITY STATE)', 'code' => 'VA'], - ['name' => 'HONDURAS', 'code' => 'HN'], - ['name' => 'HONG KONG', 'code' => 'HK'], - ['name' => 'HUNGARY', 'code' => 'HU'], - ['name' => 'ICELAND', 'code' => 'IS'], - ['name' => 'INDIA', 'code' => 'IN'], - ['name' => 'INDONESIA', 'code' => 'ID'], - ['name' => 'IRAN, ISLAMIC REPUBLIC OF', 'code' => 'IR'], - ['name' => 'IRAQ', 'code' => 'IQ'], - ['name' => 'IRELAND', 'code' => 'IE'], - ['name' => 'ISLE OF MAN', 'code' => 'IM'], - ['name' => 'ISRAEL', 'code' => 'IL'], - ['name' => 'ITALY', 'code' => 'IT'], - ['name' => 'JAMAICA', 'code' => 'JM'], - ['name' => 'JAPAN', 'code' => 'JP'], - ['name' => 'JERSEY', 'code' => 'JE'], - ['name' => 'JORDAN', 'code' => 'JO'], - ['name' => 'KAZAKHSTAN', 'code' => 'KZ'], - ['name' => 'KENYA', 'code' => 'KE'], - ['name' => 'KIRIBATI', 'code' => 'KI'], - ['name' => 'KOREA, DEMOCRATIC PEOPLE\'S REPUBLIC OF', 'code' => 'KP'], - ['name' => 'KOREA, REPUBLIC OF', 'code' => 'KR'], - ['name' => 'KUWAIT', 'code' => 'KW'], - ['name' => 'KYRGYZSTAN', 'code' => 'KG'], - ['name' => 'LAO PEOPLE\'S DEMOCRATIC REPUBLIC', 'code' => 'LA'], - ['name' => 'LATVIA', 'code' => 'LV'], - ['name' => 'LEBANON', 'code' => 'LB'], - ['name' => 'LESOTHO', 'code' => 'LS'], - ['name' => 'LIBERIA', 'code' => 'LR'], - ['name' => 'LIBYAN ARAB JAMAHIRIYA', 'code' => 'LY'], - ['name' => 'LIECHTENSTEIN', 'code' => 'LI'], - ['name' => 'LITHUANIA', 'code' => 'LT'], - ['name' => 'LUXEMBOURG', 'code' => 'LU'], - ['name' => 'MACAO', 'code' => 'MO'], - ['name' => 'MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF', 'code' => 'MK'], - ['name' => 'MADAGASCAR', 'code' => 'MG'], - ['name' => 'MALAWI', 'code' => 'MW'], - ['name' => 'MALAYSIA', 'code' => 'MY'], - ['name' => 'MALDIVES', 'code' => 'MV'], - ['name' => 'MALI', 'code' => 'ML'], - ['name' => 'MALTA', 'code' => 'MT'], - ['name' => 'MARSHALL ISLANDS', 'code' => 'MH'], - ['name' => 'MARTINIQUE', 'code' => 'MQ'], - ['name' => 'MAURITANIA', 'code' => 'MR'], - ['name' => 'MAURITIUS', 'code' => 'MU'], - ['name' => 'MAYOTTE', 'code' => 'YT'], - ['name' => 'MEXICO', 'code' => 'MX'], - ['name' => 'MICRONESIA, FEDERATED STATES OF', 'code' => 'FM'], - ['name' => 'MOLDOVA, REPUBLIC OF', 'code' => 'MD'], - ['name' => 'MONACO', 'code' => 'MC'], - ['name' => 'MONGOLIA', 'code' => 'MN'], - ['name' => 'MONTENEGRO', 'code' => 'ME'], - ['name' => 'MONTSERRAT', 'code' => 'MS'], - ['name' => 'MOROCCO', 'code' => 'MA'], - ['name' => 'MOZAMBIQUE', 'code' => 'MZ'], - ['name' => 'MYANMAR', 'code' => 'MM'], - ['name' => 'NAMIBIA', 'code' => 'NA'], - ['name' => 'NAURU', 'code' => 'NR'], - ['name' => 'NEPAL', 'code' => 'NP'], - ['name' => 'NETHERLANDS', 'code' => 'NL'], - ['name' => 'NETHERLANDS ANTILLES', 'code' => 'AN'], - ['name' => 'NEW CALEDONIA', 'code' => 'NC'], - ['name' => 'NEW ZEALAND', 'code' => 'NZ'], - ['name' => 'NICARAGUA', 'code' => 'NI'], - ['name' => 'NIGER', 'code' => 'NE'], - ['name' => 'NIGERIA', 'code' => 'NG'], - ['name' => 'NIUE', 'code' => 'NU'], - ['name' => 'NORFOLK ISLAND', 'code' => 'NF'], - ['name' => 'NORTHERN MARIANA ISLANDS', 'code' => 'MP'], - ['name' => 'NORWAY', 'code' => 'NO'], - ['name' => 'OMAN', 'code' => 'OM'], - ['name' => 'PAKISTAN', 'code' => 'PK'], - ['name' => 'PALAU', 'code' => 'PW'], - ['name' => 'PALESTINIAN TERRITORY, OCCUPIED', 'code' => 'PS'], - ['name' => 'PANAMA', 'code' => 'PA'], - ['name' => 'PAPUA NEW GUINEA', 'code' => 'PG'], - ['name' => 'PARAGUAY', 'code' => 'PY'], - ['name' => 'PERU', 'code' => 'PE'], - ['name' => 'PHILIPPINES', 'code' => 'PH'], - ['name' => 'PITCAIRN', 'code' => 'PN'], - ['name' => 'POLAND', 'code' => 'PL'], - ['name' => 'PORTUGAL', 'code' => 'PT'], - ['name' => 'PUERTO RICO', 'code' => 'PR'], - ['name' => 'QATAR', 'code' => 'QA'], - ['name' => 'RÉUNION', 'code' => 'RE'], - ['name' => 'ROMANIA', 'code' => 'RO'], - ['name' => 'RUSSIAN FEDERATION', 'code' => 'RU'], - ['name' => 'RWANDA', 'code' => 'RW'], - ['name' => 'SAINT BARTHÉLEMY', 'code' => 'BL'], - ['name' => 'SAINT HELENA, ASCENSION AND TRISTAN DA CUNHA', 'code' => 'SH'], - ['name' => 'SAINT KITTS AND NEVIS', 'code' => 'KN'], - ['name' => 'SAINT LUCIA', 'code' => 'LC'], - ['name' => 'SAINT MARTIN', 'code' => 'MF'], - ['name' => 'SAINT PIERRE AND MIQUELON', 'code' => 'PM'], - ['name' => 'SAINT VINCENT AND THE GRENADINES', 'code' => 'VC'], - ['name' => 'SAMOA', 'code' => 'WS'], - ['name' => 'SAN MARINO', 'code' => 'SM'], - ['name' => 'SAO TOME AND PRINCIPE', 'code' => 'ST'], - ['name' => 'SAUDI ARABIA', 'code' => 'SA'], - ['name' => 'SENEGAL', 'code' => 'SN'], - ['name' => 'SERBIA', 'code' => 'RS'], - ['name' => 'SEYCHELLES', 'code' => 'SC'], - ['name' => 'SIERRA LEONE', 'code' => 'SL'], - ['name' => 'SINGAPORE', 'code' => 'SG'], - ['name' => 'SLOVAKIA', 'code' => 'SK'], - ['name' => 'SLOVENIA', 'code' => 'SI'], - ['name' => 'SOLOMON ISLANDS', 'code' => 'SB'], - ['name' => 'SOMALIA', 'code' => 'SO'], - ['name' => 'SOUTH AFRICA', 'code' => 'ZA'], - ['name' => 'SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS', 'code' => 'GS'], - ['name' => 'SPAIN', 'code' => 'ES'], - ['name' => 'SRI LANKA', 'code' => 'LK'], - ['name' => 'SUDAN', 'code' => 'SD'], - ['name' => 'SURINAME', 'code' => 'SR'], - ['name' => 'SVALBARD AND JAN MAYEN', 'code' => 'SJ'], - ['name' => 'SWAZILAND', 'code' => 'SZ'], - ['name' => 'SWEDEN', 'code' => 'SE'], - ['name' => 'SWITZERLAND', 'code' => 'CH'], - ['name' => 'SYRIAN ARAB REPUBLIC', 'code' => 'SY'], - ['name' => 'TAIWAN, PROVINCE OF CHINA', 'code' => 'TW'], - ['name' => 'TAJIKISTAN', 'code' => 'TJ'], - ['name' => 'TANZANIA, UNITED REPUBLIC OF', 'code' => 'TZ'], - ['name' => 'THAILAND', 'code' => 'TH'], - ['name' => 'TIMOR-LESTE', 'code' => 'TL'], - ['name' => 'TOGO', 'code' => 'TG'], - ['name' => 'TOKELAU', 'code' => 'TK'], - ['name' => 'TONGA', 'code' => 'TO'], - ['name' => 'TRINIDAD AND TOBAGO', 'code' => 'TT'], - ['name' => 'TUNISIA', 'code' => 'TN'], - ['name' => 'TURKEY', 'code' => 'TR'], - ['name' => 'TURKMENISTAN', 'code' => 'TM'], - ['name' => 'TURKS AND CAICOS ISLANDS', 'code' => 'TC'], - ['name' => 'TUVALU', 'code' => 'TV'], - ['name' => 'UGANDA', 'code' => 'UG'], - ['name' => 'UKRAINE', 'code' => 'UA'], - ['name' => 'UNITED ARAB EMIRATES', 'code' => 'AE'], - ['name' => 'UNITED KINGDOM', 'code' => 'GB'], - ['name' => 'UNITED STATES', 'code' => 'US'], - ['name' => 'UNITED STATES MINOR OUTLYING ISLANDS', 'code' => 'UM'], - ['name' => 'URUGUAY', 'code' => 'UY'], - ['name' => 'UZBEKISTAN', 'code' => 'UZ'], - ['name' => 'VANUATU', 'code' => 'VU'], - ['name' => 'VENEZUELA, BOLIVARIAN REPUBLIC OF', 'code' => 'VE'], - ['name' => 'VIET NAM', 'code' => 'VN'], - ['name' => 'VIRGIN ISLANDS, BRITISH', 'code' => 'VG'], - ['name' => 'VIRGIN ISLANDS, U.S.', 'code' => 'VI'], - ['name' => 'WALLIS AND FUTUNA', 'code' => 'WF'], - ['name' => 'WESTERN SAHARA', 'code' => 'EH'], - ['name' => 'YEMEN', 'code' => 'YE'], - ['name' => 'ZAMBIA', 'code' => 'ZM'], - ['name' => 'ZIMBABWE', 'code' => 'ZW'] - ]; - $html = ""; - return $html; - } - - public static function get_ticket_status($status = 0, $reply = null): string - { - return match (true) { - $status == 1 && !$reply => 'Đang mở', - $status == 1 && $reply => 'Đã trả lời', - default => 'Đã đóng' - }; - } - - public static function ViewInvoiceStatus($status = 1): string - { - $html = ''; - switch ($status) { - case 2: - $html = 'Đã thanh toán'; - break; - case 3: - $html = 'Đã huỷ'; - break; - default: - $html = 'Chưa thanh toán'; - break; - } - - return $html; - } - - public static function ViewBillingCycle($billing = 0): string - { - $html = ''; - switch ($billing) { - case 1: - $html = "1 Lần"; - break; - case 2: - $html = "1 Tháng"; - break; - case 3: - $html = "3 Tháng"; - break; - case 4: - $html = "6 Tháng"; - break; - case 5: - $html = "12 Tháng"; - break; - default: - $html .= "Miễn Phí"; - break; - } - - return $html; - } - - public static function ViewServiceStatus($status = 0): string - { - - switch ($status) { - case 1: - $html = 'Hoạt động'; - break; - case 2: - $html = 'Tạm dừng'; - break; - case 3: - $html = 'Đang khởi tạo
- Loading... -
'; - break; - case 4: - $html = 'Hết hạn'; - break; - default: - $html = 'Không hoạt động'; - break; - } - return $html; - } - public static function generateUniqueCode(): string { do { @@ -445,34 +70,4 @@ class Helper return $code; } - - public static function BillingFormat($billing = 0): int - { - $number = 0; - switch ($billing) { - case 2: - $number = 1; - break; - case 3: - $number = 3; - break; - case 4: - $number = 6; - break; - case 5: - $number = 12; - break; - } - - return $number; - } - - public static function PostStatus($status = 0): string - { - return match ($status) { - 1 => 'Published', - 2 => 'Draft', - default => 'Pending', - }; - } } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index eac5406..e7e3249 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -14,7 +14,6 @@ class Kernel extends HttpKernel * @var array */ protected $middleware = [ - // \App\Http\Middleware\TrustHosts::class, \App\Http\Middleware\TrustProxies::class, \App\Http\Middleware\PreventRequestsDuringMaintenance::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, @@ -83,16 +82,10 @@ class Kernel extends HttpKernel 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, - 'LoginCheck' => \App\Http\Middleware\LoginCheck::class, - 'CustomerLoginCheck' => \App\Http\Middleware\CustomerLoginCheck::class, - 'IsLoginCheck' => \App\Http\Middleware\IsLoginCheck::class, - 'Staff' => \App\Http\Middleware\StaffCheck::class, 'json.response' => \App\Http\Middleware\ForceJsonResponse::class, 'api.auth' => \App\Http\Middleware\ApiAuthCheck::class, 'client.access-token' => \App\Http\Middleware\ClientAccessTokenCheck::class, 'perm' => \App\Http\Middleware\CheckPermission::class, 'restrict.local' => \App\Http\Middleware\RestrictToLocalEnvironment::class, - 'compression' => \App\Http\Middleware\CompressionMiddleware::class, - 'compression.advanced' => \App\Http\Middleware\AdvancedCompressionMiddleware::class, ]; } diff --git a/app/Http/Middleware/AdvancedCompressionMiddleware.php b/app/Http/Middleware/AdvancedCompressionMiddleware.php deleted file mode 100644 index 4ad052f..0000000 --- a/app/Http/Middleware/AdvancedCompressionMiddleware.php +++ /dev/null @@ -1,131 +0,0 @@ - 90% - - /** - * Content types that should be compressed - */ - private const COMPRESSIBLE_TYPES = [ - 'application/json', - 'text/plain', - 'text/html', - 'text/xml', - 'application/xml', - 'text/css', - 'application/javascript', - 'text/javascript' - ]; - - /** - * Handle an incoming request. - */ - public function handle(Request $request, Closure $next): Response - { - $response = $next($request); - - if (!$this->shouldCompress($request, $response)) { - return $response; - } - - return $this->compressResponse($request, $response); - } - - /** - * Compress the response content - */ - private function compressResponse(Request $request, Response $response): Response - { - $content = $response->getContent(); - $originalLength = strlen($content); - - if ($originalLength < self::MIN_LENGTH) { - return $response; - } - - $acceptEncoding = $request->header('Accept-Encoding', ''); - $compressed = null; - $encoding = null; - - // Try Brotli compression first (best compression ratio) - if (str_contains($acceptEncoding, 'br') && function_exists('brotli_compress')) { - $compressed = brotli_compress($content, self::COMPRESSION_LEVEL); - $encoding = 'br'; - } - // Try Gzip compression - elseif (str_contains($acceptEncoding, 'gzip') && function_exists('gzencode')) { - $compressed = gzencode($content, self::COMPRESSION_LEVEL); - $encoding = 'gzip'; - } - // Fallback to Deflate - elseif (str_contains($acceptEncoding, 'deflate') && function_exists('gzdeflate')) { - $compressed = gzdeflate($content, self::COMPRESSION_LEVEL); - $encoding = 'deflate'; - } - - // Check compression efficiency - if ($compressed !== false && $encoding) { - $compressionRatio = strlen($compressed) / $originalLength; - - // Only use compressed content if it's significantly smaller - if ($compressionRatio < self::MAX_COMPRESSION_RATIO) { - $response->setContent($compressed); - $response->headers->set('Content-Encoding', $encoding); - $response->headers->set('Content-Length', strlen($compressed)); - $response->headers->set('Vary', 'Accept-Encoding'); - - // Add compression info to headers for debugging - if (config('app.debug')) { - $response->headers->set('X-Compression-Ratio', round($compressionRatio * 100, 2) . '%'); - $response->headers->set('X-Original-Size', $originalLength); - $response->headers->set('X-Compressed-Size', strlen($compressed)); - } - } - } - - return $response; - } - - /** - * Determine if the response should be compressed - */ - private function shouldCompress(Request $request, Response $response): bool - { - // Don't compress if already compressed - if ($response->headers->has('Content-Encoding')) { - return false; - } - - // Don't compress file downloads - if ($response->headers->has('Content-Disposition')) { - return false; - } - - // Only compress successful responses - if ($response->getStatusCode() >= 400) { - return false; - } - - // Check if content type is compressible - $contentType = $response->headers->get('Content-Type', ''); - foreach (self::COMPRESSIBLE_TYPES as $type) { - if (str_contains($contentType, $type)) { - return true; - } - } - - return false; - } -} \ No newline at end of file diff --git a/app/Http/Middleware/CompressionMiddleware.php b/app/Http/Middleware/CompressionMiddleware.php deleted file mode 100644 index 52f3590..0000000 --- a/app/Http/Middleware/CompressionMiddleware.php +++ /dev/null @@ -1,90 +0,0 @@ -shouldCompress($request, $response)) { - return $response; - } - - $content = $response->getContent(); - - // Chỉ nén nếu content size > 1KB - if (strlen($content) < 1024) { - return $response; - } - - $acceptEncoding = $request->header('Accept-Encoding', ''); - - // Kiểm tra client hỗ trợ gzip - if (str_contains($acceptEncoding, 'gzip') && function_exists('gzencode')) { - $compressed = gzencode($content, 6); // Level 6 compression - - if ($compressed !== false) { - $response->setContent($compressed); - $response->headers->set('Content-Encoding', 'gzip'); - $response->headers->set('Content-Length', strlen($compressed)); - $response->headers->set('Vary', 'Accept-Encoding'); - } - } - // Fallback to deflate if gzip not supported - elseif (str_contains($acceptEncoding, 'deflate') && function_exists('gzdeflate')) { - $compressed = gzdeflate($content, 6); - - if ($compressed !== false) { - $response->setContent($compressed); - $response->headers->set('Content-Encoding', 'deflate'); - $response->headers->set('Content-Length', strlen($compressed)); - $response->headers->set('Vary', 'Accept-Encoding'); - } - } - - return $response; - } - - /** - * Determine if the response should be compressed. - */ - private function shouldCompress(Request $request, Response $response): bool - { - // Chỉ nén JSON responses - $contentType = $response->headers->get('Content-Type', ''); - if (!str_contains($contentType, 'application/json')) { - return false; - } - - // Không nén nếu đã có Content-Encoding - if ($response->headers->has('Content-Encoding')) { - return false; - } - - // Không nén file downloads - if ($response->headers->has('Content-Disposition')) { - return false; - } - - // Chỉ nén response thành công - if ($response->getStatusCode() >= 400) { - return false; - } - - return true; - } -} \ No newline at end of file diff --git a/app/Http/Middleware/CustomCKFinderAuth.php b/app/Http/Middleware/CustomCKFinderAuth.php deleted file mode 100644 index db29843..0000000 --- a/app/Http/Middleware/CustomCKFinderAuth.php +++ /dev/null @@ -1,24 +0,0 @@ - function() { - return true; - }]); - return $next($request); - } -} diff --git a/app/Http/Middleware/CustomerLoginCheck.php b/app/Http/Middleware/CustomerLoginCheck.php deleted file mode 100644 index c0d4364..0000000 --- a/app/Http/Middleware/CustomerLoginCheck.php +++ /dev/null @@ -1,29 +0,0 @@ -url(); - if (auth('users')->check()) - { - return $next($request); - } - Cookie::queue('prev_url',$url); - return redirect()->route('user.login'); - } -} diff --git a/app/Http/Middleware/IsLoginCheck.php b/app/Http/Middleware/IsLoginCheck.php deleted file mode 100644 index 092f5e7..0000000 --- a/app/Http/Middleware/IsLoginCheck.php +++ /dev/null @@ -1,24 +0,0 @@ -guard('client')->check()) { - return $next($request); - } - return redirect()->route('home'); - } -} diff --git a/app/Http/Middleware/LoginCheck.php b/app/Http/Middleware/LoginCheck.php deleted file mode 100644 index cf90d43..0000000 --- a/app/Http/Middleware/LoginCheck.php +++ /dev/null @@ -1,28 +0,0 @@ -route('dashboard'); - } - return $next($request); - } -} diff --git a/app/Http/Middleware/StaffCheck.php b/app/Http/Middleware/StaffCheck.php deleted file mode 100644 index 718c19f..0000000 --- a/app/Http/Middleware/StaffCheck.php +++ /dev/null @@ -1,56 +0,0 @@ -session()->getId(); - $auth_id = auth()->user()->id; - - $time = 60; - - // $visited = Staff::where(function ($query) use ($auth_id,$session_id) { - // $query->where('sessions_id', $session_id); - // $query->where('user_id', $auth_id); - // })->first(); - $visited = $this->staffRepository->getFirstBy([ - 'sessions_id' => $session_id, - 'user_id' => $auth_id - ]); - - if ($visited) { - $visited->last_visited = time(); - $visited->save(); - } else { - $this->staffRepository->create( - [ - 'sessions_id' => $session_id, - 'user_id' => $auth_id, - 'last_visited' => time() - ] - ); - } - - $this->staffRepository->deleteRawTime($time); - - return $next($request); - } -} diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php deleted file mode 100644 index 7186414..0000000 --- a/app/Http/Middleware/TrustHosts.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public function hosts() - { - return [ - $this->allSubdomainsOfApplicationUrl(), - ]; - } -} diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index d593a2d..936bad0 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -12,7 +12,6 @@ class VerifyCsrfToken extends Middleware * @var array */ protected $except = [ - '/administrator/ckfinder/*', 'api/*', 'v1/*', ]; diff --git a/app/Http/Requests/AddOnRequest.php b/app/Http/Requests/AddOnRequest.php deleted file mode 100644 index 61b11bb..0000000 --- a/app/Http/Requests/AddOnRequest.php +++ /dev/null @@ -1,34 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => 'required', - 'price' => 'required|numeric', - 'min' => 'required|numeric', - 'max' => 'required|numeric', - 'step' => 'required|numeric', - 'prefix' => 'required', - 'type_id' => 'required|numeric' - ]; - } -} diff --git a/app/Http/Requests/AddPayRequest.php b/app/Http/Requests/AddPayRequest.php deleted file mode 100644 index d5b4761..0000000 --- a/app/Http/Requests/AddPayRequest.php +++ /dev/null @@ -1,30 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'userid' => 'required|numeric|not__in:0', - 'action' => 'required', - 'amount' => 'required|numeric|not__in:0', - ]; - } -} diff --git a/app/Http/Requests/CouponRequest.php b/app/Http/Requests/CouponRequest.php deleted file mode 100644 index d27eb27..0000000 --- a/app/Http/Requests/CouponRequest.php +++ /dev/null @@ -1,34 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'code' => 'required', - 'start' => 'required', - 'end' => 'required', - 'count' => 'required', - 'type' => 'required', - 'discount' => 'required', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/Domain/DomainRemoveRequest.php b/app/Http/Requests/Domain/DomainRemoveRequest.php deleted file mode 100644 index a5cf00e..0000000 --- a/app/Http/Requests/Domain/DomainRemoveRequest.php +++ /dev/null @@ -1,21 +0,0 @@ - 'required|array|min:1', - 'ids.*' => 'required|integer|exists:dns_manager_domain,id', - ]; - } -} diff --git a/app/Http/Requests/Domain/DomainRequest.php b/app/Http/Requests/Domain/DomainRequest.php deleted file mode 100644 index 2068fab..0000000 --- a/app/Http/Requests/Domain/DomainRequest.php +++ /dev/null @@ -1,23 +0,0 @@ - 'required|string|max:255', - 'dns_manager_server_id' => 'required|integer|exists:dns_manager_servers,id', - 'user_id' => 'nullable|array', - 'user_id.*' => 'integer|exists:customers,id', - ]; - } -} diff --git a/app/Http/Requests/DomainRequest.php b/app/Http/Requests/DomainRequest.php deleted file mode 100644 index 7359369..0000000 --- a/app/Http/Requests/DomainRequest.php +++ /dev/null @@ -1,34 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => 'required', - 'price' => 'required', - 'renewal' => 'required', - 'transfer' => 'required', - 'type_id' => 'required', - 'auto_reg' => 'required', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/Group/GroupRequest.php b/app/Http/Requests/Group/GroupRequest.php deleted file mode 100644 index e52a903..0000000 --- a/app/Http/Requests/Group/GroupRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => 'required', - 'type_id' => 'required', - 'status' => 'nullable', - 'grid' => 'required', - ]; - } -} diff --git a/app/Http/Requests/HostRequest.php b/app/Http/Requests/HostRequest.php deleted file mode 100644 index 4c4af0a..0000000 --- a/app/Http/Requests/HostRequest.php +++ /dev/null @@ -1,28 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - // - ]; - } -} diff --git a/app/Http/Requests/HostingRequest.php b/app/Http/Requests/HostingRequest.php deleted file mode 100644 index 86ec0fb..0000000 --- a/app/Http/Requests/HostingRequest.php +++ /dev/null @@ -1,35 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'product_id' => 'required', - 'next_due_date' => 'required', - 'next_payment' => 'required', - 'name' => 'required', - 'module_server_id' => 'required', - 'user' => 'required', - 'password' => 'required', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/MenuRequest.php b/app/Http/Requests/MenuRequest.php deleted file mode 100644 index 14bac03..0000000 --- a/app/Http/Requests/MenuRequest.php +++ /dev/null @@ -1,34 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'title' => 'required', - 'slug' => 'nullable', - 'route_name' => 'nullable', - 'parent_id' => 'nullable', - 'icon' => 'required', - 'sort_by' => 'required', - 'is_active' => 'nullable', - ]; - } -} diff --git a/app/Http/Requests/ModuleDNS/CreateDNSRequest.php b/app/Http/Requests/ModuleDNS/CreateDNSRequest.php deleted file mode 100644 index 2d23efa..0000000 --- a/app/Http/Requests/ModuleDNS/CreateDNSRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => 'required|ip|ipv4', - 'user' => 'required', - 'password' => 'required', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/ModuleDNS/UpdateDNSRequest.php b/app/Http/Requests/ModuleDNS/UpdateDNSRequest.php deleted file mode 100644 index 657a87d..0000000 --- a/app/Http/Requests/ModuleDNS/UpdateDNSRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => 'required|ip|ipv4', - 'user' => 'required', - 'password' => 'nullable', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/ModuleHosting/CreateHostingRequest.php b/app/Http/Requests/ModuleHosting/CreateHostingRequest.php deleted file mode 100644 index 94b69ff..0000000 --- a/app/Http/Requests/ModuleHosting/CreateHostingRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => 'required|ip|ipv4', - 'user' => 'required', - 'password' => 'required', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/ModuleHosting/UpdateHostingRequest.php b/app/Http/Requests/ModuleHosting/UpdateHostingRequest.php deleted file mode 100644 index 9730964..0000000 --- a/app/Http/Requests/ModuleHosting/UpdateHostingRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => 'required|ip|ipv4', - 'user' => 'required', - 'password' => 'nullable', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/Order/OrderRequest.php b/app/Http/Requests/Order/OrderRequest.php deleted file mode 100644 index 230067d..0000000 --- a/app/Http/Requests/Order/OrderRequest.php +++ /dev/null @@ -1,55 +0,0 @@ - 'required|string|max:255|unique:order__order_item,server_name', - 'product_id' => 'required|integer', - 'product_type' => 'required|integer', - 'months' => 'required|integer', - 'cart_id' => 'required|integer|exists:carts,id', - 'os_id' => 'required|integer|exists:os,id', - 'addons' => 'nullable|array', - 'addons.*.qty' => 'nullable|integer|min:0', - 'addons.*.price' => 'nullable|integer|min:0', - 'totalPrice' => 'required|integer|min:0', - ]; - } - - public function messages(): array - { - return [ - 'server_name.required' => 'Vui lòng bổ sung thêm tên miền!', - 'server_name.string' => 'Tên miền phải là chuỗi ký tự.', - 'server_name.max' => 'Tên miền không được vượt quá 255 ký tự.', - 'server_name.unique' => 'Tên miền này đã được sử dụng, vui lòng chọn tên khác.', - - 'product_type.required' => 'Loại sản phẩm là bắt buộc.', - 'product_type.integer' => 'Loại sản phẩm phải là số nguyên.', - - 'os_id.required' => 'Vui lòng chọn hệ điều hành!', - 'os_id.integer' => 'Hệ điều hành phải là số nguyên.', - 'os_id.exists' => 'Hệ điều hành không tồn tại.', - - 'addons.*.qty.min' => 'Số lượng addon phải lớn hơn hoặc bằng 0.', - 'addons.*.price.min' => 'Giá addon phải lớn hơn hoặc bằng 0.', - - 'cart_id.required' => 'Mã giỏ hàng là bắt buộc', - - 'totalPrice.required' => 'Tổng giá là bắt buộc.', - 'totalPrice.integer' => 'Tổng giá phải là số nguyên.', - 'totalPrice.min' => 'Tổng giá phải lớn hơn hoặc bằng 0.', - ]; - } -} diff --git a/app/Http/Requests/Package/DatacenterRequest.php b/app/Http/Requests/Package/DatacenterRequest.php deleted file mode 100644 index 0c46946..0000000 --- a/app/Http/Requests/Package/DatacenterRequest.php +++ /dev/null @@ -1,30 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => 'required', - 'vcenter_id' => 'required', - 'datacenter_id' => 'required' - ]; - } -} diff --git a/app/Http/Requests/Package/DatastoreRequest.php b/app/Http/Requests/Package/DatastoreRequest.php deleted file mode 100644 index b454671..0000000 --- a/app/Http/Requests/Package/DatastoreRequest.php +++ /dev/null @@ -1,33 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'module_host_id' => 'required', - 'name' => 'required', - 'datastore' => 'required', - 'type' => 'required', - 'space' => 'required', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/Package/FolderRequest.php b/app/Http/Requests/Package/FolderRequest.php deleted file mode 100644 index 5a3dc34..0000000 --- a/app/Http/Requests/Package/FolderRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'module_datacenter_id' => 'required', - 'name' => 'required', - 'folder' => 'required', - 'type' => 'required' - ]; - } -} diff --git a/app/Http/Requests/Package/HostRequest.php b/app/Http/Requests/Package/HostRequest.php deleted file mode 100644 index 1bbebfe..0000000 --- a/app/Http/Requests/Package/HostRequest.php +++ /dev/null @@ -1,32 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'module_datacenter_id' => 'required', - 'name' => 'required', - 'host_id' => 'required', - 'template' => 'required', - 'console' => 'required' - ]; - } -} diff --git a/app/Http/Requests/Package/IPRequest.php b/app/Http/Requests/Package/IPRequest.php deleted file mode 100644 index 5fef91e..0000000 --- a/app/Http/Requests/Package/IPRequest.php +++ /dev/null @@ -1,38 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'module_host_id' => 'required', - 'module_network_id' => 'required', - 'gateway' => 'required', - 'option_selected' => 'required', - 'netmask' => 'required', - 'dns' => 'required', - 'from' => 'nullable', - 'to' => 'nullable', - 'list-ip' => 'nullable', - 'ip' => 'nullable', - 'status' => 'nullable', - ]; - } -} diff --git a/app/Http/Requests/Package/NetworkRequest.php b/app/Http/Requests/Package/NetworkRequest.php deleted file mode 100644 index 5c59040..0000000 --- a/app/Http/Requests/Package/NetworkRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'module_host_id' => 'required', - 'name' => 'required', - 'network' => 'required', - 'type' => 'required', - ]; - } -} diff --git a/app/Http/Requests/Package/OsGroupRequest.php b/app/Http/Requests/Package/OsGroupRequest.php deleted file mode 100644 index b101120..0000000 --- a/app/Http/Requests/Package/OsGroupRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => 'required', - 'module_host_id' => 'required', - 'thumbnail' => 'required', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/Package/OsVersionRequest.php b/app/Http/Requests/Package/OsVersionRequest.php deleted file mode 100644 index ad4a661..0000000 --- a/app/Http/Requests/Package/OsVersionRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'version' => 'required', - 'os_group_id' => 'required', - 'template_id' => 'nullable', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/PostRequest.php b/app/Http/Requests/PostRequest.php deleted file mode 100644 index 3144415..0000000 --- a/app/Http/Requests/PostRequest.php +++ /dev/null @@ -1,30 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => 'required', - 'slug' => 'required', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/Product/ProductRequest.php b/app/Http/Requests/Product/ProductRequest.php deleted file mode 100644 index 7692c52..0000000 --- a/app/Http/Requests/Product/ProductRequest.php +++ /dev/null @@ -1,208 +0,0 @@ -|string> - */ - public function rules(): array - { - $productId = $this->route('id'); - - $rules = [ - 'product_type' => ['required', 'string', 'max:50'], - 'product_categories_id' => ['required', 'exists:product_categories,id'], - 'name' => [ - 'required', - 'string', - 'max:255', - function ($attribute, $value, $fail) use ($productId) { - $slug = Str::slug($value); - $query = Product::where('slug', $slug); - - // Exclude current product when updating - if ($productId) { - $query->where('id', '!=', $productId); - } - - if ($query->exists()) { - $fail('Tên sản phẩm đã tồn tại. Vui lòng chọn tên khác.'); - } - }, - ], - 'short_description' => ['nullable', 'string', 'max:255'], - 'description' => ['nullable', 'string'], - 'thumbnail' => ['nullable', 'string', 'max:255'], - 'gallery' => ['nullable', 'array'], - 'gallery.*' => ['nullable', 'string', 'max:255'], - 'stock' => ['nullable', 'integer', 'min:0'], - 'is_unlimited' => ['boolean'], - 'cpu' => ['nullable', 'string', 'max:255'], - 'ram' => ['nullable', 'string', 'max:255'], - 'gpu' => ['nullable', 'string', 'max:255'], - 'disk' => ['nullable', 'string', 'max:255'], - 'bandwidth' => ['nullable', 'string', 'max:255'], - 'features' => ['nullable', 'json'], - 'metadata' => ['nullable', 'json'], - 'setup_fee' => ['nullable', 'numeric', 'min:0'], - 'trial_days' => ['nullable', 'integer', 'min:0'], - 'currency' => ['nullable', 'string', 'max:10'], - 'is_active' => ['boolean'], - 'sort_order' => ['nullable', 'integer'], - 'published_at' => ['nullable', 'date'], - - // OS versions (pivot table os_product) - for VPS - 'os_ids' => ['nullable', 'array'], - 'os_ids.*' => ['exists:os,id'], - - // Control Panels (pivot table product_control_panel) - for Hosting - 'control_panel_ids' => ['nullable', 'array'], - 'control_panel_ids.*' => ['exists:control_panels,id'], - 'control_panel_price_modifiers' => ['nullable', 'array'], - 'control_panel_price_modifiers.*' => ['nullable', 'numeric', 'min:0'], - 'control_panel_default' => ['nullable', 'exists:control_panels,id'], - - // Images - 'thumbnail' => ['nullable', 'string', 'max:255'], - 'gallery' => ['nullable', 'array'], - 'gallery.*' => ['nullable', 'string', 'max:255'], - - // Pricing cycles (table product_pricings) - 'pricing' => ['nullable', 'array'], - ]; - - // Add dynamic validation for each billing cycle - foreach (ProductBillingCycle::cases() as $cycle) { - $cycleKey = $cycle->value; - - $rules["pricing.{$cycleKey}.enabled"] = ['nullable', 'boolean']; - $rules["pricing.{$cycleKey}.price"] = ['nullable', 'numeric', 'min:0']; - $rules["pricing.{$cycleKey}.price_sale"] = [ - 'nullable', - 'numeric', - 'min:0', - "required_with:pricing.{$cycleKey}.price_sale_check", - function ($attribute, $value, $fail) use ($cycleKey) { - $price = $this->input("pricing.{$cycleKey}.price"); - - // price_sale requires price to be set first - if ($value !== null && $value !== '' && (empty($price) || $price <= 0)) { - $fail("Bạn phải nhập giá gốc trước khi nhập giá khuyến mãi cho {$cycleKey}."); - return; - } - - // price_sale must be less than price - if ($value !== null && $value !== '' && $price !== null && $value >= $price) { - $fail("Giá khuyến mãi phải nhỏ hơn giá gốc cho {$cycleKey}."); - } - }, - ]; - } - - return $rules; - } - - /** - * Get the error messages for the defined validation rules. - * - * @return array - */ - public function messages(): array - { - return [ - 'product_type.required' => 'Vui lòng chọn loại sản phẩm.', - 'product_type.string' => 'Loại sản phẩm phải là chuỗi ký tự.', - 'product_type.max' => 'Loại sản phẩm không được vượt quá 50 ký tự.', - - 'product_categories_id.required' => 'Vui lòng chọn danh mục sản phẩm.', - 'product_categories_id.exists' => 'Danh mục sản phẩm không hợp lệ.', - - 'name.required' => 'Vui lòng nhập tên sản phẩm.', - 'name.string' => 'Tên sản phẩm phải là chuỗi ký tự.', - 'name.max' => 'Tên sản phẩm không được vượt quá 255 ký tự.', - - 'short_description.string' => 'Mô tả ngắn phải là chuỗi ký tự.', - 'short_description.max' => 'Mô tả ngắn không được vượt quá 255 ký tự.', - - 'description.string' => 'Mô tả phải là chuỗi ký tự.', - - 'thumbnail.string' => 'Đường dẫn ảnh đại diện phải là chuỗi ký tự.', - 'thumbnail.max' => 'Đường dẫn ảnh đại diện không được vượt quá 255 ký tự.', - - 'gallery.array' => 'Thư viện ảnh không hợp lệ.', - 'gallery.*.string' => 'Đường dẫn ảnh trong thư viện phải là chuỗi ký tự.', - 'gallery.*.max' => 'Đường dẫn ảnh trong thư viện không được vượt quá 255 ký tự.', - - 'stock.integer' => 'Số lượng tồn kho phải là số nguyên.', - 'stock.min' => 'Số lượng tồn kho không được âm.', - - 'is_unlimited.boolean' => 'Giá trị không giới hạn không hợp lệ.', - - 'cpu.string' => 'CPU phải là chuỗi ký tự.', - 'cpu.max' => 'CPU không được vượt quá 255 ký tự.', - - 'ram.string' => 'RAM phải là chuỗi ký tự.', - 'ram.max' => 'RAM không được vượt quá 255 ký tự.', - - 'gpu.string' => 'GPU phải là chuỗi ký tự.', - 'gpu.max' => 'GPU không được vượt quá 255 ký tự.', - - 'disk.string' => 'Dung lượng ổ cứng phải là chuỗi ký tự.', - 'disk.max' => 'Dung lượng ổ cứng không được vượt quá 255 ký tự.', - - 'bandwidth.string' => 'Băng thông phải là chuỗi ký tự.', - 'bandwidth.max' => 'Băng thông không được vượt quá 255 ký tự.', - - 'features.json' => 'Tính năng phải là định dạng JSON hợp lệ.', - 'metadata.json' => 'Metadata phải là định dạng JSON hợp lệ.', - - 'setup_fee.numeric' => 'Phí cài đặt phải là số.', - 'setup_fee.min' => 'Phí cài đặt không được âm.', - - 'trial_days.integer' => 'Số ngày dùng thử phải là số nguyên.', - 'trial_days.min' => 'Số ngày dùng thử không được âm.', - - 'currency.string' => 'Đơn vị tiền tệ phải là chuỗi ký tự.', - 'currency.max' => 'Đơn vị tiền tệ không được vượt quá 10 ký tự.', - - 'is_active.boolean' => 'Trạng thái kích hoạt không hợp lệ.', - - 'sort_order.integer' => 'Thứ tự sắp xếp phải là số nguyên.', - - 'published_at.date' => 'Ngày xuất bản không hợp lệ.', - - 'os_ids.array' => 'Danh sách hệ điều hành không hợp lệ.', - 'os_ids.*.exists' => 'Hệ điều hành được chọn không tồn tại.', - - 'control_panel_ids.array' => 'Danh sách Control Panel không hợp lệ.', - 'control_panel_ids.*.exists' => 'Control Panel được chọn không tồn tại.', - 'control_panel_price_modifiers.array' => 'Danh sách phụ phí Control Panel không hợp lệ.', - 'control_panel_price_modifiers.*.numeric' => 'Phụ phí Control Panel phải là số.', - 'control_panel_price_modifiers.*.min' => 'Phụ phí Control Panel không được âm.', - 'control_panel_default.exists' => 'Control Panel mặc định không tồn tại.', - - 'pricing.array' => 'Thông tin giá không hợp lệ.', - 'pricing.*.price.numeric' => 'Giá phải là số.', - 'pricing.*.price.min' => 'Giá không được âm.', - 'pricing.*.price_sale.numeric' => 'Giá khuyến mãi phải là số.', - 'pricing.*.price_sale.min' => 'Giá khuyến mãi không được âm.', - ]; - } -} diff --git a/app/Http/Requests/ProductCategories/ProductCategoryRequest.php b/app/Http/Requests/ProductCategories/ProductCategoryRequest.php deleted file mode 100644 index b991b57..0000000 --- a/app/Http/Requests/ProductCategories/ProductCategoryRequest.php +++ /dev/null @@ -1,87 +0,0 @@ -route('id'); - // hoặc: $this->route('product_category') - - return [ - 'name' => [ - 'required', - 'string', - 'max:255', - ], - - 'slug' => [ - 'nullable', - 'string', - 'max:255', - Rule::unique('product_categories', 'slug') - ->ignore($categoryId), - ], - - 'parent_id' => [ - 'nullable', - 'integer', - 'exists:product_categories,id', - ], - - 'is_active' => [ - 'nullable', - 'boolean', - ], - - 'sort_order' => [ - 'nullable', - 'integer', - 'min:0', - ], - ]; - } - - /** - * Chuẩn hóa dữ liệu trước khi validate - */ - protected function prepareForValidation(): void - { - $this->merge([ - 'slug' => $this->slug - ? \Str::slug($this->slug) - : \Str::slug($this->name), - - 'is_active' => $this->has('is_active') - ? (bool) $this->is_active - : true, - ]); - } - - /** - * Custom message (tuỳ chọn) - */ - public function messages(): array - { - return [ - 'name.required' => 'Tên danh mục không được để trống.', - 'slug.unique' => 'Slug đã tồn tại.', - 'parent_id.exists' => 'Danh mục cha không tồn tại.', - ]; - } -} diff --git a/app/Http/Requests/Recharge/RechargeRequest.php b/app/Http/Requests/Recharge/RechargeRequest.php deleted file mode 100644 index a87a02e..0000000 --- a/app/Http/Requests/Recharge/RechargeRequest.php +++ /dev/null @@ -1,39 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'total_amount' => 'required|numeric|min:200000', - 'bankCode' => 'nullable|string', - ]; - } - - - public function messages() - { - return [ - 'total_amount.required' => 'Vui lòng nhập số tiền cần nạp.', - 'total_amount.numeric' => 'Số tiền phải là một số.', - 'total_amount.min' => 'Số tiền nạp vui lòng lớn hơn hoặc bằng 200.000vnđ', - ]; - } -} diff --git a/app/Http/Requests/Record/RecordRemoveRequest.php b/app/Http/Requests/Record/RecordRemoveRequest.php deleted file mode 100644 index a167e5d..0000000 --- a/app/Http/Requests/Record/RecordRemoveRequest.php +++ /dev/null @@ -1,21 +0,0 @@ - 'required|array|min:1', - 'ids.*' => 'required|integer|exists:dns_manager_ns,id', - ]; - } -} diff --git a/app/Http/Requests/Server/ServerRemoveRequest.php b/app/Http/Requests/Server/ServerRemoveRequest.php deleted file mode 100644 index 45c6a80..0000000 --- a/app/Http/Requests/Server/ServerRemoveRequest.php +++ /dev/null @@ -1,21 +0,0 @@ - 'required|array|min:1', - 'ids.*' => 'required|integer|exists:dns_manager_servers,id', - ]; - } -} diff --git a/app/Http/Requests/Server/ServerRequest.php b/app/Http/Requests/Server/ServerRequest.php deleted file mode 100644 index 1ea0f76..0000000 --- a/app/Http/Requests/Server/ServerRequest.php +++ /dev/null @@ -1,24 +0,0 @@ - 'required|string|max:255', - 'ip_address' => 'required|string|max:255', - 'username' => 'required|string|max:255', - 'password' => 'required|string|max:255', - 'status' => 'nullable|string|in:activated,Stopped', - ]; - } -} diff --git a/app/Http/Requests/Shop/AddToCartRequest.php b/app/Http/Requests/Shop/AddToCartRequest.php deleted file mode 100644 index fca3f9e..0000000 --- a/app/Http/Requests/Shop/AddToCartRequest.php +++ /dev/null @@ -1,48 +0,0 @@ - 'required|integer|min:1', - 'type_id' => 'required|integer|min:1', - 'product_price' => 'required|numeric|min:0', - ]; - } - - /** - * Get custom error messages for validation rules. - */ - public function messages(): array - { - return [ - 'product_id.required' => 'Yêu cầu không hợp lệ!', - 'product_id.integer' => 'ID sản phẩm không hợp lệ!', - 'product_id.min' => 'ID sản phẩm không hợp lệ!', - 'type_id.required' => 'Yêu cầu không hợp lệ!', - 'type_id.integer' => 'ID loại sản phẩm không hợp lệ!', - 'type_id.min' => 'ID loại sản phẩm không hợp lệ!', - 'product_price.required' => 'Giá sản phẩm không được để trống!', - 'product_price.numeric' => 'Giá sản phẩm phải là số!', - 'product_price.min' => 'Giá sản phẩm không hợp lệ!', - ]; - } -} \ No newline at end of file diff --git a/app/Http/Requests/TicketReplyRequest.php b/app/Http/Requests/TicketReplyRequest.php deleted file mode 100644 index 322bd0e..0000000 --- a/app/Http/Requests/TicketReplyRequest.php +++ /dev/null @@ -1,30 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'ticket_id' => 'required', - 'customer_id' => 'required', - 'message' => 'required' - ]; - } -} diff --git a/app/Http/Requests/TicketRequest.php b/app/Http/Requests/TicketRequest.php deleted file mode 100644 index 356bd15..0000000 --- a/app/Http/Requests/TicketRequest.php +++ /dev/null @@ -1,28 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - // - ]; - } -} diff --git a/app/Http/Requests/User/UserRegisterRequest.php b/app/Http/Requests/User/UserRegisterRequest.php deleted file mode 100644 index fee61c7..0000000 --- a/app/Http/Requests/User/UserRegisterRequest.php +++ /dev/null @@ -1,53 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'email' => 'required|email|unique:customers', - 'firstname' => 'required', - 'lastname' => 'required', - 'password' => 'required|min:8', - 'phone' => 'required|unique:customers' - ]; - } - - public function messages() - { - return [ - 'email' => [ - 'required' => 'Vui lòng nhập địa chỉ email', - 'email' => 'Địa chỉ email không hợp lệ', - 'unique' => 'Email này đã tồn tại' - ], - 'firstname.required' => 'Vui lòng nhập tên của bạn', - 'lastname.required' => 'Vui lòng nhập họ của bạn', - 'password' => [ - 'required' => 'Mật khẩu không được để trống', - 'min' => 'Mật khẩu tối thiểu 8 ký tự' - ], - 'phone' => [ - 'required' => 'Số điện thoại là bắt buộc', - 'unique' => 'Số điện thoại đã tồn tại' - ] - ]; - } -} diff --git a/app/Http/Requests/User/UserStoreInformation.php b/app/Http/Requests/User/UserStoreInformation.php deleted file mode 100644 index 7760212..0000000 --- a/app/Http/Requests/User/UserStoreInformation.php +++ /dev/null @@ -1,60 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'firstname' => 'required', - 'lastname' => 'required', - 'email' => 'nullable|email|unique:customers,email', - 'address' => 'nullable', - 'phone' => 'required', - 'gender' => 'nullable', - 'company' => 'nullable', - 'cccd' => 'nullable', - 'front_cccd' => 'nullable', - 'back_cccd' => 'nullable' - ]; - } - - public function messages(): array - { - return [ - 'firstname.required' => 'Vui lòng nhập họ.', - 'lastname.required' => 'Vui lòng nhập tên.', - 'email.email' => 'Email không đúng định dạng.', - 'address.string' => 'Địa chỉ không hợp lệ.', - 'phone.required' => 'Vui lòng nhập số điện thoại.', - 'gender.in' => 'Giới tính không hợp lệ.', - 'company.string' => 'Tên công ty không hợp lệ.', - 'cccd.required' => 'Vui lòng nhập số căn cước công dân.', - 'cccd.digits' => 'Số căn cước công dân phải gồm 12 chữ số.', - 'front_cccd.required' => 'Vui lòng tải lên hình ảnh mặt trước căn cước công dân.', - 'front_cccd.image' => 'Tệp mặt trước CCCD phải là hình ảnh.', - 'front_cccd.mimes' => 'Ảnh mặt trước CCCD phải có định dạng: jpeg, png, jpg.', - 'front_cccd.max' => 'Ảnh mặt trước CCCD không được vượt quá 2MB.', - 'back_cccd.required' => 'Vui lòng tải lên hình ảnh mặt sau căn cước công dân.', - 'back_cccd.image' => 'Tệp mặt sau CCCD phải là hình ảnh.', - 'back_cccd.mimes' => 'Ảnh mặt sau CCCD phải có định dạng: jpeg, png, jpg.', - 'back_cccd.max' => 'Ảnh mặt sau CCCD không được vượt quá 2MB.', - ]; - } -} diff --git a/app/Http/Requests/VM/SnapShotRequest.php b/app/Http/Requests/VM/SnapShotRequest.php deleted file mode 100644 index 1959d76..0000000 --- a/app/Http/Requests/VM/SnapShotRequest.php +++ /dev/null @@ -1,37 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'product_id' => 'required|string', - 'name' => 'nullable|string|max:100|regex:/^[a-zA-Z0-9_-]+$/', - ]; - } - - public function messages(): array - { - return [ - 'product_id.required' => 'Máy ảo không tồn tại', - 'name.regex' => 'Tên snapshot không hợp lệ', - ]; - } -} diff --git a/app/Http/Requests/VmwareRequest.php b/app/Http/Requests/VmwareRequest.php deleted file mode 100644 index 99e12f6..0000000 --- a/app/Http/Requests/VmwareRequest.php +++ /dev/null @@ -1,39 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'product_id' => 'required', - 'next_due_date' => 'required', - 'next_payment' => 'required', - 'name' => 'required', - 'user' => 'required', - 'password' => 'required', - 'status' => 'required', - 'ip' => 'required|ip|ipv4', - 'os_version' => 'required', - 'vmware_id' => 'required', - 'hostname' => 'required', - 'port' => 'required' - ]; - } -} diff --git a/app/Http/Requests/WhoisRequest.php b/app/Http/Requests/WhoisRequest.php deleted file mode 100644 index 6a70b2b..0000000 --- a/app/Http/Requests/WhoisRequest.php +++ /dev/null @@ -1,62 +0,0 @@ - - */ - public function rules(): array - { - return [ - 'domain' => [ - 'required', - 'string', - 'max:255', - 'regex:/^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$/' - ] - ]; - } - - /** - * Get custom validation messages. - * - * @return array - */ - public function messages(): array - { - return [ - 'domain.required' => __('Domain name is required.'), - 'domain.string' => __('Domain name must be a string.'), - 'domain.max' => __('Domain name must not exceed 255 characters.'), - 'domain.regex' => __('Please enter a valid domain name format.'), - ]; - } - - /** - * Get custom attributes for validator errors. - * - * @return array - */ - public function attributes(): array - { - return [ - 'domain' => __('domain name'), - ]; - } -} \ No newline at end of file diff --git a/app/Http/Requests/vCenter/CreatevCenterRequest.php b/app/Http/Requests/vCenter/CreatevCenterRequest.php deleted file mode 100644 index dd412ca..0000000 --- a/app/Http/Requests/vCenter/CreatevCenterRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'host' => 'required|ip|ipv4', - 'user' => 'required', - 'password' => 'required', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Requests/vCenter/UpdatevCenterRequest.php b/app/Http/Requests/vCenter/UpdatevCenterRequest.php deleted file mode 100644 index 904ba57..0000000 --- a/app/Http/Requests/vCenter/UpdatevCenterRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'host' => 'required|ip|ipv4', - 'user' => 'required', - 'password' => 'nullable', - 'status' => 'required' - ]; - } -} diff --git a/app/Http/Resources/Api/Client/Service/ServiceClientResource.php b/app/Http/Resources/Api/Client/Service/ServiceClientResource.php deleted file mode 100644 index a082e31..0000000 --- a/app/Http/Resources/Api/Client/Service/ServiceClientResource.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ - public function toArray(Request $request): array - { - if (!$this->resource) { - return []; - } - - $orderItem = $this->orderItem; - $product = $orderItem?->product; - - return [ - 'uuid' => $this->uuid, - 'server_name' => $this->server_name, - 'status' => $this->status, - 'auto_renew' => $this->auto_renew, - 'started_at' => $this->started_at?->toISOString(), - 'expires_at' => $this->expires_at?->toISOString(), - 'os' => [ - 'version' => $orderItem?->os?->version, - 'group' => $orderItem?->os?->osGroup?->name, - ], - 'specs' => $product ? [ - 'cpu' => (int) ($product->cpu ?? 0), - 'ram' => (int) ($product->ram ?? 0), - 'disk' => (int) ($product->disk ?? 0), - 'bandwidth' => (int) ($product->bandwidth ?? 0), - ] : null, - ]; - } -} diff --git a/app/Http/Resources/Api/Service/Order/ServiceDetailResource.php b/app/Http/Resources/Api/Service/Order/ServiceDetailResource.php deleted file mode 100644 index 831d45c..0000000 --- a/app/Http/Resources/Api/Service/Order/ServiceDetailResource.php +++ /dev/null @@ -1,55 +0,0 @@ - $this->id, - 'uuid' => $this->uuid, - 'service_name' => $this->product?->name ?? $this->server_name, - 'server_name' => $this->server_name, - 'product_type' => $this->product?->product_type, - 'billing_cycle' => $this->orderItem?->billing_cycle, - 'status' => $this->status, - 'status_text' => $this->status_label, - 'auto_renew' => (bool) $this->auto_renew, - 'started_at' => $this->started_at?->toISOString(), - 'expires_at' => $this->expires_at?->toISOString(), - 'note' => $this->note, - 'invoice_id' => $this->invoice_id, - 'invoice_number' => $this->invoice?->invoice_number, - 'product' => $this->whenLoaded('product', fn () => [ - 'id' => $this->product->id, - 'uuid' => $this->product->uuid, - 'name' => $this->product->name, - 'product_type' => $this->product->product_type, - 'specs' => [ - 'cpu' => $this->product->cpu, - 'ram' => $this->product->ram, - 'disk' => $this->product->disk, - 'bandwidth' => $this->product->bandwidth, - ], - ]), - 'addons' => $this->whenLoaded('addons', fn () => $this->addons->map(fn ($a) => [ - 'type' => $a->addon_type, - 'value' => $a->addon_value, - 'price' => $a->price, - ])->all()), - 'vmware' => $this->whenLoaded('vmware', fn () => $this->vmware ? [ - 'id' => $this->vmware->id, - 'name' => $this->vmware->name, - 'hostname' => $this->vmware->hostname, - 'ip_address' => $this->vmware->ipAddress?->ip, - 'os_name' => $this->vmware->os?->name, - 'status' => $this->vmware->status, - 'next_due_date' => $this->vmware->next_due_date?->toISOString(), - ] : null), - ]; - } -} diff --git a/app/Http/Services/Cart.php b/app/Http/Services/Cart.php deleted file mode 100644 index 79ba7a6..0000000 --- a/app/Http/Services/Cart.php +++ /dev/null @@ -1,149 +0,0 @@ -get('cart') ?? []; - - $userKey = "user_$user_id"; - - if (!isset($cart[$userKey])) { - $cart[$userKey] = []; - } - - foreach ($cart[$userKey] as $item) { - if ($item['product'] == $product->id && $item['product_type'] == $product->type_id) { - return $this; - } - } - - $cart[$userKey][] = [ - 'product' => $product->id, - 'product_type' => $product->type_id, - 'price' => $price - ]; - - session()->put('cart', $cart); - - return $this; - } - - - public function cartBK($user_id = 0): ?array - { - if (session()->get('cart')) { - // $cart = session()->get('cart')["user_$user_id"]; - $cart = session()->get("cart.user_$user_id", []); - - return array_map(function ($item) { - if ($item['product_type'] === 4) { - $product = VPS::find($item['product']); - $addOn = Addon::where('type_id', 1)->get(); - $group = OsGroup::with('Os')->where('module_host_id', $product->ModuleHost_id)->get(); - $item['product'] = $product; - $item['addon'] = $addOn; - $item['os_group'] = $group; - } - if ($item['product_type'] === 3) { - $product = Hosting::find($item['product']); - $item['product'] = $product; - } - return $item; - }, $cart); - } - return null; - } - - public function cart($user_id = 0): ?array - { - $cartItems = CartModel::where('customer_id', $user_id)->get(); - - if ($cartItems->isEmpty()) { - return null; - } - - return $cartItems->map(function ($item) { - $data = [ - 'id' => $item->id, - 'product_type' => $item->type_product, - 'quantity' => $item->quantity, - 'price' => $item->price, - 'months' => $item->months, - ]; - - if ($item->type_product === 4) { - $product = VPS::with('operatingSystems.osGroup')->find($item->product_id); - $addons = Addon::where('type_id', 1)->get(); - - // Get OS groups that are specifically assigned to this VPS product - $osGroups = collect(); - if ($product && $product->operatingSystems->isNotEmpty()) { - $osGroupIds = $product->operatingSystems->pluck('osGroup.id')->filter()->unique(); - $osGroups = OsGroup::with(['os' => function($query) use ($product) { - // Only get OS versions that are assigned to this specific VPS product - $query->whereHas('vpsProducts', function($q) use ($product) { - $q->where('product_id', $product->id) - ->where('product_type', 4); - }); - }]) - ->whereIn('id', $osGroupIds) - ->get(); - } - - - $data['product'] = $product; - $data['addon'] = $addons; - $data['os_group'] = $osGroups; - } elseif ($item->type_product === 3) { - $product = Hosting::find($item->product_id); - $addons = Addon::where('type_id', 1)->get(); - $osGroups = OsGroup::with('os') - ->where('module_host_id', $product->module_host_id) - ->get(); - $data['product'] = $product; - $data['addon'] = $addons; - $data['os_group'] = $osGroups; - } - - return $data; - })->toArray(); - } - - - public function removeCartItem($user_id = 0, $key = 0): bool - { - if (session()->get('cart')) { - $cart = session()->get('cart'); - - unset($cart["user_$user_id"][$key]); - - session()->forget('coupon'); - - session()->put('cart', $cart); - return true; - } - return false; - } - public function getItem($user_id = 0, $key = 0) - { - if (session()->get('cart')) { - $cart = session()->get('cart')["user_$user_id"]; - - if ($cart[$key]) { - return $cart[$key]; - } - return null; - } - return null; - } -} diff --git a/app/Http/Services/Discord.php b/app/Http/Services/Discord.php index 82337f4..c8a9ab4 100644 --- a/app/Http/Services/Discord.php +++ b/app/Http/Services/Discord.php @@ -71,100 +71,6 @@ class Discord return $this->sendDiscordMessage($payload); } - /** - * Send error message with embed - */ - public function NotifyDiscordFailure($reason, $data = null) - { - $fields = [ - [ - 'name' => '🚫 Lý do lỗi', - 'value' => $reason, - 'inline' => false, - ], - ]; - - if ($data) { - $fields[] = [ - 'name' => '📄 Dữ liệu liên quan', - 'value' => "```json\n" . substr(json_encode($data, JSON_PRETTY_PRINT), 0, 1000) . "\n```", - 'inline' => false, - ]; - } - - $embed = [ - 'title' => '🔴 Gửi Discord thất bại', - 'color' => self::COLOR_ERROR, - 'fields' => $fields, - 'timestamp' => now()->toIso8601String(), - ]; - - Log::channel('Custom')->error('NotifyDiscordFailure: ' . $reason, ['data' => $data]); - - return null; - } - - /** - * Send VPS information with enhanced embed - */ - public function SendInformation($product = [], $user = '', $password = '', $port = 22) - { - $fields = [ - [ - 'name' => '🌐 Địa chỉ IP', - 'value' => isset($product->IpAddress->ip) ? $product->IpAddress->ip : 'N/A', - 'inline' => true, - ], - [ - 'name' => '👤 Tài khoản', - 'value' => $user ?: 'root', - 'inline' => true, - ], - [ - 'name' => '🔌 Cổng SSH', - 'value' => (string) $port, - 'inline' => true, - ], - [ - 'name' => '🔐 Mật khẩu', - 'value' => "||{$password}||", // Spoiler text - 'inline' => true, - ], - [ - 'name' => '⏰ Hết hạn', - 'value' => isset($product->next_due_date) ? - Carbon::parse($product->next_due_date)->format('d/m/Y H:i') : 'Không giới hạn', - 'inline' => true, - ], - [ - 'name' => '💻 Hệ điều hành', - 'value' => (isset($product->Os->OsGroup->name) ? $product->Os->OsGroup->name : '') . ' ' . (isset($product->Os->version) ? $product->Os->version : ''), - 'inline' => true, - ], - ]; - - // Add SSH command - $ipAddress = isset($product->IpAddress->ip) ? $product->IpAddress->ip : 'IP_ADDRESS'; - $sshCommand = "ssh {$user}@{$ipAddress}"; - if ($port != 22) { - $sshCommand .= " -p {$port}"; - } - - $embed = [ - 'title' => '🎉 VPS đã được khởi tạo thành công!', - 'description' => "**Đơn hàng:** #" . (isset($product->order_id) ? $product->order_id : '') . "\n**SSH Command:**\n```bash\n{$sshCommand}\n```", - 'color' => self::COLOR_SUCCESS, - 'fields' => $fields, - 'footer' => [ - 'text' => 'VPS Management System', - 'icon_url' => 'https://cdn-icons-png.flaticon.com/512/868/868681.png' - ], - 'timestamp' => now()->toIso8601String(), - ]; - - return $this->sendDiscordMessage(['embeds' => [$embed]]); - } - /** * Send error message with enhanced formatting */ @@ -201,96 +107,6 @@ class Discord return $this->sendDiscordMessage(['embeds' => [$embed]]); } - /** - * Send new user registration notification - */ - public function SendNewUserRegistration($email, $code, $verificationLink) - { - $fields = [ - [ - 'name' => '📧 Email đăng ký', - 'value' => $email, - 'inline' => true, - ], - [ - 'name' => '🔢 Mã xác thực', - 'value' => "`{$code}`", - 'inline' => true, - ], - [ - 'name' => '🔗 Link xác thực', - 'value' => "[Nhấn để xác thực]({$verificationLink})", - 'inline' => false, - ], - ]; - - $embed = [ - 'title' => '🎉 Thành viên mới đăng ký!', - 'description' => '**Có người dùng mới vừa tạo tài khoản**', - 'color' => self::COLOR_SUCCESS, - 'fields' => $fields, - 'thumbnail' => [ - 'url' => 'https://cdn-icons-png.flaticon.com/512/1077/1077114.png' - ], - 'timestamp' => now()->toIso8601String(), - ]; - - return $this->sendDiscordMessage(['embeds' => [$embed]]); - } - - /** - * Send customer recharge notification - */ - public function SendCustomerRecharge($customer, $amount, $method = null) - { - $customerName = trim("{$customer->firstname} {$customer->lastname}"); - - $fields = [ - [ - 'name' => '👤 Khách hàng', - 'value' => $customerName ?: 'N/A', - 'inline' => true, - ], - [ - 'name' => '📧 Email', - 'value' => isset($customer->email) ? $customer->email : 'N/A', - 'inline' => true, - ], - [ - 'name' => '💰 Số tiền nạp', - 'value' => '**' . number_format($amount, 0, ',', '.') . ' VNĐ**', - 'inline' => true, - ], - ]; - - if ($method) { - $fields[] = [ - 'name' => '💳 Phương thức', - 'value' => $method, - 'inline' => true, - ]; - } - - $fields[] = [ - 'name' => '🕒 Thời gian', - 'value' => Carbon::now()->format('d/m/Y H:i:s'), - 'inline' => true, - ]; - - $embed = [ - 'title' => '💎 Khách hàng nạp tiền thành công', - 'description' => "**{$customerName}** vừa nạp tiền vào tài khoản", - 'color' => self::COLOR_VIP, - 'fields' => $fields, - 'thumbnail' => [ - 'url' => 'https://cdn-icons-png.flaticon.com/512/3135/3135715.png' - ], - 'timestamp' => now()->toIso8601String(), - ]; - - return $this->sendDiscordMessage(['embeds' => [$embed]]); - } - /** * Send job execution notification */ @@ -350,164 +166,4 @@ class Discord return $this->sendDiscordMessage(['embeds' => [$embed]]); } - - /** - * Send order paid notification with enhanced formatting - */ - public function SendOrderPaidNotification($invoice) - { - $invoiceItem = $invoice->invoiceItem()->with([ - 'vps', - 'hosting', - 'os.osGroup', - 'addons.addonItems' - ])->first(); - - if (!$invoiceItem) { - return $this->SendErrorMessage('Không tìm thấy invoice item từ invoice ID: ' . $invoice->id, 'Order Paid Process'); - } - - $product = $invoiceItem->product; - $os = $invoiceItem->os; - $osGroup = isset($os->osGroup) ? $os->osGroup : null; - $addons = $invoiceItem->addons; - $customer = $invoice->customer; - - $fields = [ - [ - 'name' => '🧾 Mã đơn hàng', - 'value' => "**#{$invoice->order_num}**", - 'inline' => true, - ], - [ - 'name' => '📦 Sản phẩm', - 'value' => "#" . (isset($product->id) ? $product->id : '') . " - " . (isset($product->name) ? $product->name : ''), - 'inline' => true, - ], - [ - 'name' => '💰 Tổng thanh toán', - 'value' => '**' . number_format($invoice->total, 0, ',', '.') . ' VNĐ**', - 'inline' => true, - ], - [ - 'name' => '👤 Khách hàng', - 'value' => trim((isset($customer->firstname) ? $customer->firstname : '') . ' ' . (isset($customer->lastname) ? $customer->lastname : '')), - 'inline' => true, - ], - [ - 'name' => '📧 Email', - 'value' => isset($customer->email) ? $customer->email : 'N/A', - 'inline' => true, - ], - [ - 'name' => '🕒 Thời gian thanh toán', - 'value' => Carbon::parse($invoice->date_paid)->format('d/m/Y H:i:s'), - 'inline' => true, - ], - ]; - - if ($osGroup || $os) { - $fields[] = [ - 'name' => '🖥️ Hệ điều hành', - 'value' => (isset($osGroup->name) ? $osGroup->name : '') . ' ' . (isset($os->version) ? $os->version : ''), - 'inline' => true, - ]; - } - - // Add addons if exist - if ($addons && $addons->count()) { - $addonsText = ""; - foreach ($addons as $addon) { - $addonsText .= "• {$addon->addonItems->name} (x{$addon->qty}) - " . number_format($addon->price, 0, ',', '.') . " VNĐ\n"; - } - $fields[] = [ - 'name' => '➕ Dịch vụ bổ sung', - 'value' => $addonsText, - 'inline' => false, - ]; - } - - $embed = [ - 'title' => '💵 Đơn hàng được thanh toán thành công!', - 'description' => "**Khách hàng:** " . (isset($customer->firstname) ? $customer->firstname : '') . " " . (isset($customer->lastname) ? $customer->lastname : '') . "\n**Đơn hàng:** #{$invoice->order_num}", - 'color' => self::COLOR_SUCCESS, - 'fields' => $fields, - 'thumbnail' => [ - 'url' => 'https://cdn-icons-png.flaticon.com/512/3135/3135673.png' - ], - 'footer' => [ - 'text' => 'Order Management System' - ], - 'timestamp' => now()->toIso8601String(), - ]; - - return $this->sendDiscordMessage(['embeds' => [$embed]]); - } - - /** - * Send service renewal notification - */ - public function SendServiceRenewal($service, $period) - { - $fields = [ - [ - 'name' => '🏷️ Dịch vụ', - 'value' => isset($service->name) ? $service->name : 'N/A', - 'inline' => true, - ], - [ - 'name' => '📅 Gia hạn thêm', - 'value' => $period, - 'inline' => true, - ], - [ - 'name' => '📅 Ngày hết hạn mới', - 'value' => Carbon::parse($service->next_due_date)->format('d/m/Y'), - 'inline' => true, - ], - ]; - - $embed = [ - 'title' => '🔄 Dịch vụ được gia hạn', - 'color' => self::COLOR_INFO, - 'fields' => $fields, - 'timestamp' => now()->toIso8601String(), - ]; - - return $this->sendDiscordMessage(['embeds' => [$embed]]); - } - - /** - * Send system maintenance notification - */ - public function SendMaintenanceNotification($title, $description, $startTime = null, $endTime = null) - { - $fields = []; - - if ($startTime) { - $fields[] = [ - 'name' => '🕐 Bắt đầu', - 'value' => Carbon::parse($startTime)->format('d/m/Y H:i:s'), - 'inline' => true, - ]; - } - - if ($endTime) { - $fields[] = [ - 'name' => '🕑 Kết thúc dự kiến', - 'value' => Carbon::parse($endTime)->format('d/m/Y H:i:s'), - 'inline' => true, - ]; - } - - $embed = [ - 'title' => "🔧 {$title}", - 'description' => $description, - 'color' => self::COLOR_WARNING, - 'fields' => $fields, - 'timestamp' => now()->toIso8601String(), - ]; - - return $this->sendDiscordMessage(['embeds' => [$embed]]); - } } \ No newline at end of file diff --git a/app/Http/Services/Hosting.php b/app/Http/Services/Hosting.php index 8095801..f87c285 100644 --- a/app/Http/Services/Hosting.php +++ b/app/Http/Services/Hosting.php @@ -4,11 +4,9 @@ namespace App\Http\Services; use App\Models\ModuleHosting; use App\Support\Eloquent\Sluggable; -use Illuminate\Support\Facades\Crypt; use App\Support\TlsHttpClientFactory; use Illuminate\Support\Facades\Log; - class Hosting { use Sluggable; @@ -18,26 +16,6 @@ class Hosting public string|null $username = null; public string|null $password = null; - /* - * get All Domains in hosting server - */ - public function getAllDomains() - { - $apiCheck = $this->apiCheck(); - - if ($apiCheck) { - - return TlsHttpClientFactory::plesk() - ->withHeaders([ - 'X-API-Key' => $this->apiKey, - 'accept' => 'application/json' - ]) - ->get("https://" . $this->url . '/api/v2/domains')->json(); - } - return "Login_Again"; - } - - /* * Check exits apikey */ @@ -58,7 +36,6 @@ class Hosting return false; } - /* * check user exits from hosting server with email client */ @@ -85,17 +62,14 @@ class Hosting ]; } - /* * get All Client in hosting server */ public function CheckUserClient($customer = []): array|string { - $apiCheck = $this->apiCheck(); if ($apiCheck) { - $allClient = $this->getAllClients(); $clientCheck = array_filter($allClient, function ($element) use ($customer) { @@ -105,7 +79,6 @@ class Hosting }); if (count($clientCheck)) { - $Service = ModuleHosting::where('customer_id', $customer->id)->first(); return [ @@ -168,7 +141,6 @@ class Hosting $apiCheck = $this->apiCheck(); if ($apiCheck) { - $ClientDomain = $this->GetAllDomainForCustomer($customer_id); if ($ClientDomain['error'] === false) { @@ -201,7 +173,6 @@ class Hosting $apiCheck = $this->apiCheck(); if ($apiCheck) { - $allDomain = TlsHttpClientFactory::plesk() ->withHeaders([ 'X-API-Key' => $this->apiKey, @@ -233,7 +204,6 @@ class Hosting $apiCheck = $this->apiCheck(); if ($apiCheck) { - $CreateDomain = TlsHttpClientFactory::plesk() ->withHeaders([ 'X-API-Key' => $this->apiKey, @@ -269,11 +239,6 @@ class Hosting return "Login_Again"; } - public function ChangeStatusHosting($domain_id = '', $status = 'active') - { - - } - public function CreateExpirationForDomain($domain = '', $date_exp = '') { $response = TlsHttpClientFactory::plesk() @@ -338,25 +303,4 @@ class Hosting 'data' => $response['message'] ]; } - - public function ResetPassword($account_id = 0, $password = ''): array - { - $response = TlsHttpClientFactory::plesk() - ->withHeaders([ - 'X-API-Key' => $this->apiKey, - 'accept' => 'application/json' - ]) - ->put("https://$this->url/api/v2/clients/$account_id", [ - 'password' => $password - ])->json(); - if (!isset($response['code'])) { - return [ - 'error' => false - ]; - } - return [ - 'error' => true, - 'data' => $response['errors']['message'] - ]; - } } diff --git a/app/Http/Services/Media.php b/app/Http/Services/Media.php deleted file mode 100644 index dd85c12..0000000 --- a/app/Http/Services/Media.php +++ /dev/null @@ -1,30 +0,0 @@ -get('pages') ? \request()->get('pages') : 1; - $images = []; - $limit = 20; - $offset = ($pages - 1) * $limit; - foreach ($list_files as $item) { - if (in_array(Storage::mimeType($item),$this->mediaType)) { - $images[] = $item; - } - } - if ($offset > count($images)) - { - return []; - } - return array_slice(array_reverse($images),$offset,$limit); - } -} diff --git a/app/Http/Services/ProductService.php b/app/Http/Services/ProductService.php deleted file mode 100644 index ffd218a..0000000 --- a/app/Http/Services/ProductService.php +++ /dev/null @@ -1,174 +0,0 @@ -repository = $repository; - } - - public function store(array $data) - { - // Prepare product data - $productData = $this->prepareData($data); - - // Create product - $product = $this->repository->create($productData); - - // Sync OS versions if provided (for VPS) - if (!empty($data['os_ids'])) { - $product->osVersions()->sync($data['os_ids']); - } - - // Sync Control Panels if provided (for Hosting) - if (!empty($data['control_panel_ids'])) { - $this->saveControlPanels($product, $data); - } - - // Save pricing cycles if provided - if (!empty($data['pricing'])) { - $this->savePricings($product, $data['pricing'], $data['currency'] ?? 'USD'); - } - - return $product; - } - - public function update(int $id, array $data) - { - // Prepare product data - $productData = $this->prepareData($data, false); - - // Update product - $result = $this->repository->update($id, $productData); - - // Get the product for relationships - $product = $this->repository->find($id); - - // Sync OS versions if provided (for VPS) - if (isset($data['os_ids'])) { - $product->osVersions()->sync($data['os_ids']); - } - - // Sync Control Panels if provided (for Hosting) - if (isset($data['control_panel_ids'])) { - $this->saveControlPanels($product, $data); - } - - // Save pricing cycles if provided - if (!empty($data['pricing'])) { - $this->savePricings($product, $data['pricing'], $data['currency'] ?? 'USD'); - } - - return $result; - } - - public function delete(int $id) - { - return $this->repository->delete($id); - } - - /** - * Save pricing cycles for product - */ - private function savePricings($product, array $pricingData, string $currency): void - { - foreach ($pricingData as $cycle => $pricing) { - // Only save if enabled and has price - if (!empty($pricing['enabled']) && isset($pricing['price'])) { - ProductPricings::updateOrCreate( - [ - 'product_id' => $product->id, - 'billing_cycle' => $cycle, - ], - [ - 'price' => $pricing['price'] ?? 0, - 'price_sale' => $pricing['price_sale'] ?? null, - 'currency' => $currency, - 'is_active' => true, - ] - ); - } else { - // Remove pricing if not enabled - ProductPricings::where('product_id', $product->id) - ->where('billing_cycle', $cycle) - ->delete(); - } - } - } - - /** - * Save Control Panels for product (for Hosting) - */ - private function saveControlPanels($product, array $data): void - { - $syncData = []; - - foreach ($data['control_panel_ids'] as $panelId) { - $priceModifier = $data['control_panel_price_modifiers'][$panelId] ?? 0; - $isDefault = isset($data['control_panel_default']) && $data['control_panel_default'] == $panelId; - - $syncData[$panelId] = [ - 'price_modifier' => $priceModifier, - 'is_default' => $isDefault, - ]; - } - - $product->controlPanels()->sync($syncData); - } - - /** - * Prepare data for create/update - */ - private function prepareData(array $data, bool $isCreate = true): array - { - $prepared = []; - - // Generate UUID and slug only on create - if ($isCreate) { - $prepared['uuid'] = (string) Str::uuid(); - } - - // Name and slug - if (isset($data['name'])) { - $prepared['name'] = $data['name']; - $prepared['slug'] = Str::slug($data['name']); - } - - // Direct mappable fields - $fillableFields = [ - 'short_description', - 'description', - 'setup_fee', - 'trial_days', - 'cpu', - 'ram', - 'gpu', - 'disk', - 'bandwidth', - 'stock', - 'is_unlimited', - 'is_active', - 'sort_order', - 'product_type', - 'product_categories_id', - 'published_at', - ]; - - foreach ($fillableFields as $field) { - if (array_key_exists($field, $data)) { - $prepared[$field] = $data[$field]; - } - } - - return $prepared; - } -} diff --git a/app/Http/Services/Telegram.php b/app/Http/Services/Telegram.php index 195e8ea..b629e0b 100644 --- a/app/Http/Services/Telegram.php +++ b/app/Http/Services/Telegram.php @@ -49,22 +49,6 @@ class Telegram } } - public function SendInformation($product = [], $user = '', $password = '', $port = 22) - { - $msg = "Thông tin truy cập VPS vừa được khởi tạo 🎉 \n"; - $msg .= "--------------------------------\n"; - $msg .= "| 🌐 IP: " . $product['IpAddress']['ip'] . "\n"; - $msg .= "| 📌 User: " . $user . "\n"; - $msg .= "| 👉 Port: " . $port . "\n"; - $msg .= "| 📺 Password: " . $password . "\n"; - $msg .= "| 💾 Hết hạn: " . Carbon::parse($product['next_due_date'])->format('d/m/Y') . "\n"; - $msg .= "| 💵 Hệ điều hành: " . $product['Os']['OsGroup']['name'] . $product['Os']['version'] . "\n"; - $msg .= "| 📁 Đơn hàng số: #" . $product['order_id'] . "\n"; - $msg .= "--------------------------------"; - - return $this->SendMessage($msg); - } - public function SendErrorMessage($data) { $msg = "Admin: Nhận được thông báo lỗi khi khởi tạo dịch vụ \n"; @@ -74,16 +58,6 @@ class Telegram return $this->SendMessage($msg); } - public function SendNewUserRegistration($email, $code, $verificationLink) - { - $msg = "🎉 Thành viên mới vừa đăng ký!\n"; - $msg .= "📧 Email: {$email}\n"; - $msg .= "🧾 Mã xác thực: {$code}\n"; - $msg .= "🔗 Link xác thực: {$verificationLink}"; - - return $this->SendMessage($msg); - } - public function SendTrialRegistration($client, array $trialConfig = []) { $msg = "🧪 Khách hàng vừa đăng ký dùng thử Cloud VPS!\n"; @@ -119,17 +93,6 @@ class Telegram return $this->SendMessage($msg); } - public function SendCustomerRecharge($customer, $amount) - { - $msg = "💰 Khách hàng vừa nạp tiền vào tài khoản\n"; - $msg .= "👤 Tên: {$customer->firstname} {$customer->lastname}\n"; - $msg .= "📧 Email: {$customer->email}\n"; - $msg .= "💵 Số tiền: " . number_format($amount, 0, ',', '.') . " VNĐ\n"; - $msg .= "🕒 Thời gian: " . Carbon::now()->format('d/m/Y H:i:s'); - - return $this->SendMessage($msg); - } - public function SendJobRunInfo($jobName, $status, $additionalInfo = null) { $msg = "⚙️ Thông báo chạy job: {$jobName}\n"; @@ -143,44 +106,6 @@ class Telegram return $this->SendMessage($msg); } - public function SendOrderPaidNotification($invoice) - { - $invoiceItem = $invoice->invoiceItem()->with([ - 'vps', - 'hosting', - 'os.osGroup', - 'addons.addonItems' - ])->first(); - if (!$invoiceItem) { - return $this->SendErrorMessage('Không tìm thấy invoice item từ invoice ID: ' . $invoice->id); - } - - $product = $invoiceItem->product; - $os = $invoiceItem->os; - $osGroup = $os?->osGroup; - $addons = $invoiceItem->addons; - - $msg = "💵 Khách đã thanh toán đơn hàng thành công!\n"; - $msg .= "--------------------------------\n"; - $msg .= "🧾 Đơn hàng: #{$invoice->order_num}\n"; - $msg .= "📦 Sản phẩm: #{$product?->id} - {$product?->name}\n"; - $msg .= "🧠 Hệ điều hành: {$osGroup?->name} {$os?->version}\n"; - $msg .= "💰 Tổng tiền: " . number_format($invoice->total, 0, ',', '.') . " VNĐ\n"; - $msg .= "🧍 Khách: {$invoice->customer?->firstname} {$invoice->customer?->lastname} ({$invoice->customer?->email})\n"; - $msg .= "🕒 Ngày thanh toán: " . Carbon::parse($invoice->date_paid)->format('d/m/Y H:i:s') . "\n"; - - if ($addons && $addons->count()) { - $msg .= "➕ Addons:\n"; - foreach ($addons as $addon) { - $msg .= "- {$addon->addonItems->name} (x{$addon->qty}) - " . number_format($addon->price, 0, ',', '.') . " VNĐ\n"; - } - } - - $msg .= "--------------------------------"; - - return $this->SendMessage($msg); - } - /** * Notify one step of the VM rebuild pipeline (success or failure). */ @@ -245,6 +170,4 @@ class Telegram return null; } } - - } diff --git a/app/Http/Services/Upload.php b/app/Http/Services/Upload.php deleted file mode 100644 index 1eaa05d..0000000 --- a/app/Http/Services/Upload.php +++ /dev/null @@ -1,80 +0,0 @@ -getClientoriginalName(); - $fileType = $file->getClientmimeType(); - - if (! in_array($fileType,$this->mineType)) - { - return [ - 'error' => true, - 'message' => 'File không hợp lệ' - ]; - } - if ($file->getSize() > $this->fileSize) - { - return [ - 'error' => true, - 'message' => 'File vượt kích thước cho phép' - ]; - } - if (Storage::exists('Upload/'. date('Y/m/d') . '/'.$fileName)) { - $fileName = time() . "_" .$fileName; - } - - Storage::putFileAs('Upload/'. date('Y/m/d') . '/',$file,$fileName); - - if (Storage::exists('Upload/'. date('Y/m/d') . '/'.$fileName)) - { - return [ - 'error' => false, - 'data' => 'Upload/'. date('Y/m/d') . '/'.$fileName, - 'filename' => $fileName - ]; - } - return [ - 'error' => true, - 'message' => 'File upload không thành công. Vui lòng thử lại!' - ]; - } - public function FileUpload($file = null) - { - - if (! $file) - { - return false; - } - - $fileName = $file->getClientoriginalName(); - $fileType = $file->getClientmimeType(); - - if (! in_array($fileType,$this->mineTypeFile)) - { - return false; - } - Storage::putFileAs('Excel/',$file,$fileName); - - if (Storage::exists('Excel/'.$fileName)) - { - return 'Excel/'.$fileName; - } - return false; - } -} diff --git a/app/Http/Services/VNPayService.php b/app/Http/Services/VNPayService.php index c1fe1de..8681090 100644 --- a/app/Http/Services/VNPayService.php +++ b/app/Http/Services/VNPayService.php @@ -2,87 +2,9 @@ namespace App\Http\Services; -use Carbon\Carbon; -use Illuminate\Support\Facades\Auth; class VNPayService { - /** - * Create a payment URL - * - * @param array $data - * @return string - */ - public function createPaymentUrl(array $data) - { - $vnp_TxnRef = $data['transaction_ref'] ?? rand(1, 10000); - - if (!is_numeric($vnp_TxnRef)) { - $vnp_TxnRef = preg_replace('/[^0-9]/', '', $vnp_TxnRef); - } - - $vnp_Amount = $data['amount'] ?? 0; - $vnp_Locale = $data['language'] ?? 'vn'; - $vnp_BankCode = $data['bankCode'] ?? ''; - $vnp_IpAddr = request()->ip(); - - $user = Auth::guard('client')->user(); - $userName = $user ? $user->fullname : 'Khách'; - - $isDeposit = isset($data['transaction_ref']); - if ($isDeposit) { - $orderInfo = "Nạp tiền vào tài khoản - Khách hàng: " . $userName; - } else { - $orderId = $data['order_id'] ?? $vnp_TxnRef; - $orderInfo = "Thông tin đơn hàng: " . $userName . " - Mã đơn hàng: " . $orderId; - } - - // Set timezone - Carbon::setLocale('vi'); - $startTime = Carbon::now()->format('YmdHis'); - $expire = Carbon::now()->addMinutes(15)->format('YmdHis'); - - $inputData = [ - "vnp_Version" => "2.1.0", - "vnp_TmnCode" => config('vnpay.tmn_code'), - "vnp_Amount" => $vnp_Amount * 100, - "vnp_Command" => "pay", - "vnp_CreateDate" => $startTime, - "vnp_CurrCode" => "VND", - "vnp_IpAddr" => $vnp_IpAddr, - "vnp_Locale" => 'vn', - "vnp_OrderInfo" => $orderInfo, - "vnp_OrderType" => "other", - "vnp_ReturnUrl" => config('vnpay.return_url'), - "vnp_TxnRef" => $vnp_TxnRef, - ]; - -// Bỏ cmt khi nếu muốn chọn thanh toán chọn thẻ thôi -// if (isset($vnp_BankCode) && $vnp_BankCode != "") { -// $inputData['vnp_BankCode'] = $vnp_BankCode; -// } - - ksort($inputData); - $query = ""; - $i = 0; - $hashdata = ""; - foreach ($inputData as $key => $value) { - if ($i == 1) { - $hashdata .= '&' . urlencode($key) . "=" . urlencode($value); - } else { - $hashdata .= urlencode($key) . "=" . urlencode($value); - $i = 1; - } - $query .= urlencode($key) . "=" . urlencode($value) . '&'; - } - - $vnp_Url = config('vnpay.url') . "?" . rtrim($query, '&'); - $vnpSecureHash = hash_hmac('sha512', $hashdata, config('vnpay.hash_secret')); - $vnp_Url .= '&vnp_SecureHash=' . $vnpSecureHash; - - return $vnp_Url; - } - /** * Verify return data from VNPAY * diff --git a/app/Http/Services/vCenter.php b/app/Http/Services/vCenter.php index d9c6449..9d246e1 100644 --- a/app/Http/Services/vCenter.php +++ b/app/Http/Services/vCenter.php @@ -129,14 +129,6 @@ class vCenter { return $this->template->getAllTemplate(); } - public function getAllVersion($library = ''): bool|array|string - { - return $this->template->getAllVersion($library); - } - public function getTemplateFromDatastore($datastore = '') - { - return $this->template->getTemplateFromDatastore($datastore); - } // ─── VM Lifecycle ───────────────────────────────────────────────────────── @@ -148,10 +140,6 @@ class vCenter { return $this->vm->findVirtualMachineByName($vmName); } - public function FindVirtualMachine($name = ''): string|bool - { - return $this->vm->FindVirtualMachine($name); - } public function CreateVirtualMachine($product_data): array|string { return $this->vm->CreateVirtualMachine($product_data); @@ -168,22 +156,10 @@ class vCenter { return $this->vm->GetIPAddressVirtualMachine($vmware_id); } - public function GetNetworkVirtualMachine($vmware_id = ''): array - { - return $this->vm->GetNetworkVirtualMachine($vmware_id); - } - public function ConfigNetworkVirtualMachine($vmware_id = '', $config = []): int - { - return $this->vm->ConfigNetworkVirtualMachine($vmware_id, $config); - } public function GetVMStats($vmware_id = ''): array { return $this->vm->GetVMStats($vmware_id); } - public function reBuildVPS(string $vmName): array|string - { - return $this->vm->reBuildVPS($vmName); - } public function UpgradeMemoryVirtualMachine($vmware_id = '', $ram = 0): array|string { return $this->vm->UpgradeMemoryVirtualMachine($vmware_id, $ram); @@ -254,21 +230,6 @@ class vCenter { return $this->snapshot->RevertSnapShot($product, $snap); } - - // ─── VM OS Configuration ───────────────────────────────────────────────── - - public function ConfigVirtualMachineUbuntu($vmware_id = '', $product = null, $password = ''): void - { - $this->vmConfig->ConfigVirtualMachineUbuntu($vmware_id, $product, $password); - } - public function ConfigCentOSVersion8($vmware_id = '', $product = [], $password = ''): void - { - $this->vmConfig->ConfigCentOSVersion8($vmware_id, $product, $password); - } - public function ConfigCentOSVersion7($vmware_id = '', $product = [], $password = ''): void - { - $this->vmConfig->ConfigCentOSVersion7($vmware_id, $product, $password); - } public function ConfigWindowsVersion10($vmware_id = '', $product = [], $password = ''): void { $this->vmConfig->ConfigWindowsVersion10($vmware_id, $product, $password); @@ -286,18 +247,6 @@ class vCenter $this->vmConfig->ConfigRockyLinuxVersion9($vmware_id, $product, $password); } - // ─── Credentials & PowerShell ──────────────────────────────────────────── - - public function resetGuestPasswordViaPs1( - string $vmName, - string $guestUser, - string $guestPassword, - string $newPassword, - string $targetUser = 'Administrator', - ): array|string { - return $this->shell->resetGuestPasswordViaPs1($vmName, $guestUser, $guestPassword, $newPassword, $targetUser); - } - public function changeRDPPortWindows( string $vmName, string $guestUser, @@ -330,9 +279,4 @@ class vCenter $hostname, ); } - - public function getWindowsRdpPort(string $vmwareId, string $guestUser, string $guestPassword): ?int - { - return $this->shell->getWindowsRdpPort($vmwareId, $guestUser, $guestPassword); - } } diff --git a/app/Http/Services/vCenter/Client.php b/app/Http/Services/vCenter/Client.php index 2d26356..07a9fb6 100644 --- a/app/Http/Services/vCenter/Client.php +++ b/app/Http/Services/vCenter/Client.php @@ -2,11 +2,9 @@ namespace App\Http\Services\vCenter; -use Illuminate\Support\Facades\Http; use App\Support\TlsHttpClientFactory; use App\Http\Facade\SignRSA; use App\Models\Vcenter; -use Exception; class Client { diff --git a/app/Http/Services/vCenter/Concerns/ManagesSsh.php b/app/Http/Services/vCenter/Concerns/ManagesSsh.php index e13fd9b..775ca8f 100644 --- a/app/Http/Services/vCenter/Concerns/ManagesSsh.php +++ b/app/Http/Services/vCenter/Concerns/ManagesSsh.php @@ -64,19 +64,6 @@ trait ManagesSsh } } - public function loginSSH(): bool - { - $conn = $this->LoginAuth(); - - if ($conn) { - Log::info('✅ Connected to vCenter via SSH successfully'); - return true; - } - - Log::error('❌ Failed to connect to vCenter via SSH'); - return false; - } - // ─── Command variants ──────────────────────────────────────────────────── /** diff --git a/app/Http/Services/vCenter/Concerns/ReadsVirtualMachineInfo.php b/app/Http/Services/vCenter/Concerns/ReadsVirtualMachineInfo.php new file mode 100644 index 0000000..21a41fd --- /dev/null +++ b/app/Http/Services/vCenter/Concerns/ReadsVirtualMachineInfo.php @@ -0,0 +1,190 @@ +client->checkApiExits()) { + $params = []; + if (!empty($datacenter)) { + $params['filter.datacenters'] = $datacenter; + } + if (!empty($host)) { + $params['filter.hosts'] = $host; + } + + $response = TlsHttpClientFactory::vCenter() + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token + ]) + ->get("https://{$this->client->apiUri}/rest/vcenter/vm", $params)->json(); + + if (!isset($response['type'])) { + return $response['value']; + } + return false; + } + return "Login_Again"; + } + + public function CheckVirtualMachine($vmware_id = '') + { + if ($this->client->checkApiExits()) { + $response = TlsHttpClientFactory::vCenter() + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token + ]) + ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id")->json(); + + if (!isset($response['type'])) { + return $response['value']; + } + return false; + } + return "Login_Again"; + } + + /** + * Search VM by exact name in vCenter. + * Returns the first matching VM array or null if not found. + */ + public function findVirtualMachineByName(string $vmName): ?array + { + $check = $this->client->checkApiExits(); + if (!$check) { + Log::warning('findVirtualMachineByName: API check failed'); + return null; + } + + $response = TlsHttpClientFactory::vCenter() + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token + ]) + ->get("https://{$this->client->apiUri}/rest/vcenter/vm", [ + 'filter.names.1' => $vmName, + ]); + + if (!$response->successful()) { + Log::warning('findVirtualMachineByName: HTTP failed', ['status' => $response->status()]); + return null; + } + + $json = $response->json(); + if (isset($json['type']) || empty($json['value'])) { + return null; + } + + return $json['value'][0] ?? null; + } + + public function GetDetailVirtualMachine($vmware_id = ''): array + { + $response = TlsHttpClientFactory::vCenter() + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token + ]) + ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id")->json(); + if (!isset($response['type'])) { + return [ + 'error' => false, + 'data' => $response['value'] + ]; + } + return [ + 'error' => true, + 'message' => $response['value'] + ]; + } + + public function GetIPAddressVirtualMachine($vmware_id = ''): array + { + $response = TlsHttpClientFactory::vCenter() + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token + ]) + ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/guest/identity")->json(); + if (!isset($response['type'])) { + return [ + 'error' => false, + 'data' => $response['value'] + ]; + } + return [ + 'error' => true, + 'message' => $response['value'] + ]; + } + + public function GetVMStats($vmware_id = ''): array + { + $stats = [ + 'cpu' => null, + 'memory' => null, + 'disk' => null, + ]; + + try { + $hardwareResponse = TlsHttpClientFactory::vCenter() + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token + ]) + ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/hardware")->json(); + + if (is_array($hardwareResponse) && !isset($hardwareResponse['type']) && isset($hardwareResponse['value'])) { + foreach ($hardwareResponse['value'] as $hardware) { + if (isset($hardware['label']) && $hardware['label'] === 'CPU') { + $stats['cpu'] = [ + 'cores' => $hardware['spec']['num_cores'] ?? 0, + 'cores_per_socket' => $hardware['spec']['cores_per_socket'] ?? 0, + ]; + } + if (isset($hardware['label']) && $hardware['label'] === 'Memory') { + $stats['memory'] = [ + 'size_MiB' => $hardware['spec']['size_MiB'] ?? 0, + 'size_GB' => round(($hardware['spec']['size_MiB'] ?? 0) / 1024, 2), + ]; + } + if (isset($hardware['label']) && $hardware['label'] === 'Hard disk') { + $stats['disk'][] = [ + 'capacity' => $hardware['capacity'] ?? 0, + 'capacity_GB' => round(($hardware['capacity'] ?? 0) / (1024 * 1024 * 1024), 2), + ]; + } + } + } + + $guestMemoryResponse = TlsHttpClientFactory::vCenter() + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token + ]) + ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/guest/memory")->json(); + + if (is_array($guestMemoryResponse) && !isset($guestMemoryResponse['type']) && isset($guestMemoryResponse['value'])) { + $stats['memory']['used_MiB'] = $guestMemoryResponse['value']['used'] ?? 0; + $stats['memory']['used_GB'] = round(($guestMemoryResponse['value']['used'] ?? 0) / 1024, 2); + $stats['memory']['usage_percent'] = $stats['memory']['size_MiB'] > 0 + ? round(($guestMemoryResponse['value']['used'] ?? 0) / $stats['memory']['size_MiB'] * 100, 1) + : 0; + } + + $guestCpuResponse = TlsHttpClientFactory::vCenter() + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token + ]) + ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/guest/cpu")->json(); + + if (is_array($guestCpuResponse) && !isset($guestCpuResponse['type']) && isset($guestCpuResponse['value'])) { + $stats['cpu']['used_percent'] = $guestCpuResponse['value']['used'] ?? 0; + } + + return ['error' => false, 'data' => $stats]; + } catch (\Exception $e) { + return ['error' => true, 'message' => $e->getMessage(), 'data' => $stats]; + } + } +} diff --git a/app/Http/Services/vCenter/Concerns/UpgradesVirtualMachine.php b/app/Http/Services/vCenter/Concerns/UpgradesVirtualMachine.php new file mode 100644 index 0000000..c8205d3 --- /dev/null +++ b/app/Http/Services/vCenter/Concerns/UpgradesVirtualMachine.php @@ -0,0 +1,212 @@ +client->checkApiExits(); + + if ($check) { + $RAMConfig = [ + "spec" => [ + "size_MiB" => $ram * 1024, + ] + ]; + $response = TlsHttpClientFactory::vCenter() + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token + ]) + ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/hardware/memory", $RAMConfig); + + if ($response->status() === 200) { + return [ + 'error' => false, + 'data' => 'success' + ]; + } + return [ + 'error' => true, + 'data' => $response['value'] + ]; + } + return "Login_Again"; + } + + public function UpgradeCPUVirtualMachine($vmware_id = '', $cpu = 0): array|string + { + $check = $this->client->checkApiExits(); + + if ($check) { + $CPUConfig = [ + "spec" => [ + "cores_per_socket" => 1, + "count" => $cpu + ] + ]; + $response = TlsHttpClientFactory::vCenter() + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token + ]) + ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/hardware/cpu", $CPUConfig); + + if ($response->status() === 200) { + return [ + 'error' => false, + 'data' => 'success' + ]; + } + return [ + 'error' => true, + 'data' => $response['value'] + ]; + } + return "Login_Again"; + } + + public function UpgradeDiskVirtualMachine($product, $disk = 0): array|string + { + $vmware_id = $product->vmware_id; + $check = $this->client->checkApiExits(); + if ($check) { + try { + Log::channel('Custom')->info("==================================================================="); + Log::channel('Custom')->info("Upgrade Disk with VirtualMachine ID # - $vmware_id"); + Log::channel('Custom')->info("\r\n"); + + $Command = "\$vm = 'VirtualMachine-$vmware_id'; \$disksize = $disk; cd /; C:\CLISave\diskresize.ps1;"; + + Log::channel('Custom')->info("$Command"); + + $this->powerShellService->executePowerCLI($Command); + + Log::channel('Custom')->info("\r\n"); + Log::channel('Custom')->info("After run Command |" . now()); + Log::channel('Custom')->info("==================================================================="); + + return [ + 'error' => false + ]; + } catch (\Exception $error) { + Log::channel('Custom')->info("\r\n"); + Log::channel('Custom')->info("==================================================================="); + Log::channel('Custom')->info("Error Log: "); + Log::channel('Custom')->info($error->getMessage()); + Log::channel('Custom')->info("==================================================================="); + return [ + 'error' => true, + 'message' => $error->getMessage() + ]; + } + } + return "Login_Again"; + } + + private function updateVMHardware($vmId, $addon, $base_cpu, $base_ram): void + { + try { + Log::info("=== BẮT ĐẦU CẬP NHẬT ADDON ==="); + + $new_cpu = $base_cpu + $addon['cpu']; + $new_ram_mib = ($base_ram + $addon['ram']) * 1024; + + if ($addon['cpu'] > 0) { + $this->updateVMCPU($vmId, $new_cpu); + } + + if ($addon['ram'] > 0) { + $this->updateVMMemory($vmId, $new_ram_mib); + } + + Log::info("=== HOÀN THÀNH CẬP NHẬT ADDON ==="); + } catch (Exception $e) { + Log::error("❌ Lỗi khi cập nhật cấu hình phần cứng VM {$vmId}: " . $e->getMessage()); + } + } + + private function updateVMCPU($vmId, $cpuCount): bool + { + try { + $coresPerSocket = 1; + if ($cpuCount > 1) { + $maxCoresPerSocket = floor($cpuCount / 2); + for ($i = $maxCoresPerSocket; $i >= 1; $i--) { + if ($cpuCount % $i === 0) { + $coresPerSocket = $i; + break; + } + } + if ($coresPerSocket === 1 && $cpuCount > 2) { + $coresPerSocket = $cpuCount; + } + } + + $payload = [ + 'spec' => [ + 'count' => $cpuCount, + 'cores_per_socket' => $coresPerSocket, + 'hot_add_enabled' => true + ] + ]; + $response = TlsHttpClientFactory::vCenter() + ->timeout(30) + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token, + 'Content-Type' => 'application/json', + ]) + ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/cpu", $payload); + + if ($response->successful()) { + Log::info("✅ API call cập nhật CPU thành công cho VM {$vmId}"); + return true; + } else { + Log::error("❌ API call cập nhật CPU thất bại cho VM {$vmId}", [ + 'status' => $response->status(), + 'response' => $response->body() + ]); + return false; + } + } catch (Exception $e) { + Log::error("❌ Exception khi cập nhật CPU cho VM {$vmId}: " . $e->getMessage()); + return false; + } + } + + private function updateVMMemory($vmId, $ramSizeMB): bool + { + try { + $payload = [ + 'spec' => [ + 'size_MiB' => $ramSizeMB + ] + ]; + + $response = TlsHttpClientFactory::vCenter() + ->timeout(30) + ->withHeaders([ + 'vmware-api-session-id' => $this->client->token, + 'Content-Type' => 'application/json', + ]) + ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/memory", $payload); + + if ($response->successful()) { + Log::info("✅ API call cập nhật RAM thành công cho VM {$vmId}"); + return true; + } else { + Log::error("❌ API call cập nhật RAM thất bại cho VM {$vmId}", [ + 'status' => $response->status(), + 'response' => $response->body() + ]); + return false; + } + } catch (Exception $e) { + Log::error("❌ Exception khi cập nhật RAM cho VM {$vmId}: " . $e->getMessage()); + return false; + } + } +} diff --git a/app/Http/Services/vCenter/DeleteVMService.php b/app/Http/Services/vCenter/DeleteVMService.php deleted file mode 100644 index 43065a8..0000000 --- a/app/Http/Services/vCenter/DeleteVMService.php +++ /dev/null @@ -1,94 +0,0 @@ -client = $client; - $this->powerShellService = $powerShellService; - } - - protected function resolveVCenterFromProduct($product): ?\App\Models\vCenter - { - if ($product->vCenter) { - return $product->vCenter; - } - - if ($product->ipaddress?->host?->datacenter?->vCenter) { - return $product->ipaddress->host->datacenter->vCenter; - } - - $service = $product->services()->first(); - if ($service) { - $service->load('vmware.vCenter'); - if ($service->vmware?->vCenter) { - return $service->vmware->vCenter; - } - } - - return null; - } - - public function deleteVM($product, string $confirmName): array - { - $vmName = (string) $product->name; - - // ponytail: confirm guard — caller must pass exact vm name to proceed - if ($confirmName !== $vmName) { - return ['error' => true, 'message' => "Tên server xác nhận không khớp. Vui lòng nhập đúng: $vmName"]; - } - - $vcenter = $this->resolveVCenterFromProduct($product); - if (!$vcenter) { - return ['error' => true, 'message' => 'Không tìm thấy thông tin vCenter cho VM']; - } - - $loginResult = $this->client->loginServer($vcenter); - if (!$loginResult) { - return ['error' => true, 'message' => 'Login_Again']; - } - - $check = $this->client->checkApiExits(); - if (!$check) { - return ['error' => true, 'message' => 'Login_Again']; - } - - $host = $this->client->apiUri; - $user = $this->client->getUsername(); - $password = $this->client->getPassword(); - - try { - Log::channel('Custom')->info("==================================================================="); - Log::channel('Custom')->info("Delete VM - $vmName"); - - $command = "& 'C:\CLISave\DeleteVM\deleteVM.ps1' -VmName '$vmName' -VCenterHost '$host' -VCenterUser '$user' -VCenterPassword '$password'"; - Log::channel('Custom')->info("Executing deleteVM command for: $vmName"); - - $cmdResult = $this->powerShellService->executePowerCLI($command); - Log::channel('Custom')->info("Command Result: " . $cmdResult); - - if (strpos($cmdResult, 'Login_Fail') !== false) { - return ['error' => true, 'message' => 'SSH Login to PowerShell server failed']; - } - - if (strpos($cmdResult, 'SUCCESS') === false) { - return ['error' => true, 'message' => 'PowerShell command failed: ' . $cmdResult]; - } - - Log::channel('Custom')->info("After run Command |" . now()); - Log::channel('Custom')->info("==================================================================="); - - return ['error' => false]; - } catch (\Exception $error) { - Log::channel('Custom')->error("Delete VM error: " . $error->getMessage()); - return ['error' => true, 'message' => $error->getMessage()]; - } - } -} diff --git a/app/Http/Services/vCenter/PowerManagementService.php b/app/Http/Services/vCenter/PowerManagementService.php index b8f6ea2..5b18abc 100644 --- a/app/Http/Services/vCenter/PowerManagementService.php +++ b/app/Http/Services/vCenter/PowerManagementService.php @@ -2,9 +2,7 @@ namespace App\Http\Services\vCenter; -use Illuminate\Support\Facades\Http; use App\Support\TlsHttpClientFactory; -use Illuminate\Support\Facades\Log; class PowerManagementService { diff --git a/app/Http/Services/vCenter/PowerShellService.php b/app/Http/Services/vCenter/PowerShellService.php index 6bfd695..0c1bf65 100644 --- a/app/Http/Services/vCenter/PowerShellService.php +++ b/app/Http/Services/vCenter/PowerShellService.php @@ -57,19 +57,6 @@ class PowerShellService } } - public function loginSSH(): bool - { - $conn = $this->LoginAuth(); - - if ($conn) { - Log::info("✅ Connected to vCenter via SSH successfully"); - return true; - } - - Log::error("❌ Failed to connect to vCenter via SSH"); - return false; - } - public function runPsCommand(string $psCommand): string { $auth = $this->LoginAuth(); @@ -202,34 +189,6 @@ PS; return "Error: " . $e->getMessage(); } } - - public function resetGuestPasswordViaPs1(string $vmName, string $guestUser, string $guestPassword, string $newPassword, string $targetUser = 'Administrator'): array|string - { - $psFile = "C:\\CLISave\\AssignIP\\SetGuestPwd.ps1"; - - $args = ' -VMName ' . PowerShellEscaper::singleQuote($vmName); - $args .= ' -GuestUser ' . PowerShellEscaper::singleQuote($guestUser); - $args .= ' -GuestPassword ' . PowerShellEscaper::singleQuote($guestPassword); - $args .= ' -TargetUser ' . PowerShellEscaper::singleQuote($targetUser); - $args .= ' -NewPassword ' . PowerShellEscaper::singleQuote($newPassword); - - $cmd = '& ' . PowerShellEscaper::singleQuote($psFile) . "{$args} | Out-String"; - - $raw = $this->CommandRSP($cmd); - if ($raw === 'Login_Fail' || $raw === false || $raw === null) { - return "Login_Fail"; - } - - $trim = trim($raw); - $data = json_decode($trim, true); - if (json_last_error() !== JSON_ERROR_NONE) { - if (preg_match('/(\{.*\}|\[.*\])/s', $trim, $m)) { - $data = json_decode($m[1], true); - } - } - - return $data ?: $raw; - } public function changeRDPPortWindows(string $vmName, string $guestUser, string $guestPassword, string $newPort): array|string { @@ -328,27 +287,4 @@ PS; return (string) $this->CommandRSP($Command); } - - public function getWindowsRdpPort(string $vmwareId, string $guestUser, string $guestPassword): ?int - { - $ps = "(Get-ItemProperty 'HKLM:\\System\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp' -Name PortNumber).PortNumber"; - - $cmd = 'Invoke-VMScript -VM ' . PowerShellEscaper::singleQuote($vmwareId) - . ' -ScriptText ' . PowerShellEscaper::singleQuote($ps) - . ' -ScriptType Powershell' - . ' -GuestUser ' . PowerShellEscaper::singleQuote($guestUser) - . ' -GuestPassword ' . PowerShellEscaper::singleQuote($guestPassword); - - $output = $this->CommandRSP($cmd); - - if (!$output || str_contains($output, 'Login_Fail')) { - return null; - } - - if (preg_match('/\b(\d{2,5})\b/', $output, $m)) { - return (int) $m[1]; - } - - return null; - } } diff --git a/app/Http/Services/vCenter/ResourceService.php b/app/Http/Services/vCenter/ResourceService.php index 5bafa2b..17a1571 100644 --- a/app/Http/Services/vCenter/ResourceService.php +++ b/app/Http/Services/vCenter/ResourceService.php @@ -2,7 +2,6 @@ namespace App\Http\Services\vCenter; -use Illuminate\Support\Facades\Http; use App\Support\TlsHttpClientFactory; use Illuminate\Support\Facades\Log; @@ -195,9 +194,4 @@ class ResourceService { return $this->getAllNetwork($host, $datacenter); } - - public function GetFolderByHost($datacenter = '') - { - return \App\Models\ModuleFolder::where('module_datacenter_id', $datacenter)->first(); - } } diff --git a/app/Http/Services/vCenter/SnapshotService.php b/app/Http/Services/vCenter/SnapshotService.php index dc5592b..1ffe34e 100644 --- a/app/Http/Services/vCenter/SnapshotService.php +++ b/app/Http/Services/vCenter/SnapshotService.php @@ -3,6 +3,7 @@ namespace App\Http\Services\vCenter; use App\Support\Cache\CacheKey; +use App\Support\PowerShellEscaper; use App\Support\TlsHttpClientFactory; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; @@ -72,7 +73,7 @@ class SnapshotService Log::channel('Custom')->info("==================================================================="); Log::channel('Custom')->info("Create Snapshot with VirtualMachine Name - $vmName - Snap #$name"); - $command = "& 'C:\CLISave\createsnapshot.ps1' -vm_name '$vmName' -name '$name'"; + $command = "& 'C:\CLISave\createsnapshot.ps1' -vm_name " . PowerShellEscaper::singleQuote($vmName) . ' -name ' . PowerShellEscaper::singleQuote($name); Log::channel('Custom')->info($command); $cmdResult = $this->powerShellService->executePowerCLI($command); @@ -309,7 +310,7 @@ class SnapshotService Log::channel('Custom')->info("Remove Snapshot with VirtualMachine Name - $vmware_name - Snap #$snap"); Log::channel('Custom')->info("\r\n"); - $Command = "& 'C:\CLISave\deletesnap.ps1' -vm_name '$vmware_name' -name '$snap'"; + $Command = "& 'C:\CLISave\deletesnap.ps1' -vm_name " . PowerShellEscaper::singleQuote($vmware_name) . ' -name ' . PowerShellEscaper::singleQuote((string) $snap); Log::channel('Custom')->info("$Command"); @@ -376,7 +377,7 @@ class SnapshotService Log::channel('Custom')->info("Revert Snapshot with VirtualMachine Name - $vmware_name - Snap #$snap"); Log::channel('Custom')->info("\r\n"); - $Command = "& 'C:\CLISave\backSnap.ps1' -vm_name '$vmware_name' -name '$snap'"; + $Command = "& 'C:\CLISave\backSnap.ps1' -vm_name " . PowerShellEscaper::singleQuote($vmware_name) . ' -name ' . PowerShellEscaper::singleQuote((string) $snap); Log::channel('Custom')->info("$Command"); diff --git a/app/Http/Services/vCenter/TemplateService.php b/app/Http/Services/vCenter/TemplateService.php index 4f0841a..c554aee 100644 --- a/app/Http/Services/vCenter/TemplateService.php +++ b/app/Http/Services/vCenter/TemplateService.php @@ -2,7 +2,6 @@ namespace App\Http\Services\vCenter; -use Illuminate\Support\Facades\Http; use App\Support\TlsHttpClientFactory; class TemplateService @@ -30,76 +29,4 @@ class TemplateService } return "Login_Again"; } - - public function getTemplatebyID($template = '') - { - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/com/vmware/content/library/id:{$template}")->json(); - - if (!isset($response['type'])) { - return $response['value']['storage_backings'][0]['datastore_id']; - } - return false; - } - - public function getTemplateFromDatastore($datastore = '') - { - $allTemplate = $this->getAllTemplate(); - if (!$allTemplate) { - return false; - } - foreach ($allTemplate as $template) { - $detailTemplate = $this->getTemplatebyID($template); - if ($detailTemplate && $detailTemplate === $datastore) { - return $template; - } - } - return false; - } - - public function getVersionDetail($version_id = '') - { - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/com/vmware/content/library/item/id:{$version_id}")->json(); - - if (!isset($response['type'])) { - return $response['value']; - } - return false; - } - - public function getAllVersion($library = ''): bool|array|string - { - if ($this->client->checkApiExits()) { - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/com/vmware/content/library/item", [ - 'library_id' => $library - ])->json(); - - if (!isset($response['type'])) { - $library_item = []; - foreach ($response['value'] as $value) { - $value_detail = $this->getVersionDetail($value); - if ($value_detail) { - $library_item[] = [ - 'name' => $value_detail['name'], - 'id' => $value_detail['id'] - ]; - } - } - return $library_item; - } - return false; - } - return "Login_Again"; - } } diff --git a/app/Http/Services/vCenter/VMConfigurationService.php b/app/Http/Services/vCenter/VMConfigurationService.php index 08044c0..9f9b151 100644 --- a/app/Http/Services/vCenter/VMConfigurationService.php +++ b/app/Http/Services/vCenter/VMConfigurationService.php @@ -23,75 +23,6 @@ class VMConfigurationService $this->client->loginServer($vcenterModel); } - public function ConfigVirtualMachineUbuntu($vmware_id = '', $product = [], $password = ''): void - { - $this->setConfig($product->IpAddress->Host->Datacenter->vCenter); - $VirtualMachine = "VirtualMachine-$vmware_id"; - $IPAddress = $product->IpAddress->ip; - $Gateway = $product->IpAddress->gateway; - $HostName = strtolower($product->hostname); - - try { - Log::channel('Custom')->info("==================================================================="); - Log::channel('Custom')->info("Configuring Ubuntu for VM #ID $VirtualMachine"); - - $cmd1 = "\$vm = get-vm -Id '$VirtualMachine';\$hostname ='$HostName'; \$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\createUbuntu1line.ps1;"; - Log::channel('Custom')->info($cmd1); - $this->powerShellService->executePowerCLI($cmd1); - - $cmd2 = "\$vm = get-vm -Id '$VirtualMachine'; \$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\changeLinux.ps1;"; - Log::channel('Custom')->info($cmd2); - $this->powerShellService->executePowerCLI($cmd2); - - Log::channel('Custom')->info("Configuration completed at " . Carbon::now()); - Log::channel('Custom')->info("==================================================================="); - } catch (Exception $error) { - Log::channel('Custom')->error("Error configuring Ubuntu: " . $error->getMessage()); - } - } - - public function ConfigCentOSVersion8($vmware_id = '', $product = [], $password = ''): void - { - $this->setConfig($product->IpAddress->Host->Datacenter->vCenter); - $VirtualMachine = "VirtualMachine-$vmware_id"; - $IPAddress = $product->IpAddress->ip; - $Gateway = $product->IpAddress->gateway; - $HostName = strtolower($product->hostname); - - try { - Log::channel('Custom')->info("Configuring CentOS 8 for VM #ID $VirtualMachine"); - - $cmd = "\$vm = get-vm -Id '$VirtualMachine';\$hostname ='$HostName'; \$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\createCentOS81line.ps1;"; - Log::channel('Custom')->info($cmd); - $this->powerShellService->executePowerCLI($cmd); - - Log::channel('Custom')->info("Configuration completed at " . Carbon::now()); - } catch (Exception $error) { - Log::channel('Custom')->error("Error configuring CentOS 8: " . $error->getMessage()); - } - } - - public function ConfigCentOSVersion7($vmware_id = '', $product = [], $password = ''): void - { - $this->setConfig($product->IpAddress->Host->Datacenter->vCenter); - $VirtualMachine = "VirtualMachine-$vmware_id"; - $IPAddress = $product->IpAddress->ip; - $Gateway = $product->IpAddress->gateway; - $HostName = strtolower($product->hostname); - - try { - Log::channel('Custom')->info("Configuring CentOS 7 for VM #ID $VirtualMachine"); - - $cmd = "\$vm = get-vm -Id '$VirtualMachine'; \$hostname ='$HostName';\$ipadd = '$IPAddress'; \$gw = '$Gateway'; \$password = \"'$password'\"; cd /; C:\CLISave\createCentOS71line.ps1;"; - Log::channel('Custom')->info($cmd); - $this->powerShellService->executePowerCLI($cmd); - - Log::channel('Custom')->info("Configuration completed at " . Carbon::now()); - } catch (Exception $error) { - Log::channel('Custom')->error("Error configuring CentOS 7: " . $error->getMessage()); - } - } - public function ConfigWindowsVersion10($vmware_id = '', $product = [], $password = ''): void { if (!$this->validateProductData($product, 'ConfigWindowsVersion10', $vmware_id)) return; diff --git a/app/Http/Services/vCenter/VirtualMachineService.php b/app/Http/Services/vCenter/VirtualMachineService.php index d9c722a..7573e79 100644 --- a/app/Http/Services/vCenter/VirtualMachineService.php +++ b/app/Http/Services/vCenter/VirtualMachineService.php @@ -2,17 +2,19 @@ namespace App\Http\Services\vCenter; -use Illuminate\Support\Facades\Http; use App\Support\TlsHttpClientFactory; use Illuminate\Support\Facades\Log; -use Illuminate\Http\Client\ConnectionException; -use Illuminate\Support\Str; use App\Http\Services\Discord; use App\Services\VirtualMachine\VmProviderSettingsService; use Exception; +use App\Http\Services\vCenter\Concerns\ReadsVirtualMachineInfo; +use App\Http\Services\vCenter\Concerns\UpgradesVirtualMachine; class VirtualMachineService { + use ReadsVirtualMachineInfo; + use UpgradesVirtualMachine; + protected Client $client; protected PowerShellService $powerShellService; @@ -22,31 +24,6 @@ class VirtualMachineService $this->powerShellService = $powerShellService; } - public function getAllVirtualMachine($datacenter = '', $host = '') - { - if ($this->client->checkApiExits()) { - $params = []; - if (!empty($datacenter)) { - $params['filter.datacenters'] = $datacenter; - } - if (!empty($host)) { - $params['filter.hosts'] = $host; - } - - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm", $params)->json(); - - if (!isset($response['type'])) { - return $response['value']; - } - return false; - } - return "Login_Again"; - } - public function CreateVirtualMachine($product_data): array|string { $vcenterModel = $product_data['vcenter']; @@ -173,575 +150,4 @@ class VirtualMachineService return ['error' => true, 'data' => $e->getMessage()]; } } - - private function updateVMHardware($vmId, $addon, $base_cpu, $base_ram): void - { - try { - Log::info("=== BẮT ĐẦU CẬP NHẬT ADDON ==="); - - $new_cpu = $base_cpu + $addon['cpu']; - $new_ram_mib = ($base_ram + $addon['ram']) * 1024; - - if ($addon['cpu'] > 0) { - $this->updateVMCPU($vmId, $new_cpu); - } - - if ($addon['ram'] > 0) { - $this->updateVMMemory($vmId, $new_ram_mib); - } - - // Disk update logic was missing in the snippet but present in the original method names? - // Actually the original `updateVMHardware` in the snippet (step 134) calls `updateVMCPU` and `updateVMMemory`. - // It gets `base_disk` but doesn't seem to call `updateVMDisk` inside `updateVMHardware`? - // Let me re-read step 134. - // ... `updateVMHardware` implementation ... - // It calls `updateVMCPU` and `updateVMMemory`. - // It calculates `$addon['disk']` in log, but doesn't call `updateVMDisk`. - // Ah, line 943 defines `updateVMDisk`. - // But `updateVMHardware` (lines 772-832) only calls `updateVMCPU` and `updateVMMemory`. - // I will implement it exactly as the original. - - Log::info("=== HOÀN THÀNH CẬP NHẬT ADDON ==="); - } catch (Exception $e) { - Log::error("❌ Lỗi khi cập nhật cấu hình phần cứng VM {$vmId}: " . $e->getMessage()); - } - } - - private function updateVMCPU($vmId, $cpuCount): bool - { - try { - $coresPerSocket = 1; - if ($cpuCount > 1) { - $maxCoresPerSocket = floor($cpuCount / 2); - for ($i = $maxCoresPerSocket; $i >= 1; $i--) { - if ($cpuCount % $i === 0) { - $coresPerSocket = $i; - break; - } - } - if ($coresPerSocket === 1 && $cpuCount > 2) { - $coresPerSocket = $cpuCount; - } - } - - $payload = [ - 'spec' => [ - 'count' => $cpuCount, - 'cores_per_socket' => $coresPerSocket, - 'hot_add_enabled' => true - ] - ]; - $response = TlsHttpClientFactory::vCenter() - ->timeout(30) - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token, - 'Content-Type' => 'application/json', - ]) - ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/cpu", $payload); - - - if ($response->successful()) { - Log::info("✅ API call cập nhật CPU thành công cho VM {$vmId}"); - return true; - } else { - Log::error("❌ API call cập nhật CPU thất bại cho VM {$vmId}", [ - 'status' => $response->status(), - 'response' => $response->body() - ]); - return false; - } - } catch (Exception $e) { - Log::error("❌ Exception khi cập nhật CPU cho VM {$vmId}: " . $e->getMessage()); - return false; - } - } - - private function updateVMMemory($vmId, $ramSizeMB): bool - { - try { - $payload = [ - 'spec' => [ - 'size_MiB' => $ramSizeMB - ] - ]; - - $response = TlsHttpClientFactory::vCenter() - ->timeout(30) - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token, - 'Content-Type' => 'application/json', - ]) - ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/memory", $payload); - - if ($response->successful()) { - Log::info("✅ API call cập nhật RAM thành công cho VM {$vmId}"); - return true; - } else { - Log::error("❌ API call cập nhật RAM thất bại cho VM {$vmId}", [ - 'status' => $response->status(), - 'response' => $response->body() - ]); - return false; - } - } catch (Exception $e) { - Log::error("❌ Exception khi cập nhật RAM cho VM {$vmId}: " . $e->getMessage()); - return false; - } - } - - private function updateVMDisk($vmId, $diskSizeGB): bool - { - try { - $diskSizeBytes = $diskSizeGB * 1024 * 1024 * 1024; - - $payload = [ - 'spec' => [ - 'capacity' => $diskSizeBytes - ] - ]; - - $diskResponse = TlsHttpClientFactory::vCenter() - ->timeout(30) - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token, - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/disk"); - - if (!$diskResponse->successful()) { - return false; - } - - $diskData = $diskResponse->json(); - if (!isset($diskData['value']) || empty($diskData['value'])) { - return false; - } - - $firstDisk = $diskData['value'][0]; - $diskKey = $firstDisk['key']; - - $response = TlsHttpClientFactory::vCenter() - ->timeout(30) - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token, - 'Content-Type' => 'application/json', - ]) - ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/disk/{$diskKey}", $payload); - - if ($response->successful()) { - return true; - } else { - return false; - } - } catch (Exception $e) { - Log::error("Exception khi cập nhật Disk cho VM {$vmId}: " . $e->getMessage()); - return false; - } - } - - public function CheckVirtualMachine($vmware_id = '') - { - if ($this->client->checkApiExits()) { - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id")->json(); - - if (!isset($response['type'])) { - return $response['value']; - } - return false; - } - return "Login_Again"; - } - - public function GetNetworkVirtualMachine($vmware_id = '') - { - $check = $this->client->checkApiExits(); - if ($check) { - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/guest/networking")->json(); - if (!isset($response['type'])) { - return $response['value']; - } - return false; - } - return "Login_Again"; - } - - /** - * Search VM by exact name in vCenter. - * Returns the first matching VM array or null if not found. - */ - public function findVirtualMachineByName(string $vmName): ?array - { - $check = $this->client->checkApiExits(); - if (!$check) { - Log::warning('findVirtualMachineByName: API check failed'); - return null; - } - - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm", [ - 'filter.names.1' => $vmName, - ]); - - if (!$response->successful()) { - Log::warning('findVirtualMachineByName: HTTP failed', ['status' => $response->status()]); - return null; - } - - $json = $response->json(); - if (isset($json['type']) || empty($json['value'])) { - return null; - } - - return $json['value'][0] ?? null; - } - - public function GetDetailVirtualMachine($vmware_id = ''): array - { - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id")->json(); - if (!isset($response['type'])) { - return [ - 'error' => false, - 'data' => $response['value'] - ]; - } - return [ - 'error' => true, - 'message' => $response['value'] - ]; - } - - public function GetIPAddressVirtualMachine($vmware_id = ''): array - { - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/guest/identity")->json(); - if (!isset($response['type'])) { - return [ - 'error' => false, - 'data' => $response['value'] - ]; - } - return [ - 'error' => true, - 'message' => $response['value'] - ]; - } - - public function GetVMStats($vmware_id = ''): array - { - $stats = [ - 'cpu' => null, - 'memory' => null, - 'disk' => null, - ]; - - try { - $hardwareResponse = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/hardware")->json(); - - if (is_array($hardwareResponse) && !isset($hardwareResponse['type']) && isset($hardwareResponse['value'])) { - foreach ($hardwareResponse['value'] as $hardware) { - if (isset($hardware['label']) && $hardware['label'] === 'CPU') { - $stats['cpu'] = [ - 'cores' => $hardware['spec']['num_cores'] ?? 0, - 'cores_per_socket' => $hardware['spec']['cores_per_socket'] ?? 0, - ]; - } - if (isset($hardware['label']) && $hardware['label'] === 'Memory') { - $stats['memory'] = [ - 'size_MiB' => $hardware['spec']['size_MiB'] ?? 0, - 'size_GB' => round(($hardware['spec']['size_MiB'] ?? 0) / 1024, 2), - ]; - } - if (isset($hardware['label']) && $hardware['label'] === 'Hard disk') { - $stats['disk'][] = [ - 'capacity' => $hardware['capacity'] ?? 0, - 'capacity_GB' => round(($hardware['capacity'] ?? 0) / (1024 * 1024 * 1024), 2), - ]; - } - } - } - - $guestMemoryResponse = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/guest/memory")->json(); - - if (is_array($guestMemoryResponse) && !isset($guestMemoryResponse['type']) && isset($guestMemoryResponse['value'])) { - $stats['memory']['used_MiB'] = $guestMemoryResponse['value']['used'] ?? 0; - $stats['memory']['used_GB'] = round(($guestMemoryResponse['value']['used'] ?? 0) / 1024, 2); - $stats['memory']['usage_percent'] = $stats['memory']['size_MiB'] > 0 - ? round(($guestMemoryResponse['value']['used'] ?? 0) / $stats['memory']['size_MiB'] * 100, 1) - : 0; - } - - $guestCpuResponse = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/guest/cpu")->json(); - - if (is_array($guestCpuResponse) && !isset($guestCpuResponse['type']) && isset($guestCpuResponse['value'])) { - $stats['cpu']['used_percent'] = $guestCpuResponse['value']['used'] ?? 0; - } - - return ['error' => false, 'data' => $stats]; - } catch (\Exception $e) { - return ['error' => true, 'message' => $e->getMessage(), 'data' => $stats]; - } - } - - public function ConfigNetworkVirtualMachine($vmware_id = '', $config = []): int - { - return TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/hardware/ethernet/4000", $config) - ->status(); - } - - public function FindVirtualMachine($name = ''): string|bool - { - $check = $this->client->checkApiExits(); - - if ($check) { - try { - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get( - "https://{$this->client->apiUri}/rest/vcenter/vm?filter.names.1=$name" - )->json(); - } catch (ConnectionException $error) { - return false; - } - - if ($response['value']) { - return $response['value'][0]['vm']; - } - return false; - } - return "Login_Again"; - } - - public function reBuildVPS(string $vmName): array|string - { - if (!$this->client->checkApiExits()) { - return "Login_Again"; - } - - try { - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm", [ - 'filter.names' => $vmName - ]); - - if (!$response->successful()) { - return [ - 'error' => true, - 'message' => 'Failed to search for VM', - 'details' => $response->json() - ]; - } - - $vmList = $response->json()['value'] ?? []; - if (empty($vmList)) { - return [ - 'error' => true, - 'message' => "VM not found with name: {$vmName}" - ]; - } - - $vmId = $vmList[0]['vm']; - - $detailResponse = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->get("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}"); - - if (!$detailResponse->successful()) { - return [ - 'error' => true, - 'message' => 'Failed to fetch VM details', - 'details' => $detailResponse->json() - ]; - } - - $vmData = $detailResponse->json(); - $guestOS = $vmData['value']['guest_OS'] ?? 'UNKNOWN'; - - $scriptPath = ''; - $osUpper = strtoupper($guestOS); - - if (Str::contains($osUpper, 'WINDOWS')) { - $scriptPath = 'C:\\CLISave\\Windows\\rebuild.ps1'; - } elseif (Str::contains($osUpper, ['UBUNTU', 'LINUX', 'CENTOS', 'DEBIAN'])) { - $scriptPath = 'C:\\CLISave\\Ubuntu\\rebuild.ps1'; - } else { - return [ - 'error' => true, - 'message' => "Unsupported OS for rebuild: {$guestOS}" - ]; - } - - $scriptDir = dirname($scriptPath); - $scriptFile = basename($scriptPath); - - $cmd = " - Write-Output 'STARTING_REBUILD'; - \$vm = Get-VM -Name '$vmName'; - cd '$scriptDir'; - if (Test-Path '.\\$scriptFile') { - Write-Output 'File found, executing...'; - & '.\\$scriptFile' | Out-String - } else { - Write-Error 'File NOT FOUND: $scriptPath'; - } - Write-Output 'ENDING_REBUILD'; - "; - - $result = $this->powerShellService->executePowerCLI($cmd); - - return [ - 'error' => false, - 'vm_name' => $vmName, - 'vm_id' => $vmId, - 'os' => $guestOS, - 'script' => $scriptPath, - 'result' => $result - ]; - - } catch (\Exception $e) { - Log::error("reBuildVPS Exception: " . $e->getMessage()); - return [ - 'error' => true, - 'message' => $e->getMessage() - ]; - } - } - - public function UpgradeMemoryVirtualMachine($vmware_id = '', $ram = 0): array|string - { - $check = $this->client->checkApiExits(); - - if ($check) { - $RAMConfig = [ - "spec" => [ - "size_MiB" => $ram * 1024, - ] - ]; - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/hardware/memory", $RAMConfig); - - if ($response->status() === 200) { - return [ - 'error' => false, - 'data' => 'success' - ]; - } - return [ - 'error' => true, - 'data' => $response['value'] - ]; - } - return "Login_Again"; - } - - public function UpgradeCPUVirtualMachine($vmware_id = '', $cpu = 0): array|string - { - $check = $this->client->checkApiExits(); - - if ($check) { - $CPUConfig = [ - "spec" => [ - "cores_per_socket" => 1, - "count" => $cpu - ] - ]; - $response = TlsHttpClientFactory::vCenter() - ->withHeaders([ - 'vmware-api-session-id' => $this->client->token - ]) - ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/hardware/cpu", $CPUConfig); - - if ($response->status() === 200) { - return [ - 'error' => false, - 'data' => 'success' - ]; - } - return [ - 'error' => true, - 'data' => $response['value'] - ]; - } - return "Login_Again"; - } - - public function UpgradeDiskVirtualMachine($product, $disk = 0): array|string - { - $vmware_id = $product->vmware_id; - $check = $this->client->checkApiExits(); - if ($check) { - try { - Log::channel('Custom')->info("==================================================================="); - Log::channel('Custom')->info("Upgrade Disk with VirtualMachine ID # - $vmware_id"); - Log::channel('Custom')->info("\r\n"); - - $Command = "\$vm = 'VirtualMachine-$vmware_id'; \$disksize = $disk; cd /; C:\CLISave\diskresize.ps1;"; - - Log::channel('Custom')->info("$Command"); - - $this->powerShellService->executePowerCLI($Command); - - Log::channel('Custom')->info("\r\n"); - Log::channel('Custom')->info("After run Command |" . now()); - Log::channel('Custom')->info("==================================================================="); - - return [ - 'error' => false - ]; - } catch (\Exception $error) { - Log::channel('Custom')->info("\r\n"); - Log::channel('Custom')->info("==================================================================="); - Log::channel('Custom')->info("Error Log: "); - Log::channel('Custom')->info($error->getMessage()); - Log::channel('Custom')->info("==================================================================="); - return [ - 'error' => true, - 'message' => $error->getMessage() - ]; - } - } - return "Login_Again"; - } } diff --git a/app/Imports/ProductCategoriesImport.php b/app/Imports/ProductCategoriesImport.php deleted file mode 100644 index e4bc366..0000000 --- a/app/Imports/ProductCategoriesImport.php +++ /dev/null @@ -1,46 +0,0 @@ -first(); - $parentId = $parent?->id; - } - - if (!empty($row['parent_id'])) { - $parentId = (int) $row['parent_id']; - } - - return new ProductCategories([ - 'name' => $row['name'], - 'slug' => $row['slug'] ?? Str::slug($row['name']), - 'parent_id' => $parentId, - 'type' => $row['type'] ?? null, - 'is_active' => in_array(strtolower($row['is_active'] ?? ''), ['yes', '1', 'true']), - 'sort_order' => $row['sort_order'] ?? 0, - ]); - } - - public function rules(): array - { - return [ - 'name' => 'required|string|max:255', - 'slug' => 'nullable|string|max:255', - 'parent_name' => 'nullable|string|max:255', - 'parent_id' => 'nullable|integer|exists:product_categories,id', - 'type' => 'nullable|string|max:50', - ]; - } -} diff --git a/app/Imports/ProductsImport.php b/app/Imports/ProductsImport.php deleted file mode 100644 index 002778c..0000000 --- a/app/Imports/ProductsImport.php +++ /dev/null @@ -1,59 +0,0 @@ -first(); - $categoryId = $category?->id; - } - - if (!empty($row['category_id'])) { - $categoryId = (int) $row['category_id']; - } - - return new Product([ - 'uuid' => (string) Str::uuid(), - 'name' => $row['name'], - 'slug' => $row['slug'] ?? Str::slug($row['name']), - 'product_categories_id' => $categoryId, - 'short_description' => $row['short_description'] ?? null, - 'description' => $row['description'] ?? null, - 'setup_fee' => $row['setup_fee'] ?? 0, - 'trial_days' => $row['trial_days'] ?? 0, - 'cpu' => $row['cpu'] ?? null, - 'ram' => $row['ram'] ?? null, - 'gpu' => $row['gpu'] ?? null, - 'disk' => $row['disk'] ?? null, - 'bandwidth' => $row['bandwidth'] ?? null, - 'stock' => $row['stock'] ?? 0, - 'is_unlimited' => in_array(strtolower($row['is_unlimited'] ?? ''), ['yes', '1', 'true']), - 'is_active' => in_array(strtolower($row['is_active'] ?? ''), ['yes', '1', 'true']), - 'sort_order' => $row['sort_order'] ?? 0, - 'published_at' => !empty($row['published_at']) ? \Carbon\Carbon::parse($row['published_at']) : null, - ]); - } - - public function rules(): array - { - return [ - 'name' => 'required|string|max:255', - 'slug' => 'nullable|string|max:255', - 'category' => 'nullable|string|max:255', - 'category_id' => 'nullable|integer|exists:product_categories,id', - ]; - } -} diff --git a/app/Jobs/Mail/Customer/CustomerPayment.php b/app/Jobs/Mail/Customer/CustomerPayment.php deleted file mode 100644 index 873496a..0000000 --- a/app/Jobs/Mail/Customer/CustomerPayment.php +++ /dev/null @@ -1,131 +0,0 @@ -template_id = $template; - $this->code = $code; - $this->email = $email; - $this->customer = $customer; - $this->telegram = new Telegram(); - } - - /** - * Execute the job. - */ - public function handle() - { - // Log job start - Log::channel('Custom')->info("Starting CustomerPayment job for email: {$this->email}"); - - // Notify via Telegram that job is starting - $this->telegram->SendJobRunInfo('CustomerPayment', 'Started', [ - 'email' => $this->email, - 'customer_id' => $this->customer - ]); - - $hash = SignRSA::EncryptFunc(base64_encode($this->code)); - - $setting = Setting::where('key', 'setting')->first(); - $parsed = json_decode($setting->value); - - $data = [ - 'company_name' => $parsed->company, - 'company_logo' => $parsed->logo, - ]; - - try { - // Send email - $email = new \App\Mail\Customer\CustomerPayment($this->template_id, $data); - Mail::to($this->email)->send($email); - - // Log email in database - \App\Models\Mail::create([ - 'template_id' => 3, - 'subject' => $email->envelope()->subject, - 'to' => $this->email, - 'customer_id' => $this->customer, - 'message' => $email->render() - ]); - - // Send Telegram notification about new user registration - $this->telegram->SendNewUserRegistration($this->email, $this->code, $verificationLink); - - // Log job completion - Log::channel('Custom')->info("CustomerPayment job completed successfully for email: {$this->email}"); - - // Notify via Telegram that job completed successfully - $this->telegram->SendJobRunInfo('CustomerPayment', 'Completed', [ - 'email' => $this->email, - 'status' => 'success' - ]); - - } catch (\Exception $exception) { - // Log error - Log::channel('Custom')->error("CustomerPayment job failed for email: {$this->email}. Error: {$exception->getMessage()}"); - - // Notify via Telegram that job failed - $this->telegram->SendJobRunInfo('CustomerPayment', 'Failed', [ - 'email' => $this->email, - 'error' => $exception->getMessage() - ]); - - throw new \Exception($exception->getMessage()); - } - } - - /** - * Handle job failure. - * - * @param \Throwable $exception - * @return void - */ - public function failed(\Throwable $exception) - { - $timestamp = Carbon::now()->format('Y-m-d H:i:s'); - - Log::channel('Custom')->info("================================="); - Log::channel('Custom')->info("Thông báo lỗi lúc: " . $timestamp); - Log::channel('Custom')->info("Notification Send Email Register for Customer: {$this->email}"); - Log::channel('Custom')->info("========"); - Log::channel('Custom')->info($exception->getMessage()); - Log::channel('Custom')->info("================================="); - - // Send error message to Telegram - $errorData = [ - 'timestamp' => $timestamp, - 'email' => $this->email, - 'customer_id' => $this->customer, - 'error' => $exception->getMessage() - ]; - - $this->telegram->SendErrorMessage(json_encode($errorData)); - } -} diff --git a/app/Jobs/Mail/Customer/OrderPaymentSuccessJob.php b/app/Jobs/Mail/Customer/OrderPaymentSuccessJob.php deleted file mode 100644 index eee195d..0000000 --- a/app/Jobs/Mail/Customer/OrderPaymentSuccessJob.php +++ /dev/null @@ -1,42 +0,0 @@ -data = $data; - } - - /** - * Execute the job. - */ - public function handle(): void - { - try { - Mail::to($this->data['customer_email']) - ->send(new OrderPaymentSuccess($this->data)); - } catch (\Throwable $e) { - \Log::error('Không thể gửi email thông báo thành công: ' . $e->getMessage(), [ - 'customer_email' => $this->data['customer_email'], - 'order_id' => $this->data['order_id'] ?? null - ]); - } - } -} \ No newline at end of file diff --git a/app/Jobs/Mail/CustomerRegister.php b/app/Jobs/Mail/CustomerRegister.php deleted file mode 100644 index 05b31c0..0000000 --- a/app/Jobs/Mail/CustomerRegister.php +++ /dev/null @@ -1,136 +0,0 @@ -template_id = $template; - $this->code = $code; - $this->email = $email; - $this->customer = $customer; - $this->telegram = new Telegram(); - } - - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - // Log job start - Log::channel('Custom')->info("Starting CustomerRegister job for email: {$this->email}"); - - // Notify via Telegram that job is starting - $this->telegram->SendJobRunInfo('CustomerRegister', 'Started', [ - 'email' => $this->email, - 'customer_id' => $this->customer - ]); - - $hash = SignRSA::EncryptFunc(base64_encode($this->code)); - $verificationLink = route('user.verify', ['verify' => $hash]); - - $setting = Setting::where('key', 'setting')->first(); - $parsed = json_decode($setting->value); - - $data = [ - 'company_name' => $parsed->company, - 'company_logo' => $parsed->logo, - 'link_verify' => $verificationLink - ]; - - try { - // Send email - $email = new \App\Mail\CustomerRegister($this->template_id, $data); - Mail::to($this->email)->send($email); - - // Log email in database - \App\Models\Mail::create([ - 'template_id' => 3, - 'subject' => $email->envelope()->subject, - 'to' => $this->email, - 'customer_id' => $this->customer, - 'message' => $email->render() - ]); - - // Send Telegram notification about new user registration - $this->telegram->SendNewUserRegistration($this->email, $this->code, $verificationLink); - - // Log job completion - Log::channel('Custom')->info("CustomerRegister job completed successfully for email: {$this->email}"); - - // Notify via Telegram that job completed successfully - $this->telegram->SendJobRunInfo('CustomerRegister', 'Completed', [ - 'email' => $this->email, - 'status' => 'success' - ]); - - } catch (\Exception $exception) { - // Log error - Log::channel('Custom')->error("CustomerRegister job failed for email: {$this->email}. Error: {$exception->getMessage()}"); - - // Notify via Telegram that job failed - $this->telegram->SendJobRunInfo('CustomerRegister', 'Failed', [ - 'email' => $this->email, - 'error' => $exception->getMessage() - ]); - - throw new \Exception($exception->getMessage()); - } - } - - /** - * Handle job failure. - * - * @param \Throwable $exception - * @return void - */ - public function failed(\Throwable $exception) - { - $timestamp = Carbon::now()->format('Y-m-d H:i:s'); - - Log::channel('Custom')->info("================================="); - Log::channel('Custom')->info("Thông báo lỗi lúc: " . $timestamp); - Log::channel('Custom')->info("Notification Send Email Register for Customer: {$this->email}"); - Log::channel('Custom')->info("========"); - Log::channel('Custom')->info($exception->getMessage()); - Log::channel('Custom')->info("================================="); - - // Send error message to Telegram - $errorData = [ - 'timestamp' => $timestamp, - 'email' => $this->email, - 'customer_id' => $this->customer, - 'error' => $exception->getMessage() - ]; - - $this->telegram->SendErrorMessage(json_encode($errorData)); - } -} diff --git a/app/Jobs/Mail/PasswordRecoverJob.php b/app/Jobs/Mail/PasswordRecoverJob.php deleted file mode 100644 index fa13a8f..0000000 --- a/app/Jobs/Mail/PasswordRecoverJob.php +++ /dev/null @@ -1,65 +0,0 @@ -data = $data; - $this->customer = $customer; - } - - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - try { - $email = new PasswordReset($this->data); - Mail::to($this->customer->email)->send($email); - \App\Models\Mail::create([ - 'template_id' => 4, - 'subject' => $email->envelope()->subject, - 'to' => $this->customer->email, - 'customer_id' => $this->customer->id, - 'message' => $email->render() - ]); - }catch (\Exception $error) - { - throw new \Exception($error->getMessage()); - } - } - public function failed(\Throwable $exception) - { - Log::channel('Custom')->info("\r\n"); - Log::channel('Custom')->info("================================="); - Log::channel('Custom')->info("Thông báo lỗi lúc: " .Carbon::now()); - Log::channel('Custom')->info("Notification Create VirtualMachine: "); - Log::channel('Custom')->info("========"); - Log::channel('Custom')->info($exception->getMessage()); - Log::channel('Custom')->info("================================="); - - } -} diff --git a/app/Jobs/Payment/GetPaymentMessage.php b/app/Jobs/Payment/GetPaymentMessage.php deleted file mode 100644 index 0e6a9e5..0000000 --- a/app/Jobs/Payment/GetPaymentMessage.php +++ /dev/null @@ -1,35 +0,0 @@ -customerId = $customerId; - $this->amount = $amount; - } - - public function handle(): void - { - $customer = Customer::find($this->customerId); - if ($customer) { - app(Telegram::class)->SendCustomerRecharge($customer, $this->amount); - // app(Discord::class)->SendCustomerRecharge($customer, $this->amount); - } - - } -} diff --git a/app/Jobs/Payment/GetTransactionPayment.php b/app/Jobs/Payment/GetTransactionPayment.php deleted file mode 100644 index df92152..0000000 --- a/app/Jobs/Payment/GetTransactionPayment.php +++ /dev/null @@ -1,101 +0,0 @@ -message = $message; - $this->time = $time; - $this->user = $user; - } - - /** - * Execute the job. - * - * @return void - */ - public function handle(MBBank $bank) - { - if ($this->time < time()) { - - Event(new Notification_Expiry_Time('Hết thời gian chờ')); - - } else { - $history = $bank->GetTransaction(); - $status = 0; - $message = ''; - if ($history === false) { - $login = $bank->Login(); - $history = $bank->GetTransaction(); - } - if ($history) { - foreach ($history as $item) { - if (empty($item->tracingType) && str_contains($item['description'], $this->message)) { - $transaction = str_replace("\\BNK", "", $item['refNo']); - - // firstOrCreate is atomic against the unique(customer_id, trans, type) index — - // a duplicate bank-statement poll (job retried, matched twice) cannot credit twice. - $amount = (int) $item['creditAmount']; - - // type/status/uuid/public_code phải set đủ: unique index là - // (customer_id, trans, type) nên type NULL làm chống trùng hụt một nửa, - // và kế toán không lọc được dòng tiền vào theo loại khi lên báo cáo. - $created = Transaction::firstOrCreate( - ['trans' => $transaction, 'customer_id' => $this->user, 'type' => 'auto'], - [ - 'uuid' => (string) Str::uuid(), - 'public_code' => 'DEP-AUTO-' . now()->format('Ymd') . '-' . strtoupper(Str::random(6)), - 'amount' => $amount, - 'received_amount' => $amount, - 'status' => TransactionStatus::COMPLETED->value, - 'note' => 'Tự động khớp sao kê MB Bank', - ] - ); - - if ($created->wasRecentlyCreated) { - Customer::find($this->user)->increment('credit', Customer::creditFor($amount)); - Customer::find($this->user)->increment('total', $amount); - - $message = number_format($item['creditAmount'], 0, ',', '.') . "đ vào tài khoản"; - $status = 1; - break; - } - } - } - } - if ($status === 1) { - Event(new Notification_Pay_Success("+ $message", $this->user)); - } else { - dispatch(new GetTransactionPayment($this->message, $this->time, - $this->user))->delay(now()->addSeconds(30)); - } - } - } -} diff --git a/app/Jobs/VirtualMachine/Action/ChangeRemotePortJob.php b/app/Jobs/VirtualMachine/Action/ChangeRemotePortJob.php deleted file mode 100644 index a6903b3..0000000 --- a/app/Jobs/VirtualMachine/Action/ChangeRemotePortJob.php +++ /dev/null @@ -1,121 +0,0 @@ -where('vmware_id', $this->productId) - ->where('customer_id', $this->userId) - ->firstOrFail(); - - $osFamily = (string) ($product->os->osGroup->name ?? ''); - $isWindows = stripos($osFamily, 'windows') !== false; - if (!$isWindows) { - throw new \RuntimeException('Only Windows is supported by this reset job.'); - } - - $vmName = $product->ipAddress->vm_name - ?? $product->name - ?? $product->hostname - ?? null; - - if (!$vmName && preg_match('/^vm-\d+$/i', (string) $product->vmware_id)) { - $rawName = trim($serviceVCenter->runPsCommand("(Get-VM -Id '" . addslashes($product->vmware_id) . "').Name")); - if (($p = strpos($rawName, '[STDERR]')) !== false) { - $rawName = trim(substr($rawName, 0, $p)); - } - $vmName = trim(preg_replace('/\s+/', ' ', $rawName)); - } - if (!$vmName) { - throw new \RuntimeException('Cannot resolve VM Name on vCenter.'); - } - - $guestUser = 'Administrator'; - - $result = $serviceVCenter->changeRDPPortWindows( - vmName: $vmName, - guestUser: 'Administrator', - guestPassword: $this->password, - newPort: $this->newPort, - ); - - if ($result === 'Login_Fail' || $result === false || $result === null) { - throw new \RuntimeException('SSH runner login failed.'); - } - if (is_string($result)) { - throw new \RuntimeException('Script returned non-JSON output: ' . $result); - } - - $row = null; - - if (is_array($result)) { - if (isset($result['vm']) && isset($result['status'])) { - $row = $result; - } else { - foreach ($result as $it) { - if (is_array($it) && (($it['vm'] ?? null) === $vmName)) { - $row = $it; - break; - } - } - if ($row === null && isset($result[0]) && is_array($result[0])) { - $row = $result[0]; - } - } - } - - if (!is_array($row) || empty($row)) { - Log::error('ChangeRemotePortJob: invalid script response shape', ['vm' => $vmName, 'raw' => $result]); - throw new \RuntimeException('Invalid script response.'); - } - - $status = strtolower((string) ($row['status'] ?? '')); - $message = (string) ($row['message'] ?? ''); - $newPort = (string) ($row['newPort'] ?? $this->newPort); - - $ok = ($status === 'ok'); - - ModuleVmware::where('id', $product->id)->update([ - 'port' => (int) $newPort, - 'updated_at' => now(), - ]); - - if (!$ok) { - throw new \RuntimeException($message !== '' ? $message : ('Change RDP port failed with status: ' . $status)); - } - - Mail::to($this->email)->send( - new ChangePortRemote(vmName: $vmName, newPort: (string) $newPort) - ); - } -} diff --git a/app/Jobs/VirtualMachine/Action/ResetVmPasswordJob.php b/app/Jobs/VirtualMachine/Action/ResetVmPasswordJob.php deleted file mode 100644 index e0d5ecd..0000000 --- a/app/Jobs/VirtualMachine/Action/ResetVmPasswordJob.php +++ /dev/null @@ -1,101 +0,0 @@ -where('vmware_id', $this->productId) - ->where('customer_id', $this->userId) - ->firstOrFail(); - - $osFamily = (string)($product->os->osGroup->name ?? ''); - $isWindows = stripos($osFamily, 'windows') !== false; - if (!$isWindows) { - throw new \RuntimeException('Only Windows is supported by this reset job.'); - } - - $vmName = $product->ipAddress->vm_name - ?? $product->name - ?? $product->hostname - ?? null; - - if (!$vmName && preg_match('/^vm-\d+$/i', (string)$product->vmware_id)) { - $rawName = trim($serviceVCenter->runPsCommand("(Get-VM -Id '".addslashes($product->vmware_id)."').Name")); - if (($p = strpos($rawName, '[STDERR]')) !== false) { - $rawName = trim(substr($rawName, 0, $p)); - } - $vmName = trim(preg_replace('/\s+/', ' ', $rawName)); - } - if (!$vmName) { - throw new \RuntimeException('Cannot resolve VM Name on vCenter.'); - } - - $guestUser = 'Administrator'; - $guestPassword = $this->oldPassword; - - $result = $serviceVCenter->resetGuestPasswordViaPs1( - vmName: $vmName, - guestUser: $guestUser, - guestPassword: $guestPassword, - newPassword: $this->newPassword, - ); - - $product->update(['password' => encrypt($this->newPassword)]); - - if ($result === 'Login_Fail' || $result === false || $result === null) { - throw new \RuntimeException('SSH runner login failed.'); - } - - if (is_string($result)) { - throw new \RuntimeException('Script returned non-JSON output: '.$result); - } - - $row = $result; - if (isset($result[0])) { - $row = $result[0]; - foreach ($result as $it) { - if (is_array($it) && (($it['vm'] ?? null) === $vmName)) { $row = $it; break; } - } - } - - $status = strtolower($row['status'] ?? ''); - if ($status !== 'ok') { - throw new \RuntimeException($row['message'] ?? 'Reset failed by script'); - } - - // Gửi mail thông báo - Mail::to($this->email)->send( - new VmPasswordResetSuccess(vmName: $vmName, newPassword: $this->newPassword) - ); - - Log::info('ResetVmPasswordJob: success', ['vm'=>$vmName]); - } -} - diff --git a/app/Jobs/VirtualMachine/Create/CheckIPAddress.php b/app/Jobs/VirtualMachine/Create/CheckIPAddress.php deleted file mode 100644 index ce7624d..0000000 --- a/app/Jobs/VirtualMachine/Create/CheckIPAddress.php +++ /dev/null @@ -1,94 +0,0 @@ -vmware_id = $vmware_id; - $this->serviceVCenter = $service; - $this->data = $data; - $this->info = $virtual_info; - } - - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - $DetailVirtualMachine = $this->serviceVCenter->GetIPAddressVirtualMachine($this->vmware_id); - - if ($DetailVirtualMachine['error']) { - throw new \Exception(json_encode($DetailVirtualMachine['message'])); - } - - // Get expected IP address with null safety - $expectedIP = null; - - // Try to get IP from old relationship first - if ($this->data && isset($this->data->IpAddress) && $this->data->IpAddress) { - $expectedIP = $this->data->IpAddress->ip; - } - - // If no IP from old relationship, try to get from ModuleVmware - if (!$expectedIP && $this->data) { - // Try to find ModuleVmware by vmware_id - $moduleVmware = \App\Models\ModuleVmware::with('ipAddress') - ->where('vmware_id', $this->vmware_id) - ->first(); - - if ($moduleVmware && $moduleVmware->ipAddress) { - $expectedIP = $moduleVmware->ipAddress->ip; - } - } - - // If still no expected IP, log warning but don't fail - if (!$expectedIP) { - \Illuminate\Support\Facades\Log::warning('⚠️ Không tìm thấy expected IP cho VM', [ - 'vmware_id' => $this->vmware_id, - 'data_class' => $this->data ? get_class($this->data) : 'null', - 'has_ip_address_relation' => isset($this->data->IpAddress) && $this->data->IpAddress ? 'yes' : 'no' - ]); - - // Skip IP check and continue to next job - $jobs_configDB = new ConfigDatabase($this->data, $this->info); - dispatch($jobs_configDB)->delay(now()->addSeconds(5)); - return; - } - - // Compare actual vs expected IP - if ($DetailVirtualMachine['data']['ip_address'] !== $expectedIP) { - \Illuminate\Support\Facades\Log::warning('⚠️ IP address mismatch', [ - 'vmware_id' => $this->vmware_id, - 'actual_ip' => $DetailVirtualMachine['data']['ip_address'], - 'expected_ip' => $expectedIP - ]); - throw new \Exception('IP address mismatch: actual=' . $DetailVirtualMachine['data']['ip_address'] . ', expected=' . $expectedIP); - } - $jobs_configDB = new ConfigDatabase($this->data,$this->info); - dispatch($jobs_configDB)->delay(now()->addSeconds(5)); - - } -} diff --git a/app/Jobs/VirtualMachine/Create/CheckStatusVirtualMachine.php b/app/Jobs/VirtualMachine/Create/CheckStatusVirtualMachine.php deleted file mode 100644 index 693e803..0000000 --- a/app/Jobs/VirtualMachine/Create/CheckStatusVirtualMachine.php +++ /dev/null @@ -1,54 +0,0 @@ -vmware_id = $vmware_id; - $this->serviceVCenter = $service; - $this->data = $data; - } - - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - $status = $this->serviceVCenter->CheckVirtualMachine($this->vmware_id); - - if ($status['error'] === true) { - throw new \Exception(json_encode($status['message'])); - } - if ($status['data']['operations_ready'] === false) { - do { - sleep(10); - $status = $this->serviceVCenter->CheckVirtualMachine($this->vmware_id); - } while ($status['data']['operations_ready'] !== true); - } - - $jobs_configNetwork = new GetNetworkVirtualMachine($this->vmware_id,$this->data,$this->serviceVCenter); - dispatch($jobs_configNetwork)->delay(now()->addSeconds(5)); - - } -} diff --git a/app/Jobs/VirtualMachine/Create/ConfigCLICommand.php b/app/Jobs/VirtualMachine/Create/ConfigCLICommand.php deleted file mode 100644 index 79f9e86..0000000 --- a/app/Jobs/VirtualMachine/Create/ConfigCLICommand.php +++ /dev/null @@ -1,208 +0,0 @@ -vmware_id = $vmware_id; - $this->serviceVCenter = $service; - $this->data = $data; - } - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - // Generate new password - $PasswordVirtualMachine = Helper::general_password(); - $usersName = ''; - $port = ''; - - // Get VM with relationships - $vm = \App\Models\ModuleVmware::with(['ipAddress', 'os.osGroup']) - ->where('vmware_id', $this->vmware_id) - ->first(); - - if (!$vm) { - Log::error('❌ ConfigCLICommand: VM not found', ['vmware_id' => $this->vmware_id]); - return; - } - - // Determine OS type and config - $osGroupName = $vm->os?->osGroup?->name ?? ''; - $osVersion = $vm->os?->version ?? ''; - - try { - switch (true) { - case (str_contains($osGroupName, "Windows") && str_contains($osVersion, 10)): - $usersName = 'administrator'; - $port = 3389; - $this->serviceVCenter->ConfigWindowsVersion10($this->vmware_id, $vm, $PasswordVirtualMachine); - break; - - case (str_contains($osGroupName, "Windows")): - $usersName = 'administrator'; - $port = 3389; - $this->serviceVCenter->ConfigWindowsServer($this->vmware_id, $vm, $PasswordVirtualMachine); - break; - - case (str_contains($osGroupName, "CentOS") && str_contains($osVersion, 7)): - $usersName = 'root'; - $port = 22; - $this->serviceVCenter->ConfigCentOSVersion7($this->vmware_id, $vm, $PasswordVirtualMachine); - break; - - case (str_contains($osGroupName, "CentOS") && str_contains($osVersion, 8)): - $usersName = 'root'; - $port = 22; - $this->serviceVCenter->ConfigCentOSVersion8($this->vmware_id, $vm, $PasswordVirtualMachine); - break; - - case (str_contains($osGroupName, "Ubuntu")): - $usersName = 'root'; - $port = 22; - $this->serviceVCenter->ConfigVirtualMachineUbuntu($this->vmware_id, $vm, $PasswordVirtualMachine); - break; - - case (str_contains($osGroupName, "Rocky")): - $usersName = 'root'; - $port = 22; - $this->serviceVCenter->ConfigRockyLinuxVersion9($this->vmware_id, $vm, $PasswordVirtualMachine); - break; - - default: - Log::warning('❌ ConfigCLICommand: Unknown OS', [ - 'vmware_id' => $this->vmware_id, - 'os_group' => $osGroupName, - 'os_version' => $osVersion, - ]); - return; - } - - // Wait for VM to be ready - sleep(20); - - $VirtualMachine = $this->serviceVCenter->CheckVirtualMachine($this->vmware_id); - - if ($VirtualMachine['data']['operations_ready'] === false) { - $maxAttempts = 30; // 5 minutes max (30 * 10 seconds) - $attempt = 0; - do { - sleep(10); - $VirtualMachine = $this->serviceVCenter->CheckVirtualMachine($this->vmware_id); - Log::channel('Custom')->info(json_encode($VirtualMachine)); - $attempt++; - } while ($VirtualMachine['data']['operations_ready'] !== true && $attempt < $maxAttempts); - } - - // Fix Windows click if Windows - if (str_contains($osGroupName, "Windows")) { - $this->serviceVCenter->ConfigFixWindowsClick($this->vmware_id, $vm); - } - - // ✅ Save password to DB AFTER successful config - $vm->update([ - 'password' => \App\Http\Facade\SignRSA::EncryptFunc($PasswordVirtualMachine), - 'plain_password' => $PasswordVirtualMachine, - 'port' => $port, - 'user' => $usersName, - ]); - - Log::info('✅ ConfigCLICommand: Password changed and saved', [ - 'vmware_id' => $this->vmware_id, - 'username' => $usersName, - 'port' => $port, - ]); - - // Send email with new password to customer - $this->sendPasswordEmail($vm, $PasswordVirtualMachine, $usersName, $port); - - // Dispatch CheckIP job - $jobs_CheckIP = new CheckIPAddress( - $this->vmware_id, - $vm, - ['user' => $usersName, 'password' => $PasswordVirtualMachine, 'port' => $port], - $this->serviceVCenter - ); - dispatch($jobs_CheckIP)->delay(now()->addSeconds(5)); - - } catch (\Exception $e) { - Log::error('❌ ConfigCLICommand: Error during configuration', [ - 'vmware_id' => $this->vmware_id, - 'error' => $e->getMessage(), - 'trace' => $e->getTraceAsString(), - ]); - throw $e; - } - } - - /** - * Send email with new password to customer - */ - private function sendPasswordEmail($vm, string $password, string $username, int $port): void - { - try { - $customer = $vm->customer; - if (!$customer) { - Log::error('ConfigCLICommand: Cannot find customer', ['vm_id' => $vm->id]); - return; - } - - $setting = Setting::where('key', 'setting')->first(); - $settingValue = $setting ? json_decode($setting->value) : null; - - $emailData = [ - 'company_name' => $settingValue->company ?? 'Thế Giới Số', - 'company_logo' => $settingValue->logo ?? '', - 'client_name' => trim($customer->firstname . ' ' . $customer->lastname), - 'product_name' => 'Cloud VPS', - 'product_billing' => 'N/A', - 'product_price' => 'N/A', - 'service_host' => $vm->ipAddress?->ip ?? 'N/A', - 'service_username' => $username, - 'service_port' => $port, - 'service_password' => $password, - 'os_name' => $vm->os?->osGroup?->name . ' ' . $vm->os?->version ?? 'Unknown OS', - 'service_due_exp' => $vm->next_due_date ? date('d/m/Y', $vm->next_due_date) : 'N/A', - ]; - - SendInformationVirtualMachine::dispatch($emailData, $customer) - ->delay(now()->addSeconds(10)); - - Log::info('✅ ConfigCLICommand: Password email dispatched', [ - 'customer_email' => $customer->email, - 'vmware_id' => $vm->vmware_id, - ]); - } catch (\Throwable $e) { - Log::error('❌ ConfigCLICommand: Failed to dispatch password email: ' . $e->getMessage(), [ - 'vmware_id' => $vm->vmware_id, - 'error' => $e->getMessage(), - ]); - } - } -} diff --git a/app/Jobs/VirtualMachine/Create/ConfigDatabase.php b/app/Jobs/VirtualMachine/Create/ConfigDatabase.php deleted file mode 100644 index 40ee000..0000000 --- a/app/Jobs/VirtualMachine/Create/ConfigDatabase.php +++ /dev/null @@ -1,84 +0,0 @@ -data = $data; - $this->info = $info; - $this->telegram = new Telegram(); - // - } - - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - $this->data->update([ - 'name' => $this->data->ipaddress->ip . '_' . $this->data->cannotustomer->firstname, - 'port' => $this->info['port'], - 'user' => $this->info['user'], - 'password' => SignRSA::EncryptFunc($this->info['password']), - 'status' => 1 - ]); - - $this->data->ipAddress->update([ - 'vm_name' => $this->data->IpAddress->ip . '_' . $this->data->Customer->firstname, - 'status' => 1 - ]); - - $setting = Setting::where('key', 'setting')->first(); - - $data = [ - 'company_name' => json_decode($setting->value)->company, - 'company_logo' => json_decode($setting->value)->logo, - 'client_name' => $this->data->Customer->firstname . " " . $this->data->Customer->lastname, - 'product_name' => $this->data->GetInvoiceItem()->GetItemProduct()->name, - 'product_billing' => Helper::billing_cycle($this->data->GetInvoiceItem()->billing), - 'product_price' => number_format($this->data->GetInvoiceItem()->amount, 0, ',', '.'), - 'service_host' => $this->data->IpAddress->ip, - 'service_username' => $this->info['user'], - 'service_port' => $this->info['port'], - 'service_password' => $this->info['password'], - 'os_name' => $this->data->Os->OsGroup->name . " " . $this->data->Os->version, - 'service_due_exp' => Carbon::parse($this->data->next_due_date)->format('d/m/Y') - ]; - - // Note: Email is now sent by ConfigCLICommand after password is changed - // $mail = new SendInformationVirtualMachine($data, $this->data->Customer); - // dispatch($mail)->delay(now()->addSeconds(5)); - - Event(new Create_VirtualMachine_Success("Máy chủ của bạn đã được khởi tạo thành công!", - $this->data->customer->id)); - - $this->telegram->SendInformation($this->data, $this->info['user'], $this->info['password'], $this->info['port']); - } -} diff --git a/app/Jobs/VirtualMachine/Create/GetNetworkVirtualMachine.php b/app/Jobs/VirtualMachine/Create/GetNetworkVirtualMachine.php deleted file mode 100644 index 8f62d9e..0000000 --- a/app/Jobs/VirtualMachine/Create/GetNetworkVirtualMachine.php +++ /dev/null @@ -1,61 +0,0 @@ -vmware_id = $vmware_id; - $this->serviceVCenter = $service; - $this->data = $data; - } - - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - - $NetworkForVirtualMachine = $this->serviceVCenter->GetNetworkVirtualMachine($this->vmware_id); - - if ($NetworkForVirtualMachine['error']) { - throw new \Exception(json_encode($NetworkForVirtualMachine['message'])); - } - $Network = $this->data->IpAddress->Network->network; - if ($Network !== $NetworkForVirtualMachine['data']['network']) { - $ConfigNetwork = [ - "spec" => [ - "backing" => [ - "type" => $this->data->IpAddress->Network->type, - "network" => $this->data->IpAddress->Network->network - ] - ] - ]; - $this->serviceVCenter->ConfigNetworkVirtualMachine($this->vmware_id,$ConfigNetwork); - - } - - $jobs_configCLI = new ConfigCLICommand($this->vmware_id,$this->data,$this->serviceVCenter); - dispatch($jobs_configCLI)->delay(now()->addSeconds(5)); - } -} diff --git a/app/Jobs/VirtualMachine/Create/GetVirtualMachine.php b/app/Jobs/VirtualMachine/Create/GetVirtualMachine.php deleted file mode 100644 index 35c85e0..0000000 --- a/app/Jobs/VirtualMachine/Create/GetVirtualMachine.php +++ /dev/null @@ -1,57 +0,0 @@ -name = $name; - $this->data = $data; - $this->serviceVCenter = $service; - } - - /** - * Execute the job. - * - * @return bool|string - */ - public function handle() - { - - $vmware_id = $this->serviceVCenter->FindVirtualMachine($this->name); - - Log::channel('detail')->info("=================="); - - Log::channel('detail')->info($vmware_id); - - return $vmware_id; - - } - - public function failed(\Throwable $error) - { - Log::channel('error')->info($error->getMessage()); - } - -} diff --git a/app/Jobs/VirtualMachine/Handle/WaitStatusVirtualMachine.php b/app/Jobs/VirtualMachine/Handle/WaitStatusVirtualMachine.php deleted file mode 100644 index c80cd30..0000000 --- a/app/Jobs/VirtualMachine/Handle/WaitStatusVirtualMachine.php +++ /dev/null @@ -1,95 +0,0 @@ -productId = $productId; - $this->status = $status; - } - - public function handle(ServiceVCenter $serviceVCenter) - { - $product = ModuleVmware::with(['IpAddress.Host.Datacenter.vCenter'])->find($this->productId); - - if (!$product || !$product->IpAddress?->Host?->Datacenter?->vCenter) { - Log::error("WaitStatusVirtualMachine: Product {$this->productId} missing vCenter relationship"); - return; - } - - $vcenter = $product->IpAddress->Host->Datacenter->vCenter; - $serviceVCenter->setConnection($vcenter->host, $vcenter->token); - - $vmwareId = $product->vmware_id; - $userId = $product->customer_id; - - Log::info("WaitStatusVirtualMachine: Started for VM {$vmwareId}, Target Status: " . ($this->status ? 'True' : 'False')); - - $status = $serviceVCenter->CheckVirtualMachine($vmwareId); - - if ($status['error'] === true) { - Log::error("WaitStatusVirtualMachine: Error checking status for VM {$vmwareId}: " . json_encode($status['message'])); - throw new \Exception(json_encode($status['message'])); - } - - Log::info("WaitStatusVirtualMachine: Initial operations_ready: " . ($status['data']['operations_ready'] ? 'True' : 'False')); - - if ($status['data']['operations_ready'] === $this->status) { - Log::info("WaitStatusVirtualMachine: Entering wait loop..."); - do { - sleep(10); - $status = $serviceVCenter->CheckVirtualMachine($vmwareId); - - if ($status['error'] === true) { - Log::warning("WaitStatusVirtualMachine: Failed to check status in loop. Breaking."); - break; - } - - Log::info("WaitStatusVirtualMachine: Loop check - operations_ready: " . ($status['data']['operations_ready'] ? 'True' : 'False')); - - } while ($status['data']['operations_ready'] === $this->status); - } else { - Log::info("WaitStatusVirtualMachine: Skipping wait loop, condition already met."); - } - - \Illuminate\Support\Facades\Cache::forget(\App\Support\Cache\CacheKey::build('vm', 'info', ['id' => $vmwareId])); - - $product->refresh(); - - if ($product) { - $newStatus = ($this->status === true) ? 0 : 1; - - Log::info("WaitStatusVirtualMachine: Setting status to $newStatus"); - - $product->update(['status' => $newStatus]); - - $msg = ($newStatus == 1) - ? __('Máy ảo đã hoạt động trở lại.') - : __('Máy ảo đã tắt hoàn toàn.'); - - $statusStr = ($newStatus == 1) ? 'running' : 'stopped'; - - \App\Events\VirtualMachineStatusChanged::dispatch($userId, $vmwareId, $statusStr, $msg); - } - - Event(new VirtualMachine_Panel_Status('Yêu cầu được xử lý thành công', $userId)); - } -} diff --git a/app/Jobs/VirtualMachine/Power/RebootVmJob.php b/app/Jobs/VirtualMachine/Power/RebootVmJob.php deleted file mode 100644 index e88f961..0000000 --- a/app/Jobs/VirtualMachine/Power/RebootVmJob.php +++ /dev/null @@ -1,86 +0,0 @@ -productId = $productId; - $this->userId = $userId; - } - - /** - * Execute the job. - */ - public function handle(vCenter $vCenterService) - { - Log::info("Job RebootVmJob started for Product ID: {$this->productId}"); - - $product = ModuleVmware::with('ipAddress.host.datacenter.vCenter')->find($this->productId); - - if (!$product) { - Log::error("RebootVmJob: Product not found ID: {$this->productId}"); - return; - } - - try { - $result = $vCenterService->RebootVirtualMachine($product, $product->vmware_id); - - if ($result === 'Login_Again') { - $vCenterService->loginServer($product->ipAddress->host->datacenter->vCenter); - $result = $vCenterService->RebootVirtualMachine($product, $product->vmware_id); - } - - if (is_array($result) && isset($result['error']) && $result['error'] === false) { - Log::info("RebootVmJob: Command sent successfully for VM {$product->vmware_id}"); - - // Clear VM info cache - \Illuminate\Support\Facades\Cache::forget(\App\Support\Cache\CacheKey::build('vm', 'info', ['id' => $product->vmware_id])); - - // Dispatch Event to Frontend - \App\Events\VirtualMachineStatusChanged::dispatch( - $this->userId, - $product->vmware_id, - 'rebooted', - __('Máy ảo đã được khởi động lại thành công.') - ); - - $product->update(['status' => 3]); // Processing - - // In VMJobs: $jobs = new WaitStatusVirtualMachine($product->vmware_id,$serviceVCenter,$product->customer_id,false); - - dispatch(new WaitStatusVirtualMachine($this->productId, false))->delay(now()->addSeconds(5)); - - } else { - $msg = is_array($result) ? ($result['data'] ?? 'Unknown error') : $result; - Log::error("RebootVmJob: Failed for VM {$product->vmware_id}. Error: {$msg}"); - } - - } catch (\Exception $e) { - Log::error("RebootVmJob: Exception for VM {$product->vmware_id}: " . $e->getMessage()); - $this->fail($e); - } - } -} diff --git a/app/Jobs/VirtualMachine/Power/StartVmJob.php b/app/Jobs/VirtualMachine/Power/StartVmJob.php deleted file mode 100644 index 7161e1e..0000000 --- a/app/Jobs/VirtualMachine/Power/StartVmJob.php +++ /dev/null @@ -1,97 +0,0 @@ -productId = $productId; - $this->userId = $userId; - } - - /** - * Execute the job. - */ - public function handle(vCenter $vCenterService) - { - Log::info("Job StartVmJob started for Product ID: {$this->productId}"); - - $product = ModuleVmware::with('ipAddress.host.datacenter.vCenter')->find($this->productId); - - if (!$product) { - Log::error("StartVmJob: Product not found ID: {$this->productId}"); - return; - } - - try { - // Call vCenter Service - $result = $vCenterService->StartVirtualMachine($product, $product->vmware_id); - - // Check for Login_Again - if ($result === 'Login_Again') { - $vCenterService->loginServer($product->ipAddress->host->datacenter->vCenter); - $result = $vCenterService->StartVirtualMachine($product, $product->vmware_id); - } - - if (is_array($result) && isset($result['error']) && $result['error'] === false) { - Log::info("StartVmJob: Command sent successfully for VM {$product->vmware_id}"); - - // Clear VM info cache - \Illuminate\Support\Facades\Cache::forget(\App\Support\Cache\CacheKey::build('vm', 'info', ['id' => $product->vmware_id])); - - // Dispatch Event to Frontend - \App\Events\VirtualMachineStatusChanged::dispatch( - $this->userId, - $product->vmware_id, - 'running', - __('Máy ảo đã được khởi động thành công.') - ); - - // Update status to 'processing' (e.g., 3) if needed, or leave it to WaitStatus - $product->update(['status' => 3]); // 3 = Processing/Pending - - // Dispatch wait status job - // Note: WaitStatusVirtualMachine expects ($vmware_id, $service, $user, $status_to_wait_for?) - // Looking at existing code: - // $status = 2; // Suspend - // $jobs = new WaitStatusVirtualMachine($product->vmware_id,$serviceVCenter,$product->customer_id,true); (true = wait for operations_ready?) - - // For Start: - // $jobs = new WaitStatusVirtualMachine($product->vmware_id, $serviceVCenter, $product->customer_id, false); - - dispatch(new WaitStatusVirtualMachine($this->productId, false))->delay(now()->addSeconds(5)); - - } else { - $msg = is_array($result) ? ($result['data'] ?? 'Unknown error') : $result; - Log::error("StartVmJob: Failed for VM {$product->vmware_id}. Error: {$msg}"); - // Optionally notify user via Pusher/Telegram here - } - - } catch (\Exception $e) { - Log::error("StartVmJob: Exception for VM {$product->vmware_id}: " . $e->getMessage()); - $this->fail($e); - } - } -} diff --git a/app/Jobs/VirtualMachine/Power/StopVmJob.php b/app/Jobs/VirtualMachine/Power/StopVmJob.php deleted file mode 100644 index 710e7f0..0000000 --- a/app/Jobs/VirtualMachine/Power/StopVmJob.php +++ /dev/null @@ -1,84 +0,0 @@ -productId = $productId; - $this->userId = $userId; - } - - /** - * Execute the job. - */ - public function handle(vCenter $vCenterService) - { - Log::info("Job StopVmJob started for Product ID: {$this->productId}"); - - $product = ModuleVmware::with('ipAddress.host.datacenter.vCenter')->find($this->productId); - - if (!$product) { - Log::error("StopVmJob: Product not found ID: {$this->productId}"); - return; - } - - try { - // Call vCenter Service - Graceful Shutdown - $result = $vCenterService->ShutdownGuestVirtualMachine($product, $product->vmware_id); - - // Check for Login_Again - if ($result === 'Login_Again') { - $vCenterService->loginServer($product->ipAddress->host->datacenter->vCenter); - $result = $vCenterService->ShutdownGuestVirtualMachine($product, $product->vmware_id); - } - - if (is_array($result) && isset($result['error']) && $result['error'] === false) { - // Clear VM info cache - \Illuminate\Support\Facades\Cache::forget(\App\Support\Cache\CacheKey::build('vm', 'info', ['id' => $product->vmware_id])); - - // Dispatch Event to Frontend - \App\Events\VirtualMachineStatusChanged::dispatch( - $this->userId, - $product->vmware_id, - 'stopped', - __('Máy ảo đã được tắt thành công.') - ); - - $product->update(['status' => 3]); // Processing - - dispatch(new WaitStatusVirtualMachine($this->productId, true))->delay(now()->addSeconds(5)); - - } else { - $msg = is_array($result) ? ($result['data'] ?? 'Unknown error') : $result; - Log::error("StopVmJob: Failed for VM {$product->vmware_id}. Error: {$msg}"); - } - - } catch (\Exception $e) { - Log::error("StopVmJob: Exception for VM {$product->vmware_id}: " . $e->getMessage()); - $this->fail($e); - } - } -} diff --git a/app/Jobs/VirtualMachine/Provision/AssignIpToGuestJob.php b/app/Jobs/VirtualMachine/Provision/AssignIpToGuestJob.php index c4493cf..15da1a6 100644 --- a/app/Jobs/VirtualMachine/Provision/AssignIpToGuestJob.php +++ b/app/Jobs/VirtualMachine/Provision/AssignIpToGuestJob.php @@ -6,12 +6,9 @@ namespace App\Jobs\VirtualMachine\Provision; use App\Http\Facade\SignRSA; use App\Http\Services\Telegram; -use App\Models\IpPool; -use App\Models\ModuleIpAddress; use App\Models\ModuleVmware; use App\Models\Os; use App\Models\Service; -use App\Models\VirtualMachines; use App\Http\Services\vCenter as vCenterService; use App\Models\vCenter; use App\Services\VirtualMachine\VmProvisionLogger; @@ -26,6 +23,7 @@ use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; +use App\Jobs\VirtualMachine\Provision\Concerns\ManagesIpReservation; /** * AssignIpToGuestJob * @@ -48,6 +46,7 @@ use Illuminate\Support\Facades\Log; class AssignIpToGuestJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + use ManagesIpReservation; /** All retries handled internally — no Laravel-level retry needed */ public int $tries = 1; @@ -318,181 +317,6 @@ class AssignIpToGuestJob implements ShouldQueue // IP reservation helpers // ───────────────────────────────────────────────────────────────────────── - /** - * Reserve a free IP from the pool inside a DB transaction with row-level locks. - * - * Strategy: - * 1. Lock all existing ModuleIpAddress rows for the network → prevents double-pick - * 2. Lock the chosen IpPool row → prevents concurrent reservations - * 3. Create a pending ModuleIpAddress (status=0) to hold the slot - * - * Returns the pending ModuleIpAddress record, or null if pool is exhausted. - */ - /** - * @return array{reservation:ModuleIpAddress,ip_pool_id:int,gateway:string,cidr:int}|null - */ - private function reserveIp( - int $networkId, - int $hostId, - string $dns, - ): ?array { - return DB::transaction(function () use ($networkId, $hostId, $dns) { - // ip_pools.status is the single source of truth for availability. - $ipPool = IpPool::where('module_network_id', $networkId) - ->whereIn('status', ['free', 'reserved']) - ->lockForUpdate() - ->inRandomOrder() - ->first(); - - if (!$ipPool) { - return null; - } - - // Hold the pool slot - $ipPool->update(['status' => 'reserved']); - - $cidr = (int) ($ipPool->cidr ?: 24); - $gateway = (string) ($ipPool->gateway ?? ''); - $netmask = $ipPool->netmask ?: $this->cidrToNetmask($cidr); - - // module_ip_address mirrors ip_pools — update the existing synced row - // (created when the IP was added to ip_pools) instead of inserting a new one. - $reservation = ModuleIpAddress::where('ip', $ipPool->ip_address) - ->where('module_network_id', $networkId) - ->lockForUpdate() - ->first(); - - $attrs = [ - 'vm_name' => '__pending__', - 'module_host_id' => $hostId, - 'module_network_id' => $networkId, - 'ip' => $ipPool->ip_address, - 'gateway' => $gateway, - 'netmask' => $netmask, - 'dns' => $dns, - 'status' => 0, // 0 = pending/reserved, not yet confirmed - ]; - - if ($reservation) { - $reservation->update($attrs); - } else { - $reservation = ModuleIpAddress::create($attrs); - } - - return [ - 'reservation' => $reservation, - 'ip_pool_id' => $ipPool->id, - 'gateway' => $gateway, - 'cidr' => $cidr, - ]; - }); - } - - /** - * Load a reservation that was created upstream (e.g. by CreateVirtualMachineJob). - * Returns the same shape as reserveIp() so the rest of the flow is identical. - * - * @return array{reservation:ModuleIpAddress,ip_pool_id:int,gateway:string,cidr:int}|null - */ - private function loadReservation(int $reservationId, ?int $ipPoolId, VmProvisionLogger $logger): ?array - { - $reservation = ModuleIpAddress::find($reservationId); - if (!$reservation) { - $logger->error('upstream reservation missing', [ - 'reservation_id' => $reservationId, - ]); - return null; - } - - $ipPool = $ipPoolId ? IpPool::find($ipPoolId) : null; - $cidr = (int) ($ipPool->cidr ?? 24); - $gateway = (string) ($reservation->gateway ?: ($ipPool->gateway ?? '')); - - return [ - 'reservation' => $reservation, - 'ip_pool_id' => $ipPoolId ?? 0, - 'gateway' => $gateway, - 'cidr' => $cidr ?: 24, - ]; - } - - /** - * Release a failed reservation by resetting the mirrored ModuleIpAddress row. - * This returns the IP to the available pool. - */ - private function releaseIp(ModuleIpAddress $reservation, ?int $ipPoolId, VmProvisionLogger $logger): void - { - try { - $reservation->update(['vm_name' => null, 'status' => 0]); - if ($ipPoolId) { - IpPool::where('id', $ipPoolId)->update(['status' => 'free']); - } - $logger->step('IP reservation released', ['ip' => $reservation->ip]); - } catch (\Throwable $e) { - $logger->warning('could not release IP reservation', [ - 'ip' => $reservation->ip, - 'error' => $e->getMessage(), - ]); - } - } - - /** - * Confirm a successful IP assignment: - * - Update ModuleIpAddress: status → 1 (assigned), vm_name → real name - * - Update ModuleVmware: ip, dns, module_ip_address FK - * - Update VirtualMachines: ip, ip_pool_id FK (matched by vmId) - * - * IP is only written to DB AFTER guest-side verification passes. - */ - private function persistIp( - ModuleIpAddress $reservation, - ModuleVmware $moduleVmware, - ?int $ipPoolId, - ?int $serviceId, - ): void { - DB::transaction(function () use ($reservation, $moduleVmware, $ipPoolId, $serviceId) { - $reservation->update([ - 'vm_name' => $moduleVmware->name ?? ('VM-' . $this->vmId), - 'status' => 1, // assigned - ]); - - $moduleVmware->update([ - 'ip' => $reservation->ip, - 'dns' => $reservation->dns, - 'module_ip_address' => $reservation->id, - ]); - - // Sync to virtual_machines — matched by vSphere vmId - VirtualMachines::where('vm_id', $this->vmId)->update([ - 'ip' => $reservation->ip, - 'ip_pool_id' => $ipPoolId, - ]); - - // Flip ip_pools row to assigned and link service - if ($ipPoolId) { - IpPool::where('id', $ipPoolId)->update([ - 'status' => 'assigned', - 'service_id' => $serviceId, - ]); - } - - // Touch services row so server_name reflects the provisioned VM. - // Note: services.vm_id is an FK to module_vmware.id (already set by VmServiceSyncService) — do not overwrite with vSphere vmId string. - if ($serviceId) { - Service::where('id', $serviceId)->update([ - 'server_name' => $moduleVmware->name, - ]); - } - }); - } - - /** Convert CIDR prefix length to dotted-decimal netmask (e.g. 24 → 255.255.255.0). */ - private function cidrToNetmask(int $cidr): string - { - $cidr = max(0, min(32, $cidr)); - return long2ip(-1 << (32 - $cidr)); - } - // ───────────────────────────────────────────────────────────────────────── // Micro-helpers // ───────────────────────────────────────────────────────────────────────── @@ -507,5 +331,4 @@ class AssignIpToGuestJob implements ShouldQueue $data = json_decode(trim($output), true); return is_array($data) && ($data['status'] ?? '') === 'ok'; } - } diff --git a/app/Jobs/VirtualMachine/Provision/ChangePasswordJob.php b/app/Jobs/VirtualMachine/Provision/ChangePasswordJob.php deleted file mode 100644 index 2bd5c8e..0000000 --- a/app/Jobs/VirtualMachine/Provision/ChangePasswordJob.php +++ /dev/null @@ -1,138 +0,0 @@ - $this->vmId, - 'ip' => $this->guestIp, - 'os_family' => $this->osFamily, - 'username' => $this->username, - ]); - - // [7] Generate secure password - $newPassword = $provision->generatePassword(16); - - // [8] Change password on OS - $this->changePassword($provision, $newPassword); - - // [9] Verify new credentials work (critical — throws on failure) - $this->verifyPassword($provision, $newPassword); - - // [10] Persist encrypted password + guest IP to DB - $provision->savePassword($this->moduleVmwareId, $this->guestIp, $newPassword); - - // [11] Send credentials email - SendVmCredentialsEmailJob::dispatch($this->moduleVmwareId); - - Log::info('ChangePasswordJob: completed', [ - 'vm_id' => $this->vmId, - 'module_vmware_id' => $this->moduleVmwareId, - ]); - } - - public function failed(\Throwable $e): void - { - Log::error('ChangePasswordJob: failed after max retries', [ - 'vm_id' => $this->vmId, - 'module_vmware_id' => $this->moduleVmwareId, - 'error' => $e->getMessage(), - ]); - - \App\Models\ModuleVmware::where('id', $this->moduleVmwareId) - ->update(['status' => 0]); - } - - // ------------------------------------------------------------------------- - - private function changePassword(VmProvisionService $provision, string $newPassword): void - { - if ($this->osFamily === VmProvisionService::OS_WINDOWS) { - Log::info('ChangePasswordJob: changing password via WinRM', [ - 'vm_id' => $this->vmId, - 'ip' => $this->guestIp, - ]); - - $provision->winrmChangePassword( - $this->guestIp, - $this->username, - $newPassword, - $this->vmId, - ); - } else { - Log::info('ChangePasswordJob: changing password via SSH', [ - 'vm_id' => $this->vmId, - 'ip' => $this->guestIp, - ]); - - $provision->sshChangePassword( - $this->guestIp, - $this->username, - $newPassword, - $this->vmId, - ); - } - } - - private function verifyPassword(VmProvisionService $provision, string $newPassword): void - { - Log::info('ChangePasswordJob: verifying new credentials', [ - 'vm_id' => $this->vmId, - 'ip' => $this->guestIp, - ]); - - if ($this->osFamily === VmProvisionService::OS_WINDOWS) { - $provision->verifyWinrmLogin($this->guestIp, $this->username, $newPassword, $this->vmId); - } else { - $provision->verifySshLogin($this->guestIp, $this->username, $newPassword, $this->vmId); - } - - Log::info('ChangePasswordJob: credential verification passed', [ - 'vm_id' => $this->vmId, - ]); - } -} diff --git a/app/Jobs/VirtualMachine/Provision/Concerns/ManagesIpReservation.php b/app/Jobs/VirtualMachine/Provision/Concerns/ManagesIpReservation.php new file mode 100644 index 0000000..5a4d5e9 --- /dev/null +++ b/app/Jobs/VirtualMachine/Provision/Concerns/ManagesIpReservation.php @@ -0,0 +1,189 @@ +whereIn('status', ['free', 'reserved']) + ->lockForUpdate() + ->inRandomOrder() + ->first(); + + if (!$ipPool) { + return null; + } + + // Hold the pool slot + $ipPool->update(['status' => 'reserved']); + + $cidr = (int) ($ipPool->cidr ?: 24); + $gateway = (string) ($ipPool->gateway ?? ''); + $netmask = $ipPool->netmask ?: $this->cidrToNetmask($cidr); + + // module_ip_address mirrors ip_pools — update the existing synced row + // (created when the IP was added to ip_pools) instead of inserting a new one. + $reservation = ModuleIpAddress::where('ip', $ipPool->ip_address) + ->where('module_network_id', $networkId) + ->lockForUpdate() + ->first(); + + $attrs = [ + 'vm_name' => '__pending__', + 'module_host_id' => $hostId, + 'module_network_id' => $networkId, + 'ip' => $ipPool->ip_address, + 'gateway' => $gateway, + 'netmask' => $netmask, + 'dns' => $dns, + 'status' => 0, // 0 = pending/reserved, not yet confirmed + ]; + + if ($reservation) { + $reservation->update($attrs); + } else { + $reservation = ModuleIpAddress::create($attrs); + } + + return [ + 'reservation' => $reservation, + 'ip_pool_id' => $ipPool->id, + 'gateway' => $gateway, + 'cidr' => $cidr, + ]; + }); + } + + /** + * Load a reservation that was created upstream (e.g. by CreateVirtualMachineJob). + * Returns the same shape as reserveIp() so the rest of the flow is identical. + * + * @return array{reservation:ModuleIpAddress,ip_pool_id:int,gateway:string,cidr:int}|null + */ + private function loadReservation(int $reservationId, ?int $ipPoolId, VmProvisionLogger $logger): ?array + { + $reservation = ModuleIpAddress::find($reservationId); + if (!$reservation) { + $logger->error('upstream reservation missing', [ + 'reservation_id' => $reservationId, + ]); + return null; + } + + $ipPool = $ipPoolId ? IpPool::find($ipPoolId) : null; + $cidr = (int) ($ipPool->cidr ?? 24); + $gateway = (string) ($reservation->gateway ?: ($ipPool->gateway ?? '')); + + return [ + 'reservation' => $reservation, + 'ip_pool_id' => $ipPoolId ?? 0, + 'gateway' => $gateway, + 'cidr' => $cidr ?: 24, + ]; + } + + /** + * Release a failed reservation by resetting the mirrored ModuleIpAddress row. + * This returns the IP to the available pool. + */ + private function releaseIp(ModuleIpAddress $reservation, ?int $ipPoolId, VmProvisionLogger $logger): void + { + try { + $reservation->update(['vm_name' => null, 'status' => 0]); + if ($ipPoolId) { + IpPool::where('id', $ipPoolId)->update(['status' => 'free']); + } + $logger->step('IP reservation released', ['ip' => $reservation->ip]); + } catch (\Throwable $e) { + $logger->warning('could not release IP reservation', [ + 'ip' => $reservation->ip, + 'error' => $e->getMessage(), + ]); + } + } + + /** + * Confirm a successful IP assignment: + * - Update ModuleIpAddress: status → 1 (assigned), vm_name → real name + * - Update ModuleVmware: ip, dns, module_ip_address FK + * - Update VirtualMachines: ip, ip_pool_id FK (matched by vmId) + * + * IP is only written to DB AFTER guest-side verification passes. + */ + private function persistIp( + ModuleIpAddress $reservation, + ModuleVmware $moduleVmware, + ?int $ipPoolId, + ?int $serviceId, + ): void { + DB::transaction(function () use ($reservation, $moduleVmware, $ipPoolId, $serviceId) { + $reservation->update([ + 'vm_name' => $moduleVmware->name ?? ('VM-' . $this->vmId), + 'status' => 1, // assigned + ]); + + $moduleVmware->update([ + 'ip' => $reservation->ip, + 'dns' => $reservation->dns, + 'module_ip_address' => $reservation->id, + ]); + + // Sync to virtual_machines — matched by vSphere vmId + VirtualMachines::where('vm_id', $this->vmId)->update([ + 'ip' => $reservation->ip, + 'ip_pool_id' => $ipPoolId, + ]); + + // Flip ip_pools row to assigned and link service + if ($ipPoolId) { + IpPool::where('id', $ipPoolId)->update([ + 'status' => 'assigned', + 'service_id' => $serviceId, + ]); + } + + // Touch services row so server_name reflects the provisioned VM. + // Note: services.vm_id is an FK to module_vmware.id (already set by VmServiceSyncService) — do not overwrite with vSphere vmId string. + if ($serviceId) { + Service::where('id', $serviceId)->update([ + 'server_name' => $moduleVmware->name, + ]); + } + }); + } + + /** Convert CIDR prefix length to dotted-decimal netmask (e.g. 24 → 255.255.255.0). */ + private function cidrToNetmask(int $cidr): string + { + $cidr = max(0, min(32, $cidr)); + return long2ip(-1 << (32 - $cidr)); + } +} diff --git a/app/Jobs/VirtualMachine/Provision/WaitVmReadyJob.php b/app/Jobs/VirtualMachine/Provision/WaitVmReadyJob.php deleted file mode 100644 index 564a389..0000000 --- a/app/Jobs/VirtualMachine/Provision/WaitVmReadyJob.php +++ /dev/null @@ -1,136 +0,0 @@ -attempts(); - - Log::info('WaitVmReadyJob: polling', [ - 'vm_id' => $this->vmId, - 'attempt' => $attempt, - 'os_family' => $this->osFamily, - ]); - - // ── 1. Login to vCenter ─────────────────────────────────────────────── - $vcenter = \App\Models\vCenter::find($this->vcenterId); - - if (!$vcenter) { - Log::error('WaitVmReadyJob: vCenter model not found', ['vcenter_id' => $this->vcenterId]); - $this->fail(new \RuntimeException("vCenter id={$this->vcenterId} not found")); - return; - } - - try { - $provision->loginVCenter($vcenter, $vCenterService); - } catch (\Throwable $e) { - Log::warning('WaitVmReadyJob: vCenter login failed, will retry', [ - 'vm_id' => $this->vmId, - 'error' => $e->getMessage(), - ]); - $this->releaseOrFail(15, $attempt); - return; - } - - // ── 2. Check VMware Tools + guest IP ───────────────────────────────── - // Invoke-VMScript requires VMware Tools to be running inside the guest. - $guest = $provision->getVmGuestInfo($vCenterService, $this->vmId); - - Log::info('WaitVmReadyJob: guest info', [ - 'vm_id' => $this->vmId, - 'attempt' => $attempt, - 'tools_running' => $guest['tools_running'], - 'ip_address' => $guest['ip_address'], - ]); - - if (!$guest['tools_running'] || $guest['ip_address'] === null) { - Log::info('WaitVmReadyJob: VMware Tools not ready yet', ['vm_id' => $this->vmId]); - $this->releaseOrFail(15, $attempt); - return; - } - - $guestIp = $guest['ip_address']; - - // ── 3. VM ready — dispatch password-change job ──────────────────────── - Log::info('WaitVmReadyJob: VMware Tools ready, dispatching ChangePasswordViaVsphereJob', [ - 'vm_id' => $this->vmId, - 'guest_ip' => $guestIp, - ]); - - ChangePasswordViaVsphereJob::dispatch( - $this->moduleVmwareId, - $this->vmId, - $this->osFamily, - ); - } - - // ------------------------------------------------------------------------- - - public function failed(\Throwable $e): void - { - Log::error('WaitVmReadyJob: exceeded max attempts, marking VM failed', [ - 'vm_id' => $this->vmId, - 'module_vmware_id' => $this->moduleVmwareId, - 'error' => $e->getMessage(), - ]); - - \App\Models\ModuleVmware::where('id', $this->moduleVmwareId) - ->update(['status' => 0]); - } - - // ------------------------------------------------------------------------- - - private function releaseOrFail(int $delaySeconds, int $attempt): void - { - if ($attempt >= $this->tries) { - $this->fail(new \RuntimeException( - "VM {$this->vmId} not ready after {$this->tries} attempts (~10 min)", - )); - return; - } - - $this->release($delaySeconds); - } -} diff --git a/app/Jobs/VirtualMachine/Rebuild/RebuildFinalizeJob.php b/app/Jobs/VirtualMachine/Rebuild/RebuildFinalizeJob.php index 3e091ee..b7e898c 100644 --- a/app/Jobs/VirtualMachine/Rebuild/RebuildFinalizeJob.php +++ b/app/Jobs/VirtualMachine/Rebuild/RebuildFinalizeJob.php @@ -10,7 +10,6 @@ use App\Jobs\Mail\Customer\VmRebuildFailedJob; use App\Jobs\Mail\SendInformationVirtualMachine; use App\Models\Invoice; use App\Models\ModuleVmware; -use App\Models\Service; use App\Models\ReinstallLog; use App\Models\Setting; use App\Services\VirtualMachine\Rebuild\VmRebuildService; diff --git a/app/Jobs/VirtualMachine/Sync/UpdateVMInfoJob.php b/app/Jobs/VirtualMachine/Sync/UpdateVMInfoJob.php deleted file mode 100644 index f306e7b..0000000 --- a/app/Jobs/VirtualMachine/Sync/UpdateVMInfoJob.php +++ /dev/null @@ -1,157 +0,0 @@ -invoiceId = $invoiceId; - $this->vmId = $vmId; - } - - /** - * Execute the job. - */ - public function handle(vCenter $vCenterService): void - { - try { - $invoice = Invoice::with([ - 'invoiceItem.os.osGroup', - 'invoiceItem.addons' - ])->find($this->invoiceId); - - if (!$invoice) { - Log::warning('Invoice not found for VM info update', ['invoice_id' => $this->invoiceId]); - return; - } - - $orderItem = $invoice->invoiceItem->first(); - if (!$orderItem) { - Log::warning('Order item not found for invoice', ['invoice_id' => $this->invoiceId]); - return; - } - - $product = $orderItem->getProductAttribute(); - if (!$product) { - Log::warning('Product not found for order item', ['order_item_id' => $orderItem->id]); - return; - } - - $host = $product->host; - $datacenter = $host?->datacenter; - $vcenter = $datacenter?->vcenter; - - if (!$vcenter) { - Log::warning('vCenter not found for VM', ['vm_id' => $this->vmId]); - return; - } - - // Login to vCenter - if (!$vCenterService->loginServer($vcenter)) { - Log::warning('Failed to login to vCenter', ['vm_id' => $this->vmId]); - return; - } - - // Get VM details - $vmDetail = $vCenterService->GetDetailVirtualMachine($this->vmId); - if ($vmDetail['error'] === true) { - Log::warning('Failed to get VM details', ['vm_id' => $this->vmId, 'error' => $vmDetail['message'] ?? 'Unknown error']); - return; - } - - $vmData = $vmDetail['data']; - - // Get IP information - $ipInfo = $vCenterService->GetIPAddressVirtualMachine($this->vmId); - $ipAddress = 'N/A'; - if ($ipInfo['error'] === false && !empty($ipInfo['data']['ip_addresses'])) { - $ipAddress = $ipInfo['data']['ip_addresses'][0] ?? 'N/A'; - } - - // Calculate hardware information - $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' => $this->vmId, - '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 result for 5 minutes - $cacheKey = \App\Support\Cache\CacheKey::build('vm', 'info', ['id' => $this->vmId]); - Cache::put($cacheKey, $vmInfo, 300); - - Log::info('VM info updated successfully', ['vm_id' => $this->vmId]); - - } catch (\Exception $e) { - Log::error('Error updating VM info', [ - 'vm_id' => $this->vmId, - 'invoice_id' => $this->invoiceId, - 'error' => $e->getMessage() - ]); - - // Don't throw exception to prevent job from failing completely - // The job will be retried automatically - } - } - - /** - * Handle a job failure. - */ - public function failed(\Throwable $exception): void - { - Log::error('UpdateVMInfoJob failed', [ - 'vm_id' => $this->vmId, - 'invoice_id' => $this->invoiceId, - 'error' => $exception->getMessage() - ]); - } -} \ No newline at end of file diff --git a/app/Mail/ChangePortRemote.php b/app/Mail/ChangePortRemote.php deleted file mode 100644 index 84cdc7d..0000000 --- a/app/Mail/ChangePortRemote.php +++ /dev/null @@ -1,42 +0,0 @@ -renderFromTemplate('vm_port_change', $this->vars())['subject'], - ); - } - - public function content(): Content - { - return new Content( - htmlString: $this->renderFromTemplate('vm_port_change', $this->vars())['html'], - ); - } - - private function vars(): array - { - return [ - 'vm_name' => $this->vmName, - 'new_port' => $this->newPort, - ]; - } -} diff --git a/app/Mail/Customer/CustomerPayment.php b/app/Mail/Customer/CustomerPayment.php deleted file mode 100644 index fbafbda..0000000 --- a/app/Mail/Customer/CustomerPayment.php +++ /dev/null @@ -1,45 +0,0 @@ -renderFromTemplate('customer_payment', $this->vars())['subject'], - ); - } - - public function content(): Content - { - return new Content( - htmlString: $this->renderFromTemplate('customer_payment', $this->vars())['html'], - ); - } - - public function attachments(): array - { - return []; - } - - private function vars(): array - { - return [ - 'company_name' => $this->data['company_name'] ?? '', - 'company_logo' => $this->data['company_logo'] ?? '', - ]; - } -} diff --git a/app/Mail/Customer/OrderPaymentSuccess.php b/app/Mail/Customer/OrderPaymentSuccess.php deleted file mode 100644 index 94bc5b8..0000000 --- a/app/Mail/Customer/OrderPaymentSuccess.php +++ /dev/null @@ -1,81 +0,0 @@ -data = $data; - } - - public function envelope(): Envelope - { - return new Envelope( - subject: $this->renderFromTemplate('order_payment_success', $this->vars())['subject'], - ); - } - - public function content(): Content - { - return new Content( - htmlString: $this->renderFromTemplate('order_payment_success', $this->vars())['html'], - ); - } - - public function attachments(): array - { - return []; - } - - private function vars(): array - { - $vmRows = ''; - foreach ($this->data['vm_results'] ?? [] as $vm) { - $rows = ''; - if (!empty($vm['ip'])) { - $rows .= '
🌐 IP Address' . $vm['ip'] . '
'; - } - if (!empty($vm['username'])) { - $rows .= '
👤 Username' . $vm['username'] . '
'; - } - if (!empty($vm['password'])) { - $rows .= '
🔑 Password' . $vm['password'] . '
'; - } - if (!empty($vm['os'])) { - $rows .= '
💿 Hệ điều hành' . $vm['os'] . '
'; - } - $vmRows .= '
' . $rows . '
'; - } - $vmBlock = $vmRows ? '' . $vmRows : ''; - - $addonRows = ''; - foreach ($this->data['addons_summary'] ?? [] as $addon) { - $addonRows .= '' . $addon['name'] . '' . $addon['qty'] . '' . number_format((float) $addon['subtotal'], 0, ',', '.') . ' VNĐ'; - } - $addonBlock = $addonRows - ? '' . $addonRows . '
Dịch vụ addonSố lượngThành tiền
' - : ''; - - return [ - 'customer_name' => $this->data['customer_name'] ?? 'Khách hàng', - 'order_num' => $this->data['order_num'] ?? 'N/A', - 'amount_formatted'=> number_format((float) ($this->data['amount'] ?? 0), 0, ',', '.'), - 'vm_block' => $vmBlock, - 'addon_block' => $addonBlock, - 'support_email' => config('mail.support_email', 'support@example.com'), - 'support_phone' => config('mail.support_phone', '1900-xxxx'), - ]; - } -} diff --git a/app/Mail/CustomerRegister.php b/app/Mail/CustomerRegister.php deleted file mode 100644 index 28fb67a..0000000 --- a/app/Mail/CustomerRegister.php +++ /dev/null @@ -1,46 +0,0 @@ -renderFromTemplate('customer_signup_verify', $this->vars())['subject'], - ); - } - - public function content(): Content - { - return new Content( - htmlString: $this->renderFromTemplate('customer_signup_verify', $this->vars())['html'], - ); - } - - public function attachments(): array - { - return []; - } - - private function vars(): array - { - return [ - 'company_name' => $this->data['company_name'] ?? '', - 'company_logo' => $this->data['company_logo'] ?? '', - 'link_verify' => $this->data['link_verify'] ?? '', - ]; - } -} diff --git a/app/Mail/PasswordReset.php b/app/Mail/PasswordReset.php deleted file mode 100644 index 29e00ff..0000000 --- a/app/Mail/PasswordReset.php +++ /dev/null @@ -1,45 +0,0 @@ -renderFromTemplate('customer_password_reset', $this->vars())['subject'], - ); - } - - public function content(): Content - { - return new Content( - htmlString: $this->renderFromTemplate('customer_password_reset', $this->vars())['html'], - ); - } - - public function attachments(): array - { - return []; - } - - private function vars(): array - { - return [ - 'company_name' => $this->data['company_name'] ?? '', - 'company_logo' => $this->data['company_logo'] ?? '', - 'password' => $this->data['password'] ?? '', - ]; - } -} diff --git a/app/Mail/VmPasswordResetSuccess.php b/app/Mail/VmPasswordResetSuccess.php deleted file mode 100644 index cc112d1..0000000 --- a/app/Mail/VmPasswordResetSuccess.php +++ /dev/null @@ -1,42 +0,0 @@ -renderFromTemplate('vm_password_reset_success', $this->vars())['subject'], - ); - } - - public function content(): Content - { - return new Content( - htmlString: $this->renderFromTemplate('vm_password_reset_success', $this->vars())['html'], - ); - } - - private function vars(): array - { - return [ - 'vm_name' => $this->vmName, - 'new_password' => $this->newPassword, - ]; - } -} diff --git a/app/Models/ActivityLog.php b/app/Models/ActivityLog.php index f5b9bf6..5dffb6b 100644 --- a/app/Models/ActivityLog.php +++ b/app/Models/ActivityLog.php @@ -38,13 +38,4 @@ class ActivityLog extends Model $model->created_at = now(); }); } - - public function causer() - { - return match ($this->causer_type) { - 'admin' => $this->belongsTo(User::class, 'causer_id'), - 'customer' => $this->belongsTo(Customer::class, 'causer_id'), - default => null, - }; - } } diff --git a/app/Models/Banned.php b/app/Models/Banned.php deleted file mode 100644 index 8fa8b87..0000000 --- a/app/Models/Banned.php +++ /dev/null @@ -1,18 +0,0 @@ -product_id) { - $product = Product::find($this->product_id); - if ($product) { - return $product; - } - } - - // Legacy flow: fallback to old system based on type_product - return match ($this->type_product) { - 3 => $this->belongsTo(Hosting::class, 'product_id'), - 4 => $this->belongsTo(VPS::class, 'product_id'), - default => null, - }; - } - - /** - * Relationship: Cart belongs to Product (new system) - */ - public function productNew() - { - return $this->belongsTo(Product::class, 'product_id'); - } - - /** - * Relationship: Cart belongs to Customer - */ - public function customer() - { - return $this->belongsTo(Customer::class, 'customer_id'); - } - - /** - * Relationship: Cart has pricing info - */ - public function pricing() - { - return $this->belongsTo(ProductPricings::class, 'product_pricing_id'); - } - - /** - * Relationship: Cart has many addons - */ - public function addons() - { - return $this->hasMany(CartAddon::class, 'cart_id'); - } - - /** - * Get total price including addons - */ - public function getTotalPriceAttribute() - { - $basePrice = $this->price * $this->quantity; - $addonsPrice = $this->addons->sum('price'); - return $basePrice + $addonsPrice; - } -} diff --git a/app/Models/CartAddon.php b/app/Models/CartAddon.php deleted file mode 100644 index 233fad5..0000000 --- a/app/Models/CartAddon.php +++ /dev/null @@ -1,36 +0,0 @@ -belongsTo(Cart::class, 'cart_id'); - } - - /** - * Relationship: CartAddon belongs to Addon - */ - public function addon() - { - return $this->belongsTo(Addon::class, 'addon_id'); - } -} diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 140d21e..94f2bba 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -80,15 +80,6 @@ class Customer extends Authenticatable implements JWTSubject 'gender', ]; - public function updatableFill(array $data): self - { - $data = array_intersect_key($data, array_flip($this->updatable)); - foreach ($data as $key => $value) { - $this->{$key} = $value; - } - return $this; - } - /** * Appends for API */ @@ -112,7 +103,6 @@ class Customer extends Authenticatable implements JWTSubject 'remember_token', ]; - /** * Quan hệ với ModuleHosting (Hosting). */ @@ -137,19 +127,6 @@ class Customer extends Authenticatable implements JWTSubject return $this->hasMany(ModuleVmware::class); } - /** - * Quan hệ N-N với DnsDomain (DNS Manager) qua bảng pivot dns_domain_user. - */ - public function dnsDomain() - { - return $this->belongsToMany( - DnsDomain::class, - 'dns_domain_user', - 'user_id', - 'domain_id', - )->withTimestamps(); - } - /** * Quan hệ với Service (tbl.services). */ @@ -222,22 +199,6 @@ class Customer extends Authenticatable implements JWTSubject return $this->hasMany(Log::class); } - /** - * Quan hệ với ModuleSnapshotLimit (Snapshot). - */ - public function snapShot() - { - return $this->hasOne(ModuleSnapshotLimit::class); - } - public function getStatus() - { - return match ($this->attributes['status']) { - 1 => 'Actived', - 2 => 'De-actived', - default => 'Banned' - }; - } - public function getEmailMarketingBoolAttribute() { return (bool) $this->email_marketing; @@ -248,7 +209,6 @@ class Customer extends Authenticatable implements JWTSubject return (bool) $this->receive_deposit; } - public function getPointAttribute() { return (string) floor($this->total / 100); diff --git a/app/Models/CustomerLog.php b/app/Models/CustomerLog.php deleted file mode 100644 index bf6d8ff..0000000 --- a/app/Models/CustomerLog.php +++ /dev/null @@ -1,20 +0,0 @@ -belongsTo(DnsDomain::class, 'dns_manager_domain_id', 'id'); - } } diff --git a/app/Models/DnsManagerUser.php b/app/Models/DnsManagerUser.php deleted file mode 100644 index 56dab71..0000000 --- a/app/Models/DnsManagerUser.php +++ /dev/null @@ -1,28 +0,0 @@ -belongsTo(Customer::class, 'user_id', 'id'); - } - - public function domain() - { - return $this->belongsTo(DnsDomain::class, 'domain_id', 'id'); - } -} diff --git a/app/Models/DnsServer.php b/app/Models/DnsServer.php index ef25567..e7ebc21 100644 --- a/app/Models/DnsServer.php +++ b/app/Models/DnsServer.php @@ -24,15 +24,6 @@ class DnsServer extends Model 'type', ]; - public function getStatus(): string - { - return match ($this->attributes['status']) { - 'activated' => 'Activated', - 'Stopped' => 'Stopped', - default => 'Unknown', - }; - } - public function servesHosting(): bool { $type = $this->attributes['type'] ?? ServerType::DOMAIN->value; diff --git a/app/Models/Domain.php b/app/Models/Domain.php index 8b2dcc9..829f33e 100644 --- a/app/Models/Domain.php +++ b/app/Models/Domain.php @@ -21,8 +21,4 @@ class Domain extends Model 'type_id', 'status' ]; - public function type() - { - return $this->belongsTo(\App\Models\Type::class); - } } diff --git a/app/Models/Group.php b/app/Models/Group.php deleted file mode 100644 index b8be71f..0000000 --- a/app/Models/Group.php +++ /dev/null @@ -1,37 +0,0 @@ -belongsTo(\App\Models\Type::class); - } - - public function hostings() - { - return $this->hasMany(\App\Models\Hosting::class); - } - - public function vps() - { - return $this->hasMany(\App\Models\VPS::class); - } - -} diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index dd63c63..e147ce9 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -58,27 +58,11 @@ class Invoice extends Model return $this->belongsTo(Service::class, 'service_id'); } - public function payment() { return $this->belongsTo(Payment::class, 'payment_method', 'id'); } - // ------------------------------------------------------------------------- - // Helpers - // ------------------------------------------------------------------------- - - public function getStatus(): string - { - return match ($this->status) { - 1 => 'Chưa thanh toán', - 2 => 'Đã thanh toán', - 3 => 'Đã hủy', - 4 => 'Quá hạn', - default => 'Không xác định', - }; - } - public function getStatusLabelAttribute(): string { return InvoiceStatus::from($this->status)->label(); diff --git a/app/Models/InvoiceAddons.php b/app/Models/InvoiceAddons.php deleted file mode 100644 index a1cff46..0000000 --- a/app/Models/InvoiceAddons.php +++ /dev/null @@ -1,25 +0,0 @@ -belongsTo(InvoiceItem::class, 'invoice_item_id'); - } -} diff --git a/app/Models/IpPool.php b/app/Models/IpPool.php index e3f9518..8edab25 100644 --- a/app/Models/IpPool.php +++ b/app/Models/IpPool.php @@ -78,11 +78,6 @@ class IpPool extends Model return $this->belongsTo(Service::class, 'service_id'); } - public function virtualMachines() - { - return $this->hasMany(VirtualMachines::class, 'ip_pool_id'); - } - public function contacts() { return $this->hasMany(IpPoolContact::class); diff --git a/app/Models/IpPoolContact.php b/app/Models/IpPoolContact.php index eeae797..e87eae6 100644 --- a/app/Models/IpPoolContact.php +++ b/app/Models/IpPoolContact.php @@ -7,9 +7,4 @@ use Illuminate\Database\Eloquent\Model; class IpPoolContact extends Model { protected $fillable = ['ip_pool_id', 'email', 'type']; - - public function ipPool() - { - return $this->belongsTo(IpPool::class); - } } diff --git a/app/Models/Mail.php b/app/Models/Mail.php index b340e7f..f9682cd 100644 --- a/app/Models/Mail.php +++ b/app/Models/Mail.php @@ -43,9 +43,4 @@ class Mail extends Model { return $this->belongsTo(Template::class, 'template_id'); } - - public function campaign() - { - return $this->belongsTo(MailCampaign::class, 'campaign_id'); - } } diff --git a/app/Models/ModuleDatacenter.php b/app/Models/ModuleDatacenter.php index 029a3e1..1b893ba 100644 --- a/app/Models/ModuleDatacenter.php +++ b/app/Models/ModuleDatacenter.php @@ -22,9 +22,4 @@ class ModuleDatacenter extends Model { return $this->belongsTo(\App\Models\vCenter::class,'vcenter_id','id'); } - - public function hosts() - { - return $this->hasMany(\App\Models\ModuleHost::class, 'module_datacenter_id', 'id'); - } } diff --git a/app/Models/ModuleDnsServer.php b/app/Models/ModuleDnsServer.php index 9df3ff7..86ecef4 100644 --- a/app/Models/ModuleDnsServer.php +++ b/app/Models/ModuleDnsServer.php @@ -18,11 +18,4 @@ class ModuleDnsServer extends Model 'counter', 'status', ]; - public function getStatus() - { - return match ($this->status) { - 1 => 'Active', - default => 'No Active' - }; - } } diff --git a/app/Models/ModuleHosting.php b/app/Models/ModuleHosting.php index 84b0e3c..2da45c4 100644 --- a/app/Models/ModuleHosting.php +++ b/app/Models/ModuleHosting.php @@ -41,15 +41,6 @@ class ModuleHosting extends Model { return $this->belongsTo(\App\Models\ModuleHostingServer::class,'module_server_id','id'); } - - public function getStatus() - { - return match ($this->attributes['status']){ - 1 => 'Active', - 2 => 'Cancel', - default => 'Pending' - }; - } public function scopeIsActive(Builder $query) { $query->where('active', 1); diff --git a/app/Models/ModuleHostingServer.php b/app/Models/ModuleHostingServer.php index bb5ff4e..c695157 100644 --- a/app/Models/ModuleHostingServer.php +++ b/app/Models/ModuleHostingServer.php @@ -19,12 +19,4 @@ class ModuleHostingServer extends Model 'counter', 'status' ]; - public function getStatus() - { - return match ($this->status) { - 1 => 'Active', - default => 'No Active' - }; - } - } diff --git a/app/Models/ModuleIpAddress.php b/app/Models/ModuleIpAddress.php index 4333208..6a0b28b 100644 --- a/app/Models/ModuleIpAddress.php +++ b/app/Models/ModuleIpAddress.php @@ -36,11 +36,4 @@ class ModuleIpAddress extends Model { $query->where('status',0); } - public function getStatus() - { - return match($this->attributes['status']) { - 1 => 'Assigned', - default => 'Free' - }; - } } diff --git a/app/Models/ModuleNetwork.php b/app/Models/ModuleNetwork.php index 138b799..83dc7db 100644 --- a/app/Models/ModuleNetwork.php +++ b/app/Models/ModuleNetwork.php @@ -29,13 +29,4 @@ class ModuleNetwork extends Model { return $this->belongsTo(\App\Models\vCenter::class, 'vcenter_id', 'id'); } - public function ipAddresses() - { - return $this->hasMany(\App\Models\ModuleIpAddress::class, 'module_network_id'); - } - - public function ipPools() - { - return $this->hasMany(\App\Models\IpPool::class, 'module_network_id'); - } } diff --git a/app/Models/ModuleVmware.php b/app/Models/ModuleVmware.php index afa81a8..791c0ad 100644 --- a/app/Models/ModuleVmware.php +++ b/app/Models/ModuleVmware.php @@ -86,15 +86,6 @@ class ModuleVmware extends Model return $this->belongsTo(\App\Models\Service::class, 'service_id'); } - public function getStatus() - { - return match ($this->status) { - 1 => 'Active', - 2 => 'Cancel', - default => 'Pending', - }; - } - /** * `virtual_machine_id`/`cpu`/`ram_mb` are only written when a service is * created directly against a known vCenter inventory row (admin "create diff --git a/app/Models/OrderAddon.php b/app/Models/OrderAddon.php deleted file mode 100644 index 54969aa..0000000 --- a/app/Models/OrderAddon.php +++ /dev/null @@ -1,25 +0,0 @@ -belongsTo(OrderItem::class, 'order_item_id'); - } -} diff --git a/app/Models/OrderItem.php b/app/Models/OrderItem.php index fd2fc33..74b188a 100644 --- a/app/Models/OrderItem.php +++ b/app/Models/OrderItem.php @@ -4,7 +4,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsTo; class OrderItem extends Model { diff --git a/app/Models/Os.php b/app/Models/Os.php index 577767f..59722ea 100644 --- a/app/Models/Os.php +++ b/app/Models/Os.php @@ -52,13 +52,6 @@ class Os extends Model { return $this->belongsTo(\App\Models\ModuleFolder::class, 'folder_id'); } - public function getStatus() - { - return match ($this->attributes['status']){ - 1 => 'Active', - default => 'De-Active' - }; - } /** * Many-to-many relationship với Products (new table: os_product) */ diff --git a/app/Models/OsGroup.php b/app/Models/OsGroup.php index f9cf243..ffc8aad 100644 --- a/app/Models/OsGroup.php +++ b/app/Models/OsGroup.php @@ -26,11 +26,4 @@ class OsGroup extends Model { return $this->hasMany(\App\Models\Os::class); } - public function getStatus() - { - return match ($this->attributes['status']){ - 1 => 'Active', - default => 'De-Active' - }; - } } diff --git a/app/Models/Payment.php b/app/Models/Payment.php index 05df2f6..46557f2 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -29,11 +29,4 @@ class Payment extends Model { return $this->belongsTo(\App\Models\Order::class); } - public function getStatus() - { - return match ($this->attributes['status']) { - 1 => 'Active', - default => 'InActive' - }; - } } diff --git a/app/Models/Product.php b/app/Models/Product.php index d720c31..20e045f 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -74,11 +74,6 @@ class Product extends Model return $this->belongsTo(ProductCategories::class, 'product_categories_id'); } - public function servicePlan() - { - return $this->belongsTo(ServicePlan::class); - } - /** * Relationship: Product has many OS versions (pivot table: os_product) */ @@ -97,16 +92,6 @@ class Product extends Model ->withTimestamps(); } - /** - * Relationship: Product has many Control Panels (for Hosting) - */ - public function controlPanels() - { - return $this->belongsToMany(ControlPanel::class, 'product_control_panel') - ->withPivot(['price_modifier', 'is_default']) - ->withTimestamps(); - } - /** * Relationship: Product has many pricings */ @@ -148,16 +133,6 @@ class Product extends Model return $this->belongsTo(ModuleIpAddress::class, 'module_ip_address'); } - public function provisioningHost() - { - return $this->belongsTo(\App\Models\ModuleHost::class, 'module_host_id'); - } - - public function provisioningDatastore() - { - return $this->belongsTo(\App\Models\ModuleDatastore::class, 'module_datastore_id'); - } - public function addons() { return $this->hasMany(Addon::class, 'type', 'product_type') diff --git a/app/Models/ProductCategories.php b/app/Models/ProductCategories.php index a486f31..e503c0a 100644 --- a/app/Models/ProductCategories.php +++ b/app/Models/ProductCategories.php @@ -68,9 +68,4 @@ class ProductCategories extends Model } return ProductTypeEnums::tryFrom($this->type)?->label(); } - - public static function getTypeOptions(): array - { - return array_map(fn($case) => $case->value, ProductTypeEnums::cases()); - } } diff --git a/app/Models/ProductVMTemplates.php b/app/Models/ProductVMTemplates.php deleted file mode 100644 index cd9e704..0000000 --- a/app/Models/ProductVMTemplates.php +++ /dev/null @@ -1,11 +0,0 @@ -belongsTo(AdministrativeUnit::class, 'administrative_unit_id'); - } - /** * Get the wards for the province. */ diff --git a/app/Models/Service.php b/app/Models/Service.php index 8b44b25..ca96678 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -3,7 +3,6 @@ namespace App\Models; use App\Traits\LogsActivity; -use App\View\Components\Module\OsVersion; use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -116,21 +115,6 @@ class Service extends Model return $this->status === 'active'; } - public function isPending(): bool - { - return $this->status === 'pending'; - } - - public function canRetryProvisioning(): bool - { - return $this->isPending() && $this->retry_requested_at === null; - } - - public function retryRequested(): bool - { - return $this->retry_requested_at !== null; - } - public function isExpired(): bool { return $this->expires_at !== null && $this->expires_at->isPast(); @@ -148,10 +132,4 @@ class Service extends Model default => ucfirst($this->status), }; } - - /** Convenience: get an addon value by type. */ - public function getAddon(string $type): ?string - { - return $this->addons->firstWhere('addon_type', $type)?->addon_value; - } } diff --git a/app/Models/ServicePlanPlesk.php b/app/Models/ServicePlanPlesk.php index 3595d98..6db8019 100644 --- a/app/Models/ServicePlanPlesk.php +++ b/app/Models/ServicePlanPlesk.php @@ -13,9 +13,4 @@ class ServicePlanPlesk extends Model 'plesk_plan_name', 'note', ]; - - public function servicePlan() - { - return $this->belongsTo(ServicePlan::class); - } } diff --git a/app/Models/VirtualMachines.php b/app/Models/VirtualMachines.php index f3a25fc..9d92a02 100644 --- a/app/Models/VirtualMachines.php +++ b/app/Models/VirtualMachines.php @@ -37,11 +37,6 @@ class VirtualMachines extends Model return $this->belongsTo(ModuleHost::class, 'host_id'); } - public function ipPool() - { - return $this->belongsTo(IpPool::class, 'ip_pool_id'); - } - public function vmware() { return $this->hasOne(ModuleVmware::class, 'vmware_id', 'vm_id'); diff --git a/app/Models/VmProvisionLock.php b/app/Models/VmProvisionLock.php index 794bd15..748613d 100644 --- a/app/Models/VmProvisionLock.php +++ b/app/Models/VmProvisionLock.php @@ -7,9 +7,4 @@ use Illuminate\Database\Eloquent\Model; class VmProvisionLock extends Model { protected $fillable = ['customer_id', 'order_item_id', 'status']; - - public static function isRunning(int $customerId): bool - { - return static::where('customer_id', $customerId)->where('status', 'running')->exists(); - } } diff --git a/app/Models/Ward.php b/app/Models/Ward.php index 83af1dd..d3f70fd 100644 --- a/app/Models/Ward.php +++ b/app/Models/Ward.php @@ -67,13 +67,5 @@ class Ward extends Model { return $this->belongsTo(Province::class, 'province_code', 'code'); } - - /** - * Get the administrative unit that owns the ward. - */ - public function administrativeUnit() - { - return $this->belongsTo(AdministrativeUnit::class, 'administrative_unit_id'); - } } diff --git a/app/Models/vCenter.php b/app/Models/vCenter.php index 6b101f7..9ee450f 100644 --- a/app/Models/vCenter.php +++ b/app/Models/vCenter.php @@ -24,11 +24,4 @@ class vCenter extends Model { return $this->hasMany(\App\Models\ModuleDatacenter::class, 'vcenter_id'); } - public function getStatus() - { - return match ($this->attributes['status']) { - 1 => 'Active', - default => 'No Active' - }; - } } diff --git a/app/Notifications/JobsSuccess.php b/app/Notifications/JobsSuccess.php deleted file mode 100644 index 4a33377..0000000 --- a/app/Notifications/JobsSuccess.php +++ /dev/null @@ -1,61 +0,0 @@ -line('The introduction to the notification.') - ->action('Notification Action', url('/')) - ->line('Thank you for using our application!'); - } - - /** - * Get the array representation of the notification. - * - * @param mixed $notifiable - * @return array - */ - public function toArray($notifiable) - { - return [ - // - ]; - } -} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index e8122f8..ed3f334 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -30,25 +30,6 @@ class AppServiceProvider extends ServiceProvider return new Helper(); }); - // Register Whois Repository binding - $this->app->bind( - \App\Repositories\Whois\WhoisRepositoryInterface::class, - \App\Repositories\Whois\CachedWhoisRepository::class - ); - - // Admin API Repositories - $this->app->bind( - \App\Repositories\Admin\Customer\AdminCustomerRepositoryInterface::class, - \App\Repositories\Admin\Customer\AdminCustomerRepository::class - ); - $this->app->bind( - \App\Repositories\Admin\Order\AdminOrderRepositoryInterface::class, - \App\Repositories\Admin\Order\AdminOrderRepository::class - ); - $this->app->bind( - \App\Repositories\Admin\Invoice\AdminInvoiceRepositoryInterface::class, - \App\Repositories\Admin\Invoice\AdminInvoiceRepository::class - ); $this->app->bind( \App\Repositories\Admin\Product\AdminProductRepositoryInterface::class, \App\Repositories\Admin\Product\AdminProductRepository::class diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 6d9b74a..7f1bb5c 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -6,7 +6,6 @@ use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Mail\Events\MessageSending; -use Illuminate\Support\Facades\Event; use App\Listeners\Mail\CheckMailEnabled; class EventServiceProvider extends ServiceProvider diff --git a/app/Providers/RepositoryProvider.php b/app/Providers/RepositoryProvider.php index 746700f..372ab0b 100644 --- a/app/Providers/RepositoryProvider.php +++ b/app/Providers/RepositoryProvider.php @@ -6,24 +6,10 @@ use App\Repositories\Addon\AddonCachedRepository; use App\Repositories\Addon\AddonRepositoryInterface; use App\Repositories\Admin\ProductCategory\ProductCategoryRepository; use App\Repositories\Admin\ProductCategory\ProductCategoryRepositoryInterface; -use App\Repositories\Banned\BannedCachedRepository; -use App\Repositories\Banned\BannedRepositoryInterface; use App\Repositories\Customer\CustomerCachedRepository; -use App\Repositories\Customer\CustomerLogCachedRepository; -use App\Repositories\Customer\CustomerLogRepositoryInterface; use App\Repositories\Customer\CustomerRepositoryInterface; -use App\Repositories\Discount\DiscountCachedRepository; -use App\Repositories\Discount\DiscountRepositoryInterface; use App\Repositories\Domain\DomainCachedRepository; use App\Repositories\Domain\DomainRepositoryInterface; -use App\Repositories\Domain\DomainTypeCachedRepository; -use App\Repositories\Domain\DomainTypeRepositoryInterface; -use App\Repositories\File\FileCachedRepository; -use App\Repositories\File\FileRepositoryInterface; -use App\Repositories\Group\GroupCachedRepository; -use App\Repositories\Group\GroupRepositoryInterface; -use App\Repositories\Hosting\HostingCachedRepository; -use App\Repositories\Hosting\HostingRepositoryInterface; use App\Repositories\Invoice\InvoiceRepository; use App\Repositories\Invoice\InvoiceRepositoryInterface; use App\Repositories\InvoiceAttachment\InvoiceAttachmentRepository; @@ -32,36 +18,16 @@ use App\Repositories\InvoiceItem\InvoiceItemCachedRepository; use App\Repositories\InvoiceItem\InvoiceItemRepositoryInterface; use App\Repositories\Log\LogCachedRepository; use App\Repositories\Log\LogRepositoryInterface; -use App\Repositories\Mail\MailCachedRepository; -use App\Repositories\Mail\MailRepositoryInterface; -use App\Repositories\Menu\MenuCachedRepository; -use App\Repositories\Menu\MenuRepositoryInterface; -use App\Repositories\Module\Datacenter\DatacenterCachedRepository; -use App\Repositories\Module\Datacenter\DatacenterRepositoryInterface; -use App\Repositories\Module\Datastore\DatastoreCachedRepository; -use App\Repositories\Module\Datastore\DatastoreRepositoryInterface; use App\Repositories\Module\DnsServer\DnsServerCachedRepository; use App\Repositories\Module\DnsServer\DnsServerRepositoryInterface; -use App\Repositories\Module\IPPool\IPPoolCachedRepository; -use App\Repositories\Module\IPPool\IPPoolRepositoryInterface; -use App\Repositories\Note\NoteCachedRepository; -use App\Repositories\Note\NoteRepositoryInterface; use App\Repositories\Order\OrderRepository; use App\Repositories\Order\OrderRepositoryInterface; -use App\Repositories\Os\OsCachedRepository; -use App\Repositories\Os\OsRepositoryInterface; -use App\Repositories\OsGroup\OsGroupCachedRepository; -use App\Repositories\OsGroup\OsGroupRepositoryInterface; use App\Repositories\Payment\PaymentCachedReppository; use App\Repositories\Payment\PaymentReppositoryInterface; -use App\Repositories\Post\PostCachedRepository; -use App\Repositories\Post\PostRepositoryInterface; use App\Repositories\Setting\SettingCachedRepository; use App\Repositories\Setting\SettingRepositoryInterface; use App\Repositories\Staff\StaffRepository; use App\Repositories\Staff\StaffRepositoryInterface; -use App\Repositories\Template\TemplateCachedRepository; -use App\Repositories\Template\TemplateRepositoryInterface; use App\Repositories\Ticket\TicketCachedRepository; use App\Repositories\Ticket\TicketRepositoryInterface; use App\Repositories\TicketReply\TicketReplyCachedRepository; @@ -71,12 +37,6 @@ use App\Repositories\Transaction\TransactionRepositoryInterface; use App\Repositories\Service\ServiceRepository; use App\Repositories\Service\ServiceRepositoryInterface; use Illuminate\Support\ServiceProvider; -use App\Repositories\Module\Domain\DomainRepositoryInterface as ModuleDomainRepositoryInterface; -use App\Repositories\Module\Domain\DomainCachedRepository as ModuleDomainCachedRepository; -use App\Repositories\Module\Folder\FolderCachedRepository; -use App\Repositories\Module\Folder\FolderRepositoryInterface; -use App\Repositories\Module\Host\HostCachedRepository; -use App\Repositories\Module\Host\HostRepositoryInterface; use App\Repositories\Module\Hosting\HostingCachedRepository as ModuleHostingCachedRepository; use App\Repositories\Module\Hosting\HostingRepositoryInterface as ModuleHostingRepositoryInterface; @@ -84,30 +44,10 @@ use App\Repositories\Module\HostingServer\HostingServerCachedRepository; use App\Repositories\Module\HostingServer\HostingServerRepositoryInterface; use App\Repositories\Module\HostingAccount\HostingAccountCachedRepository; use App\Repositories\Module\HostingAccount\HostingAccountRepositoryInterface; -use App\Repositories\Module\IpAddress\IpAddressCachedRepository; -use App\Repositories\Module\IpAddress\IpAddressRepositoryInterface; -use App\Repositories\Module\Network\NetworkCachedRepository; -use App\Repositories\Module\Network\NetworkRepositoryInterface; -use App\Repositories\Module\Snapshot\SnapshotCachedRepository; -use App\Repositories\Module\Snapshot\SnapshotLimitCachedRepository; -use App\Repositories\Module\Snapshot\SnapshotLimitRepositoryInterface; -use App\Repositories\Module\Snapshot\SnapshotRepositoryInterface; use App\Repositories\Module\Vmware\VmwareCachedRepository; use App\Repositories\Module\Vmware\VmwareRepositoryInterface; -use App\Repositories\Admin\Product\ProductRepository; -use App\Repositories\Admin\Product\ProductRepositoryInterface; -use App\Repositories\User\UserCachedRepository; -use App\Repositories\User\UserRepositoryInterface; -use App\Repositories\vCenter\vCenterCachedRepository; -use App\Repositories\vCenter\vCenterRepositoryInterface; -use App\Repositories\VPS\VPSCachedRepository; -use App\Repositories\VPS\VPSRepositoryInterface; -use App\Repositories\Module\VirtualMachine\VirtualMachineRepository; -use App\Repositories\Module\VirtualMachine\VirtualMachineRepositoryInterface; use App\Repositories\DnsManager\Domain\DnsDomainRepository; use App\Repositories\DnsManager\Domain\DnsDomainRepositoryInterface; -use App\Repositories\DnsManager\Server\DnsServerRepository as DnsManagerServerRepository; -use App\Repositories\DnsManager\Server\DnsServerRepositoryInterface as DnsManagerServerRepositoryInterface; use App\Repositories\ActivityLog\ActivityLogRepository; use App\Repositories\ActivityLog\ActivityLogRepositoryInterface; use App\Repositories\DnsManager\Record\DnsRecordRepository; @@ -124,26 +64,12 @@ class RepositoryProvider extends ServiceProvider { //Addon $this->app->singleton(AddonRepositoryInterface::class, AddonCachedRepository::class); - //Banned - $this->app->singleton(BannedRepositoryInterface::class, BannedCachedRepository::class); //Customer $this->app->singleton(CustomerRepositoryInterface::class, CustomerCachedRepository::class); //Customer $this->app->singleton(CustomerCachedRepository::class); - //Customer Log - $this->app->singleton(CustomerLogRepositoryInterface::class, CustomerLogCachedRepository::class); - //Discount - $this->app->singleton(DiscountRepositoryInterface::class, DiscountCachedRepository::class); //Domain $this->app->singleton(DomainRepositoryInterface::class, DomainCachedRepository::class); - //Domain Type - $this->app->singleton(DomainTypeRepositoryInterface::class, DomainTypeCachedRepository::class); - //File - $this->app->singleton(FileRepositoryInterface::class, FileCachedRepository::class); - //Group - $this->app->singleton(GroupRepositoryInterface::class, GroupCachedRepository::class); - //Hosting - $this->app->singleton(HostingRepositoryInterface::class, HostingCachedRepository::class); //Invoice $this->app->singleton(InvoiceRepositoryInterface::class, InvoiceRepository::class); //Invoice Attachment @@ -154,28 +80,14 @@ class RepositoryProvider extends ServiceProvider $this->app->singleton(ActivityLogRepositoryInterface::class, ActivityLogRepository::class); //Log $this->app->singleton(LogRepositoryInterface::class, LogCachedRepository::class); - //Mail - $this->app->singleton(MailRepositoryInterface::class, MailCachedRepository::class); - //Note - $this->app->singleton(NoteRepositoryInterface::class, NoteCachedRepository::class); //Order $this->app->singleton(OrderRepositoryInterface::class, OrderRepository::class); - //Os - $this->app->singleton(OsRepositoryInterface::class, OsCachedRepository::class); - // Os Group - $this->app->singleton(OsGroupRepositoryInterface::class, OsGroupCachedRepository::class); // Payment $this->app->singleton(PaymentReppositoryInterface::class, PaymentCachedReppository::class); - // Menu - $this->app->singleton(MenuRepositoryInterface::class, MenuCachedRepository::class); - //Post - $this->app->singleton(PostRepositoryInterface::class, PostCachedRepository::class); //Setting $this->app->singleton(SettingRepositoryInterface::class, SettingCachedRepository::class); //Staff $this->app->singleton(StaffRepositoryInterface::class, StaffRepository::class); - //Template - $this->app->singleton(TemplateRepositoryInterface::class, TemplateCachedRepository::class); //Ticket $this->app->singleton(TicketRepositoryInterface::class, TicketCachedRepository::class); //Ticket Reply @@ -184,49 +96,16 @@ class RepositoryProvider extends ServiceProvider $this->app->singleton(TransactionRepositoryInterface::class, TransactionCachedRepository::class); //Service $this->app->singleton(ServiceRepositoryInterface::class, ServiceRepository::class); - //Module Datacenter - $this->app->singleton(DatacenterRepositoryInterface::class, DatacenterCachedRepository::class); - //Module Datastore - $this->app->singleton(DatastoreRepositoryInterface::class, DatastoreCachedRepository::class); // Module DnsServer $this->app->singleton(DnsServerRepositoryInterface::class, DnsServerCachedRepository::class); - //Module Domain - $this->app->singleton(ModuleDomainRepositoryInterface::class, ModuleDomainCachedRepository::class); - //Module Folder - $this->app->singleton(FolderRepositoryInterface::class, FolderCachedRepository::class); - //Host - $this->app->singleton(HostRepositoryInterface::class, HostCachedRepository::class); //Hosting $this->app->singleton(ModuleHostingRepositoryInterface::class, ModuleHostingCachedRepository::class); //Hosting Server $this->app->singleton(HostingServerRepositoryInterface::class, HostingServerCachedRepository::class); //Hosting Account $this->app->singleton(HostingAccountRepositoryInterface::class, HostingAccountCachedRepository::class); - //IpAddress - $this->app->singleton(IpAddressRepositoryInterface::class, IpAddressCachedRepository::class); - //Network - $this->app->singleton(NetworkRepositoryInterface::class, NetworkCachedRepository::class); - //Snapshot - $this->app->singleton(SnapshotRepositoryInterface::class, SnapshotCachedRepository::class); - //Snapshot Limit - $this->app->singleton(SnapshotLimitRepositoryInterface::class, SnapshotLimitCachedRepository::class); //vmware $this->app->singleton(VmwareRepositoryInterface::class, VmwareCachedRepository::class); - //User - $this->app->singleton(UserRepositoryInterface::class, UserCachedRepository::class); - //vCenter - $this->app->singleton(vCenterRepositoryInterface::class, vCenterCachedRepository::class); - //vps - $this->app->singleton(VPSRepositoryInterface::class, VPSCachedRepository::class); - - //Module IP Pool - $this->app->singleton(IPPoolRepositoryInterface::class, IPPoolCachedRepository::class); - - //Module VirtualMachine - $this->app->singleton(VirtualMachineRepositoryInterface::class, VirtualMachineRepository::class); - - // Product - $this->app->singleton(ProductRepositoryInterface::class, ProductRepository::class); // Product Category $this->app->singleton(ProductCategoryRepositoryInterface::class, ProductCategoryRepository::class); @@ -237,6 +116,15 @@ class RepositoryProvider extends ServiceProvider \App\Repositories\Api\Client\ClientRepository::class, ); + $this->app->singleton( + \App\Repositories\Api\Order\OrderRepositoryInterface::class, + \App\Repositories\Api\Order\OrderRepository::class, + ); + $this->app->singleton( + \App\Repositories\Api\Invoice\InvoiceRepositoryInterface::class, + \App\Repositories\Api\Invoice\InvoiceRepository::class, + ); + // Service API $this->app->singleton( \App\Repositories\Api\Service\ServiceRepositoryInterface::class, @@ -249,8 +137,14 @@ class RepositoryProvider extends ServiceProvider \App\Repositories\Api\ServiceClient\ServiceClientRepository::class, ); - // DNS Manager - $this->app->singleton(DnsManagerServerRepositoryInterface::class, DnsManagerServerRepository::class); + $this->app->singleton( + \App\Repositories\Module\Domain\DomainRepositoryInterface::class, + \App\Repositories\Module\Domain\DomainCachedRepository::class, + ); + $this->app->singleton( + \App\Repositories\DnsManager\Server\DnsServerRepositoryInterface::class, + \App\Repositories\DnsManager\Server\DnsServerRepository::class, + ); $this->app->singleton(DnsDomainRepositoryInterface::class, DnsDomainRepository::class); $this->app->singleton(DnsRecordRepositoryInterface::class, DnsRecordRepository::class); } @@ -262,6 +156,5 @@ class RepositoryProvider extends ServiceProvider */ public function boot() { - // } } diff --git a/app/Providers/ViewServiceProvider.php b/app/Providers/ViewServiceProvider.php deleted file mode 100644 index 9004342..0000000 --- a/app/Providers/ViewServiceProvider.php +++ /dev/null @@ -1,34 +0,0 @@ -composer( - 'frontend.layouts.sidebar', - MenuComposer::class - ); - - view()->composer( - 'frontend.home.index', - PostComposer::class - ); - } -} diff --git a/app/Repositories/Admin/Customer/AdminCustomerRepository.php b/app/Repositories/Admin/Customer/AdminCustomerRepository.php deleted file mode 100644 index 127428b..0000000 --- a/app/Repositories/Admin/Customer/AdminCustomerRepository.php +++ /dev/null @@ -1,38 +0,0 @@ - 1, - 'inactive' => 2, - 'pending' => 0, - 'suspended' => 3, - ]; - - public function paginate(array $filters, int $perPage): LengthAwarePaginator - { - $query = Customer::query()->orderBy('id', 'desc'); - - if ($filter = $filters['filter'] ?? null) { - $query->where(function ($q) use ($filter) { - $q->where('fullname', 'like', "%{$filter}%") - ->orWhere('email', 'like', "%{$filter}%") - ->orWhere('phone', 'like', "%{$filter}%"); - }); - } - - if ($status = $filters['status'] ?? null) { - $statusInt = self::STATUS_MAP[$status] ?? null; - if ($statusInt !== null) { - $query->where('status', $statusInt); - } - } - - return $query->paginate($perPage); - } -} diff --git a/app/Repositories/Admin/Customer/AdminCustomerRepositoryInterface.php b/app/Repositories/Admin/Customer/AdminCustomerRepositoryInterface.php deleted file mode 100644 index 289d2cf..0000000 --- a/app/Repositories/Admin/Customer/AdminCustomerRepositoryInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -orderBy('id', 'desc'); - - if ($filter = $filters['filter'] ?? null) { - $query->where(function ($q) use ($filter) { - $q->where('order_num', 'like', "%{$filter}%") - ->orWhereHas('customer', fn($q2) => - $q2->where('fullname', 'like', "%{$filter}%") - ->orWhere('email', 'like', "%{$filter}%") - ); - }); - } - - if (($status = $filters['status'] ?? null) !== null && $status !== '') { - $query->where('status', (int) $status); - } - - return $query->paginate($perPage); - } -} diff --git a/app/Repositories/Admin/Invoice/AdminInvoiceRepositoryInterface.php b/app/Repositories/Admin/Invoice/AdminInvoiceRepositoryInterface.php deleted file mode 100644 index 840e26e..0000000 --- a/app/Repositories/Admin/Invoice/AdminInvoiceRepositoryInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -orderBy('id', 'desc'); - - if ($filter = $filters['filter'] ?? null) { - $query->where(function ($q) use ($filter) { - $q->where('order_num', 'like', "%{$filter}%") - ->orWhereHas('customer', fn($q2) => - $q2->where('fullname', 'like', "%{$filter}%") - ->orWhere('email', 'like', "%{$filter}%") - ); - }); - } - - if (($status = $filters['status'] ?? null) !== null && $status !== '') { - $query->where('status', (int) $status); - } - - return $query->paginate($perPage); - } -} diff --git a/app/Repositories/Admin/Order/AdminOrderRepositoryInterface.php b/app/Repositories/Admin/Order/AdminOrderRepositoryInterface.php deleted file mode 100644 index 246b5f8..0000000 --- a/app/Repositories/Admin/Order/AdminOrderRepositoryInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -model - ->select($select) - ->with($relationships); - - // if (!empty($data['vcenter_id'])) { - // $query->where('vcenter_id', $data['vcenter_id']); - // } - - if (!empty($withCount)) { - $query->withCount($withCount); - } - - if ($returnQuery) { - return $query; - } - - if (!empty($data['paginate'])) { - return $query->paginate( - $data['per_page'] ?? 15 - ); - } - - return $query->get(); - } -} diff --git a/app/Repositories/Admin/Product/ProductRepositoryInterface.php b/app/Repositories/Admin/Product/ProductRepositoryInterface.php deleted file mode 100644 index d7bf0ba..0000000 --- a/app/Repositories/Admin/Product/ProductRepositoryInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -model->create($data); } - public function getAll() - { - return $this->model->all(); - } - public function find($id) { return $this->model->find($id); @@ -63,9 +58,4 @@ abstract class BaseApiRepository implements BaseRepositoryInterface return false; } - - public function getCount() - { - return $this->model->count('id'); - } } diff --git a/app/Repositories/Api/Client/ClientRepository.php b/app/Repositories/Api/Client/ClientRepository.php index 0e8dbe1..739f464 100644 --- a/app/Repositories/Api/Client/ClientRepository.php +++ b/app/Repositories/Api/Client/ClientRepository.php @@ -11,12 +11,4 @@ class ClientRepository extends BaseApiRepository implements ClientRepositoryInte { return Customer::class; } - - /** - * Tìm khách hàng theo email - */ - public function findByEmail(string $email) - { - return $this->model->where('email', $email)->first(); - } } diff --git a/app/Repositories/Api/Client/ClientRepositoryInterface.php b/app/Repositories/Api/Client/ClientRepositoryInterface.php index 94a7858..12f87f7 100644 --- a/app/Repositories/Api/Client/ClientRepositoryInterface.php +++ b/app/Repositories/Api/Client/ClientRepositoryInterface.php @@ -6,8 +6,4 @@ use App\Repositories\BaseRepositoryInterface; interface ClientRepositoryInterface extends BaseRepositoryInterface { - /** - * Tìm khách hàng theo email - */ - public function findByEmail(string $email); } diff --git a/app/Repositories/Api/Invoice/InvoiceRepository.php b/app/Repositories/Api/Invoice/InvoiceRepository.php index c016528..3936731 100644 --- a/app/Repositories/Api/Invoice/InvoiceRepository.php +++ b/app/Repositories/Api/Invoice/InvoiceRepository.php @@ -8,7 +8,6 @@ use App\Models\Invoice; use App\Models\InvoiceItem; use App\Models\Order; use App\Repositories\Api\BaseApiRepository; -use Illuminate\Contracts\Pagination\LengthAwarePaginator; class InvoiceRepository extends BaseApiRepository implements InvoiceRepositoryInterface { @@ -17,18 +16,6 @@ class InvoiceRepository extends BaseApiRepository implements InvoiceRepositoryIn return Invoice::class; } - // ------------------------------------------------------------------------- - // getInvoice - // ------------------------------------------------------------------------- - - public function getInvoice(int $limit = 10, int $customerId = 0): LengthAwarePaginator - { - return $this->model - ->where('customer_id', $customerId) - ->orderByDesc('created_at') - ->paginate($limit); - } - // ------------------------------------------------------------------------- // createFromOrder // ------------------------------------------------------------------------- @@ -97,5 +84,4 @@ class InvoiceRepository extends BaseApiRepository implements InvoiceRepositoryIn // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- - } diff --git a/app/Repositories/Api/Invoice/InvoiceRepositoryInterface.php b/app/Repositories/Api/Invoice/InvoiceRepositoryInterface.php index a786114..ea1b7ee 100644 --- a/app/Repositories/Api/Invoice/InvoiceRepositoryInterface.php +++ b/app/Repositories/Api/Invoice/InvoiceRepositoryInterface.php @@ -5,15 +5,9 @@ namespace App\Repositories\Api\Invoice; use App\Models\Invoice; use App\Models\Order; use App\Repositories\BaseRepositoryInterface; -use Illuminate\Contracts\Pagination\LengthAwarePaginator; interface InvoiceRepositoryInterface extends BaseRepositoryInterface { - /** - * Paginated invoice list for a customer. - */ - public function getInvoice(int $limit, int $customerId): LengthAwarePaginator; - /** * Create an invoice (+ invoice_items) from a completed Order. */ diff --git a/app/Repositories/Api/Order/OrderRepository.php b/app/Repositories/Api/Order/OrderRepository.php index b6263f1..6c748a5 100644 --- a/app/Repositories/Api/Order/OrderRepository.php +++ b/app/Repositories/Api/Order/OrderRepository.php @@ -2,7 +2,6 @@ namespace App\Repositories\Api\Order; -use App\Enums\Invoice\InvoiceStatus; use App\Enums\Order\OrderStatus; use App\Models\Order; use App\Models\OrderItem; @@ -30,28 +29,6 @@ class OrderRepository extends BaseApiRepository implements OrderRepositoryInterf ); } - // ------------------------------------------------------------------------- - // processPayment - // ------------------------------------------------------------------------- - - public function processPayment(Order $order, string $method): Order - { - $order->update([ - 'status' => OrderStatus::ACTIVE->value, - 'payment_method' => $method, - ]); - - if ($order->invoice) { - $order->invoice->update([ - 'status' => InvoiceStatus::PAID->value, - 'payment_method' => $method, - 'paid_at' => now(), - ]); - } - - return $order->fresh(['orderItems', 'invoice', 'customer']); - } - // ------------------------------------------------------------------------- // hasPendingOrder // ------------------------------------------------------------------------- diff --git a/app/Repositories/Api/Order/OrderRepositoryInterface.php b/app/Repositories/Api/Order/OrderRepositoryInterface.php index 1960610..9a01ca8 100644 --- a/app/Repositories/Api/Order/OrderRepositoryInterface.php +++ b/app/Repositories/Api/Order/OrderRepositoryInterface.php @@ -15,11 +15,6 @@ interface OrderRepositoryInterface extends BaseRepositoryInterface */ public function createItem(int $orderId, array $data): OrderItem; - /** - * Mark order as completed, update the related invoice as paid. - */ - public function processPayment(Order $order, string $method): Order; - /** * Return true if the customer has any pending orders. */ @@ -34,4 +29,6 @@ interface OrderRepositoryInterface extends BaseRepositoryInterface * Find an order by order_num, scoped to the owning customer. */ public function findByOrderNum(string $orderNum, int $customerId): ?Order; + + public function findForCustomer(int $id, int $customerId): ?Order; } diff --git a/app/Repositories/Banned/BannedCachedRepository.php b/app/Repositories/Banned/BannedCachedRepository.php deleted file mode 100644 index e77b15b7..0000000 --- a/app/Repositories/Banned/BannedCachedRepository.php +++ /dev/null @@ -1,19 +0,0 @@ -bannedRepository->getModel(); - } -} diff --git a/app/Repositories/Banned/BannedRepository.php b/app/Repositories/Banned/BannedRepository.php deleted file mode 100644 index c0ea621..0000000 --- a/app/Repositories/Banned/BannedRepository.php +++ /dev/null @@ -1,14 +0,0 @@ -cache->remember($key, fn () => parent::find($id), CacheServiceInterface::TTL_LONG); } - public function getAll() - { - $key = CacheKey::build($this->getModuleName(), 'all'); - - return $this->cache->remember($key, fn () => parent::getAll(), CacheServiceInterface::TTL_LONG); - } - - public function getCount() - { - $key = CacheKey::build($this->getModuleName(), 'count'); - - return $this->cache->remember($key, fn () => parent::getCount(), CacheServiceInterface::TTL_LONG); - } - public function create($data = []) { $result = parent::create($data); diff --git a/app/Repositories/BaseCachedRepositoryInterface.php b/app/Repositories/BaseCachedRepositoryInterface.php deleted file mode 100644 index abd533a..0000000 --- a/app/Repositories/BaseCachedRepositoryInterface.php +++ /dev/null @@ -1,25 +0,0 @@ -model->create($data); } - - public function getAll() - { - return $this->model->all(); - } public function find($id) { return $this->model->find($id); @@ -59,8 +54,4 @@ abstract class BaseRepository implements BaseRepositoryInterface return false; } - public function getCount() - { - return $this->model->count('id'); - } } diff --git a/app/Repositories/BaseRepositoryInterface.php b/app/Repositories/BaseRepositoryInterface.php index 76695ab..6b86862 100644 --- a/app/Repositories/BaseRepositoryInterface.php +++ b/app/Repositories/BaseRepositoryInterface.php @@ -4,10 +4,8 @@ namespace App\Repositories; interface BaseRepositoryInterface { public function find($id); - public function getAll(); public function create(array $data = []); public function update(int $id = 0,array $data = []); public function insert(array $data = []); public function delete($id); - public function getCount(); } \ No newline at end of file diff --git a/app/Repositories/Customer/CustomerLogCachedRepository.php b/app/Repositories/Customer/CustomerLogCachedRepository.php deleted file mode 100644 index a5c181c..0000000 --- a/app/Repositories/Customer/CustomerLogCachedRepository.php +++ /dev/null @@ -1,19 +0,0 @@ -customerLogRepository->getModel(); - } -} diff --git a/app/Repositories/Customer/CustomerLogRepository.php b/app/Repositories/Customer/CustomerLogRepository.php deleted file mode 100644 index 25650eb..0000000 --- a/app/Repositories/Customer/CustomerLogRepository.php +++ /dev/null @@ -1,14 +0,0 @@ -discountRepository->getModel(); - } - public function filter(array $data = [], array $relationships = [], array $withCount = [], array $select = ['*']) - { - return $this->rememberFilter( - compact('data', 'relationships', 'withCount', 'select'), - fn() => $this->discountRepository->filter($data, $relationships, $withCount, $select) - ); - } - public function findBy(string $key = '', string $value = '') - { - if (empty($value) || empty($key)) { - return false; - } - $cacheKey = CacheKey::build($this->getModuleName(), 'find-by', ['key' => $key, 'value' => $value]); - return $this->cache->remember($cacheKey, function () use ($key, $value) { - return $this->model->where($key, $value)->first(); - }); - } -} diff --git a/app/Repositories/Discount/DiscountRepository.php b/app/Repositories/Discount/DiscountRepository.php deleted file mode 100644 index ae61d6b..0000000 --- a/app/Repositories/Discount/DiscountRepository.php +++ /dev/null @@ -1,72 +0,0 @@ -model; - if (count($relationships) > 0) { - $model = $model->with($relationships); - } - $model = $model->select($select); - - if (isset($data['start']) && ! empty($data['start'])) { - $model = $model->whereDate("start", ">=", $data['start']); - } - if (isset($data['end'])) { - $model = $model->where(function ($query) use ($data) { - if (isset($data['end']['condition'])) { - switch (strtoupper($data['end']['condition'])) { - case "IN": - $query->whereIn($data['end']['key'], $data['end']['value']); - break; - case "NotIn": - $query->whereNotIn($data['end']['key'], $data['end']['value']); - break; - default: - $query->where($data['end']['key'], $data['end']['condition'], $data['end']['value']); - break; - } - } else { - $query->where($data['end']['key'], $data['end']['value']); - } - }); - } - if (isset($data['type']) && ! empty($data['type'])) { - $model = $model->where("type", $data['phone']); - } - if (isset($data['status']) && ! empty($data['status']) && $data['status'] != 'full') { - $model = $model->where("status", match ($data['status']) { - 'active' => 1, - 'de_active' => 0, - 'closed' => 2, - }); - } - - if (count($withCount) > 0) { - $model = $model->withCount($withCount); - } - if (isset($data['page'])) { - - return $model->paginate(15); - } - return $model->get(); - } - public function findBy(string $key, string $value) - { - if (empty($value) || empty($key)) { - return false; - } - return $this->model->where($key, $value)->first(); - } -} diff --git a/app/Repositories/Discount/DiscountRepositoryInterface.php b/app/Repositories/Discount/DiscountRepositoryInterface.php deleted file mode 100644 index 8d33a2d..0000000 --- a/app/Repositories/Discount/DiscountRepositoryInterface.php +++ /dev/null @@ -1,9 +0,0 @@ -model->where('domain_name', $domain)->first(); - } - public function filter(array $data = [], array $relationships = [], string $order = 'id', array $select = ['*']) { $model = $this->model; @@ -76,14 +71,4 @@ class DnsDomainRepository extends BaseRepository implements DnsDomainRepositoryI return $model->get(); } - - public function findWhereIn(string $key, array $value = []) - { - return $this->model->whereIn($key, $value)->get(); - } - - public function findWithRelations(int $id, array $relationships = []) - { - return $this->model->with($relationships)->find($id); - } } diff --git a/app/Repositories/DnsManager/Domain/DnsDomainRepositoryInterface.php b/app/Repositories/DnsManager/Domain/DnsDomainRepositoryInterface.php index 52b20ea..7c7eb0f 100644 --- a/app/Repositories/DnsManager/Domain/DnsDomainRepositoryInterface.php +++ b/app/Repositories/DnsManager/Domain/DnsDomainRepositoryInterface.php @@ -6,11 +6,5 @@ use App\Repositories\BaseRepositoryInterface; interface DnsDomainRepositoryInterface extends BaseRepositoryInterface { - public function findDomain(string $domain = ''); - public function filter(array $data = [], array $relationships = [], string $order = 'id', array $select = ['*']); - - public function findWhereIn(string $key, array $value = []); - - public function findWithRelations(int $id, array $relationships = []); } diff --git a/app/Repositories/DnsManager/Record/DnsRecordRepository.php b/app/Repositories/DnsManager/Record/DnsRecordRepository.php index 7e4ae85..25b4f26 100644 --- a/app/Repositories/DnsManager/Record/DnsRecordRepository.php +++ b/app/Repositories/DnsManager/Record/DnsRecordRepository.php @@ -12,16 +12,6 @@ class DnsRecordRepository extends BaseRepository implements DnsRecordRepositoryI return DnsRecord::class; } - public function findWithRelations(int $id, array $relationships = []) - { - return $this->model->with($relationships)->find($id); - } - - public function findWhereIn(string $key, array $value = [], array $relationships = []) - { - return $this->model->with($relationships)->whereIn($key, $value)->get(); - } - public function filter(array $data = [], array $relationships = [], string $order = 'id', array $select = ['*']) { $model = $this->model; diff --git a/app/Repositories/DnsManager/Record/DnsRecordRepositoryInterface.php b/app/Repositories/DnsManager/Record/DnsRecordRepositoryInterface.php index 9aeb580..485e036 100644 --- a/app/Repositories/DnsManager/Record/DnsRecordRepositoryInterface.php +++ b/app/Repositories/DnsManager/Record/DnsRecordRepositoryInterface.php @@ -7,8 +7,4 @@ use App\Repositories\BaseRepositoryInterface; interface DnsRecordRepositoryInterface extends BaseRepositoryInterface { public function filter(array $data = [], array $relationships = [], string $order = 'id', array $select = ['*']); - - public function findWithRelations(int $id, array $relationships = []); - - public function findWhereIn(string $key, array $value = [], array $relationships = []); } diff --git a/app/Repositories/DnsManager/Server/DnsServerRepository.php b/app/Repositories/DnsManager/Server/DnsServerRepository.php index d58a198..c9f7ee1 100644 --- a/app/Repositories/DnsManager/Server/DnsServerRepository.php +++ b/app/Repositories/DnsManager/Server/DnsServerRepository.php @@ -16,9 +16,4 @@ class DnsServerRepository extends BaseRepository implements DnsServerRepositoryI { return $this->model->isActive()->get(); } - - public function findWhereIn(string $key, array $value = []) - { - return $this->model->whereIn($key, $value)->get(); - } } diff --git a/app/Repositories/DnsManager/Server/DnsServerRepositoryInterface.php b/app/Repositories/DnsManager/Server/DnsServerRepositoryInterface.php index 9b8a283..c34dd30 100644 --- a/app/Repositories/DnsManager/Server/DnsServerRepositoryInterface.php +++ b/app/Repositories/DnsManager/Server/DnsServerRepositoryInterface.php @@ -7,6 +7,4 @@ use App\Repositories\BaseRepositoryInterface; interface DnsServerRepositoryInterface extends BaseRepositoryInterface { public function getActived(); - - public function findWhereIn(string $key, array $value = []); } diff --git a/app/Repositories/Domain/DomainCachedRepository.php b/app/Repositories/Domain/DomainCachedRepository.php index be8c617..788bb7e 100644 --- a/app/Repositories/Domain/DomainCachedRepository.php +++ b/app/Repositories/Domain/DomainCachedRepository.php @@ -2,9 +2,7 @@ namespace App\Repositories\Domain; -use App\Models\Domain; use App\Repositories\BaseCachedRepository; -use App\Repositories\BaseRepository; use Illuminate\Support\Facades\Cache; class DomainCachedRepository extends BaseCachedRepository implements DomainRepositoryInterface diff --git a/app/Repositories/Domain/DomainTypeCachedRepository.php b/app/Repositories/Domain/DomainTypeCachedRepository.php deleted file mode 100644 index 16d4d98..0000000 --- a/app/Repositories/Domain/DomainTypeCachedRepository.php +++ /dev/null @@ -1,18 +0,0 @@ -domainTypeRepository->getModel(); - } -} diff --git a/app/Repositories/Domain/DomainTypeRepository.php b/app/Repositories/Domain/DomainTypeRepository.php deleted file mode 100644 index 6dd6933..0000000 --- a/app/Repositories/Domain/DomainTypeRepository.php +++ /dev/null @@ -1,14 +0,0 @@ -fileRepository->getModel(); - } -} diff --git a/app/Repositories/File/FileRepository.php b/app/Repositories/File/FileRepository.php deleted file mode 100644 index 38461f5..0000000 --- a/app/Repositories/File/FileRepository.php +++ /dev/null @@ -1,14 +0,0 @@ -groupRepository->getModel(); - } - - public function getProduct(int $group_id = 0, int $type_id = 0) - { - $cacheKey = CacheKey::build($this->getModuleName(), 'product', ['group_id' => $group_id, 'type_id' => $type_id]); - return $this->cache->remember($cacheKey, function () use ($type_id, $group_id) { - return $this->groupRepository->getProduct($group_id, $type_id); - }); - } - - public function filter(array $data = [], array $relationships = [], array $withCount = [], array $select = ['*']) - { - return $this->rememberFilter( - compact('data', 'relationships', 'withCount', 'select'), - fn() => $this->groupRepository->filter($data, $relationships, $withCount, $select) - ); - } - - public function findBySlugWithProduct(string $slug) - { - return $this->model->where('slug', $slug)->with(['hostings', 'vps'])->first(); - } -} diff --git a/app/Repositories/Group/GroupRepository.php b/app/Repositories/Group/GroupRepository.php deleted file mode 100644 index 48123f4..0000000 --- a/app/Repositories/Group/GroupRepository.php +++ /dev/null @@ -1,87 +0,0 @@ - $this->hostingRepository->filter(['group_id' => $group_id]), - 4 => $this->vPSRepository->filter(['group_id' => $group_id]), - default => null - }; - } - - public function filter(array $data = [], array $relationships = [], array $withCount = [], array $select = ['*']) - { - $model = $this->model->with($relationships)->select($select); - - if (isset($data['status'])) { - $model = $model->where("status", $data['status']); - } - if (isset($data['relations'])) { - foreach ($data['relations'] as $key => $value) { - $model = $model->whereHas($key, function ($query) use ($value) { - if (isset($value['condition'])) { - switch (strtoupper($value['condition'])) { - case "IN": - $query->whereIn($value['key'], $value['value']); - break; - case "NotIn": - $query->whereNotIn($value['key'], $value['value']); - break; - default: - $query->where($value['key'], $value['condition'], $value['value']); - break; - } - } else { - $query->where($value['key'], $value['value']); - } - }); - } - } - if (count($withCount) > 0) { - $model = $model->withCount($withCount); - } - if (isset($data['page'])) { - return $model->paginate(15); - } - - return $model->get(); - } - - public function findBySlugWithProduct(string $slug) - { - return $this->model->where('slug', $slug) - ->where('status', 1) // Only active groups - ->with(['hostings' => function ($query) { - $query->where('status', 1); // Only active hostings - }, 'vps' => function ($query) { - $query->where('status', 1); // Only active VPS - }]) - ->first(); - } -} diff --git a/app/Repositories/Group/GroupRepositoryInterface.php b/app/Repositories/Group/GroupRepositoryInterface.php deleted file mode 100644 index fbf2013..0000000 --- a/app/Repositories/Group/GroupRepositoryInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -get(); } - public function get_billing_year(int $year = 0){ - return $this->model->whereYear('created_at',$year)->get()->sum('total'); - } - public function get_billing_month(int $month = 0){ - return $this->model->whereMonth('created_at',$month)->get()->sum('total'); - } - public function get_billing_day(int|string $day = ''){ - return $this->model->whereDate('created_at',$day)->get()->sum('total'); - } public function findWithRelationships(string $ordernum = '', array $relationships = [], array $withCount = [], array $withSum = []) { $model = $this->model; diff --git a/app/Repositories/Invoice/InvoiceRepositoryInterface.php b/app/Repositories/Invoice/InvoiceRepositoryInterface.php index bf55feb..4e71b2d 100644 --- a/app/Repositories/Invoice/InvoiceRepositoryInterface.php +++ b/app/Repositories/Invoice/InvoiceRepositoryInterface.php @@ -6,9 +6,5 @@ use App\Repositories\BaseRepositoryInterface; interface InvoiceRepositoryInterface extends BaseRepositoryInterface{ public function filter(array $data = [],array $relationships = [],array $withCount = [],array $select = ['*'], bool $returnQuery = false); - public function get_billing_year(int $year = 0); - public function get_billing_month(int $month = 0); - public function get_billing_day(int|string $day = ''); public function findWithRelationships(string $ordernum = '', array $relationships = [],array $withCount = [], array $withSum = []); - } \ No newline at end of file diff --git a/app/Repositories/InvoiceItem/InvoiceItemCachedRepository.php b/app/Repositories/InvoiceItem/InvoiceItemCachedRepository.php index 2188fbb..fae7320 100644 --- a/app/Repositories/InvoiceItem/InvoiceItemCachedRepository.php +++ b/app/Repositories/InvoiceItem/InvoiceItemCachedRepository.php @@ -16,20 +16,6 @@ class InvoiceItemCachedRepository extends BaseCachedRepository implements Invoic { return $this->invoiceItemRepository->getModel(); } - public function getItemBy(array $type_in = [], int $invoice_type_id = 0) - { - $cacheKey = $this->getCacheKey(md5(json_encode( - [ - 'getItemBy' => [ - 'type_in' => $type_in, - 'invoice_type_id' => $invoice_type_id - ] - ] - ))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($type_in, $invoice_type_id) { - return $this->invoiceItemRepository->getItemBy($type_in, $invoice_type_id); - }); - } public function findBy(int $userId = 0, array $data = [], array $relationships = []) { $cacheKey = $this->getCacheKey(md5(json_encode( @@ -59,19 +45,6 @@ class InvoiceItemCachedRepository extends BaseCachedRepository implements Invoic return $this->invoiceItemRepository->filter($data, $relationships, $withCount, $select); }); } - public function findServiceStatus(int $relid = 0, int $type_id = 0) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findServiceStatus' => [ - 'relid' => $relid, - 'type_id' => $type_id, - - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($relid, $type_id) { - return $this->invoiceItemRepository->findServiceStatus($relid, $type_id); - }); - } public function findWithRelationships(int $id = 0, array $relationships = [],array $withCount = [], array $withSum = []) { $cacheKey = $this->getCacheKey(md5(json_encode([ diff --git a/app/Repositories/InvoiceItem/InvoiceItemRepository.php b/app/Repositories/InvoiceItem/InvoiceItemRepository.php index 659ac25..92540b0 100644 --- a/app/Repositories/InvoiceItem/InvoiceItemRepository.php +++ b/app/Repositories/InvoiceItem/InvoiceItemRepository.php @@ -19,13 +19,6 @@ class InvoiceItemRepository extends BaseRepository implements InvoiceItemReposit { return InvoiceItem::class; } - public function getItemBy(array $type_in = [], int $invoice_type_id = 0) - { - return $this->model->with(['hosting', 'vps', 'os'])->where(function ($query) use ($type_in, $invoice_type_id) { - $query->whereIn('product_type', $type_in); - $query->where('invoice_type', $invoice_type_id); - })->get(); - } public function findBy(int $userId = 0, array $data = [], array $relationships = []) { $query = $this->model->with($relationships); @@ -113,14 +106,6 @@ class InvoiceItemRepository extends BaseRepository implements InvoiceItemReposit } return $model->get(); } - public function findServiceStatus(int $relid = 0, int $type_id = 0) - { - return match ($type_id) { - 3 => $this->hostingRepository->find($relid), - 4 => $this->vmwareRepository->find($relid), - default => null - }; - } public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) { $model = $this->model; diff --git a/app/Repositories/InvoiceItem/InvoiceItemRepositoryInterface.php b/app/Repositories/InvoiceItem/InvoiceItemRepositoryInterface.php index 584d74b..ff6eb02 100644 --- a/app/Repositories/InvoiceItem/InvoiceItemRepositoryInterface.php +++ b/app/Repositories/InvoiceItem/InvoiceItemRepositoryInterface.php @@ -4,10 +4,7 @@ namespace App\Repositories\InvoiceItem; use App\Repositories\BaseRepositoryInterface; interface InvoiceItemRepositoryInterface extends BaseRepositoryInterface{ - public function getItemBy(array $type_in = [], int $invoice_type_id = 0); public function findBy(int $userId = 0, array $data = [],array $relationships = []); public function filter(array $data = [],array $relationships = [],array $withCount = [],array $select = ['*']); - public function findServiceStatus(int $relid = 0, int $type_id = 0); public function findWithRelationships(int $id = 0, array $relationships = [],array $withCount = [], array $withSum = []); - } \ No newline at end of file diff --git a/app/Repositories/Mail/MailCachedRepository.php b/app/Repositories/Mail/MailCachedRepository.php deleted file mode 100644 index ab66107..0000000 --- a/app/Repositories/Mail/MailCachedRepository.php +++ /dev/null @@ -1,33 +0,0 @@ -mailRepository->getModel(); - } - public function filter(array $data = [], array $relationships = [], array $withCount = [], array $select = ['*']) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filterItem' => [ - 'data' => $data, - 'relationships' => $relationships, - 'select' => $select, - 'withCount' => $withCount - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($data, $relationships, $select, $withCount) { - return $this->mailRepository->filter($data, $relationships, $withCount, $select); - }); - } -} diff --git a/app/Repositories/Mail/MailRepository.php b/app/Repositories/Mail/MailRepository.php deleted file mode 100644 index 19c6e11..0000000 --- a/app/Repositories/Mail/MailRepository.php +++ /dev/null @@ -1,30 +0,0 @@ -model->with($relationships)->select($select); - - if (isset($data['customer_id']) && ! empty($data['customer_id'])) { - $model = $model->where("customer_id", $data['customer_id']); - } - - if (count($withCount) > 0) { - $model = $model->withCount($withCount); - } - if (isset($data['page'])) { - return $model->paginate(15); - } - return $model->get(); - } -} diff --git a/app/Repositories/Mail/MailRepositoryInterface.php b/app/Repositories/Mail/MailRepositoryInterface.php deleted file mode 100644 index 96bdf48..0000000 --- a/app/Repositories/Mail/MailRepositoryInterface.php +++ /dev/null @@ -1,8 +0,0 @@ -menuRepository->getModel(); - } - - public function filter(array $data) - { - $query = $this->model->query(); - - // Các điều kiện lọc - if (isset($data['is_active'])) { - $query->where('is_active', $data['is_active']); - } - - if (isset($data['title'])) { - $query->where('title', 'like', '%'.$data['title'].'%'); - } - - // Dùng paginate thay vì get() - return $query->defaultOrder()->paginate(10); - } - - public function getMenuParent($excludeId = null) - { - $query = $this->model->query(); - - if ($excludeId) { - $query->where('id', '!=', $excludeId); - } - - return $query->get(); - } - - public function store(array $data) - { - if (isset($data['parent_id']) && $data['parent_id']) { - $parent = $this->model->findOrFail($data['parent_id']); - return $parent->children()->create($data); - } else { - return $this->model->create($data); - } - } - - public function getTree() - { - return $this->model->defaultOrder()->get()->toTree(); - } -} diff --git a/app/Repositories/Menu/MenuRepository.php b/app/Repositories/Menu/MenuRepository.php deleted file mode 100644 index e809b3b..0000000 --- a/app/Repositories/Menu/MenuRepository.php +++ /dev/null @@ -1,57 +0,0 @@ -model->query(); - - if (isset($data['is_active'])) { - $query->where('is_active', $data['is_active']); - } - - if (isset($data['title'])) { - $query->where('title', 'like', '%' . $data['title'] . '%'); - } - - return $query->defaultOrder()->paginate(10); - } - - - public function store(array $data) - { - if (isset($data['parent_id']) && $data['parent_id']) { - $parent = $this->model->findOrFail($data['parent_id']); - return $parent->children()->create($data); - } else { - return $this->model->create($data); - } - } - - public function getMenuParent($excludeId = null) - { - $query = $this->model->query(); - - if ($excludeId) { - $query->where('id', '!=', $excludeId); - } - - return $query->get(); - } - - public function getTree() - { - return $this->model->defaultOrder()->get()->toTree(); - } -} diff --git a/app/Repositories/Menu/MenuRepositoryInterface.php b/app/Repositories/Menu/MenuRepositoryInterface.php deleted file mode 100644 index 15c69a3..0000000 --- a/app/Repositories/Menu/MenuRepositoryInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -datacenterRepository->getModel(); - } - public function filter( - array $data = [], - array $relationships = [], - array $withCount = [], - array $select = ['*'], - bool $returnQuery = false - ) { - if ( - $returnQuery || - !empty($data['paginate']) - ) { - return $this->datacenterRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery); - } - - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filter' => compact('data', 'relationships', 'withCount', 'select') - ]))); - - return Cache::remember( - $cacheKey, - $this->cacheTime, - fn() => - $this->datacenterRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery) - ); - } - public function findBy(string $key = '', string|int|null $value = '') - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findBy' => [ - 'key' => $key, - 'value' => $value - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($key, $value) { - return $this->datacenterRepository->findBy($key, $value); - }); - } -} diff --git a/app/Repositories/Module/Datacenter/DatacenterRepository.php b/app/Repositories/Module/Datacenter/DatacenterRepository.php deleted file mode 100644 index d699f4d..0000000 --- a/app/Repositories/Module/Datacenter/DatacenterRepository.php +++ /dev/null @@ -1,55 +0,0 @@ -model - ->select($select) - ->with($relationships); - - if (!empty($data['vcenter_id'])) { - $query->where('vcenter_id', $data['vcenter_id']); - } - - if (!empty($withCount)) { - $query->withCount($withCount); - } - - if ($returnQuery) { - return $query; - } - - if (!empty($data['paginate'])) { - return $query->paginate( - $data['per_page'] ?? 15 - ); - } - - return $query->get(); - } - - - public function findBy(string $key = '', string|int|null $value = '') - { - if (empty($key) || empty($value)) { - return false; - } - return $this->model->where($key, $value)->first(); - } -} diff --git a/app/Repositories/Module/Datacenter/DatacenterRepositoryInterface.php b/app/Repositories/Module/Datacenter/DatacenterRepositoryInterface.php deleted file mode 100644 index e81d3a9..0000000 --- a/app/Repositories/Module/Datacenter/DatacenterRepositoryInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -datastoreRepository->getModel(); - } - public function filter( - array $data = [], - array $relationships = [], - array $withCount = [], - array $select = ['*'], - bool $returnQuery = false - ) { - if ( - $returnQuery || - !empty($data['paginate']) - ) { - return $this->datastoreRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery); - } - - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filter' => compact('data', 'relationships', 'withCount', 'select') - ]))); - - return Cache::remember( - $cacheKey, - $this->cacheTime, - fn() => - $this->datastoreRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery) - ); - } - public function findBy(string $key = '', string|int|null $value = '') - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findBy' => [ - 'key' => $key, - 'value' => $value - ] - ]))); - return Cache::remember( - $cacheKey, - $this->cacheTime, - fn() => $this->datastoreRepository->findBy($key, $value) - ); - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findWithRelationships' => [ - 'id' => $id, - 'relationships' => $relationships - ] - ]))); - return Cache::remember( - $cacheKey, - $this->cacheTime, - fn() => $this->datastoreRepository->findWithRelationships($id, $relationships, $withCount, $withSum) - ); - } -} diff --git a/app/Repositories/Module/Datastore/DatastoreRepository.php b/app/Repositories/Module/Datastore/DatastoreRepository.php deleted file mode 100644 index cb32501..0000000 --- a/app/Repositories/Module/Datastore/DatastoreRepository.php +++ /dev/null @@ -1,98 +0,0 @@ -model->with($relationships)->select($select); - - if (isset($data['status'])) { - $query = $query->where("status", $data['status']); - } - if (isset($data['module_host_id']) && $data['module_host_id']) { - $query->where("module_host_id", $data['module_host_id']); - } - if (count($withCount) > 0) { - $query->withCount($withCount); - } - if ($returnQuery) { - return $query; - } - - if (!empty($data['paginate'])) { - return $query->paginate( - $data['per_page'] ?? 15 - ); - } - - return $query->get(); - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $model = $this->model; - if (count($relationships) > 0) { - $validRelationships = []; - foreach ($relationships as $relationship) { - $firstRelation = explode('.', $relationship)[0]; - if (method_exists($model, $firstRelation)) { - $validRelationships[] = $relationship; - } - } - if (count($validRelationships) > 0) { - $model = $model->with($validRelationships); - } - } - - // Xử lý withCount - if (count($withCount) > 0) { - foreach ($withCount as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withCount([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ]); - } - } else { - $model = $model->withCount($key); - } - } - } - - // Xử lý withSum - if (count($withSum) > 0) { - foreach ($withSum as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withSum([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ], $item['sum']); - } - } else { - $model = $model->withSum($key); - } - } - } - - return $model->find($id); - } - public function findBy(string $key = '', string|int|null $value = '') - { - if (empty($key) || empty($value)) { - return false; - } - return $this->model->where($key, $value)->first(); - } -} diff --git a/app/Repositories/Module/Datastore/DatastoreRepositoryInterface.php b/app/Repositories/Module/Datastore/DatastoreRepositoryInterface.php deleted file mode 100644 index 608371b..0000000 --- a/app/Repositories/Module/Datastore/DatastoreRepositoryInterface.php +++ /dev/null @@ -1,18 +0,0 @@ -folderRepository->getModel(); - } - public function filter( - array $data = [], - array $relationships = [], - array $withCount = [], - array $select = ['*'], - bool $returnQuery = false - ) { - if ( - $returnQuery || - !empty($data['paginate']) - ) { - return $this->folderRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery); - } - - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filter' => compact('data', 'relationships', 'withCount', 'select') - ]))); - - return Cache::remember( - $cacheKey, - $this->cacheTime, - fn() => - $this->folderRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery) - ); - } - public function findBy(string $key = '', string|int|null $value = '') - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findBy' => [ - 'key' => $key, - 'value' => $value - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($key, $value) { - return $this->folderRepository->findBy($key, $value); - }); - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findWithRelationships' => [ - 'id' => $id, - 'relationships' => $relationships - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($id, $relationships, $withCount, $withSum) { - return $this->folderRepository->findWithRelationships($id, $relationships, $withCount, $withSum); - }); - } -} diff --git a/app/Repositories/Module/Folder/FolderRepository.php b/app/Repositories/Module/Folder/FolderRepository.php deleted file mode 100644 index f0e50d5..0000000 --- a/app/Repositories/Module/Folder/FolderRepository.php +++ /dev/null @@ -1,88 +0,0 @@ -model->with($relationships)->select($select); - - - if (isset($data['module_datacenter_id']) && $data['module_datacenter_id']) { - $query->where("module_datacenter_id", $data['module_datacenter_id']); - } - if (count($withCount) > 0) { - $query->withCount($withCount); - } - if ($returnQuery) { - return $query; - } - if (!empty($data['paginate'])) { - return $query->paginate( - $data['per_page'] ?? 15 - ); - } - return $query->get(); - } - public function findBy(string $key = '', string|int|null $value = '') - { - if (empty($key) || empty($value)) { - return false; - } - return $this->model->where($key, $value)->first(); - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $model = $this->model; - if (count($relationships) > 0) { - foreach ($relationships as $relationship) { - if (method_exists($model, $relationship)) { - $model = $model->with($relationship); - } - } - } - // Xử lý withCount - if (count($withCount) > 0) { - foreach ($withCount as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withCount([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ]); - } - } else { - $model = $model->withCount($key); - } - } - } - - // Xử lý withSum - if (count($withSum) > 0) { - foreach ($withSum as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withSum([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ], $item['sum']); - } - } else { - $model = $model->withSum($key); - } - } - } - - return $model->find($id); - } -} diff --git a/app/Repositories/Module/Folder/FolderRepositoryInterface.php b/app/Repositories/Module/Folder/FolderRepositoryInterface.php deleted file mode 100644 index 1579253..0000000 --- a/app/Repositories/Module/Folder/FolderRepositoryInterface.php +++ /dev/null @@ -1,18 +0,0 @@ -hostRepository->getModel(); - } - public function filter( - array $data = [], - array $relationships = [], - array $withCount = [], - array $select = ['*'], - bool $returnQuery = false - ) { - if ($returnQuery || isset($data['page']) || isset($data['paginate'])) { - return $this->hostRepository->filter($data, $relationships, $withCount, $select, $returnQuery); - } - - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filter' => compact('data', 'relationships', 'withCount', 'select') - ]))); - - return Cache::remember($cacheKey, $this->cacheTime, function () use ($data, $relationships, $select, $withCount, $returnQuery) { - return $this->hostRepository->filter($data, $relationships, $withCount, $select, $returnQuery); - }); - } - public function findWithRelationships(int $id = 0, array $relationships = [],array $withCount = [], array $withSum = []) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findWithRelationships' => [ - 'id' => $id, - 'relationships' => $relationships - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($id, $relationships,$withCount,$withSum) { - return $this->hostRepository->findWithRelationships($id, $relationships,$withCount,$withSum); - }); - } - public function findBy(string $key = '', string|int|null $value = '') - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findBy' => [ - 'key' => $key, - 'value' => $value - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($key, $value) { - return $this->hostRepository->findBy($key, $value); - }); - } -} diff --git a/app/Repositories/Module/Host/HostRepository.php b/app/Repositories/Module/Host/HostRepository.php deleted file mode 100644 index f778ab9..0000000 --- a/app/Repositories/Module/Host/HostRepository.php +++ /dev/null @@ -1,97 +0,0 @@ -model->with($relationships)->select($select); - - if (isset($data['module_datacenter_id']) && $data['module_datacenter_id']) { - $query = $query->where("module_datacenter_id", $data['module_datacenter_id']); - } - - if (count($withCount) > 0) { - $query = $query->withCount($withCount); - } - - if ($returnQuery) { - return $query; - } - - if (isset($data['page']) || isset($data['paginate'])) { - return $query->paginate($data['per_page'] ?? 15); - } - - return $query->get(); - } - - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $model = $this->model; - if (count($relationships) > 0) { - foreach ($relationships as $relationship) { - if (method_exists($model, $relationship)) { - $model = $model->with($relationship); - } - } - } - // Xử lý withCount - if (count($withCount) > 0) { - foreach ($withCount as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withCount([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ]); - } - } else { - $model = $model->withCount($key); - } - } - } - - // Xử lý withSum - if (count($withSum) > 0) { - foreach ($withSum as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withSum([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ], $item['sum']); - } - } else { - $model = $model->withSum($key); - } - } - } - - return $model->find($id); - } - - public function findBy(string $key = '', string|int|null $value = '') - { - if (empty($key) || empty($value)) { - return false; - } - return $this->model->where($key, $value)->first(); - } -} diff --git a/app/Repositories/Module/Host/HostRepositoryInterface.php b/app/Repositories/Module/Host/HostRepositoryInterface.php deleted file mode 100644 index 9d0842f..0000000 --- a/app/Repositories/Module/Host/HostRepositoryInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -ipPoolRepository->getModel(); - } - public function filter(array $data = [], array $relationships = [], array $withCount = [], array $select = ['*']) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filterItem' => [ - 'data' => $data, - 'relationships' => $relationships, - 'select' => $select, - 'withCount' => $withCount - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($data, $relationships, $select, $withCount) { - return $this->ipPoolRepository->filter($data, $relationships, $withCount, $select); - }); - } - public function findByName(string $name = '') - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findByName' => [ - 'name' => $name - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($name){ - return $this->ipPoolRepository->findByName($name); - }); - } -} diff --git a/app/Repositories/Module/IPPool/IPPoolRepository.php b/app/Repositories/Module/IPPool/IPPoolRepository.php deleted file mode 100644 index da668d1..0000000 --- a/app/Repositories/Module/IPPool/IPPoolRepository.php +++ /dev/null @@ -1,54 +0,0 @@ -model->with($relationships)->select($select); - if (isset($data['relations'])) { - foreach ($data['relations'] as $key => $value) { - $model = $model->whereHas($key, function ($query) use ($value) { - if (isset($value['condition'])) { - switch (strtoupper($value['condition'])) { - case "IN": - $query->whereIn($value['key'], $value['value']); - break; - case "NotIn": - $query->whereNotIn($value['key'], $value['value']); - break; - default: - $query->where($value['key'], $value['condition'], $value['value']); - break; - } - } else { - $query->where($value['key'], $value['value']); - } - }); - } - } - - if (count($withCount) > 0) { - $model = $model->withCount($withCount); - } - if (isset($data['page'])) { - return $model->paginate(15); - } - return $model->get(); - } - public function findByName(string $name = '') - { - if (empty($name)) { - return false; - } - return $this->model->where('name','LIKE',"%$name%")->first(); - } -} diff --git a/app/Repositories/Module/IPPool/IPPoolRepositoryInterface.php b/app/Repositories/Module/IPPool/IPPoolRepositoryInterface.php deleted file mode 100644 index 718d38b..0000000 --- a/app/Repositories/Module/IPPool/IPPoolRepositoryInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -ipAddressRepository->getModel(); - } - public function filter( - array $data = [], - array $relationships = [], - array $withCount = [], - array $select = ['*'], - bool $returnQuery = false - ) { - if ( - $returnQuery || - !empty($data['paginate']) - ) { - return $this->ipAddressRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery); - } - - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filter' => compact('data', 'relationships', 'withCount', 'select') - ]))); - - return Cache::remember( - $cacheKey, - $this->cacheTime, - fn() => - $this->ipAddressRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery) - ); - } - public function updateBy(array $condistions = [], array $data = []) - { - $this->ipAddressRepository->updateBy($condistions, $data); - $this->clearCache(); - return $this; - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findWithRelationships' => [ - 'id' => $id, - 'relationships' => $relationships - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($id, $relationships, $withCount, $withSum) { - return $this->ipAddressRepository->findWithRelationships($id, $relationships, $withCount, $withSum); - }); - } - public function findBy(string $key = '', string|int|null $value = '') - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findBy' => [ - 'key' => $key, - 'value' => $value - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($key, $value) { - return $this->ipAddressRepository->findBy($key, $value); - }); - } -} diff --git a/app/Repositories/Module/IpAddress/IpAddressRepository.php b/app/Repositories/Module/IpAddress/IpAddressRepository.php deleted file mode 100644 index 1bc48ee..0000000 --- a/app/Repositories/Module/IpAddress/IpAddressRepository.php +++ /dev/null @@ -1,107 +0,0 @@ -model->with($relationships)->select($select); - - if (isset($data['status'])) { - $query = $query->where("status", $data['status']); - } - if (isset($data['module_network_id']) && $data['module_network_id']) { - $query->where("module_network_id", $data['module_network_id']); - } - if (count($withCount) > 0) { - $query->withCount($withCount); - } - if ($returnQuery) { - return $query; - } - - if (!empty($data['paginate'])) { - return $query->paginate( - $data['per_page'] ?? 15 - ); - } - - return $query->get(); - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $model = $this->model; - if (count($relationships) > 0) { - foreach ($relationships as $relationship) { - if (method_exists($model, $relationship)) { - $model = $model->with($relationship); - } - } - } - // Xử lý withCount - if (count($withCount) > 0) { - foreach ($withCount as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withCount([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ]); - } - } else { - $model = $model->withCount($key); - } - } - } - - // Xử lý withSum - if (count($withSum) > 0) { - foreach ($withSum as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withSum([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ], $item['sum']); - } - } else { - $model = $model->withSum($key); - } - } - } - - return $model->find($id); - } - public function updateBy(array $condistions = [], array $data = []) - { - $model = $this->model; - if (count($condistions) > 0) { - foreach ($condistions as $key => $value) { - $model = $model->where($key, $value); - } - $model = $model->first(); - if ($model) { - return $model->update($data); - } - return false; - } - return false; - } - public function findBy(string $key = '', string|int|null $value = '') - { - if (empty($key) || empty($value)) { - return false; - } - return $this->model->isFree()->where($key, $value)->first(); - } -} diff --git a/app/Repositories/Module/IpAddress/IpAddressRepositoryInterface.php b/app/Repositories/Module/IpAddress/IpAddressRepositoryInterface.php deleted file mode 100644 index 11b8f38..0000000 --- a/app/Repositories/Module/IpAddress/IpAddressRepositoryInterface.php +++ /dev/null @@ -1,20 +0,0 @@ -networkRepository->getModel(); - } - public function filter( - array $data = [], - array $relationships = [], - array $withCount = [], - array $select = ['*'], - bool $returnQuery = false - ) { - if ( - $returnQuery || - !empty($data['paginate']) - ) { - return $this->networkRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery); - } - - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filter' => compact('data', 'relationships', 'withCount', 'select') - ]))); - - return Cache::remember( - $cacheKey, - $this->cacheTime, - fn() => - $this->networkRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery) - ); - } - public function findBy(string $key = '', string|int|null $value = '') - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findBy' => [ - 'key' => $key, - 'value' => $value - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($key, $value) { - return $this->networkRepository->findBy($key, $value); - }); - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findWithRelationships' => [ - 'id' => $id, - 'relationships' => $relationships - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($id, $relationships, $withCount, $withSum) { - return $this->networkRepository->findWithRelationships($id, $relationships, $withCount, $withSum); - }); - } -} \ No newline at end of file diff --git a/app/Repositories/Module/Network/NetworkRepository.php b/app/Repositories/Module/Network/NetworkRepository.php deleted file mode 100644 index 98f29f3..0000000 --- a/app/Repositories/Module/Network/NetworkRepository.php +++ /dev/null @@ -1,90 +0,0 @@ -model->with($relationships)->select($select); - - if (isset($data['status'])) { - $query = $query->where("status", $data['status']); - } - if (isset($data['module_host_id']) && $data['module_host_id']) { - $query->where("module_host_id", $data['module_host_id']); - } - if (count($withCount) > 0) { - $query->withCount($withCount); - } - if ($returnQuery) { - return $query; - } - - if (!empty($data['paginate'])) { - return $query->paginate( - $data['per_page'] ?? 15 - ); - } - - return $query->get(); - } - public function findBy(string $key = '', string|int|null $value = '') - { - if (empty($key) || empty($value)) { - return false; - } - return $this->model->where($key, $value)->first(); - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $model = $this->model; - if (count($relationships) > 0) { - foreach ($relationships as $relationship) { - if (method_exists($model, $relationship)) { - $model = $model->with($relationship); - } - } - } - // Xử lý withCount - if (count($withCount) > 0) { - foreach ($withCount as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withCount([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ]); - } - } else { - $model = $model->withCount($key); - } - } - } - - // Xử lý withSum - if (count($withSum) > 0) { - foreach ($withSum as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withSum([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ], $item['sum']); - } - } else { - $model = $model->withSum($key); - } - } - } - - return $model->find($id); - } -} \ No newline at end of file diff --git a/app/Repositories/Module/Network/NetworkRepositoryInterface.php b/app/Repositories/Module/Network/NetworkRepositoryInterface.php deleted file mode 100644 index 5f698f2..0000000 --- a/app/Repositories/Module/Network/NetworkRepositoryInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -snapshotRepository->getModel(); - } -} diff --git a/app/Repositories/Module/Snapshot/SnapshotLimitCachedRepository.php b/app/Repositories/Module/Snapshot/SnapshotLimitCachedRepository.php deleted file mode 100644 index 0c72e53..0000000 --- a/app/Repositories/Module/Snapshot/SnapshotLimitCachedRepository.php +++ /dev/null @@ -1,18 +0,0 @@ -snapshotLimitRepository->getModel(); - } -} diff --git a/app/Repositories/Module/Snapshot/SnapshotLimitRepository.php b/app/Repositories/Module/Snapshot/SnapshotLimitRepository.php deleted file mode 100644 index db6e26d..0000000 --- a/app/Repositories/Module/Snapshot/SnapshotLimitRepository.php +++ /dev/null @@ -1,12 +0,0 @@ -model - ->select($select) - ->with($relationships); - - if (!empty($data['datacenter_id'])) { - $query->where('datacenter_id', $data['datacenter_id']); - } - - if (!empty($data['host_id'])) { - $query->where('host_id', $data['host_id']); - } - - if (!empty($data['name'])) { - $query->where('name', 'like', '%' . $data['name'] . '%'); - } - - if (!empty($withCount)) { - $query->withCount($withCount); - } - - if ($returnQuery) { - return $query; - } - - if (!empty($data['paginate']) || !empty($data['page'])) { - return $query->paginate( - $data['per_page'] ?? 15 - ); - } - - return $query->get(); - } - - public function findBy(string $key = '', string|int|null $value = '') - { - if (empty($key) || empty($value)) { - return false; - } - return $this->model->where($key, $value)->first(); - } -} diff --git a/app/Repositories/Module/VirtualMachine/VirtualMachineRepositoryInterface.php b/app/Repositories/Module/VirtualMachine/VirtualMachineRepositoryInterface.php deleted file mode 100644 index c8c5a63..0000000 --- a/app/Repositories/Module/VirtualMachine/VirtualMachineRepositoryInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -noteRepository->getModel(); - } -} \ No newline at end of file diff --git a/app/Repositories/Note/NoteRepository.php b/app/Repositories/Note/NoteRepository.php deleted file mode 100644 index acb7566..0000000 --- a/app/Repositories/Note/NoteRepository.php +++ /dev/null @@ -1,14 +0,0 @@ -find($id); } - public function updateBy(string $key = '', string|int $value = '', array $data = []) - { - if (empty($key) || empty($value)) { - return false; - } - return $this->model->where($key, $value)->update($data); - } public function findByOrderNum(string $orderNum) { diff --git a/app/Repositories/Order/OrderRepositoryInterface.php b/app/Repositories/Order/OrderRepositoryInterface.php index 225bff0..f80c189 100644 --- a/app/Repositories/Order/OrderRepositoryInterface.php +++ b/app/Repositories/Order/OrderRepositoryInterface.php @@ -6,6 +6,5 @@ use App\Repositories\BaseRepositoryInterface; interface OrderRepositoryInterface extends BaseRepositoryInterface{ 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 updateBy(string $key = '',string|int $value = '',array $data =[]); public function findByOrderNum(string $orderNum); } \ No newline at end of file diff --git a/app/Repositories/Os/OsCachedRepository.php b/app/Repositories/Os/OsCachedRepository.php deleted file mode 100644 index 031d37a..0000000 --- a/app/Repositories/Os/OsCachedRepository.php +++ /dev/null @@ -1,57 +0,0 @@ -osRepository->getModel(); - } - public function filter( - array $data = [], - array $relationships = [], - array $withCount = [], - array $select = ['*'], - bool $returnQuery = false - ) { - if ( - $returnQuery || - !empty($data['paginate']) - ) { - return $this->osRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery); - } - - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filter' => compact('data', 'relationships', 'withCount', 'select') - ]))); - - return Cache::remember( - $cacheKey, - $this->cacheTime, - fn() => - $this->osRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery) - ); - } - public function findWithRelationships(int $id = 0, array $relationships = [],array $withCount = [], array $withSum = []) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findWithRelationships' => [ - 'id' => $id, - 'relationships' => $relationships - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($id, $relationships,$withCount,$withSum) { - return $this->osRepository->findWithRelationships($id, $relationships,$withCount,$withSum); - }); - } -} \ No newline at end of file diff --git a/app/Repositories/Os/OsRepository.php b/app/Repositories/Os/OsRepository.php deleted file mode 100644 index f9fb884..0000000 --- a/app/Repositories/Os/OsRepository.php +++ /dev/null @@ -1,91 +0,0 @@ -model - ->select($select) - ->with($relationships); - - if (!empty($data['module_host_id'])) { - $query->where('module_host_id', $data['module_host_id']); - } - - if (!empty($withCount)) { - $query->withCount($withCount); - } - - if ($returnQuery) { - return $query; - } - - if (!empty($data['paginate'])) { - return $query->paginate( - $data['per_page'] ?? 15 - ); - } - - return $query->get(); - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $model = $this->model; - if (count($relationships) > 0) { - foreach ($relationships as $relationship) { - if (method_exists($model, $relationship)) { - $model = $model->with($relationship); - } - } - } - // Xử lý withCount - if (count($withCount) > 0) { - foreach ($withCount as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withCount([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ]); - } - } else { - $model = $model->withCount($key); - } - } - } - - // Xử lý withSum - if (count($withSum) > 0) { - foreach ($withSum as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withSum([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ], $item['sum']); - } - } else { - $model = $model->withSum($key); - } - } - } - - return $model->find($id); - } -} diff --git a/app/Repositories/Os/OsRepositoryInterface.php b/app/Repositories/Os/OsRepositoryInterface.php deleted file mode 100644 index f957fc5..0000000 --- a/app/Repositories/Os/OsRepositoryInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -osGroupRepository->getModel(); - } - public function filter( - array $data = [], - array $relationships = [], - array $withCount = [], - array $select = ['*'], - bool $returnQuery = false - ) { - if ( - $returnQuery || - !empty($data['paginate']) - ) { - return $this->osGroupRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery); - } - - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filter' => compact('data', 'relationships', 'withCount', 'select') - ]))); - - return Cache::remember( - $cacheKey, - $this->cacheTime, - fn() => - $this->osGroupRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery) - ); - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findWithRelationships' => [ - 'id' => $id, - 'relationships' => $relationships - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($id, $relationships, $withCount, $withSum) { - return $this->osGroupRepository->findWithRelationships($id, $relationships, $withCount, $withSum); - }); - } -} diff --git a/app/Repositories/OsGroup/OsGroupRepository.php b/app/Repositories/OsGroup/OsGroupRepository.php deleted file mode 100644 index 211b8c8..0000000 --- a/app/Repositories/OsGroup/OsGroupRepository.php +++ /dev/null @@ -1,91 +0,0 @@ -model - ->select($select) - ->with($relationships); - - if (!empty($data['module_host_id'])) { - $query->where('module_host_id', $data['module_host_id']); - } - - if (!empty($withCount)) { - $query->withCount($withCount); - } - - if ($returnQuery) { - return $query; - } - - if (!empty($data['paginate'])) { - return $query->paginate( - $data['per_page'] ?? 15 - ); - } - - return $query->get(); - } - public function findWithRelationships(int $id = 0, array $relationships = [], array $withCount = [], array $withSum = []) - { - $model = $this->model; - if (count($relationships) > 0) { - foreach ($relationships as $relationship) { - if (method_exists($model, $relationship)) { - $model = $model->with($relationship); - } - } - } - // Xử lý withCount - if (count($withCount) > 0) { - foreach ($withCount as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withCount([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ]); - } - } else { - $model = $model->withCount($key); - } - } - } - - // Xử lý withSum - if (count($withSum) > 0) { - foreach ($withSum as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withSum([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ], $item['sum']); - } - } else { - $model = $model->withSum($key); - } - } - } - - return $model->find($id); - } -} diff --git a/app/Repositories/OsGroup/OsGroupRepositoryInterface.php b/app/Repositories/OsGroup/OsGroupRepositoryInterface.php deleted file mode 100644 index 347aaf7..0000000 --- a/app/Repositories/OsGroup/OsGroupRepositoryInterface.php +++ /dev/null @@ -1,18 +0,0 @@ -postRepository->getModel(); - } - public function filter(array $data = [], array $relationships = [], array $withCount = [], array $select = ['*']) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filterItem' => [ - 'data' => $data, - 'relationships' => $relationships, - 'select' => $select, - 'withCount' => $withCount - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($data, $relationships, $select, $withCount) { - return $this->postRepository->filter($data, $relationships, $withCount, $select); - }); - } - - public function findBy(string $key = '',string $value = '') { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findBy' => [ - 'key' => $key, - 'value' => $value, - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($key,$value) { - return $this->postRepository->findBy($key,$value); - }); - } -} diff --git a/app/Repositories/Post/PostRepository.php b/app/Repositories/Post/PostRepository.php deleted file mode 100644 index fca621c..0000000 --- a/app/Repositories/Post/PostRepository.php +++ /dev/null @@ -1,47 +0,0 @@ -model->with($relationships)->select($select); - - if (isset($data['status']) && ! empty($data['status']) && $data['status'] != 'full') { - $model = $model->where("status", match (strtolower($data['status'])) { - 'published' => 1, - 'draft' => 2, - 'pending' => 0, - }); - } - - if (count($withCount) > 0) { - $model = $model->withCount($withCount); - } - if (isset($data['limit']) && $data['limit']) { - - return $model->latest('id')->take($data['limit'])->get(); - } - if (isset($data['page']) && $data['page']) { - - return $model->paginate(15); - } - return $model->get(); - } - - public function findBy(string $key = '',string $value = '') { - if(empty($key) || empty($value)) - { - return false; - } - return $this->model->where($key,$value)->first(); - } -} diff --git a/app/Repositories/Post/PostRepositoryInterface.php b/app/Repositories/Post/PostRepositoryInterface.php deleted file mode 100644 index 0b7f194..0000000 --- a/app/Repositories/Post/PostRepositoryInterface.php +++ /dev/null @@ -1,9 +0,0 @@ -staffRepository->getModel(); - } - public function getFirstBy(array $data = []) - { - return $this->staffRepository->getFirstBy($data); - } - public function deleteRawTime(int $time = 0) - { - return $this->staffRepository->deleteRawTime($time); - } - public function filter(array $data = [], array $relationships = [], array $withCount = [], array $select = ['*']) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'filterItem' => [ - 'data' => $data, - 'relationships' => $relationships, - 'select' => $select, - 'withCount' => $withCount - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($data, $relationships, $select, $withCount) { - return $this->staffRepository->filter($data, $relationships, $withCount, $select); - }); - } -} diff --git a/app/Repositories/Template/TemplateCachedRepository.php b/app/Repositories/Template/TemplateCachedRepository.php deleted file mode 100644 index 07db555..0000000 --- a/app/Repositories/Template/TemplateCachedRepository.php +++ /dev/null @@ -1,18 +0,0 @@ -templateRepository->getModel(); - } -} diff --git a/app/Repositories/Template/TemplateRepository.php b/app/Repositories/Template/TemplateRepository.php deleted file mode 100644 index ddf71d3..0000000 --- a/app/Repositories/Template/TemplateRepository.php +++ /dev/null @@ -1,14 +0,0 @@ -ticketRepository->filter($data, $relationships, $withCount, $select); }); } - public function findWithRelationshipsByKey(string $key = '', string|int $value = '', array $relationships = [], array $withCount = [], array $withSum = []) - { - $cacheKey = $this->getCacheKey(md5(json_encode([ - 'findWithRelationshipsByKey' => [ - 'key' => $key, - 'value' => $value, - 'relationships' => $relationships, - 'withCount' => $withCount, - 'withSum' => $withSum, - ] - ]))); - return Cache::remember($cacheKey, $this->cacheTime, function () use ($key, $value, $relationships, $withCount, $withSum) { - return $this->ticketRepository->findWithRelationshipsByKey($key, $value, $relationships, $withCount, $withSum); - }); - } public function findBy(string $key = '', string $value) { $cacheKey = $this->getCacheKey(md5(json_encode([ @@ -57,10 +42,4 @@ class TicketCachedRepository extends BaseCachedRepository implements TicketRepos return $this->ticketRepository->findBy($key, $value); }); } - public function updateBy(string $key = '', string $value = '', array $data = []) - { - $this->ticketRepository->updateBy($key, $value, $data); - $this->clearCache(); - return $this; - } } diff --git a/app/Repositories/Ticket/TicketRepository.php b/app/Repositories/Ticket/TicketRepository.php index fc50377..e5214bf 100644 --- a/app/Repositories/Ticket/TicketRepository.php +++ b/app/Repositories/Ticket/TicketRepository.php @@ -15,7 +15,6 @@ class TicketRepository extends BaseRepository implements TicketRepositoryInterfa { $model = $this->model->with($relationships)->select($select); - if (isset($data['status']) && ! empty($data['status']) && $data['status'] != 'full') { $model = $model->where("status", match (strtolower($data['status'])) { 'pending' => 1, @@ -28,61 +27,10 @@ class TicketRepository extends BaseRepository implements TicketRepositoryInterfa $model = $model->withCount($withCount); } if (isset($data['page']) && $data['page']) { - return $model->paginate(15); } return $model->get(); } - public function findWithRelationshipsByKey(string $key = '', string|int $value = '', array $relationships = [], array $withCount = [], array $withSum = []) - { - if (empty($key) || empty($value)) { - return false; - } - $model = $this->model; - if (count($relationships) > 0) { - foreach ($relationships as $relationship) { - if (method_exists($model, $relationship)) { - $model = $model->with($relationship); - } - } - } - - // Xử lý withCount - if (count($withCount) > 0) { - foreach ($withCount as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withCount([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ]); - } - } else { - $model = $model->withCount($key); - } - } - } - - // Xử lý withSum - if (count($withSum) > 0) { - foreach ($withSum as $key => $items) { - if (is_array($items)) { - foreach ($items as $alias => $item) { - $model = $model->withSum([ - $key . ' as ' . $alias => function ($query) use ($item) { - $query->where($item['key'], $item['condition'], $item['value']); - } - ], $item['sum']); - } - } else { - $model = $model->withSum($key); - } - } - } - - return $model->where($key, $value)->latest('id')->get(); - } public function findBy(string $key = '', string $value) { if (empty($key) || empty($value)) { @@ -90,11 +38,4 @@ class TicketRepository extends BaseRepository implements TicketRepositoryInterfa } return $this->model->where($key, $value)->first(); } - public function updateBy(string $key = '', string $value = '', array $data = []) - { - if (empty($key) || empty($value) || count($data) < 1) { - return false; - } - return $this->model->where($key, $value)->update($data); - } } diff --git a/app/Repositories/Ticket/TicketRepositoryInterface.php b/app/Repositories/Ticket/TicketRepositoryInterface.php index 851b41e..30715ad 100644 --- a/app/Repositories/Ticket/TicketRepositoryInterface.php +++ b/app/Repositories/Ticket/TicketRepositoryInterface.php @@ -5,7 +5,5 @@ use App\Repositories\BaseRepositoryInterface; interface TicketRepositoryInterface extends BaseRepositoryInterface{ public function filter(array $data = [],array $relationships = [],array $withCount = [],array $select = ['*']); - public function findWithRelationshipsByKey(string $key = '',string|int $value = '', array $relationships = [],array $withCount = [], array $withSum = []); public function findBy(string $key = '',string $value); - public function updateBy(string $key = '', string $value = '', array $data = []); } \ No newline at end of file diff --git a/app/Repositories/User/UserCachedRepository.php b/app/Repositories/User/UserCachedRepository.php deleted file mode 100644 index 2b7592b..0000000 --- a/app/Repositories/User/UserCachedRepository.php +++ /dev/null @@ -1,18 +0,0 @@ -userRepository->getModel(); - } -} diff --git a/app/Repositories/User/UserRepository.php b/app/Repositories/User/UserRepository.php deleted file mode 100644 index 4011df0..0000000 --- a/app/Repositories/User/UserRepository.php +++ /dev/null @@ -1,14 +0,0 @@ -cache = $cache; - } - - /** - * Get domain information from cache - * - * @param string $domain - * @return array|null - */ - public function getDomainInfo(string $domain): ?array - { - $cacheKey = CacheKey::build('whois', 'domain', ['domain' => strtolower($domain)]); - return $this->cache->get($cacheKey); - } - - /** - * Cache domain information - * - * @param string $domain - * @param array $data - * @return bool - */ - public function cacheDomainInfo(string $domain, array $data): bool - { - $cacheKey = CacheKey::build('whois', 'domain', ['domain' => strtolower($domain)]); - $this->cache->put($cacheKey, $data); - return true; - } - - /** - * Clear domain cache - * - * @param string $domain - * @return bool - */ - public function clearDomainCache(string $domain): bool - { - $cacheKey = CacheKey::build('whois', 'domain', ['domain' => strtolower($domain)]); - $this->cache->forget($cacheKey); - return true; - } - - /** - * Clear all whois cache - * - * @return bool - */ - public function clearAllCache(): bool - { - $this->cache->clearByModule('whois'); - return true; - } -} diff --git a/app/Repositories/Whois/WhoisRepositoryInterface.php b/app/Repositories/Whois/WhoisRepositoryInterface.php deleted file mode 100644 index 8b39563..0000000 --- a/app/Repositories/Whois/WhoisRepositoryInterface.php +++ /dev/null @@ -1,38 +0,0 @@ -vCenterRepository->getModel(); - } - public function filter( - array $data = [], - array $relationships = [], - array $withCount = [], - array $select = ['*'], - bool $returnQuery = false - ) { - if ( - $returnQuery || - !empty($data['paginate']) - ) { - return $this->vCenterRepository - ->filter($data, $relationships, $withCount, $select, $returnQuery); - } - - return $this->rememberFilter( - compact('data', 'relationships', 'withCount', 'select'), - fn() => $this->vCenterRepository->filter($data, $relationships, $withCount, $select, $returnQuery) - ); - } - public function findByName(string $name = '') - { - $cacheKey = CacheKey::build($this->getModuleName(), 'find-by-name', ['name' => $name]); - return $this->cache->remember($cacheKey, function () use ($name) { - return $this->vCenterRepository->findByName($name); - }); - } -} diff --git a/app/Repositories/vCenter/vCenterRepository.php b/app/Repositories/vCenter/vCenterRepository.php deleted file mode 100644 index 1ae4939..0000000 --- a/app/Repositories/vCenter/vCenterRepository.php +++ /dev/null @@ -1,52 +0,0 @@ -model - ->select($select) - ->with($relationships); - - // if (!empty($data['vcenter_id'])) { - // $query->where('vcenter_id', $data['vcenter_id']); - // } - - if (!empty($withCount)) { - $query->withCount($withCount); - } - - if ($returnQuery) { - return $query; - } - - if (!empty($data['paginate'])) { - return $query->paginate( - $data['per_page'] ?? 15 - ); - } - - return $query->get(); - } - public function findByName(string $name = '') - { - if (empty($name)) { - return false; - } - return $this->model->where('host', 'LIKE', "%$name%")->first(); - } -} diff --git a/app/Repositories/vCenter/vCenterRepositoryInterface.php b/app/Repositories/vCenter/vCenterRepositoryInterface.php deleted file mode 100644 index 180360a..0000000 --- a/app/Repositories/vCenter/vCenterRepositoryInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -get(); } - public function findCustomerService(int $customerId, string $uuid): ?Service - { - return Service::query() - ->with(['product', 'addons', 'invoice', 'orderItem', 'vmware.ipAddress', 'vmware.os', 'vmware.vCenter']) - ->where('uuid', $uuid) - ->where('customer_id', $customerId) - ->first(); - } - - public function canRetryProvisioning(Service $service): bool - { - if ($service->status !== 'pending' || $service->retry_requested_at) { - return false; - } - - $paidAt = $service->invoice?->paid_at; - - return $paidAt ? Carbon::parse($paidAt)->diffInMinutes(now()) >= 180 : false; - } - public function retryProvisioning(Order $order, int $customerId): array { $order = Order::where('id', $order->id) diff --git a/app/Services/Api/Order/OrderService.php b/app/Services/Api/Order/OrderService.php index b99cf79..66e85e6 100644 --- a/app/Services/Api/Order/OrderService.php +++ b/app/Services/Api/Order/OrderService.php @@ -7,8 +7,8 @@ use App\Helpers\OrderHelper; use App\Jobs\Mail\Admin\AdminOrderNotificationJob; use App\Models\Order; use App\Models\Product; -use App\Repositories\Api\Invoice\InvoiceRepository; -use App\Repositories\Api\Order\OrderRepository; +use App\Repositories\Api\Invoice\InvoiceRepositoryInterface; +use App\Repositories\Api\Order\OrderRepositoryInterface; use App\Repositories\Api\Service\ServiceRepository; use App\Services\InvoicePaymentService; use App\Support\AddonQuantityValidator; @@ -19,9 +19,9 @@ use Illuminate\Support\Facades\Log; class OrderService { public function __construct( - protected OrderRepository $orderRepo, + protected OrderRepositoryInterface $orderRepo, protected ServiceRepository $productRepo, - protected InvoiceRepository $invoiceRepo, + protected InvoiceRepositoryInterface $invoiceRepo, protected InvoicePaymentService $invoicePaymentService, ) {} diff --git a/app/Services/CompressionService.php b/app/Services/CompressionService.php deleted file mode 100644 index 5d89c6f..0000000 --- a/app/Services/CompressionService.php +++ /dev/null @@ -1,84 +0,0 @@ -header('Accept-Encoding', ''); - - return [ - 'supports_gzip' => str_contains($acceptEncoding, 'gzip'), - 'supports_deflate' => str_contains($acceptEncoding, 'deflate'), - 'supports_brotli' => str_contains($acceptEncoding, 'br'), - 'raw_header' => $acceptEncoding, - ]; - } - - /** - * Get compression info for debugging - */ - public static function getCompressionInfo(string $content): array - { - $originalSize = strlen($content); - $info = ['original_size' => $originalSize]; - - if (function_exists('gzencode')) { - $gzipped = gzencode($content, 6); - if ($gzipped !== false) { - $gzippedSize = strlen($gzipped); - $info['gzip'] = [ - 'compressed_size' => $gzippedSize, - 'ratio' => round(($gzippedSize / $originalSize) * 100, 2) . '%', - 'savings' => round((1 - $gzippedSize / $originalSize) * 100, 2) . '%' - ]; - } - } - - if (function_exists('gzdeflate')) { - $deflated = gzdeflate($content, 6); - if ($deflated !== false) { - $deflatedSize = strlen($deflated); - $info['deflate'] = [ - 'compressed_size' => $deflatedSize, - 'ratio' => round(($deflatedSize / $originalSize) * 100, 2) . '%', - 'savings' => round((1 - $deflatedSize / $originalSize) * 100, 2) . '%' - ]; - } - } - - if (function_exists('brotli_compress')) { - $brotlied = brotli_compress($content, 6); - if ($brotlied !== false) { - $brotliedSize = strlen($brotlied); - $info['brotli'] = [ - 'compressed_size' => $brotliedSize, - 'ratio' => round(($brotliedSize / $originalSize) * 100, 2) . '%', - 'savings' => round((1 - $brotliedSize / $originalSize) * 100, 2) . '%' - ]; - } - } - - return $info; - } - - /** - * Format bytes to human readable format - */ - public static function formatBytes(int $bytes, int $precision = 2): string - { - $units = ['B', 'KB', 'MB', 'GB', 'TB']; - - for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) { - $bytes /= 1024; - } - - return round($bytes, $precision) . ' ' . $units[$i]; - } -} \ No newline at end of file diff --git a/app/Services/Concerns/ActivatesPaidInvoices.php b/app/Services/Concerns/ActivatesPaidInvoices.php new file mode 100644 index 0000000..f56b091 --- /dev/null +++ b/app/Services/Concerns/ActivatesPaidInvoices.php @@ -0,0 +1,245 @@ +order?->loadMissing(['orderItems.product', 'customer', 'invoice']); + if (! $order) { + return; + } + + // payment_method/paid_at always mirror the invoice that was just paid, even if the + // order's status doesn't change (e.g. it's already ACTIVE from an earlier payment). + $order->update([ + 'status' => OrderStatus::ACTIVE->value, + 'payment_method' => $invoice->payment_method, + 'paid_at' => $invoice->paid_at, + ]); + + foreach ($order->orderItems as $item) { + $isHostingProvision = ! $item->os_id + && $item->server_name + && $item->product?->product_type === 'hosting'; + + $service = Service::firstOrCreate( + ['order_item_id' => $item->id], + [ + 'customer_id' => $order->customer_id, + 'product_id' => $item->product_id, + 'invoice_id' => $invoice->id, + 'server_name' => $item->server_name, + 'status' => ($item->os_id || $isHostingProvision) ? 'pending' : 'active', + 'auto_renew' => false, + 'started_at' => now(), + 'expires_at' => now()->addMonths($item->months ?: OrderHelper::cycleToMonths($item->billing_cycle)), + ] + ); + + $this->attachServiceAddons($service, $item); + + if ($service->wasRecentlyCreated && $item->product_id) { + if ($item->os_id) { + dispatch(new CreateVirtualMachineJob($item)); + } elseif ($isHostingProvision) { + dispatch(new ProvisionHostingAccountJob($item)); + } + } + } + } + + private function activateRenewal(Invoice $invoice): void + { + $service = Service::query() + ->with(['orderItem.product']) + ->where('invoice_id', $invoice->id) + ->first(); + + if (! $service) { + return; + } + + if ($invoice->order) { + // Same rule as activateOrder(): payment_method/paid_at always mirror the invoice + // that was just paid, regardless of whether the order's status actually changes. + $invoice->order->update([ + 'status' => OrderStatus::ACTIVE->value, + 'payment_method' => $invoice->payment_method, + 'paid_at' => $invoice->paid_at, + ]); + } + + $months = (int) ($invoice->invoiceItems()->where('type', 'renewal')->value('months') ?: $service->orderItem?->months ?: 1); + $baseDate = $service->expires_at && $service->expires_at->isFuture() + ? $service->expires_at + : now(); + $expiresAt = Carbon::parse($baseDate)->addMonths($months); + + $service->update([ + 'status' => 'active', + 'expires_at' => $expiresAt, + 'suspended_at' => null, + ]); + + if ($service->vm_id) { + dispatch(new VMJobs((int) $service->vm_id, 'Start', false))->delay(now()->addSeconds(5)); + } + } + + /** + * Merges the purchased addons into order_items.addons_meta (so RenewalPricing charges + * them on the next renewal) and applies the new absolute CPU/RAM/disk totals to the VM. + */ + private function activateUpgrade(Invoice $invoice): void + { + $service = Service::query() + ->with(['orderItem.product', 'vmware']) + ->find($invoice->service_id); + + $orderItem = $service?->orderItem; + if (! $service || ! $orderItem) { + return; + } + + $upgradeItems = $invoice->invoiceItems()->where('type', 'upgrade')->get(); + if ($upgradeItems->isEmpty()) { + return; + } + + $meta = collect(is_array($orderItem->addons_meta) ? $orderItem->addons_meta : []); + $purchasedCodes = []; + + foreach ($upgradeItems as $item) { + $addon = Addon::find($item->addon_id); + if (! $addon) { + continue; + } + + $code = strtolower((string) $addon->code); + $purchasedCodes[] = $code; + + $qty = (int) $item->quantity; + $index = $meta->search(fn ($entry) => (int) ($entry['addon_id'] ?? 0) === $addon->id); + $currentQty = $index === false ? 0 : (int) ($meta->get($index)['qty'] ?? 0); + $newQty = $currentQty + $qty; + + $entry = [ + 'addon_id' => $addon->id, + 'code' => $code, + 'name' => $addon->name, + 'qty' => $newQty, + 'price' => (float) $addon->price, + 'subtotal' => $newQty * (float) $addon->price, + ]; + + if ($index === false) { + $meta->push($entry); + } else { + $meta->put($index, $entry); + } + } + + $orderItem->update(['addons_meta' => $meta->values()->all()]); + + $moduleVmware = $service->vmware; + if (! $moduleVmware) { + return; + } + + $addonQtyByCode = $meta->keyBy(fn ($entry) => $entry['code'] ?? ''); + $product = $orderItem->product; + + if (array_intersect(['cpu', 'ram'], $purchasedCodes)) { + $totalCpu = (int) ($product?->cpu ?? 0) + (int) ($addonQtyByCode->get('cpu')['qty'] ?? 0); + $totalRam = (int) ($product?->ram ?? 0) + (int) ($addonQtyByCode->get('ram')['qty'] ?? 0); + + UpdateVmConfigJob::dispatch( + $moduleVmware->id, + (string) $moduleVmware->vmware_id, + '', + $totalCpu, + $totalRam, + )->afterCommit(); + } + + if (in_array('disk', $purchasedCodes, true)) { + $totalDisk = (int) ($product?->disk ?? 0) + (int) ($addonQtyByCode->get('disk')['qty'] ?? 0); + $vcenter = $moduleVmware->vCenter ?? $moduleVmware->ipAddress?->host?->datacenter?->vCenter; + + if ($vcenter) { + DB::afterCommit(function () use ($vcenter, $moduleVmware, $totalDisk, $invoice) { + try { + $serviceVCenter = new ServiceVCenter(); + if (! $serviceVCenter->loginServer($vcenter)) { + throw new \RuntimeException('vCenter login failed'); + } + $serviceVCenter->UpgradeDiskVirtualMachine($moduleVmware, $totalDisk); + } catch (\Throwable $e) { + Log::error('activateUpgrade: disk upgrade failed', [ + 'invoice_id' => $invoice->id, + 'module_vmware_id' => $moduleVmware->id, + 'total_disk' => $totalDisk, + 'error' => $e->getMessage(), + ]); + } + }); + } + } + } + + private function attachServiceAddons(Service $service, OrderItem $item): void + { + if ($service->addons()->exists()) { + return; + } + + $product = $item->product; + if ($product) { + foreach (['cpu' => $product->cpu, 'ram' => $product->ram, 'disk' => $product->disk, 'bandwidth' => $product->bandwidth] as $type => $value) { + if ($value !== null && $value !== '') { + ServiceAddon::create([ + 'service_id' => $service->id, + 'addon_type' => $type, + 'addon_value' => (string) $value, + 'price' => 0, + ]); + } + } + } + + $addonsMeta = is_array($item->addons_meta) + ? $item->addons_meta + : json_decode($item->addons_meta ?? '[]', true); + + foreach ((array) $addonsMeta as $meta) { + $addon = Addon::find($meta['addon_id'] ?? null); + if (!$addon) { + continue; + } + ServiceAddon::create([ + 'service_id' => $service->id, + 'addon_type' => strtolower((string) $addon->code), + 'addon_value' => (string) ($meta['qty'] ?? 1), + 'price' => (int) ($meta['price'] ?? $addon->price), + ]); + } + } +} diff --git a/app/Services/Customer/CustomerBillingService.php b/app/Services/Customer/CustomerBillingService.php index 638b22d..21ff31e 100644 --- a/app/Services/Customer/CustomerBillingService.php +++ b/app/Services/Customer/CustomerBillingService.php @@ -5,7 +5,6 @@ namespace App\Services\Customer; use App\Models\Invoice; use App\Models\Order; use App\Models\Transaction; -use App\Repositories\Customer\CustomerRepositoryInterface; use App\Repositories\Invoice\InvoiceRepositoryInterface; use App\Repositories\Order\OrderRepositoryInterface; use App\Repositories\Transaction\TransactionRepositoryInterface; @@ -17,7 +16,6 @@ class CustomerBillingService use ResolvesCustomer; public function __construct( - private readonly CustomerRepositoryInterface $customerRepository, private readonly InvoiceRepositoryInterface $invoiceRepository, private readonly TransactionRepositoryInterface $transactionRepository, private readonly OrderRepositoryInterface $orderRepository, diff --git a/app/Services/Customer/CustomerImpersonationService.php b/app/Services/Customer/CustomerImpersonationService.php index 9f88f5e..4b8ab51 100644 --- a/app/Services/Customer/CustomerImpersonationService.php +++ b/app/Services/Customer/CustomerImpersonationService.php @@ -5,7 +5,6 @@ namespace App\Services\Customer; use App\Http\Resources\Api\Client\ClientResource; use App\Models\Log; use App\Models\User; -use App\Repositories\Customer\CustomerRepositoryInterface; use App\Services\Api\Client\ClientAuthService; use App\Services\Customer\Concerns\ResolvesCustomer; use Illuminate\Http\Request; @@ -20,7 +19,6 @@ class CustomerImpersonationService private const TTL_SECONDS = 120; public function __construct( - private readonly CustomerRepositoryInterface $customerRepository, private readonly ClientAuthService $clientAuthService, ) {} diff --git a/app/Services/Customer/CustomerNoteService.php b/app/Services/Customer/CustomerNoteService.php index eb5a4c5..a4dbdce 100644 --- a/app/Services/Customer/CustomerNoteService.php +++ b/app/Services/Customer/CustomerNoteService.php @@ -3,7 +3,6 @@ namespace App\Services\Customer; use App\Models\Note; -use App\Repositories\Customer\CustomerRepositoryInterface; use App\Services\Customer\Concerns\ResolvesCustomer; use Illuminate\Support\Collection; @@ -11,10 +10,6 @@ class CustomerNoteService { use ResolvesCustomer; - public function __construct( - private readonly CustomerRepositoryInterface $customerRepository, - ) {} - /** * Ghi chú của khách hàng (không gắn với dịch vụ cụ thể). */ diff --git a/app/Services/Customer/CustomerServiceProvisioningService.php b/app/Services/Customer/CustomerServiceProvisioningService.php index 7f4748c..7f60ff4 100644 --- a/app/Services/Customer/CustomerServiceProvisioningService.php +++ b/app/Services/Customer/CustomerServiceProvisioningService.php @@ -16,7 +16,6 @@ use App\Models\Product; use App\Models\Service; use App\Models\Transaction; use App\Models\VirtualMachines; -use App\Repositories\Customer\CustomerRepositoryInterface; use App\Repositories\Module\Hosting\HostingRepositoryInterface as ModuleHostingRepositoryInterface; use App\Repositories\Module\Vmware\VmwareRepositoryInterface; use App\Repositories\Service\ServiceRepositoryInterface; @@ -33,45 +32,11 @@ class CustomerServiceProvisioningService use ResolvesCustomer; public function __construct( - private readonly CustomerRepositoryInterface $customerRepository, private readonly ModuleHostingRepositoryInterface $moduleHostingRepository, private readonly VmwareRepositoryInterface $vmwareRepository, private readonly ServiceRepositoryInterface $serviceRepository, ) {} - public function getProducts(string $uuid): Collection - { - $customer = $this->resolveCustomer($uuid); - - $hostingServices = $this->moduleHostingRepository->filter(['customer_id' => $customer->id], ['server']); - $vpsServices = $this->vmwareRepository->filter( - ['customer_id' => $customer->id], - ['ipaddress.host.datacenter.vCenter', 'os.osGroup'], - ); - - $allService = collect(); - - foreach ($hostingServices as $hosting) { - $allService->push([ - 'product_id' => $hosting->id, - 'product_type' => 3, - 'name' => $hosting->name, - 'description' => $hosting->name . ' (Hosting)', - ]); - } - - foreach ($vpsServices as $vps) { - $allService->push([ - 'product_id' => $vps->id, - 'product_type' => 4, - 'name' => $vps->name, - 'description' => $vps->name . ' (VPS)', - ]); - } - - return $allService->values(); - } - public function getServices(string $uuid): Collection { $services = $this->serviceRepository->filter( @@ -397,5 +362,4 @@ class CustomerServiceProvisioningService return $vm; }); } - } diff --git a/app/Services/Imports/AdminZoneDnsSqlImporter.php b/app/Services/Imports/AdminZoneDnsSqlImporter.php index 101b049..53140ee 100644 --- a/app/Services/Imports/AdminZoneDnsSqlImporter.php +++ b/app/Services/Imports/AdminZoneDnsSqlImporter.php @@ -2,14 +2,19 @@ namespace App\Services\Imports; -use App\Models\Customer; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; -use Illuminate\Support\Str; use RuntimeException; +use App\Services\Imports\Concerns\ParsesSqlDump; +use App\Services\Imports\Concerns\ImportsAdminZoneCustomers; +use App\Services\Imports\Concerns\ImportsAdminZoneDns; class AdminZoneDnsSqlImporter { + use ParsesSqlDump; + use ImportsAdminZoneCustomers; + use ImportsAdminZoneDns; + private ?int $canonicalServerId = null; public function import( @@ -101,480 +106,6 @@ class AdminZoneDnsSqlImporter } } - private function importCustomers(array $rows, array &$summary, bool $preview): array - { - $idMap = []; - - foreach ($rows as $row) { - $sourceId = (int) ($row['id'] ?? 0); - if ($sourceId <= 0) { - $summary['customers']['skipped']++; - continue; - } - - $match = $this->findCustomerMatch($row); - $payload = $this->buildCustomerPayload($row); - - if (! $match) { - $existingById = Customer::withTrashed()->find($sourceId); - - if ($preview) { - $summary['customers']['created']++; - $idMap[$sourceId] = $existingById?->id ?? $sourceId; - continue; - } - - if (! $existingById) { - $payload['id'] = $sourceId; - } - - $payload['uuid'] = (string) Str::uuid(); - - $customerId = DB::table('customers')->insertGetId($payload); - $summary['customers']['created']++; - $idMap[$sourceId] = $customerId; - continue; - } - - $idMap[$sourceId] = $match->id; - - if ($preview) { - $summary['customers']['updated']++; - continue; - } - - $restored = method_exists($match, 'trashed') && $match->trashed(); - if ($restored) { - $match->restore(); - $summary['customers']['restored']++; - } - - DB::table('customers') - ->where('id', $match->id) - ->update($payload); - - $summary['customers']['updated']++; - } - - return $idMap; - } - - private function importDnsDomains(array $rows, array &$summary, bool $preview): array - { - $idMap = []; - - foreach ($rows as $row) { - $sourceId = (int) ($row['id'] ?? 0); - if ($sourceId <= 0) { - $summary['dns_domains']['skipped']++; - continue; - } - - $domainName = trim((string) ($row['domain_name'] ?? '')); - if ($domainName === '') { - $summary['dns_domains']['skipped']++; - continue; - } - - $existing = DB::table('dns_manager_domain')->where('id', $sourceId)->first(); - if (! $existing) { - $existing = DB::table('dns_manager_domain')->where('domain_name', $domainName)->first(); - } - - $targetId = $existing?->id ?? $sourceId; - $idMap[$sourceId] = (int) $targetId; - - $payload = [ - 'domain_name' => $domainName, - 'dns_manager_server_id' => $this->resolveDnsServerId((int) ($row['dns_manager_server_id'] ?? 0)), - 'is_created' => (int) ($row['is_created'] ?? 0), - 'plesk_id' => $this->nullableInt($row['plesk_id'] ?? null), - 'created_at' => $this->normalizeDateTime($row['created_at'] ?? null) ?? now(), - 'updated_at' => $this->normalizeDateTime($row['updated_at'] ?? null) ?? now(), - ]; - - if ($preview) { - $summary['dns_domains'][$existing ? 'updated' : 'created']++; - continue; - } - - if ($existing) { - DB::table('dns_manager_domain')->where('id', $existing->id)->update($payload); - $summary['dns_domains']['updated']++; - continue; - } - - $payload['id'] = $sourceId; - DB::table('dns_manager_domain')->insert($payload); - $summary['dns_domains']['created']++; - } - - return $idMap; - } - - private function importDnsRecords(array $rows, array $domainIdMap, array &$summary, bool $preview): void - { - foreach ($rows as $row) { - $sourceId = (int) ($row['id'] ?? 0); - $sourceDomainId = (int) ($row['dns_manager_domain_id'] ?? 0); - $targetDomainId = $domainIdMap[$sourceDomainId] ?? null; - - if ($sourceId <= 0 || ! $targetDomainId) { - $summary['dns_records']['skipped']++; - continue; - } - - $existing = DB::table('dns_manager_ns')->where('id', $sourceId)->first(); - $payload = [ - 'dns_manager_domain_id' => $targetDomainId, - 'name' => (string) ($row['name'] ?? ''), - 'type' => (string) ($row['type'] ?? 'A'), - 'priority' => $this->nullableString($row['priority'] ?? null, '0'), - 'weight' => $this->nullableString($row['weight'] ?? null, '0'), - 'port' => $this->nullableString($row['port'] ?? null, '0'), - 'service_name' => $this->nullableString($row['service_name'] ?? null), - 'protocol' => $this->nullableString($row['protocol'] ?? null), - 'value' => $this->nullableString($row['value'] ?? null), - 'plesk_id' => $this->nullableInt($row['plesk_id'] ?? null), - 'ttl' => (int) ($row['ttl'] ?? 300), - 'created_at' => $this->normalizeDateTime($row['created_at'] ?? null) ?? now(), - 'updated_at' => $this->normalizeDateTime($row['updated_at'] ?? null) ?? now(), - ]; - - if (Schema::hasColumn('dns_manager_ns', 'type_valid')) { - $payload['type_valid'] = $existing?->type_valid ?? (string) ($row['type'] ?? 'A'); - } - - if ($preview) { - $summary['dns_records'][$existing ? 'updated' : 'created']++; - continue; - } - - if ($existing) { - DB::table('dns_manager_ns')->where('id', $sourceId)->update($payload); - $summary['dns_records']['updated']++; - continue; - } - - $payload['id'] = $sourceId; - DB::table('dns_manager_ns')->insert($payload); - $summary['dns_records']['created']++; - } - } - - private function importDnsCustomerLinks( - array $rows, - array $customerIdMap, - array $domainIdMap, - array &$summary, - bool $preview, - ): void { - foreach ($rows as $row) { - $domainId = $domainIdMap[(int) ($row['domain_id'] ?? 0)] ?? null; - $customerId = $customerIdMap[(int) ($row['user_id'] ?? 0)] ?? null; - - if (! $domainId || ! $customerId) { - $summary['dns_links']['skipped']++; - continue; - } - - $existing = DB::table('dns_domain_user') - ->where('domain_id', $domainId) - ->where('user_id', $customerId) - ->first(); - - $payload = [ - 'domain_id' => $domainId, - 'user_id' => $customerId, - 'created_at' => $this->normalizeDateTime($row['created_at'] ?? null) ?? now(), - 'updated_at' => $this->normalizeDateTime($row['updated_at'] ?? null) ?? now(), - ]; - - if ($preview) { - $summary['dns_links'][$existing ? 'updated' : 'created']++; - continue; - } - - if ($existing) { - DB::table('dns_domain_user') - ->where('id', $existing->id) - ->update(['updated_at' => $payload['updated_at']]); - $summary['dns_links']['updated']++; - continue; - } - - DB::table('dns_domain_user')->insert($payload); - $summary['dns_links']['created']++; - } - } - - private function findCustomerMatch(array $row): ?Customer - { - $email = $this->nullableString($row['email'] ?? null); - if ($email) { - $match = Customer::withTrashed()->where('email', $email)->first(); - if ($match) { - return $match; - } - } - - $username = $this->nullableString($row['username'] ?? null); - if ($username && Schema::hasColumn('customers', 'username')) { - return Customer::withTrashed()->where('username', $username)->first(); - } - - return null; - } - - private function buildCustomerPayload(array $row): array - { - $email = $this->nullableString($row['email'] ?? null); - - return [ - 'fullname' => $this->nullableString($row['name'] ?? null) ?? $this->nullableString($row['username'] ?? null) ?? $email, - 'username' => $this->nullableString($row['username'] ?? null), - 'email' => $email, - 'email_verified_at' => $this->normalizeDateTime($row['email_verified_at'] ?? null), - 'password' => $this->nullableString($row['password'] ?? null), - 'type' => $this->nullableString($row['type'] ?? null, 'guest'), - 'remember_token' => $this->nullableString($row['remember_token'] ?? null), - 'status' => $this->mapCustomerStatus($row['status'] ?? null), - 'created_at' => $this->normalizeDateTime($row['created_at'] ?? null) ?? now(), - 'updated_at' => $this->normalizeDateTime($row['updated_at'] ?? null) ?? now(), - ]; - } - - private function mapCustomerStatus(mixed $status): int - { - return match (strtolower(trim((string) $status))) { - 'verified', 'active', 'activated', '1' => 1, - 'unverified', 'inactive', 'deactivated', '0', '2' => 2, - default => 2, - }; - } - - private function resolveDnsServerId(int $sourceServerId): int - { - if ($sourceServerId > 0 && DB::table('dns_manager_servers')->where('id', $sourceServerId)->exists()) { - return $sourceServerId; - } - - if ($this->canonicalServerId !== null) { - return $this->canonicalServerId; - } - - $server = DB::table('dns_manager_servers') - ->select('id') - ->orderByRaw('CASE WHEN id = 1 THEN 0 ELSE 1 END') - ->orderBy('id') - ->first(); - - if (! $server) { - throw new RuntimeException('No dns_manager_servers available to map imported domains.'); - } - - return $this->canonicalServerId = (int) $server->id; - } - - private function parseSqlDump(string $path, array $tables): array - { - $result = []; - foreach ($tables as $table) { - $result[$table] = []; - } - - $handle = fopen($path, 'rb'); - if (! $handle) { - throw new RuntimeException("Unable to open SQL file: {$path}"); - } - - $activeTable = null; - $activeColumns = []; - $buffer = ''; - $state = ['in_string' => false, 'escaped' => false]; - - while (($line = fgets($handle)) !== false) { - if ($activeTable === null) { - $trimmed = trim($line); - if ( - preg_match('/^INSERT INTO `([^`]+)` \((.+)\) VALUES$/', $trimmed, $matches) - && in_array($matches[1], $tables, true) - ) { - $activeTable = $matches[1]; - $activeColumns = $this->parseColumns($matches[2]); - $buffer = ''; - $state = ['in_string' => false, 'escaped' => false]; - } - - continue; - } - - $buffer .= $line; - - if (! $this->lineEndsStatement($line, $state)) { - continue; - } - - $valueBlock = preg_replace('/;\s*$/', '', trim($buffer)); - $result[$activeTable] = array_merge( - $result[$activeTable], - $this->parseValueBlock($valueBlock, $activeColumns) - ); - - $activeTable = null; - $activeColumns = []; - $buffer = ''; - $state = ['in_string' => false, 'escaped' => false]; - } - - fclose($handle); - - return $result; - } - - private function parseColumns(string $columnSegment): array - { - preg_match_all('/`([^`]+)`/', $columnSegment, $matches); - return $matches[1] ?? []; - } - - private function lineEndsStatement(string $line, array &$state): bool - { - $length = strlen($line); - - for ($i = 0; $i < $length; $i++) { - $char = $line[$i]; - - if ($state['in_string']) { - if ($state['escaped']) { - $state['escaped'] = false; - continue; - } - - if ($char === '\\') { - $state['escaped'] = true; - continue; - } - - if ($char === "'") { - $state['in_string'] = false; - } - - continue; - } - - if ($char === "'") { - $state['in_string'] = true; - continue; - } - - if ($char === ';') { - return true; - } - } - - return false; - } - - private function parseValueBlock(string $valueBlock, array $columns): array - { - $rows = []; - $fields = []; - $field = ''; - $depth = 0; - $inString = false; - $escaped = false; - $length = strlen($valueBlock); - - for ($i = 0; $i < $length; $i++) { - $char = $valueBlock[$i]; - - if ($inString) { - $field .= $char; - - if ($escaped) { - $escaped = false; - continue; - } - - if ($char === '\\') { - $escaped = true; - continue; - } - - if ($char === "'") { - $inString = false; - } - - continue; - } - - if ($char === "'") { - $inString = true; - $field .= $char; - continue; - } - - if ($char === '(') { - if ($depth > 0) { - $field .= $char; - } - $depth++; - continue; - } - - if ($char === ')') { - if ($depth > 1) { - $field .= $char; - $depth--; - continue; - } - - $fields[] = $this->normalizeSqlValue($field); - $rows[] = array_combine($columns, $fields); - $fields = []; - $field = ''; - $depth = 0; - continue; - } - - if ($char === ',' && $depth === 1) { - $fields[] = $this->normalizeSqlValue($field); - $field = ''; - continue; - } - - if ($depth >= 1) { - $field .= $char; - } - } - - return $rows; - } - - private function normalizeSqlValue(string $value): mixed - { - $value = trim($value); - - if (strcasecmp($value, 'NULL') === 0) { - return null; - } - - if ($value === '') { - return ''; - } - - if ($value[0] === "'" && substr($value, -1) === "'") { - return stripcslashes(substr($value, 1, -1)); - } - - if (is_numeric($value)) { - return str_contains($value, '.') ? (float) $value : (int) $value; - } - - return $value; - } - private function normalizeDateTime(mixed $value): ?string { $value = $this->nullableString($value); diff --git a/app/Services/Imports/Concerns/ImportsAdminZoneCustomers.php b/app/Services/Imports/Concerns/ImportsAdminZoneCustomers.php new file mode 100644 index 0000000..b7dcca5 --- /dev/null +++ b/app/Services/Imports/Concerns/ImportsAdminZoneCustomers.php @@ -0,0 +1,114 @@ +findCustomerMatch($row); + $payload = $this->buildCustomerPayload($row); + + if (! $match) { + $existingById = Customer::withTrashed()->find($sourceId); + + if ($preview) { + $summary['customers']['created']++; + $idMap[$sourceId] = $existingById?->id ?? $sourceId; + continue; + } + + if (! $existingById) { + $payload['id'] = $sourceId; + } + + $payload['uuid'] = (string) Str::uuid(); + + $customerId = DB::table('customers')->insertGetId($payload); + $summary['customers']['created']++; + $idMap[$sourceId] = $customerId; + continue; + } + + $idMap[$sourceId] = $match->id; + + if ($preview) { + $summary['customers']['updated']++; + continue; + } + + $restored = method_exists($match, 'trashed') && $match->trashed(); + if ($restored) { + $match->restore(); + $summary['customers']['restored']++; + } + + DB::table('customers') + ->where('id', $match->id) + ->update($payload); + + $summary['customers']['updated']++; + } + + return $idMap; + } + + private function findCustomerMatch(array $row): ?Customer + { + $email = $this->nullableString($row['email'] ?? null); + if ($email) { + $match = Customer::withTrashed()->where('email', $email)->first(); + if ($match) { + return $match; + } + } + + $username = $this->nullableString($row['username'] ?? null); + if ($username && Schema::hasColumn('customers', 'username')) { + return Customer::withTrashed()->where('username', $username)->first(); + } + + return null; + } + + private function buildCustomerPayload(array $row): array + { + $email = $this->nullableString($row['email'] ?? null); + + return [ + 'fullname' => $this->nullableString($row['name'] ?? null) ?? $this->nullableString($row['username'] ?? null) ?? $email, + 'username' => $this->nullableString($row['username'] ?? null), + 'email' => $email, + 'email_verified_at' => $this->normalizeDateTime($row['email_verified_at'] ?? null), + 'password' => $this->nullableString($row['password'] ?? null), + 'type' => $this->nullableString($row['type'] ?? null, 'guest'), + 'remember_token' => $this->nullableString($row['remember_token'] ?? null), + 'status' => $this->mapCustomerStatus($row['status'] ?? null), + 'created_at' => $this->normalizeDateTime($row['created_at'] ?? null) ?? now(), + 'updated_at' => $this->normalizeDateTime($row['updated_at'] ?? null) ?? now(), + ]; + } + + private function mapCustomerStatus(mixed $status): int + { + return match (strtolower(trim((string) $status))) { + 'verified', 'active', 'activated', '1' => 1, + 'unverified', 'inactive', 'deactivated', '0', '2' => 2, + default => 2, + }; + } +} diff --git a/app/Services/Imports/Concerns/ImportsAdminZoneDns.php b/app/Services/Imports/Concerns/ImportsAdminZoneDns.php new file mode 100644 index 0000000..f106fee --- /dev/null +++ b/app/Services/Imports/Concerns/ImportsAdminZoneDns.php @@ -0,0 +1,182 @@ +where('id', $sourceId)->first(); + if (! $existing) { + $existing = DB::table('dns_manager_domain')->where('domain_name', $domainName)->first(); + } + + $targetId = $existing?->id ?? $sourceId; + $idMap[$sourceId] = (int) $targetId; + + $payload = [ + 'domain_name' => $domainName, + 'dns_manager_server_id' => $this->resolveDnsServerId((int) ($row['dns_manager_server_id'] ?? 0)), + 'is_created' => (int) ($row['is_created'] ?? 0), + 'plesk_id' => $this->nullableInt($row['plesk_id'] ?? null), + 'created_at' => $this->normalizeDateTime($row['created_at'] ?? null) ?? now(), + 'updated_at' => $this->normalizeDateTime($row['updated_at'] ?? null) ?? now(), + ]; + + if ($preview) { + $summary['dns_domains'][$existing ? 'updated' : 'created']++; + continue; + } + + if ($existing) { + DB::table('dns_manager_domain')->where('id', $existing->id)->update($payload); + $summary['dns_domains']['updated']++; + continue; + } + + $payload['id'] = $sourceId; + DB::table('dns_manager_domain')->insert($payload); + $summary['dns_domains']['created']++; + } + + return $idMap; + } + + private function importDnsRecords(array $rows, array $domainIdMap, array &$summary, bool $preview): void + { + foreach ($rows as $row) { + $sourceId = (int) ($row['id'] ?? 0); + $sourceDomainId = (int) ($row['dns_manager_domain_id'] ?? 0); + $targetDomainId = $domainIdMap[$sourceDomainId] ?? null; + + if ($sourceId <= 0 || ! $targetDomainId) { + $summary['dns_records']['skipped']++; + continue; + } + + $existing = DB::table('dns_manager_ns')->where('id', $sourceId)->first(); + $payload = [ + 'dns_manager_domain_id' => $targetDomainId, + 'name' => (string) ($row['name'] ?? ''), + 'type' => (string) ($row['type'] ?? 'A'), + 'priority' => $this->nullableString($row['priority'] ?? null, '0'), + 'weight' => $this->nullableString($row['weight'] ?? null, '0'), + 'port' => $this->nullableString($row['port'] ?? null, '0'), + 'service_name' => $this->nullableString($row['service_name'] ?? null), + 'protocol' => $this->nullableString($row['protocol'] ?? null), + 'value' => $this->nullableString($row['value'] ?? null), + 'plesk_id' => $this->nullableInt($row['plesk_id'] ?? null), + 'ttl' => (int) ($row['ttl'] ?? 300), + 'created_at' => $this->normalizeDateTime($row['created_at'] ?? null) ?? now(), + 'updated_at' => $this->normalizeDateTime($row['updated_at'] ?? null) ?? now(), + ]; + + if (Schema::hasColumn('dns_manager_ns', 'type_valid')) { + $payload['type_valid'] = $existing?->type_valid ?? (string) ($row['type'] ?? 'A'); + } + + if ($preview) { + $summary['dns_records'][$existing ? 'updated' : 'created']++; + continue; + } + + if ($existing) { + DB::table('dns_manager_ns')->where('id', $sourceId)->update($payload); + $summary['dns_records']['updated']++; + continue; + } + + $payload['id'] = $sourceId; + DB::table('dns_manager_ns')->insert($payload); + $summary['dns_records']['created']++; + } + } + + private function importDnsCustomerLinks( + array $rows, + array $customerIdMap, + array $domainIdMap, + array &$summary, + bool $preview, + ): void { + foreach ($rows as $row) { + $domainId = $domainIdMap[(int) ($row['domain_id'] ?? 0)] ?? null; + $customerId = $customerIdMap[(int) ($row['user_id'] ?? 0)] ?? null; + + if (! $domainId || ! $customerId) { + $summary['dns_links']['skipped']++; + continue; + } + + $existing = DB::table('dns_domain_user') + ->where('domain_id', $domainId) + ->where('user_id', $customerId) + ->first(); + + $payload = [ + 'domain_id' => $domainId, + 'user_id' => $customerId, + 'created_at' => $this->normalizeDateTime($row['created_at'] ?? null) ?? now(), + 'updated_at' => $this->normalizeDateTime($row['updated_at'] ?? null) ?? now(), + ]; + + if ($preview) { + $summary['dns_links'][$existing ? 'updated' : 'created']++; + continue; + } + + if ($existing) { + DB::table('dns_domain_user') + ->where('id', $existing->id) + ->update(['updated_at' => $payload['updated_at']]); + $summary['dns_links']['updated']++; + continue; + } + + DB::table('dns_domain_user')->insert($payload); + $summary['dns_links']['created']++; + } + } + + private function resolveDnsServerId(int $sourceServerId): int + { + if ($sourceServerId > 0 && DB::table('dns_manager_servers')->where('id', $sourceServerId)->exists()) { + return $sourceServerId; + } + + if ($this->canonicalServerId !== null) { + return $this->canonicalServerId; + } + + $server = DB::table('dns_manager_servers') + ->select('id') + ->orderByRaw('CASE WHEN id = 1 THEN 0 ELSE 1 END') + ->orderBy('id') + ->first(); + + if (! $server) { + throw new RuntimeException('No dns_manager_servers available to map imported domains.'); + } + + return $this->canonicalServerId = (int) $server->id; + } +} diff --git a/app/Services/Imports/Concerns/ParsesSqlDump.php b/app/Services/Imports/Concerns/ParsesSqlDump.php new file mode 100644 index 0000000..1626059 --- /dev/null +++ b/app/Services/Imports/Concerns/ParsesSqlDump.php @@ -0,0 +1,207 @@ + false, 'escaped' => false]; + + while (($line = fgets($handle)) !== false) { + if ($activeTable === null) { + $trimmed = trim($line); + if ( + preg_match('/^INSERT INTO `([^`]+)` \((.+)\) VALUES$/', $trimmed, $matches) + && in_array($matches[1], $tables, true) + ) { + $activeTable = $matches[1]; + $activeColumns = $this->parseColumns($matches[2]); + $buffer = ''; + $state = ['in_string' => false, 'escaped' => false]; + } + + continue; + } + + $buffer .= $line; + + if (! $this->lineEndsStatement($line, $state)) { + continue; + } + + $valueBlock = preg_replace('/;\s*$/', '', trim($buffer)); + $result[$activeTable] = array_merge( + $result[$activeTable], + $this->parseValueBlock($valueBlock, $activeColumns) + ); + + $activeTable = null; + $activeColumns = []; + $buffer = ''; + $state = ['in_string' => false, 'escaped' => false]; + } + + fclose($handle); + + return $result; + } + + private function parseColumns(string $columnSegment): array + { + preg_match_all('/`([^`]+)`/', $columnSegment, $matches); + return $matches[1] ?? []; + } + + private function lineEndsStatement(string $line, array &$state): bool + { + $length = strlen($line); + + for ($i = 0; $i < $length; $i++) { + $char = $line[$i]; + + if ($state['in_string']) { + if ($state['escaped']) { + $state['escaped'] = false; + continue; + } + + if ($char === '\\') { + $state['escaped'] = true; + continue; + } + + if ($char === "'") { + $state['in_string'] = false; + } + + continue; + } + + if ($char === "'") { + $state['in_string'] = true; + continue; + } + + if ($char === ';') { + return true; + } + } + + return false; + } + + private function parseValueBlock(string $valueBlock, array $columns): array + { + $rows = []; + $fields = []; + $field = ''; + $depth = 0; + $inString = false; + $escaped = false; + $length = strlen($valueBlock); + + for ($i = 0; $i < $length; $i++) { + $char = $valueBlock[$i]; + + if ($inString) { + $field .= $char; + + if ($escaped) { + $escaped = false; + continue; + } + + if ($char === '\\') { + $escaped = true; + continue; + } + + if ($char === "'") { + $inString = false; + } + + continue; + } + + if ($char === "'") { + $inString = true; + $field .= $char; + continue; + } + + if ($char === '(') { + if ($depth > 0) { + $field .= $char; + } + $depth++; + continue; + } + + if ($char === ')') { + if ($depth > 1) { + $field .= $char; + $depth--; + continue; + } + + $fields[] = $this->normalizeSqlValue($field); + $rows[] = array_combine($columns, $fields); + $fields = []; + $field = ''; + $depth = 0; + continue; + } + + if ($char === ',' && $depth === 1) { + $fields[] = $this->normalizeSqlValue($field); + $field = ''; + continue; + } + + if ($depth >= 1) { + $field .= $char; + } + } + + return $rows; + } + + private function normalizeSqlValue(string $value): mixed + { + $value = trim($value); + + if (strcasecmp($value, 'NULL') === 0) { + return null; + } + + if ($value === '') { + return ''; + } + + if ($value[0] === "'" && substr($value, -1) === "'") { + return stripcslashes(substr($value, 1, -1)); + } + + if (is_numeric($value)) { + return str_contains($value, '.') ? (float) $value : (int) $value; + } + + return $value; + } +} diff --git a/app/Services/InvoicePaymentService.php b/app/Services/InvoicePaymentService.php index 566712b..f3198d3 100644 --- a/app/Services/InvoicePaymentService.php +++ b/app/Services/InvoicePaymentService.php @@ -3,29 +3,19 @@ namespace App\Services; use App\Enums\Invoice\InvoiceStatus; -use App\Enums\Order\OrderStatus; use App\Enums\TransactionStatus; -use App\Helpers\OrderHelper; -use App\Jobs\Hosting\ProvisionHostingAccountJob; -use App\Jobs\VirtualMachine\Action\VMJobs; -use App\Jobs\VirtualMachine\Create\CreateVirtualMachineJob; -use App\Jobs\VirtualMachine\Provision\UpdateVmConfigJob; -use App\Http\Services\vCenter as ServiceVCenter; -use App\Models\Addon; use App\Models\Customer; use App\Models\Invoice; use App\Models\Log as CustomerActivityLog; -use App\Models\OrderItem; -use App\Models\Service; -use App\Models\ServiceAddon; use App\Models\Transaction; -use Carbon\Carbon; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; +use App\Services\Concerns\ActivatesPaidInvoices; class InvoicePaymentService { + use ActivatesPaidInvoices; + /** Trạng thái hoá đơn còn có thể thu tiền. Dùng chung cho cả khách trả lẫn admin xác nhận. */ public const PAYABLE_STATUSES = [InvoiceStatus::UNPAID->value, InvoiceStatus::OVERDUE->value]; @@ -307,228 +297,4 @@ class InvoicePaymentService ] ); } - - private function activateOrder(Invoice $invoice): void - { - $order = $invoice->order?->loadMissing(['orderItems.product', 'customer', 'invoice']); - if (! $order) { - return; - } - - // payment_method/paid_at always mirror the invoice that was just paid, even if the - // order's status doesn't change (e.g. it's already ACTIVE from an earlier payment). - $order->update([ - 'status' => OrderStatus::ACTIVE->value, - 'payment_method' => $invoice->payment_method, - 'paid_at' => $invoice->paid_at, - ]); - - foreach ($order->orderItems as $item) { - $isHostingProvision = ! $item->os_id - && $item->server_name - && $item->product?->product_type === 'hosting'; - - $service = Service::firstOrCreate( - ['order_item_id' => $item->id], - [ - 'customer_id' => $order->customer_id, - 'product_id' => $item->product_id, - 'invoice_id' => $invoice->id, - 'server_name' => $item->server_name, - 'status' => ($item->os_id || $isHostingProvision) ? 'pending' : 'active', - 'auto_renew' => false, - 'started_at' => now(), - 'expires_at' => now()->addMonths($item->months ?: OrderHelper::cycleToMonths($item->billing_cycle)), - ] - ); - - $this->attachServiceAddons($service, $item); - - if ($service->wasRecentlyCreated && $item->product_id) { - if ($item->os_id) { - dispatch(new CreateVirtualMachineJob($item)); - } elseif ($isHostingProvision) { - dispatch(new ProvisionHostingAccountJob($item)); - } - } - } - } - - private function activateRenewal(Invoice $invoice): void - { - $service = Service::query() - ->with(['orderItem.product']) - ->where('invoice_id', $invoice->id) - ->first(); - - if (! $service) { - return; - } - - if ($invoice->order) { - // Same rule as activateOrder(): payment_method/paid_at always mirror the invoice - // that was just paid, regardless of whether the order's status actually changes. - $invoice->order->update([ - 'status' => OrderStatus::ACTIVE->value, - 'payment_method' => $invoice->payment_method, - 'paid_at' => $invoice->paid_at, - ]); - } - - $months = (int) ($invoice->invoiceItems()->where('type', 'renewal')->value('months') ?: $service->orderItem?->months ?: 1); - $baseDate = $service->expires_at && $service->expires_at->isFuture() - ? $service->expires_at - : now(); - $expiresAt = Carbon::parse($baseDate)->addMonths($months); - - $service->update([ - 'status' => 'active', - 'expires_at' => $expiresAt, - 'suspended_at' => null, - ]); - - if ($service->vm_id) { - dispatch(new VMJobs((int) $service->vm_id, 'Start', false))->delay(now()->addSeconds(5)); - } - } - - /** - * Merges the purchased addons into order_items.addons_meta (so RenewalPricing charges - * them on the next renewal) and applies the new absolute CPU/RAM/disk totals to the VM. - */ - private function activateUpgrade(Invoice $invoice): void - { - $service = Service::query() - ->with(['orderItem.product', 'vmware']) - ->find($invoice->service_id); - - $orderItem = $service?->orderItem; - if (! $service || ! $orderItem) { - return; - } - - $upgradeItems = $invoice->invoiceItems()->where('type', 'upgrade')->get(); - if ($upgradeItems->isEmpty()) { - return; - } - - $meta = collect(is_array($orderItem->addons_meta) ? $orderItem->addons_meta : []); - $purchasedCodes = []; - - foreach ($upgradeItems as $item) { - $addon = Addon::find($item->addon_id); - if (! $addon) { - continue; - } - - $code = strtolower((string) $addon->code); - $purchasedCodes[] = $code; - - $qty = (int) $item->quantity; - $index = $meta->search(fn ($entry) => (int) ($entry['addon_id'] ?? 0) === $addon->id); - $currentQty = $index === false ? 0 : (int) ($meta->get($index)['qty'] ?? 0); - $newQty = $currentQty + $qty; - - $entry = [ - 'addon_id' => $addon->id, - 'code' => $code, - 'name' => $addon->name, - 'qty' => $newQty, - 'price' => (float) $addon->price, - 'subtotal' => $newQty * (float) $addon->price, - ]; - - if ($index === false) { - $meta->push($entry); - } else { - $meta->put($index, $entry); - } - } - - $orderItem->update(['addons_meta' => $meta->values()->all()]); - - $moduleVmware = $service->vmware; - if (! $moduleVmware) { - return; - } - - $addonQtyByCode = $meta->keyBy(fn ($entry) => $entry['code'] ?? ''); - $product = $orderItem->product; - - if (array_intersect(['cpu', 'ram'], $purchasedCodes)) { - $totalCpu = (int) ($product?->cpu ?? 0) + (int) ($addonQtyByCode->get('cpu')['qty'] ?? 0); - $totalRam = (int) ($product?->ram ?? 0) + (int) ($addonQtyByCode->get('ram')['qty'] ?? 0); - - UpdateVmConfigJob::dispatch( - $moduleVmware->id, - (string) $moduleVmware->vmware_id, - '', - $totalCpu, - $totalRam, - )->afterCommit(); - } - - if (in_array('disk', $purchasedCodes, true)) { - $totalDisk = (int) ($product?->disk ?? 0) + (int) ($addonQtyByCode->get('disk')['qty'] ?? 0); - $vcenter = $moduleVmware->vCenter ?? $moduleVmware->ipAddress?->host?->datacenter?->vCenter; - - if ($vcenter) { - DB::afterCommit(function () use ($vcenter, $moduleVmware, $totalDisk, $invoice) { - try { - $serviceVCenter = new ServiceVCenter(); - if (! $serviceVCenter->loginServer($vcenter)) { - throw new \RuntimeException('vCenter login failed'); - } - $serviceVCenter->UpgradeDiskVirtualMachine($moduleVmware, $totalDisk); - } catch (\Throwable $e) { - Log::error('activateUpgrade: disk upgrade failed', [ - 'invoice_id' => $invoice->id, - 'module_vmware_id' => $moduleVmware->id, - 'total_disk' => $totalDisk, - 'error' => $e->getMessage(), - ]); - } - }); - } - } - } - - private function attachServiceAddons(Service $service, OrderItem $item): void - { - if ($service->addons()->exists()) { - return; - } - - $product = $item->product; - if ($product) { - foreach (['cpu' => $product->cpu, 'ram' => $product->ram, 'disk' => $product->disk, 'bandwidth' => $product->bandwidth] as $type => $value) { - if ($value !== null && $value !== '') { - ServiceAddon::create([ - 'service_id' => $service->id, - 'addon_type' => $type, - 'addon_value' => (string) $value, - 'price' => 0, - ]); - } - } - } - - $addonsMeta = is_array($item->addons_meta) - ? $item->addons_meta - : json_decode($item->addons_meta ?? '[]', true); - - foreach ((array) $addonsMeta as $meta) { - $addon = Addon::find($meta['addon_id'] ?? null); - if (!$addon) { - continue; - } - ServiceAddon::create([ - 'service_id' => $service->id, - 'addon_type' => strtolower((string) $addon->code), - 'addon_value' => (string) ($meta['qty'] ?? 1), - 'price' => (int) ($meta['price'] ?? $addon->price), - ]); - } - } - } diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php index 04931b8..78050b5 100644 --- a/app/Services/OrderService.php +++ b/app/Services/OrderService.php @@ -22,7 +22,6 @@ use App\Repositories\Invoice\InvoiceRepositoryInterface; use App\Repositories\InvoiceItem\InvoiceItemRepositoryInterface; use App\Repositories\Module\Hosting\HostingRepositoryInterface as ModuleHostingRepositoryInterface; use App\Repositories\Module\HostingServer\HostingServerRepositoryInterface; -use App\Repositories\Module\Vmware\VmwareRepositoryInterface; use App\Repositories\Order\OrderRepositoryInterface; use Carbon\Carbon; use Illuminate\Support\Facades\Auth; @@ -36,7 +35,6 @@ class OrderService private readonly InvoiceRepositoryInterface $invoiceRepository, private readonly InvoiceItemRepositoryInterface $invoiceItemRepository, private readonly ModuleHostingRepositoryInterface $moduleHostingRepository, - private readonly VmwareRepositoryInterface $moduleVmwareRepository, private readonly HostingServerRepositoryInterface $moduleHostingServerRepository, private readonly AddonRepositoryInterface $addonRepository, ) {} diff --git a/app/Services/Plesk/PleskDomainService.php b/app/Services/Plesk/PleskDomainService.php index 7a0ffda..3553df0 100644 --- a/app/Services/Plesk/PleskDomainService.php +++ b/app/Services/Plesk/PleskDomainService.php @@ -83,80 +83,6 @@ class PleskDomainService return false; } - public function updateDomain(DnsServer $server, int $pleskDomainId, array $data = []): array|false - { - $this->lastError = null; - - if (empty($pleskDomainId) || empty($data)) { - $this->lastError = 'Invalid request'; - return false; - } - - $param = [ - 'name' => $data['domain_name'], - 'description' => '', - 'hosting_type' => 'virtual', - 'hosting_settings' => [ - 'ftp_login' => $data['ftp_login'] ?? str_replace('.', '_', $data['domain_name']), - 'ftp_password' => $data['ftp_password'] ?? Str::random(12), - ], - 'plan' => ['name' => $data['plan'] ?? 'Default Domain'], - ]; - - if (!empty($data['ip_addresses'])) { - $param['ip_addresses'] = $data['ip_addresses']; - $param['ipv4'] = $data['ip_addresses']; - } - - try { - $response = $this->plesk->client($server, 30) - ->put($this->plesk->url($server, 'domains', "/{$pleskDomainId}"), $param); - - $responseData = $response->json(); - - Log::info('Response Plesk API Updated', ['data' => $responseData]); - - if ($response->successful() && isset($responseData['id'], $responseData['guid'])) { - return $responseData; - } - - $this->lastError = $responseData['message'] ?? 'Plesk domain update failed'; - } catch (RequestException $e) { - $this->lastError = $e->response?->json('message') ?? $e->getMessage(); - Log::error('Plesk domain update failed', [ - 'error' => $e->getMessage(), - 'response' => $e->response?->body(), - 'data' => $param, - ]); - } - - return false; - } - - public function removeDomain(DnsServer $server, int $pleskDomainId): bool - { - try { - $response = $this->plesk->client($server, 30) - ->delete($this->plesk->url($server, 'domains', "/{$pleskDomainId}")); - - if ($response->successful()) { - Log::info('Xoá domain thành công', ['plesk_id' => $pleskDomainId, 'status' => $response->status()]); - return true; - } - - Log::error('Xoá domain thất bại', [ - 'plesk_id' => $pleskDomainId, - 'status' => $response->status(), - 'response' => $response->body(), - ]); - - return false; - } catch (\Exception $e) { - Log::error('Exception khi xoá domain', ['plesk_id' => $pleskDomainId, 'error' => $e->getMessage()]); - return false; - } - } - public function getDomainInfo(DnsServer $server, string $domain = ''): array|false { if (empty($domain)) { diff --git a/app/Services/ServerService.php b/app/Services/ServerService.php index 5a32e42..a0d5069 100644 --- a/app/Services/ServerService.php +++ b/app/Services/ServerService.php @@ -234,36 +234,6 @@ class ServerService return $result; } - public function updateDomain(int $pleskDomainId, array $data = []): array|false - { - if (empty($pleskDomainId) || empty($data)) { - return false; - } - - if (!$this->server) { - $this->server = $this->serverRepository->find($data['dns_manager_server_id']); - } - - $result = $this->pleskDomainService->updateDomain($this->server, $pleskDomainId, $data); - - $this->resetServer(); - - return $result; - } - - public function removeDomain(int $pleskDomainId): bool - { - if (empty($this->server)) { - return false; - } - - $result = $this->pleskDomainService->removeDomain($this->server, $pleskDomainId); - - $this->resetServer(); - - return $result; - } - public function addServerById(int $serverId): bool { $this->server = $this->serverRepository->find($serverId); diff --git a/app/Services/StudentService.php b/app/Services/StudentService.php deleted file mode 100644 index 730a2ea..0000000 --- a/app/Services/StudentService.php +++ /dev/null @@ -1,100 +0,0 @@ - $page]; - - if ($status !== null) { - $params['status'] = $status; - } - - $key = CacheKey::build(self::MODULE, 'list', $params); - - return $this->cache->remember( - key: $key, - callback: fn () => $this->fetchList($page, $status), - ttl: CacheServiceInterface::TTL_MEDIUM, - tags: [self::MODULE], - ); - } - - public function getDetail(int $id): array - { - $key = CacheKey::build(self::MODULE, 'detail', ['id' => $id]); - - return $this->cache->remember( - key: $key, - callback: fn () => $this->fetchDetail($id), - ttl: CacheServiceInterface::TTL_LONG, - tags: [self::MODULE], - ); - } - - public function update(int $id, array $_data): void - { - // ... update DB logic ... - - // Forget only the stale detail entry - $this->cache->forget( - CacheKey::build(self::MODULE, 'detail', ['id' => $id]) - ); - - // Also bust all list caches for this module - $this->cache->clearByModule(self::MODULE); - } - - public function getByIds(array $ids): array - { - // Array params are joined with "," — key: students:bulk:ids:1,2,3 - $key = CacheKey::build(self::MODULE, 'bulk', ['ids' => $ids]); - - return $this->cache->remember( - key: $key, - callback: fn () => $this->fetchByIds($ids), - ttl: CacheServiceInterface::TTL_SHORT, - tags: [self::MODULE], - ); - } - - // ------------------------------------------------------------------------- - // Private DB fetch methods (replace with real Eloquent/Repository calls) - // ------------------------------------------------------------------------- - - private function fetchList(int $_page, ?string $_status): array - { - return []; // replace with Eloquent/Repository query - } - - private function fetchDetail(int $_id): array - { - return []; // replace with Eloquent/Repository query - } - - private function fetchByIds(array $_ids): array - { - return []; // replace with Eloquent/Repository query - } -} diff --git a/app/Services/VMHelperService.php b/app/Services/VMHelperService.php deleted file mode 100644 index e1ac166..0000000 --- a/app/Services/VMHelperService.php +++ /dev/null @@ -1,117 +0,0 @@ - $status, - 'isPoweredOn' => $status === 'POWERED_ON', - 'isPoweredOff' => $status === 'POWERED_OFF', - 'displayStatus' => self::getDisplayStatus($status), - 'statusClass' => self::getStatusClass($status) - ]; - } - - /** - * Get display status text - */ - private static function getDisplayStatus(?string $status): string - { - return match($status) { - 'POWERED_ON' => __('RUNNING'), - 'POWERED_OFF' => __('STOPPED'), - default => __('UNKNOWN') - }; - } - - /** - * Get status CSS class - */ - private static function getStatusClass(?string $status): string - { - return match($status) { - 'POWERED_ON' => 'bg-green-500 text-white', - 'POWERED_OFF' => 'bg-red-500 text-white', - default => 'bg-gray-500 text-white' - }; - } - - /** - * Get VM connection information - */ - public static function getVmConnectionInfo($vmInfo, $invoice): array - { - $isWindows = self::isWindowsOS($vmInfo, $invoice); - - return [ - 'ip' => $vmInfo['ip'] ?? '103.42.56.177', - 'port' => $isWindows ? '3389' : '22', - 'user' => $isWindows ? 'administrator' : 'root', - 'isWindows' => $isWindows - ]; - } - - /** - * Check if OS is Windows - */ - private static function isWindowsOS($vmInfo, $invoice): bool - { - $vmOs = Str::lower($vmInfo['os'] ?? ''); - $invoiceOs = Str::lower($invoice->invoiceItem->first()->os->osGroup->name ?? ''); - - return Str::contains($vmOs, 'windows') || Str::contains($invoiceOs, 'windows'); - } - - /** - * Get VM specifications - */ - public static function getVmSpecs($vmInfo): array - { - return [ - 'cpu' => $vmInfo['cpu'] ?? '3', - 'ram' => $vmInfo['ram'] ?? '1 GB', - 'disk' => $vmInfo['disk'] ?? '25 GB' - ]; - } - - /** - * Format date for display - */ - public static function formatDate($date): string - { - return $date ? Carbon::parse($date)->format('d/m/Y') : 'N/A'; - } - - /** - * Format currency amount - */ - public static function formatCurrency($amount): string - { - return number_format($amount ?? 0, 0, ',', '.') . ' đ'; - } - - /** - * Get service status information - */ - public static function getServiceStatus($invoice): array - { - $isExpired = $invoice->status == 1; - - return [ - 'isExpired' => $isExpired, - 'statusText' => $isExpired ? __('Hết hạn') : __('Hoạt động'), - 'statusClass' => $isExpired ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' - ]; - } -} \ No newline at end of file diff --git a/app/Services/VirtualMachine/Rebuild/VmRebuildSwapService.php b/app/Services/VirtualMachine/Rebuild/VmRebuildSwapService.php index 29c13ae..4f0b7a2 100644 --- a/app/Services/VirtualMachine/Rebuild/VmRebuildSwapService.php +++ b/app/Services/VirtualMachine/Rebuild/VmRebuildSwapService.php @@ -12,7 +12,6 @@ use App\Models\vCenter as VCenterModel; use App\Models\VirtualMachines; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; -use Throwable; /** * VmRebuildSwapService @@ -121,40 +120,4 @@ final class VmRebuildSwapService 'new_vm_id' => $ctx->newVmId, ]); } - - /** - * Delete the old VM from vCenter. Best-effort: the DB is already swapped, - * so a leftover ghost VM is recoverable manually and must not throw past - * the queue. - */ - public function deleteOldVm(RebuildContext $ctx): void - { - if ($ctx->oldVmId === '') { - return; - } - - try { - $vcenter = VCenterModel::find($ctx->vcenterId); - if (!$vcenter || !$this->vCenter->loginServer($vcenter)) { - Log::error('VmRebuildSwapService: cannot login to delete old VM', [ - 'old_vm_id' => $ctx->oldVmId, - ]); - return; - } - - // Force power-off before delete to avoid "VM is running" rejection. - $this->vCenter->PowerOffVirtualMachine($ctx->oldVmId); - $res = $this->vCenter->DeleteVirtualMachine($ctx->oldVmId); - - Log::info('VmRebuildSwapService: old VM deleted', [ - 'old_vm_id' => $ctx->oldVmId, - 'result' => $res, - ]); - } catch (Throwable $e) { - Log::error('VmRebuildSwapService: deleteOldVm threw', [ - 'old_vm_id' => $ctx->oldVmId, - 'error' => $e->getMessage(), - ]); - } - } } diff --git a/app/Services/VirtualMachine/VmPowerService.php b/app/Services/VirtualMachine/VmPowerService.php deleted file mode 100644 index c1b0273..0000000 --- a/app/Services/VirtualMachine/VmPowerService.php +++ /dev/null @@ -1,35 +0,0 @@ -checkApiExits()) { - return 'Login_Again'; - } - - $response = TlsHttpClientFactory::vCenter() - ->withHeaders(['vmware-api-session-id' => $vCenter->token]) - ->post("https://{$vCenter->apiUri}/rest/vcenter/vm/{$vmId}/power/start"); - - if ($response->status() === 200) { - return ['error' => false, 'data' => 'success']; - } - - return ['error' => true, 'data' => $response->body()]; - } catch (Throwable $e) { - Log::error('VmPowerService: start VM failed: ' . $e->getMessage()); - return ['error' => true, 'data' => $e->getMessage()]; - } - } -} diff --git a/app/Services/VirtualMachine/VmProviderSettingsService.php b/app/Services/VirtualMachine/VmProviderSettingsService.php index 2a97fb4..4d91b45 100644 --- a/app/Services/VirtualMachine/VmProviderSettingsService.php +++ b/app/Services/VirtualMachine/VmProviderSettingsService.php @@ -79,9 +79,4 @@ class VmProviderSettingsService { return (string) $this->get('default_resource_pool', self::DEFAULTS['default_resource_pool']); } - - public function defaultDatastore(): string - { - return (string) $this->get('default_datastore', self::DEFAULTS['default_datastore']); - } } diff --git a/app/Services/VmProvisionService.php b/app/Services/VmProvisionService.php index eec3847..86c952a 100644 --- a/app/Services/VmProvisionService.php +++ b/app/Services/VmProvisionService.php @@ -5,9 +5,7 @@ declare(strict_types=1); namespace App\Services; use App\Http\Facade\SignRSA; -use App\Http\Services\vCenter as VCenterService; use App\Models\ModuleVmware; -use App\Support\TlsHttpClientFactory; use Illuminate\Support\Facades\Log; final class VmProvisionService @@ -21,48 +19,6 @@ final class VmProvisionService return config('vmware.template_password'); } - // ------------------------------------------------------------------------- - // vCenter REST helpers (used by WaitVmReadyJob if still in flow) - // ------------------------------------------------------------------------- - - public function loginVCenter(\App\Models\vCenter $vcenter, VCenterService $service): void - { - if (!$service->loginServer($vcenter)) { - throw new \RuntimeException("vCenter login failed for host={$vcenter->host}"); - } - } - - /** - * @return array{tools_running:bool,ip_address:string|null,os_family:string|null} - */ - public function getVmGuestInfo(VCenterService $service, string $vmId): array - { - try { - $response = TlsHttpClientFactory::vCenter() - ->withHeaders(['vmware-api-session-id' => $service->token]) - ->timeout(10) - ->get("https://{$service->apiUri}/rest/vcenter/vm/{$vmId}/guest/identity"); - - if (!$response->successful()) { - return ['tools_running' => false, 'ip_address' => null, 'os_family' => null]; - } - - $value = $response->json('value', []); - $ip = trim((string) ($value['ip_address'] ?? '')); - - return [ - 'tools_running' => $ip !== '' && $ip !== '0.0.0.0', - 'ip_address' => $ip ?: null, - 'os_family' => isset($value['family']) ? strtoupper((string) $value['family']) : null, - ]; - } catch (\Throwable $e) { - Log::warning('VmProvisionService::getVmGuestInfo error', [ - 'vm_id' => $vmId, 'error' => $e->getMessage(), - ]); - return ['tools_running' => false, 'ip_address' => null, 'os_family' => null]; - } - } - // ------------------------------------------------------------------------- // Password // ------------------------------------------------------------------------- @@ -124,21 +80,4 @@ final class VmProvisionService 'module_vmware_id' => $moduleVmwareId, ]); } - - /** - * Update password + overwrite IP (used when real guest IP differs from pool IP). - */ - public function savePassword(int $moduleVmwareId, string $ip, string $plainPassword): void - { - ModuleVmware::findOrFail($moduleVmwareId)->update([ - 'ip' => $ip, - 'password' => $this->encryptPassword($plainPassword), - 'plain_password' => $plainPassword, - ]); - - Log::info('VmProvisionService::savePassword saved', [ - 'module_vmware_id' => $moduleVmwareId, - 'ip' => $ip, - ]); - } } diff --git a/app/Services/WhoisService.php b/app/Services/WhoisService.php deleted file mode 100644 index 6c83aa4..0000000 --- a/app/Services/WhoisService.php +++ /dev/null @@ -1,92 +0,0 @@ -whoisRepository = $whoisRepository; - } - - /** - * Get domain whois information - * - * @param string $domain - * @return array - * @throws Exception - */ - public function getDomainInfo(string $domain): array - { - try { - // Check cache first - $cachedData = $this->whoisRepository->getDomainInfo($domain); - if ($cachedData) { - return $cachedData; - } - - // Make API request to whois.inet.vn - $response = Http::timeout(30)->get("https://whois.inet.vn/api/whois/domainspecify/{$domain}"); - - if (!$response->successful()) { - throw new Exception(__('Failed to retrieve domain information')); - } - - $data = $response->json(); - - // Cache the result - $this->whoisRepository->cacheDomainInfo($domain, $data); - - Log::info('Domain whois query successful', ['domain' => $domain]); - - return $data; - - } catch (Exception $e) { - Log::error('Domain whois query failed', [ - 'domain' => $domain, - 'error' => $e->getMessage() - ]); - - throw $e; - } - } - - /** - * Check if domain is available - * - * @param string $domain - * @return bool - */ - public function isDomainAvailable(string $domain): bool - { - try { - $info = $this->getDomainInfo($domain); - return isset($info['code']) && $info['code'] !== '0'; - } catch (Exception $e) { - return false; - } - } - - /** - * Get domain status in Vietnamese - * - * @param string $domain - * @return string - */ - public function getDomainStatus(string $domain): string - { - try { - $info = $this->getDomainInfo($domain); - return $info['message'] ?? __('Unknown status'); - } catch (Exception $e) { - return __('Unable to check domain status'); - } - } -} \ No newline at end of file diff --git a/app/Support/Breadcrumb/Breadcrumb.php b/app/Support/Breadcrumb/Breadcrumb.php index bcfbc2d..4395fa5 100644 --- a/app/Support/Breadcrumb/Breadcrumb.php +++ b/app/Support/Breadcrumb/Breadcrumb.php @@ -4,20 +4,5 @@ namespace App\Support\Breadcrumb; class Breadcrumb { - public array $breadcrumbs = []; - - public function getBreadcrumbs(): array - { - return $this->breadcrumbs; - } - - public function add(string $label, string $url = ''): static - { - $this->breadcrumbs[] = [ - 'label' => $label, - 'url' => $url - ]; - return $this; - } } diff --git a/app/Traits/GetConfig.php b/app/Traits/GetConfig.php deleted file mode 100644 index 70fa281..0000000 --- a/app/Traits/GetConfig.php +++ /dev/null @@ -1,12 +0,0 @@ -pJ%rBh82b80TiRl(51jnIcxRUA$16vk;10;Tig}>kj_yWXp z9FZ`- ztK&B^z|U@*4#-hKC2alt)~o6K!WE?yef~5QSGYW1tla|h>BIf&$>#BSe>ise?b!0F zvA=oaGRBQME4*hLs@hd1d;L+(8c*MkUyklVjXmoX?z6(LBU|;6<@!{V0cAiL_$v&c zXS1X`f@+lkWk4CI8Q|}Og)(Lly;eDw-kG8=jOQATId;+h2y+noq|DV#farrybBEizvTfii|7U6f#{Dwph1l? I@S_ZT0EqQj3;+NC diff --git a/app/View/Components/AdminBadge.php b/app/View/Components/AdminBadge.php deleted file mode 100644 index 2970b8d..0000000 --- a/app/View/Components/AdminBadge.php +++ /dev/null @@ -1,73 +0,0 @@ -variant = $variant; - - $styles = [ - 'success' => 'bg-success-subtle text-success border border-success-subtle', - 'danger' => 'bg-danger-subtle text-danger border border-danger-subtle', - 'warning' => 'bg-warning-subtle text-warning border border-warning-subtle', - 'info' => 'bg-info-subtle text-info border border-info-subtle', - 'secondary' => 'bg-secondary-subtle text-secondary border border-secondary-subtle', - ]; - - // Status aliases - $aliases = [ - 1 => 'success', - 0 => 'danger', - 2 => 'warning', - 3 => 'info', - 'active' => 'success', - 'inactive' => 'danger', - 'pending' => 'warning' - ]; - - // Resolve alias if exists - $resolvedVariant = $aliases[$variant] ?? $variant; - - // Auto-icon logic if default 'circle' is used and we have a specific mapping - if ($icon === 'circle') { - $iconMap = [ - 'success' => 'check-circle', - 'danger' => 'x-circle', - 'warning' => 'alert-circle', - 'info' => 'info', - ]; - $this->icon = $iconMap[$resolvedVariant] ?? 'circle'; - } else { - $this->icon = $icon; - } - - $this->class = $styles[$resolvedVariant] ?? $styles['info']; - } - - /** - * Get the view / contents that represent the component. - */ - public function render(): View|Closure|string - { - return view('components.admin-badge'); - } -} diff --git a/app/View/Components/AdminBreadcrumb.php b/app/View/Components/AdminBreadcrumb.php deleted file mode 100644 index 0853bff..0000000 --- a/app/View/Components/AdminBreadcrumb.php +++ /dev/null @@ -1,26 +0,0 @@ -type = $type; - $this->route = $route; - $this->params = $params; - $this->url = $url; - $this->icon = $icon; - $this->variant = $variant; - $this->size = $size; - $this->confirm = $confirm; - $this->confirmUrl = $confirmUrl; - $this->confirmName = $confirmName; - } - - /** - * Get the view / contents that represent the component. - */ - public function render(): View|Closure|string - { - return view('components.button-c-r-m'); - } -} diff --git a/app/View/Components/Card/CardBody.php b/app/View/Components/Card/CardBody.php deleted file mode 100644 index 7c28f9f..0000000 --- a/app/View/Components/Card/CardBody.php +++ /dev/null @@ -1,26 +0,0 @@ -href = $href; - $this->icon = $icon; - } - - public function render(): View|Closure|string - { - return view('components.card-link'); - } -} diff --git a/app/View/Components/Card/CardService.php b/app/View/Components/Card/CardService.php deleted file mode 100644 index c3c900b..0000000 --- a/app/View/Components/Card/CardService.php +++ /dev/null @@ -1,26 +0,0 @@ -id = $id; - $this->ajax = $ajax; - $this->columns = $columns; - $this->bulkDeleteUrl = $bulkDeleteUrl; - - if ($this->bulkDeleteUrl) { - array_unshift($this->columns, [ - 'key' => 'checkbox', - 'label' => '
', - 'orderable' => false, - 'searchable' => false, - 'className' => 'text-center align-middle', - 'width' => '30px' - ]); - } - } - - /** - * Get the view / contents that represent the component. - */ - public function render(): View|Closure|string - { - return view('components.data-table'); - } -} diff --git a/app/View/Components/FallingEffect.php b/app/View/Components/FallingEffect.php deleted file mode 100644 index 4d9cc15..0000000 --- a/app/View/Components/FallingEffect.php +++ /dev/null @@ -1,26 +0,0 @@ -customerRepository->filter([],[],[]); - return view('components.form.customer',compact('customers')); - } -} diff --git a/app/View/Components/Form/Payment.php b/app/View/Components/Form/Payment.php deleted file mode 100644 index 29d0ccc..0000000 --- a/app/View/Components/Form/Payment.php +++ /dev/null @@ -1,31 +0,0 @@ -paymentReppository->filter([],[],[]); - return view('components.form.payment',compact('payments')); - } -} diff --git a/app/View/Components/ImagePicker.php b/app/View/Components/ImagePicker.php deleted file mode 100644 index 030bbb1..0000000 --- a/app/View/Components/ImagePicker.php +++ /dev/null @@ -1,26 +0,0 @@ -name = $name; - $this->value = $value; - $this->checked = filter_var($checked, FILTER_VALIDATE_BOOLEAN); - } - - /** - * Get the view / contents that represent the component. - */ - public function render(): View|Closure|string - { - return view('components.input.switch'); - } -} diff --git a/app/View/Components/LivewireAssets.php b/app/View/Components/LivewireAssets.php deleted file mode 100644 index d898bf1..0000000 --- a/app/View/Components/LivewireAssets.php +++ /dev/null @@ -1,26 +0,0 @@ -model?->vCenter?->id; - - $datacenters = null; - - if ($vcenterId) { - $datacenters = $this->datacenterRepository->filter( - ['vcenter_id' => $vcenterId], - [], - [], - ['id', 'name'] - ); - } - - return view('components.module.datacenter', compact('datacenters')); - } -} diff --git a/app/View/Components/Module/Host.php b/app/View/Components/Module/Host.php deleted file mode 100644 index 60da216..0000000 --- a/app/View/Components/Module/Host.php +++ /dev/null @@ -1,48 +0,0 @@ -model && - ( - isset($this->model->ipaddress->host->module_datacenter_id) || - isset($this->model->host->module_datacenter_id) - ) - ) - { - $hosts = $this->hostRepository->filter( - [ - 'module_datacenter_id' => $this->model->ipaddress->host->module_datacenter_id ?? $this->model->host->module_datacenter_id - ], - [], - [], - ['id', 'name'] - ); - } - return view('components.module.host',compact('hosts')); - } -} diff --git a/app/View/Components/Module/IpAddress.php b/app/View/Components/Module/IpAddress.php deleted file mode 100644 index b0adf90..0000000 --- a/app/View/Components/Module/IpAddress.php +++ /dev/null @@ -1,40 +0,0 @@ -model && isset($this->model->ipaddress->module_host_id)) { - $ipaddress = $this->ipAddressRepository->filter( - [ - 'module_host_id' => $this->model->ipaddress->module_host_id - ], - [], - [], - ['id', 'ip'] - ); - } - return view('components.module.ip-address',compact('ipaddress')); - } -} diff --git a/app/View/Components/Module/Network.php b/app/View/Components/Module/Network.php deleted file mode 100644 index d0ea821..0000000 --- a/app/View/Components/Module/Network.php +++ /dev/null @@ -1,45 +0,0 @@ -model && ( - isset($this->model->ipaddress->module_host_id) || - isset($this->model->module_host_id) - )) - { - $networks = $this->networkRepository->filter( - [ - 'module_host_id' => $this->model->ipaddress->module_host_id ?? $this->model->module_host_id - ], - [], - [], - ['id', 'name'] - ); - } - return view('components.module.network',compact('networks')); - } -} diff --git a/app/View/Components/Module/Os.php b/app/View/Components/Module/Os.php deleted file mode 100644 index 0dd9bc6..0000000 --- a/app/View/Components/Module/Os.php +++ /dev/null @@ -1,45 +0,0 @@ -model && ( - isset($this->model->ipaddress->modulVe_host_id) || - isset($this->model->module_host_id) - )) - { - $lists = $this->osGroupRepository->filter( - [ - 'module_host_id' => $this->model->ipaddress->module_host_id ?? $this->model->module_host_id - ], - [], - [], - ['id', 'name'] - ); - } - return view('components.module.os',compact('lists')); - } -} diff --git a/app/View/Components/Module/OsServerVersion.php b/app/View/Components/Module/OsServerVersion.php deleted file mode 100644 index 8150ca1..0000000 --- a/app/View/Components/Module/OsServerVersion.php +++ /dev/null @@ -1,52 +0,0 @@ -fetchVersions(); - return view('components.module.os-server-version', compact('versions')); - } - - protected function fetchVersions() - { - $vCenter = $this->model?->osGroup?->host?->datacenter?->vCenter; - - if (!$vCenter || !$vCenter->host) { - return null; - } - - $this->vCenter->token = $vCenter->token ?? ''; - $this->vCenter->apiUri = $vCenter->host ?? ''; - - $templateId = $this->model?->osGroup?->host?->template; - - $versions = $this->vCenter->getAllVersion($templateId); - - if ($versions === "Login_Again") { - $token = $this->vCenter->loginServer($vCenter); - if ($token) { - $this->vCenterRepository->update($vCenter->id, ['token' => $token]); - $this->vCenter->token = $token; - $versions = $this->vCenter->getAllVersion($templateId); - } - } - - return $versions; - } -} diff --git a/app/View/Components/Module/OsVersion.php b/app/View/Components/Module/OsVersion.php deleted file mode 100644 index e6a2c40..0000000 --- a/app/View/Components/Module/OsVersion.php +++ /dev/null @@ -1,53 +0,0 @@ -model && isset($this->model->os_id)) - { - // First get the current OS to find its group - $currentOs = $this->osRepositoryInterface->find($this->model->os_id); - if($currentOs && $currentOs->os_group_id) { - // Then get all OS versions in the same group - $os_versions = $this->osRepositoryInterface->filter( - [ - 'os_group_id' => $currentOs->os_group_id - ], - [], - [], - ['id', 'version'] - ); - } - } - - return view('components.module.os-version', [ - 'os_versions' => $os_versions, - 'os' => $this->os, - 'model' => $this->model - ]); - } -} diff --git a/app/View/Components/Module/VCenter.php b/app/View/Components/Module/VCenter.php deleted file mode 100644 index 3f80c92..0000000 --- a/app/View/Components/Module/VCenter.php +++ /dev/null @@ -1,40 +0,0 @@ -vCenterRepository->filter( - [ - 'status' => 1 - ], - [], - [], - ['id', 'host'] - ); - - return view('components.module.v-center', compact('vcenters')); - } -} diff --git a/app/View/Components/Order/Customer.php b/app/View/Components/Order/Customer.php deleted file mode 100644 index 293f4b0..0000000 --- a/app/View/Components/Order/Customer.php +++ /dev/null @@ -1,30 +0,0 @@ -customerRepository->filter([],[],[]); - return view('components.order.customer',compact('customers')); - } -} diff --git a/app/View/Components/Order/Discount.php b/app/View/Components/Order/Discount.php deleted file mode 100644 index 2e67a92..0000000 --- a/app/View/Components/Order/Discount.php +++ /dev/null @@ -1,30 +0,0 @@ -discountRepository->filter(); - return view('components.order.discount',compact('discounts')); - } -} diff --git a/app/View/Components/Order/Payment.php b/app/View/Components/Order/Payment.php deleted file mode 100644 index fa85616..0000000 --- a/app/View/Components/Order/Payment.php +++ /dev/null @@ -1,30 +0,0 @@ -paymentReppository->filter([],[],[]); - return view('components.order.payment',compact('payments')); - } -} diff --git a/app/View/Components/Order/Type.php b/app/View/Components/Order/Type.php deleted file mode 100644 index 5395bd4..0000000 --- a/app/View/Components/Order/Type.php +++ /dev/null @@ -1,29 +0,0 @@ -menuRepositoryInterface = $menuRepositoryInterface; - $this->menu = $this->menuRepositoryInterface->getTree(); - } - -// public function routeName($routeName, $param) -// { -// return $routeName ? route($routeName, $param) : '#'; -// } - - /** - * Get the view / contents that represent the component. - */ - public function render(): View|Closure|string - { - return view('components.siderbar-user'); - } -} diff --git a/app/View/Components/Status.php b/app/View/Components/Status.php deleted file mode 100644 index c3369a3..0000000 --- a/app/View/Components/Status.php +++ /dev/null @@ -1,28 +0,0 @@ -menuRepositoryInterface = $menuRepositoryInterface; - } - - public function compose(View $view): void - { - $menus = $this->menuRepositoryInterface->getTree(); - - $view->with('menus', $menus); - } -} \ No newline at end of file diff --git a/app/View/Composers/Posts/PostComposer.php b/app/View/Composers/Posts/PostComposer.php deleted file mode 100644 index b8e8ee9..0000000 --- a/app/View/Composers/Posts/PostComposer.php +++ /dev/null @@ -1,25 +0,0 @@ -postRepositoryInterface = $postRepositoryInterface; - } - - public function compose(View $view): void - { - $posts = $this->postRepositoryInterface->getAll(); - - $view->with('posts', $posts); - } -} diff --git a/config/admin-sidebar.php b/config/admin-sidebar.php deleted file mode 100644 index 28b7396..0000000 --- a/config/admin-sidebar.php +++ /dev/null @@ -1,139 +0,0 @@ - 'category', - 'title' => 'Modules', - ], - [ - 'type' => 'group', - 'title' => 'Package vCenter', - 'icon' => 'box', - 'active' => ['package.datacenter.*', 'package.host.*', 'package.datastore.*', 'package.folder.*', 'package.network.*', 'package.group.*', 'package.version.*', 'package.ipaddress.*', 'package.vm.*'], - 'children' => [ - ['title' => 'Datacenter', 'route' => 'package.datacenter.index', 'active' => ['package.datacenter.*']], - ['title' => 'Host', 'route' => 'package.host.index', 'active' => ['package.host.*']], - ['title' => 'Datastore', 'route' => 'package.datastore.index', 'active' => ['package.datastore.*']], - ['title' => 'Folder', 'route' => 'package.folder.index', 'active' => ['package.folder.*']], - ['title' => 'Network Adapter', 'route' => 'package.network.index', 'active' => ['package.network.*']], - ['title' => 'Group Os', 'route' => 'package.group.index', 'active' => ['package.group.*']], - ['title' => 'Os Version', 'route' => 'package.version.index', 'active' => ['package.version.*']], - ['title' => 'IP Mapping', 'route' => 'package.ipaddress.index', 'active' => ['package.ipaddress.*']], - ['title' => 'Virtual Machines', 'route' => 'package.vm.index', 'active' => ['package.vm.*']], - ], - ], - [ - 'type' => 'group', - 'title' => 'Module', - 'icon' => 'server', - 'active' => ['module.dns.*', 'module.hosting.*', 'module.vcenter.*', 'module.ippool.*'], - 'children' => [ - ['title' => 'Module DNS', 'route' => 'module.dns.index', 'active' => ['module.dns.*']], - ['title' => 'Module Hosting', 'route' => 'module.hosting.index', 'active' => ['module.hosting.*']], - ['title' => 'Module vCenter', 'route' => 'module.vcenter.index', 'active' => ['module.vcenter.*']], - ['title' => 'Module IP Pool', 'route' => 'module.ippool.index', 'active' => ['module.ippool.*']], - ], - ], - [ - 'type' => 'category', - 'title' => 'Manager', - ], - [ - 'type' => 'group', - 'title' => 'Manager Admin', - 'icon' => 'users', - 'active' => ['administrator.pages.user.*', 'role.*'], - 'children' => [ - ['title' => 'List Admin', 'route' => 'administrator.pages.user.index', 'active' => ['administrator.pages.user.*']], - ['title' => 'Administrator Roles', 'route' => 'role.index', 'active' => ['role.*']], - ], - ], - [ - 'type' => 'group', - 'title' => 'Client', - 'icon' => 'users', - 'active' => ['customer.*'], - 'children' => [ - ['title' => 'View All Client', 'route' => 'customer.index', 'active' => ['customer.index', 'customer.info', 'customer.profile', 'customer.product', 'customer.domain', 'customer.invoice', 'customer.trans', 'customer.email', 'customer.log']], - ['title' => 'Add new Client', 'route' => 'customer.create', 'active' => ['customer.create']], - ], - ], - [ - 'type' => 'group', - 'title' => 'Product', - 'icon' => 'cloud', - 'active' => ['product.*', 'addon.*', 'domain.*'], - 'children' => [ - ['title' => 'All Product/Services', 'route' => 'products.index', 'active' => ['product.*']], - ['title' => 'Product Addon', 'route' => 'addon.index', 'active' => ['addon.*']], - ['title' => 'Domain Pricing', 'route' => 'domain.index', 'active' => ['domain.*']], - ], - ], - [ - 'type' => 'group', - 'title' => 'Orders', - 'icon' => 'shopping-cart', - 'active' => ['administrator.pages.order.*'], - 'children' => [ - ['title' => 'View All Orders', 'route' => 'administrator.pages.order.index', 'active' => ['administrator.pages.order.index'], 'params' => []], - ['title' => 'Pending Orders', 'route' => 'administrator.pages.order.index', 'active' => [], 'params' => ['status' => 'pending']], - ['title' => 'Processing Orders', 'route' => 'administrator.pages.order.index', 'active' => [], 'params' => ['status' => 'processing']], - ['title' => 'Active Orders', 'route' => 'administrator.pages.order.index', 'active' => [], 'params' => ['status' => 'active']], - ['title' => 'Cancel Orders', 'route' => 'administrator.pages.order.index', 'active' => [], 'params' => ['status' => 'cancel']], - ['title' => 'Add New Orders', 'route' => 'administrator.pages.order.create', 'active' => ['administrator.pages.order.create']], - ], - ], - [ - 'type' => 'group', - 'title' => 'Invoices', - 'icon' => 'file-text', - 'active' => ['administrator.pages.invoice.*'], - 'children' => [ - ['title' => 'View All Invoice', 'route' => 'administrator.pages.invoice.index', 'active' => ['administrator.pages.invoice.index'], 'params' => []], - ['title' => 'Pending Invoice', 'route' => 'administrator.pages.invoice.index', 'active' => [], 'params' => ['status' => 'unpaid']], - ['title' => 'Paid Invoice', 'route' => 'administrator.pages.invoice.index', 'active' => [], 'params' => ['status' => 'active']], - ['title' => 'Cancel Invoice', 'route' => 'administrator.pages.invoice.index', 'active' => [], 'params' => ['status' => 'cancel']], - ], - ], - [ - 'type' => 'category', - 'title' => 'Settings', - ], - [ - 'type' => 'group', - 'title' => 'Menus', - 'icon' => 'folder', - 'active' => ['menu.*'], - 'children' => [ - ['title' => 'View All Menu', 'route' => 'menu.index', 'active' => ['menu.*']], - ], - ], - [ - 'type' => 'item', - 'title' => 'Coupon', - 'icon' => 'tags', - 'route' => 'coupon.index', - 'active' => ['coupon.*'], - ], - [ - 'type' => 'item', - 'title' => 'Posts', - 'icon' => 'pencil', - 'route' => 'post.index', - 'active' => ['post.*'], - ], - [ - 'type' => 'item', - 'title' => 'Support', - 'icon' => 'ticket', - 'route' => 'support.index', - 'active' => ['support.*'], - ], - [ - 'type' => 'item', - 'title' => 'Payment Method', - 'icon' => 'credit-card', - 'route' => 'payment.index', - 'active' => ['payment.*'], - ], -]; diff --git a/config/app.php b/config/app.php index dffc0cd..3c05608 100644 --- a/config/app.php +++ b/config/app.php @@ -174,7 +174,6 @@ return [ App\Providers\MailConfigServiceProvider::class, App\Providers\ObServerProvider::class, - App\Providers\ViewServiceProvider::class, /* * Package Service Providers... */ diff --git a/config/assets.php b/config/assets.php deleted file mode 100644 index d4c4118..0000000 --- a/config/assets.php +++ /dev/null @@ -1,37 +0,0 @@ - [ - 'css' => [ - 'resources/sass/app.scss', - ], - 'js' => [ - 'resources/js/app-vite.js', - 'resources/js/pages/template.js', - ], - ], - 'static' => [ - 'css' => [ - 'build/plugins/select2/select2.min.css', - 'build/plugins/perfect-scrollbar/perfect-scrollbar.css', - ], - 'js' => [ - 'build/plugins/jquery/jquery.min.js', - 'build/plugins/bootstrap/bootstrap.bundle.min.js', - 'build/plugins/select2/select2.min.js', - 'build/plugins/lucide/lucide.min.js', - 'build/plugins/perfect-scrollbar/perfect-scrollbar.min.js', - ], - ], - 'datatable' => [ - 'css' => [ - 'build/plugins/select2/select2.min.css' - ], - 'js' => [ - 'build/plugins/jquery/jquery.min.js', - 'build/plugins/select2/select2.min.js', - 'build/plugins/datatables.net/dataTables.min.js', - 'build/plugins/datatables.net-bs5/dataTables.bootstrap5.min.js' - ] - ] -]; \ No newline at end of file diff --git a/config/ckfinder.php b/config/ckfinder.php deleted file mode 100644 index 50efa35..0000000 --- a/config/ckfinder.php +++ /dev/null @@ -1,191 +0,0 @@ - 'laravel_cache', - 'tags' => 'ckfinder/tags', - 'cache' => 'ckfinder/cache', - 'thumbs' => 'ckfinder/cache/thumbs', - 'logs' => array( - 'backend' => 'laravel_logs', - 'path' => 'ckfinder/logs' - ) -); - -/*============================ Images and Thumbnails ==================================*/ -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_images - -$config['images'] = array( - 'maxWidth' => 1600, - 'maxHeight' => 1200, - 'quality' => 80, - 'sizes' => array( - 'small' => array('width' => 480, 'height' => 320, 'quality' => 80), - 'medium' => array('width' => 600, 'height' => 480, 'quality' => 80), - 'large' => array('width' => 800, 'height' => 600, 'quality' => 80) - ) -); - -/*=================================== Backends ========================================*/ -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_backends - -// The two backends defined below are internal CKFinder backends for cache and logs. -// Plase do not change these, unless you really want it. -$config['backends']['laravel_cache'] = array( - 'name' => 'laravel_cache', - 'adapter' => 'local', - 'root' => storage_path('framework/cache') -); - -$config['backends']['laravel_logs'] = array( - 'name' => 'laravel_logs', - 'adapter' => 'local', - 'root' => storage_path('logs') -); - -// Backends - -$config['backends']['default'] = array( - 'name' => 'default', - 'adapter' => 'local', - 'baseUrl' => config('app.url').'/uploads/', - 'root' => public_path('/uploads/'), - 'chmodFiles' => 0777, - 'chmodFolders' => 0755, - 'filesystemEncoding' => 'UTF-8' -); - -/*================================ Resource Types =====================================*/ -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_resourceTypes - -$config['defaultResourceTypes'] = ''; - -$config['resourceTypes'][] = array( - 'name' => 'Files', // Single quotes not allowed. - 'directory' => 'files', - 'maxSize' => 0, - 'allowedExtensions' => '7z,aiff,asf,avi,bmp,csv,doc,docx,fla,flv,gif,gz,gzip,jpeg,jpg,mid,mov,mp3,mp4,mpc,mpeg,mpg,ods,odt,pdf,png,ppt,pptx,pxd,qt,ram,rar,rm,rmi,rmvb,rtf,sdc,sitd,swf,sxc,sxw,tar,tgz,tif,tiff,txt,vsd,wav,webp,wma,wmv,xls,xlsx,zip,svg', - 'deniedExtensions' => '', - 'backend' => 'default' -); - -$config['resourceTypes'][] = array( - 'name' => 'Images', - 'directory' => 'images', - 'maxSize' => 0, - 'allowedExtensions' => 'bmp,gif,jpeg,jpg,png,webp,svg', - 'deniedExtensions' => '', - 'backend' => 'default' -); - -/*================================ Access Control =====================================*/ -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_roleSessionVar - -$config['roleSessionVar'] = 'CKFinder_UserRole'; - -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_accessControl -$config['accessControl'][] = array( - 'role' => '*', - 'resourceType' => '*', - 'folder' => '/', - - 'FOLDER_VIEW' => true, - 'FOLDER_CREATE' => true, - 'FOLDER_RENAME' => true, - 'FOLDER_DELETE' => true, - - 'FILE_VIEW' => true, - 'FILE_UPLOAD' => true, - 'FILE_RENAME' => true, - 'FILE_DELETE' => true, - - 'IMAGE_RESIZE' => true, - 'IMAGE_RESIZE_CUSTOM' => true -); - - -/*================================ Other Settings =====================================*/ -// http://docs.cksource.com/ckfinder3-php/configuration.html - -$config['overwriteOnUpload'] = false; -$config['checkDoubleExtension'] = true; -$config['disallowUnsafeCharacters'] = false; -$config['secureImageUploads'] = true; -$config['checkSizeAfterScaling'] = true; -$config['htmlExtensions'] = array('html', 'htm', 'xml', 'js'); -$config['hideFolders'] = array('.*', 'CVS', '__thumbs'); -$config['hideFiles'] = array('.*'); -$config['forceAscii'] = false; -$config['xSendfile'] = false; - -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_debug -$config['debug'] = false; - -/*==================================== Plugins ========================================*/ -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_plugins - -$config['plugins'] = array(); - -/*================================ Cache settings =====================================*/ -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_cache - -$config['cache'] = array( - 'imagePreview' => 24 * 3600, - 'thumbnails' => 24 * 3600 * 365 -); - -/*============================ Temp Directory settings ================================*/ -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_tempDirectory - -$config['tempDirectory'] = sys_get_temp_dir(); - -/*============================ Session Cause Performance Issues =======================*/ -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_sessionWriteClose - -$config['sessionWriteClose'] = true; - -/*================================= CSRF protection ===================================*/ -// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_csrfProtection - -$config['csrfProtection'] = true; - -/*============================== End of Configuration =================================*/ - -/** - * Config must be returned - do not change it. - */ -return $config; diff --git a/config/debugbar.php b/config/debugbar.php deleted file mode 100644 index 48d9dc8..0000000 --- a/config/debugbar.php +++ /dev/null @@ -1,329 +0,0 @@ - env('DEBUGBAR_ENABLED', null), - 'hide_empty_tabs' => false, // Hide tabs until they have content - 'except' => [ - 'telescope*', - 'horizon*', - ], - - /* - |-------------------------------------------------------------------------- - | Storage settings - |-------------------------------------------------------------------------- - | - | Debugbar stores data for session/ajax requests. - | You can disable this, so the debugbar stores data in headers/session, - | but this can cause problems with large data collectors. - | By default, file storage (in the storage folder) is used. Redis and PDO - | can also be used. For PDO, run the package migrations first. - | - | Warning: Enabling storage.open will allow everyone to access previous - | request, do not enable open storage in publicly available environments! - | Specify a callback if you want to limit based on IP or authentication. - | Leaving it to null will allow localhost only. - */ - 'storage' => [ - 'enabled' => true, - 'open' => env('DEBUGBAR_OPEN_STORAGE'), // bool/callback. - 'driver' => 'file', // redis, file, pdo, socket, custom - 'path' => storage_path('debugbar'), // For file driver - 'connection' => null, // Leave null for default connection (Redis/PDO) - 'provider' => '', // Instance of StorageInterface for custom driver - 'hostname' => '127.0.0.1', // Hostname to use with the "socket" driver - 'port' => 2304, // Port to use with the "socket" driver - ], - - /* - |-------------------------------------------------------------------------- - | Editor - |-------------------------------------------------------------------------- - | - | Choose your preferred editor to use when clicking file name. - | - | Supported: "phpstorm", "vscode", "vscode-insiders", "vscode-remote", - | "vscode-insiders-remote", "vscodium", "textmate", "emacs", - | "sublime", "atom", "nova", "macvim", "idea", "netbeans", - | "xdebug", "espresso" - | - */ - - 'editor' => env('DEBUGBAR_EDITOR') ?: env('IGNITION_EDITOR', 'phpstorm'), - - /* - |-------------------------------------------------------------------------- - | Remote Path Mapping - |-------------------------------------------------------------------------- - | - | If you are using a remote dev server, like Laravel Homestead, Docker, or - | even a remote VPS, it will be necessary to specify your path mapping. - | - | Leaving one, or both of these, empty or null will not trigger the remote - | URL changes and Debugbar will treat your editor links as local files. - | - | "remote_sites_path" is an absolute base path for your sites or projects - | in Homestead, Vagrant, Docker, or another remote development server. - | - | Example value: "/home/vagrant/Code" - | - | "local_sites_path" is an absolute base path for your sites or projects - | on your local computer where your IDE or code editor is running on. - | - | Example values: "/Users//Code", "C:\Users\\Documents\Code" - | - */ - - 'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH'), - 'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', env('IGNITION_LOCAL_SITES_PATH')), - - /* - |-------------------------------------------------------------------------- - | Vendors - |-------------------------------------------------------------------------- - | - | Vendor files are included by default, but can be set to false. - | This can also be set to 'js' or 'css', to only include javascript or css vendor files. - | Vendor files are for css: font-awesome (including fonts) and highlight.js (css files) - | and for js: jquery and highlight.js - | So if you want syntax highlighting, set it to true. - | jQuery is set to not conflict with existing jQuery scripts. - | - */ - - 'include_vendors' => true, - - /* - |-------------------------------------------------------------------------- - | Capture Ajax Requests - |-------------------------------------------------------------------------- - | - | The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors), - | you can use this option to disable sending the data through the headers. - | - | Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools. - | - | Note for your request to be identified as ajax requests they must either send the header - | X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header. - | - | By default `ajax_handler_auto_show` is set to true allowing ajax requests to be shown automatically in the Debugbar. - | Changing `ajax_handler_auto_show` to false will prevent the Debugbar from reloading. - */ - - 'capture_ajax' => true, - 'add_ajax_timing' => false, - 'ajax_handler_auto_show' => true, - 'ajax_handler_enable_tab' => true, - - /* - |-------------------------------------------------------------------------- - | Custom Error Handler for Deprecated warnings - |-------------------------------------------------------------------------- - | - | When enabled, the Debugbar shows deprecated warnings for Symfony components - | in the Messages tab. - | - */ - 'error_handler' => false, - - /* - |-------------------------------------------------------------------------- - | Clockwork integration - |-------------------------------------------------------------------------- - | - | The Debugbar can emulate the Clockwork headers, so you can use the Chrome - | Extension, without the server-side code. It uses Debugbar collectors instead. - | - */ - 'clockwork' => false, - - /* - |-------------------------------------------------------------------------- - | DataCollectors - |-------------------------------------------------------------------------- - | - | Enable/disable DataCollectors - | - */ - - 'collectors' => [ - 'phpinfo' => true, // Php version - 'messages' => true, // Messages - 'time' => true, // Time Datalogger - 'memory' => true, // Memory usage - 'exceptions' => true, // Exception displayer - 'log' => true, // Logs from Monolog (merged in messages if enabled) - 'db' => true, // Show database (PDO) queries and bindings - 'views' => true, // Views with their data - 'route' => true, // Current route information - 'auth' => false, // Display Laravel authentication status - 'gate' => true, // Display Laravel Gate checks - 'session' => true, // Display session data - 'symfony_request' => true, // Only one can be enabled.. - 'mail' => true, // Catch mail messages - 'laravel' => false, // Laravel version and environment - 'events' => false, // All events fired - 'default_request' => false, // Regular or special Symfony request logger - 'logs' => false, // Add the latest log messages - 'files' => false, // Show the included files - 'config' => false, // Display config settings - 'cache' => true, // Display cache events - 'models' => true, // Display models - 'livewire' => true, // Display Livewire (when available) - 'jobs' => false, // Display dispatched jobs - 'pennant' => false, // Display Pennant feature flags - ], - - /* - |-------------------------------------------------------------------------- - | Extra options - |-------------------------------------------------------------------------- - | - | Configure some DataCollectors - | - */ - - 'options' => [ - 'time' => [ - 'memory_usage' => false, // Calculated by subtracting memory start and end, it may be inaccurate - ], - 'messages' => [ - 'trace' => true, // Trace the origin of the debug message - ], - 'memory' => [ - 'reset_peak' => false, // run memory_reset_peak_usage before collecting - 'with_baseline' => false, // Set boot memory usage as memory peak baseline - 'precision' => 0, // Memory rounding precision - ], - 'auth' => [ - 'show_name' => true, // Also show the users name/email in the debugbar - 'show_guards' => true, // Show the guards that are used - ], - 'db' => [ - 'with_params' => true, // Render SQL with the parameters substituted - 'exclude_paths' => [ // Paths to exclude entirely from the collector -// 'vendor/laravel/framework/src/Illuminate/Session', // Exclude sessions queries - ], - 'backtrace' => true, // Use a backtrace to find the origin of the query in your files. - 'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults) - 'timeline' => false, // Add the queries to the timeline - 'duration_background' => true, // Show shaded background on each query relative to how long it took to execute. - 'explain' => [ // Show EXPLAIN output on queries - 'enabled' => false, - ], - 'hints' => false, // Show hints for common mistakes - 'show_copy' => true, // Show copy button next to the query, - 'slow_threshold' => false, // Only track queries that last longer than this time in ms - 'memory_usage' => false, // Show queries memory usage - 'soft_limit' => 100, // After the soft limit, no parameters/backtrace are captured - 'hard_limit' => 500, // After the hard limit, queries are ignored - ], - 'mail' => [ - 'timeline' => false, // Add mails to the timeline - 'show_body' => true, - ], - 'views' => [ - 'timeline' => false, // Add the views to the timeline (Experimental) - 'data' => false, // True for all data, 'keys' for only names, false for no parameters. - 'group' => 50, // Group duplicate views. Pass value to auto-group, or true/false to force - 'exclude_paths' => [ // Add the paths which you don't want to appear in the views - 'vendor/filament' // Exclude Filament components by default - ], - ], - 'route' => [ - 'label' => true, // Show complete route on bar - ], - 'session' => [ - 'hiddens' => [], // Hides sensitive values using array paths - ], - 'symfony_request' => [ - 'hiddens' => [], // Hides sensitive values using array paths, example: request_request.password - ], - 'events' => [ - 'data' => false, // Collect events data, listeners - ], - 'logs' => [ - 'file' => null, - ], - 'cache' => [ - 'values' => true, // Collect cache values - ], - ], - - /* - |-------------------------------------------------------------------------- - | Inject Debugbar in Response - |-------------------------------------------------------------------------- - | - | Usually, the debugbar is added just before , by listening to the - | Response after the App is done. If you disable this, you have to add them - | in your template yourself. See http://phpdebugbar.com/docs/rendering.html - | - */ - - 'inject' => true, - - /* - |-------------------------------------------------------------------------- - | Debugbar route prefix - |-------------------------------------------------------------------------- - | - | Sometimes you want to set route prefix to be used by Debugbar to load - | its resources from. Usually the need comes from misconfigured web server or - | from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97 - | - */ - 'route_prefix' => '_debugbar', - - /* - |-------------------------------------------------------------------------- - | Debugbar route middleware - |-------------------------------------------------------------------------- - | - | Additional middleware to run on the Debugbar routes - */ - 'route_middleware' => [], - - /* - |-------------------------------------------------------------------------- - | Debugbar route domain - |-------------------------------------------------------------------------- - | - | By default Debugbar route served from the same domain that request served. - | To override default domain, specify it as a non-empty value. - */ - 'route_domain' => null, - - /* - |-------------------------------------------------------------------------- - | Debugbar theme - |-------------------------------------------------------------------------- - | - | Switches between light and dark theme. If set to auto it will respect system preferences - | Possible values: auto, light, dark - */ - 'theme' => env('DEBUGBAR_THEME', 'auto'), - - /* - |-------------------------------------------------------------------------- - | Backtrace stack limit - |-------------------------------------------------------------------------- - | - | By default, the Debugbar limits the number of frames returned by the 'debug_backtrace()' function. - | If you need larger stacktraces, you can increase this number. Setting it to 0 will result in no limit. - */ - 'debug_backtrace_limit' => 50, -]; diff --git a/config/livewire.php b/config/livewire.php deleted file mode 100644 index 02c5fb7..0000000 --- a/config/livewire.php +++ /dev/null @@ -1,160 +0,0 @@ - 'App\\Http\\Livewire', - - /* - |--------------------------------------------------------------------------- - | View Path - |--------------------------------------------------------------------------- - | - | This value is used to specify where Livewire component Blade templates are - | stored when running file creation commands like `artisan make:livewire`. - | It is also used if you choose to omit a component's render() method. - | - */ - - 'view_path' => resource_path('views/livewire'), - - /* - |--------------------------------------------------------------------------- - | Layout - |--------------------------------------------------------------------------- - | The view that will be used as the layout when rendering a single component - | as an entire page via `Route::get('/post/create', CreatePost::class);`. - | In this case, the view returned by CreatePost will render into $slot. - | - */ - - 'layout' => 'components.layouts.app', - - /* - |--------------------------------------------------------------------------- - | Lazy Loading Placeholder - |--------------------------------------------------------------------------- - | Livewire allows you to lazy load components that would otherwise slow down - | the initial page load. Every component can have a custom placeholder or - | you can define the default placeholder view for all components below. - | - */ - - 'lazy_placeholder' => null, - - /* - |--------------------------------------------------------------------------- - | Temporary File Uploads - |--------------------------------------------------------------------------- - | - | Livewire handles file uploads by storing uploads in a temporary directory - | before the file is stored permanently. All file uploads are directed to - | a global endpoint for temporary storage. You may configure this below: - | - */ - - 'temporary_file_upload' => [ - 'disk' => null, // Example: 'local', 's3' | Default: 'default' - 'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB) - 'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp' - 'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1' - 'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs... - 'png', 'gif', 'bmp', 'svg', 'wav', 'mp4', - 'mov', 'avi', 'wmv', 'mp3', 'm4a', - 'jpg', 'jpeg', 'mpga', 'webp', 'wma', - ], - 'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated... - 'cleanup' => true, // Should cleanup temporary uploads older than 24 hrs... - ], - - /* - |--------------------------------------------------------------------------- - | Render On Redirect - |--------------------------------------------------------------------------- - | - | This value determines if Livewire will run a component's `render()` method - | after a redirect has been triggered using something like `redirect(...)` - | Setting this to true will render the view once more before redirecting - | - */ - - 'render_on_redirect' => false, - - /* - |--------------------------------------------------------------------------- - | Eloquent Model Binding - |--------------------------------------------------------------------------- - | - | Previous versions of Livewire supported binding directly to eloquent model - | properties using wire:model by default. However, this behavior has been - | deemed too "magical" and has therefore been put under a feature flag. - | - */ - - 'legacy_model_binding' => false, - - /* - |--------------------------------------------------------------------------- - | Auto-inject Frontend Assets - |--------------------------------------------------------------------------- - | - | By default, Livewire automatically injects its JavaScript and CSS into the - | and of pages containing Livewire components. By disabling - | this behavior, you need to use @livewireStyles and @livewireScripts. - | - */ - - 'inject_assets' => true, - - /* - |--------------------------------------------------------------------------- - | Navigate (SPA mode) - |--------------------------------------------------------------------------- - | - | By adding `wire:navigate` to links in your Livewire application, Livewire - | will prevent the default link handling and instead request those pages - | via AJAX, creating an SPA-like effect. Configure this behavior here. - | - */ - - 'navigate' => [ - 'show_progress_bar' => true, - 'progress_bar_color' => '#000', - ], - - /* - |--------------------------------------------------------------------------- - | HTML Morph Markers - |--------------------------------------------------------------------------- - | - | Livewire intelligently "morphs" existing HTML into the newly rendered HTML - | after each update. To make this process more reliable, Livewire injects - | "markers" into the rendered Blade surrounding @if, @class & @foreach. - | - */ - - 'inject_morph_markers' => true, - - /* - |--------------------------------------------------------------------------- - | Pagination Theme - |--------------------------------------------------------------------------- - | - | When enabling Livewire's pagination feature by using the `WithPagination` - | trait, Livewire will use Tailwind templates to render pagination views - | on the page. If you want Bootstrap CSS, you can specify: "bootstrap" - | - */ - - 'pagination_theme' => 'tailwind', -]; diff --git a/config/sidebar.php b/config/sidebar.php deleted file mode 100644 index 473bf05..0000000 --- a/config/sidebar.php +++ /dev/null @@ -1,204 +0,0 @@ - 'Package vCenter', - 'icon' => 'fas fa-cubes', - 'children' => [ - [ - 'title' => 'Infrastructure', - 'route' => 'vcenter.manager', - 'icon' => 'fas fa-sitemap', - ], - [ - 'title' => 'Group Os', - 'route' => 'package.group.index', - 'icon' => 'fas fa-layer-group', - ], - [ - 'title' => 'Os Version', - 'route' => 'package.version.index', - 'icon' => 'fas fa-code-branch', - ], - ], - ], - [ - 'title' => 'Module', - 'icon' => 'fas fa-tools', - 'children' => [ - [ - 'title' => 'Module DNS', - 'route' => 'module.dns.index', - 'icon' => 'fas fa-globe', - ], - [ - 'title' => 'Module Hosting', - 'route' => 'module.hosting.index', - 'icon' => 'fas fa-server', - ], - [ - 'title' => 'Module vCenter', - 'route' => 'module.vcenter.index', - 'icon' => 'fas fa-desktop', - ], - [ - 'title' => 'Module IP Pool', - 'route' => 'module.ippool.index', - 'icon' => 'fas fa-network-wired', - ], - ], - ], - [ - 'title' => 'Manager Admin', - 'icon' => 'fas fa-users-cog', - 'children' => [ - [ - 'title' => 'List Admin', - 'route' => 'administrator.pages.user.index', - 'icon' => 'fas fa-users', - ], - [ - 'title' => 'Administrator Roles', - 'route' => 'role.index', - 'icon' => 'fas fa-user-tag', - ], - ], - ], - [ - 'title' => 'Client', - 'icon' => 'fas fa-users', - 'children' => [ - [ - 'title' => 'View All Client', - 'route' => 'customer.index', - 'icon' => 'fas fa-user-friends', - ], - [ - 'title' => 'Add new Client', - 'route' => 'customer.create', - 'icon' => 'fas fa-user-plus', - ], - ], - ], - [ - 'title' => 'Product', - 'icon' => 'fab fa-aws', - 'children' => [ - [ - 'title' => 'All Product/Services', - 'route' => 'product.index', - 'icon' => 'fas fa-list', - ], - [ - 'title' => 'Product Addon', - 'route' => 'addon.index', - 'icon' => 'fas fa-puzzle-piece', - ], - [ - 'title' => 'Domain Pricing', - 'route' => 'domain.index', - 'icon' => 'fas fa-globe-europe', - ], - ], - ], - [ - 'title' => 'Orders', - 'icon' => 'fas fa-store', - 'children' => [ - [ - 'title' => 'View All Orders', - 'route' => 'administrator.pages.order.index', - 'icon' => 'fas fa-shopping-cart', - ], - [ - 'title' => 'Pending Orders', - 'route' => 'administrator.pages.order.index', - 'params' => ['status' => 'pending'], - 'icon' => 'fas fa-shopping-cart', - ], - [ - 'title' => 'Processing Orders', - 'route' => 'administrator.pages.order.index', - 'params' => ['status' => 'processing'], - 'icon' => 'fas fa-cogs', - ], - [ - 'title' => 'Active Orders', - 'route' => 'administrator.pages.order.index', - 'params' => ['status' => 'active'], - 'icon' => 'fas fa-shopping-cart', - ], - [ - 'title' => 'Cancel Orders', - 'route' => 'administrator.pages.order.index', - 'params' => ['status' => 'cancel'], - 'icon' => 'fas fa-shopping-cart', - ], - [ - 'title' => 'Add New Orders', - 'route' => 'administrator.pages.order.create', - 'icon' => 'fas fa-cart-plus', - ], - ], - ], - [ - 'title' => 'Invoices', - 'icon' => 'fas fa-file-invoice', - 'children' => [ - [ - 'title' => 'View All Invoice', - 'route' => 'administrator.pages.invoice.index', - 'icon' => 'fas fa-file-invoice', - ], - [ - 'title' => 'Pending Invoice', - 'route' => 'administrator.pages.invoice.index', - 'params' => ['status' => 'unpaid'], - 'icon' => 'fas fa-file-invoice', - ], - [ - 'title' => 'Paid Invoice', - 'route' => 'administrator.pages.invoice.index', - 'params' => ['status' => 'active'], - 'icon' => 'fas fa-file-invoice', - ], - [ - 'title' => 'Cancel Invoice', - 'route' => 'administrator.pages.invoice.index', - 'params' => ['status' => 'cancel'], - 'icon' => 'fas fa-file-invoice', - ], - ], - ], - [ - 'title' => 'Menus', - 'icon' => 'fas fa-folder', - 'children' => [ - [ - 'title' => 'View All Menu', - 'route' => 'menu.index', - 'icon' => 'fas fa-file-invoice', - ], - ], - ], - [ - 'title' => 'Coupon', - 'route' => 'coupon.index', - 'icon' => 'fas fa-tags', - ], - [ - 'title' => 'Posts', - 'route' => 'post.index', - 'icon' => 'fas fa-blog', - ], - [ - 'title' => 'Support', - 'route' => 'support.index', - 'icon' => 'fas fa-ticket-alt', - ], - [ - 'title' => 'Payment Method', - 'route' => 'payment.index', - 'icon' => 'far fa-credit-card', - ], -]; \ No newline at end of file diff --git a/config/user_sidebar.php b/config/user_sidebar.php deleted file mode 100644 index 459b542..0000000 --- a/config/user_sidebar.php +++ /dev/null @@ -1,86 +0,0 @@ - 'Dashboard', - 'routeName' => 'index_new', - 'icon' => '', - 'roles' => [ - ], - 'sub' => [], - ], - [ - 'title' => 'Đăng ký dịch vụ', - 'routeName' => 'dashboard', - 'icon' => '', - 'roles' => [ - ], - 'sub' => [ - [ - 'title' => 'Cloud VPS', - 'routeName' => 'dashboard', - 'icon' => '', - 'roles' => [ - ], - 'sub' => [], - ], - [ - 'title' => 'Cloud VPS GPU', - 'routeName' => 'dashboard', - 'icon' => '', - 'roles' => [ - ], - 'sub' => [], - ] - ], - ], - [ - 'title' => 'Dịch vụ của tôi', - 'routeName' => 'dashboard', - 'icon' => '', - 'roles' => [ - ], - 'sub' => [], - ], - [ - 'title' => 'Quản lý thanh toán', - 'routeName' => 'dashboard', - 'icon' => '', - 'roles' => [ - ], - 'sub' => [ - [ - 'title' => 'Hoá đơn', - 'routeName' => 'dashboard', - 'icon' => '', - 'roles' => [ - ], - 'sub' => [], - ], - ], - ], - [ - 'title' => 'Nạp tiền', - 'routeName' => 'dashboard', - 'icon' => '', - 'roles' => [ - ], - 'sub' => [], - ], - [ - 'title' => 'Hỗ trợ', - 'routeName' => 'dashboard', - 'icon' => '', - 'roles' => [ - ], - 'sub' => [], - ], - [ - 'title' => 'Hỗ trợ', - 'routeName' => 'dashboard', - 'icon' => '', - 'roles' => [ - ], - 'sub' => [], - ], -]; diff --git a/database/seeders/AdminSeeder.php b/database/seeders/AdminSeeder.php index 798424a..e489dae 100644 --- a/database/seeders/AdminSeeder.php +++ b/database/seeders/AdminSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; diff --git a/database/seeders/ControlPanelSeeder.php b/database/seeders/ControlPanelSeeder.php index 1c7ba95..3f3ba32 100644 --- a/database/seeders/ControlPanelSeeder.php +++ b/database/seeders/ControlPanelSeeder.php @@ -4,7 +4,6 @@ namespace Database\Seeders; use App\Models\ControlPanel; use Illuminate\Database\Seeder; -use Illuminate\Support\Str; class ControlPanelSeeder extends Seeder { diff --git a/database/seeders/CustomerSeeder.php b/database/seeders/CustomerSeeder.php index ffe19b1..53c79cb 100644 --- a/database/seeders/CustomerSeeder.php +++ b/database/seeders/CustomerSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class CustomerSeeder extends Seeder diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 33f1b43..f94619b 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use App\Models\Customer; use App\Models\User; use Illuminate\Database\Seeder; diff --git a/database/seeders/DomainTypeSeeder.php b/database/seeders/DomainTypeSeeder.php index 4d573c3..43f01bd 100644 --- a/database/seeders/DomainTypeSeeder.php +++ b/database/seeders/DomainTypeSeeder.php @@ -3,7 +3,6 @@ namespace Database\Seeders; use App\Models\DomainType; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class DomainTypeSeeder extends Seeder diff --git a/database/seeders/GroupSeeder.php b/database/seeders/GroupSeeder.php deleted file mode 100644 index 9d51ec6..0000000 --- a/database/seeders/GroupSeeder.php +++ /dev/null @@ -1,35 +0,0 @@ - [ - 'name' => 'Premium Hosting', - 'type_id' => 3, - 'status' => 1 - ], - 2 => [ - 'name' => 'VPS NVME', - 'type_id' => 4, - 'status' => 1 - ] - ]; - foreach ($group as $item) - { - Group::create($item); - } - } -} diff --git a/database/seeders/MenuSeeder.php b/database/seeders/MenuSeeder.php index 3af6e04..f471044 100644 --- a/database/seeders/MenuSeeder.php +++ b/database/seeders/MenuSeeder.php @@ -4,7 +4,6 @@ namespace Database\Seeders; use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; -use Carbon\Carbon; class MenuSeeder extends Seeder { diff --git a/database/seeders/Payment.php b/database/seeders/Payment.php index 2cf6cad..9c3587f 100644 --- a/database/seeders/Payment.php +++ b/database/seeders/Payment.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; use App\Models\Payment as ModelPayment; diff --git a/database/seeders/SnapshotLimitedSeeder.php b/database/seeders/SnapshotLimitedSeeder.php index fc5c7dc..e5ad4b3 100644 --- a/database/seeders/SnapshotLimitedSeeder.php +++ b/database/seeders/SnapshotLimitedSeeder.php @@ -4,7 +4,6 @@ namespace Database\Seeders; use App\Models\Customer; use App\Models\ModuleSnapshotLimit; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class SnapshotLimitedSeeder extends Seeder diff --git a/database/seeders/TemplateSeeder.php b/database/seeders/TemplateSeeder.php index 4b487ee..8a5b1c1 100644 --- a/database/seeders/TemplateSeeder.php +++ b/database/seeders/TemplateSeeder.php @@ -3,7 +3,6 @@ namespace Database\Seeders; use App\Models\Template; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class TemplateSeeder extends Seeder diff --git a/modules/DomainFluctuation/App/Models/DomainFluctuationName.php b/modules/DomainFluctuation/App/Models/DomainFluctuationName.php deleted file mode 100644 index fa678c5..0000000 --- a/modules/DomainFluctuation/App/Models/DomainFluctuationName.php +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - -
- - - - - - - - -
- {{-- Thay bằng đường dẫn logo thực tế --}} - Logo - -

HÓA ĐƠN GIÁ TRỊ GIA TĂNG

-

(VAT INVOICE)

- -

- Ngày (Date) 09 - tháng (month) 07 - năm (year) 2026 -

- -

- Mã CQT (Code): -

-
-

- Ký hiệu (Serial): - 1C26TCP -

- -

- Số (No.): - <Chưa cấp số> -

-
- -
- -
-

- CÔNG TY CỔ PHẦN GIẢI PHÁP CÔNG NGHỆ THẾ GIỚI SỐ -

- -
- Mã số thuế (Tax code): 0318443116 -
- -
- Địa chỉ (Address): - Số 128/38 đường Tân Hương, Phường Phú Thọ Hòa, - Thành phố Hồ Chí Minh, Việt Nam -
- -
- Tên cửa hàng (Shop name): -
- - - - - - - - - - -
- Số tài khoản (Bank account): - - 7379868 Ngân hàng TMCP Á Châu (ACB) Chi nhánh TP. Hồ Chí Minh -
- 797379868 Ngân hàng TMCP Quân Đội (MB) Chi nhánh Phú Nhuận -
- -
- QR Code -
-
- -
- -
-
- Họ tên người mua hàng (Buyer): - PHẠM GIA BẢO -
- -
- Tên đơn vị (Company's name): - CÔNG TY TNHH MỘT THÀNH VIÊN CÔNG NGHỆ DEVRO -
- -
- Mã số thuế (Tax code): 079203005707 -
- -
- Địa chỉ (Address): - 194/19/12 Chiến Lược, Phường Bình Trị Đông, Thành Phố Hồ Chí Minh -
- -
- Hình thức thanh toán (Payment method): - TM/CK -
-
-
- Watermark - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
STT
(No)
- Tên hàng hóa, dịch vụ
- (Name of goods and services) -
Đơn vị tính
(Unit)
Số lượng
(Quantity)
Đơn giá
(Unit price)
Thành tiền
(Amount)
1Phí đăng ký và duy trì tên miền quốc tế .COMNăm2,00340.000,00680.000
- Cộng tiền hàng (Total amount excl. VAT): - 680.000
- Thuế suất GTGT (VAT rate): - 10% - Tiền thuế GTGT (VAT amount): - 68.000
- Tổng tiền thanh toán (Total amount): - 748.000
- Số tiền viết bằng chữ - (Total amount in words): - Bảy trăm bốn mươi tám nghìn đồng chẵn. -
-
- - - - - - - - -
-
- Người mua hàng (Buyer) -
- -
- (Chữ ký số nếu có)
- (Digital signature if any) -
-
-
- Người bán hàng (Seller) -
- -
- (Chữ ký điện tử, Chữ ký số)
- (E-signature, Digital signature) -
- -
- Signature Valid
- Ký bởi (Signed By):
- Ký ngày (Signing Date): -
-
- - -
- - diff --git a/resources/views/pdf/invoice.blade.php b/resources/views/pdf/invoice.blade.php index e709365..38edbbb 100644 --- a/resources/views/pdf/invoice.blade.php +++ b/resources/views/pdf/invoice.blade.php @@ -22,578 +22,26 @@ Hóa đơn #{{ $invoice->invoice_number }} - + @include('pdf.invoice.styles')
- - - - - - - - -
- @if(($cfg['show_logo'] ?? true) && $companyLogo) - {{ $companyName }} - @else - {{ $companyName }} - @endif - -

HÓA ĐƠN GIÁ TRỊ GIA TĂNG

-

(VAT INVOICE)

- -

- Ngày (Date) {{ $invoiceDate?->format('d') ?? '' }} - tháng (month) {{ $invoiceDate?->format('m') ?? '' }} - năm (year) {{ $invoiceDate?->format('Y') ?? '' }} -

- -

- Mã CQT (Code): -

-
-

- Ký hiệu (Serial): - 1C26TCP -

- -

- Số (No.): - {{ $invoice->invoice_number ?? '' }} -

-
+ @include('pdf.invoice.header')
-
-

- {{ $companyName }} -

- -
- Mã số thuế (Tax code): {{ $companyTaxCode }} -
- -
- Địa chỉ (Address): - {{ $companyAddress }} -
- -
- Tên cửa hàng (Shop name): -
- - - - - - - - - - -
- Số tài khoản (Bank account): - - 7379868 Ngân hàng TMCP Á Châu (ACB) Chi nhánh TP. Hồ Chí Minh -
- 797379868 Ngân hàng TMCP Quân Đội (MB) Chi nhánh Phú Nhuận -
- -
- QR Code -
-
+ @include('pdf.invoice.seller')
-
-
- Họ tên người mua hàng (Buyer): - {{ $customer?->fullname ?? '' }} -
- -
- Tên đơn vị (Company's name): - {{ $customer?->company ?? '' }} -
- -
- Mã số thuế (Tax code): {{ $customer?->identity_number ?? '' }} -
- -
- Địa chỉ (Address): - {{ $customer?->address ?? '' }} -
- -
- Hình thức thanh toán (Payment method): - {{ $invoice->payment_method ? strtoupper($invoice->payment_method) : 'TM/CK' }} -
-
-
- Watermark - - - - - - - - - - - - - - - - - - - - - - @foreach($invoiceItems as $i => $item) - - - - - - - - - @endforeach - - @if($cfg['show_summary'] ?? true) - - - - - - @if($invoice->discount_amount > 0) - - - - - @endif - - - - - - - - - - - - - @endif - - - - - -
STT
(No)
- Tên hàng hóa, dịch vụ
- (Name of goods and services) -
Đơn vị tính
(Unit)
Số lượng
(Quantity)
Đơn giá
(Unit price)
Thành tiền
(Amount)
{{ $i + 1 }}{{ $item->description ?? ($item->product?->name ?? 'Dịch vụ') }}{{ ($item->months && $item->months > 0) ? 'Tháng' : 'Lần' }}{{ number_format($item->quantity ?? 1, 2, ',', '.') }}{{ number_format($item->unit_price, 0, ',', '.') }}{{ number_format($item->total_price, 0, ',', '.') }}
- Cộng tiền hàng (Total amount excl. VAT): - {{ number_format($invoice->sub_total, 0, ',', '.') }}
- Giảm giá (Discount): - -{{ number_format($invoice->discount_amount, 0, ',', '.') }}
- Thuế suất GTGT (VAT rate): - {{ $taxRate > 0 ? $taxRate . '%' : 'Không áp dụng' }} - Tiền thuế GTGT (VAT amount): - {{ number_format($invoice->tax, 0, ',', '.') }}
- Tổng tiền thanh toán (Total amount): - {{ number_format($invoice->total, 0, ',', '.') }}
- Số tiền viết bằng chữ - (Total amount in words): - {{ \App\Helpers\NumberToWords::convert((int) $invoice->total) }}. -
-
+ @include('pdf.invoice.buyer') + @include('pdf.invoice.items') - - - - - - -
-
- Người mua hàng (Buyer) -
+ @include('pdf.invoice.signature') -
- (Chữ ký số nếu có)
- (Digital signature if any) -
- -
- {{ $customer?->fullname ?? '' }} -
-
-
- Người bán hàng (Seller) -
- -
- (Chữ ký điện tử, Chữ ký số)
- (E-signature, Digital signature) -
- -
- Signature Valid
- Ký bởi (Signed By): {{ $companyName }}
- Ký ngày (Signing Date): -
-
- - + @include('pdf.invoice.footer')
diff --git a/resources/views/pdf/invoice/buyer.blade.php b/resources/views/pdf/invoice/buyer.blade.php new file mode 100644 index 0000000..3a99b4f --- /dev/null +++ b/resources/views/pdf/invoice/buyer.blade.php @@ -0,0 +1,25 @@ +
+
+ Họ tên người mua hàng (Buyer): + {{ $customer?->fullname ?? '' }} +
+ +
+ Tên đơn vị (Company's name): + {{ $customer?->company ?? '' }} +
+ +
+ Mã số thuế (Tax code): {{ $customer?->identity_number ?? '' }} +
+ +
+ Địa chỉ (Address): + {{ $customer?->address ?? '' }} +
+ +
+ Hình thức thanh toán (Payment method): + {{ $invoice->payment_method ? strtoupper($invoice->payment_method) : 'TM/CK' }} +
+
diff --git a/resources/views/pdf/invoice/footer.blade.php b/resources/views/pdf/invoice/footer.blade.php new file mode 100644 index 0000000..4b849f7 --- /dev/null +++ b/resources/views/pdf/invoice/footer.blade.php @@ -0,0 +1,26 @@ + diff --git a/resources/views/pdf/invoice/header.blade.php b/resources/views/pdf/invoice/header.blade.php new file mode 100644 index 0000000..a659bee --- /dev/null +++ b/resources/views/pdf/invoice/header.blade.php @@ -0,0 +1,38 @@ + + + + + + + + +
+ @if(($cfg['show_logo'] ?? true) && $companyLogo) + {{ $companyName }} + @else + {{ $companyName }} + @endif + +

HÓA ĐƠN GIÁ TRỊ GIA TĂNG

+

(VAT INVOICE)

+ +

+ Ngày (Date) {{ $invoiceDate?->format('d') ?? '' }} + tháng (month) {{ $invoiceDate?->format('m') ?? '' }} + năm (year) {{ $invoiceDate?->format('Y') ?? '' }} +

+ +

+ Mã CQT (Code): +

+
+

+ Ký hiệu (Serial): + 1C26TCP +

+ +

+ Số (No.): + {{ $invoice->invoice_number ?? '' }} +

+
diff --git a/resources/views/pdf/invoice/items.blade.php b/resources/views/pdf/invoice/items.blade.php new file mode 100644 index 0000000..1caa1a3 --- /dev/null +++ b/resources/views/pdf/invoice/items.blade.php @@ -0,0 +1,83 @@ +
+ Watermark + + + + + + + + + + + + + + + + + + + + + + @foreach($invoiceItems as $i => $item) + + + + + + + + + @endforeach + + @if($cfg['show_summary'] ?? true) + + + + + + @if($invoice->discount_amount > 0) + + + + + @endif + + + + + + + + + + + + + @endif + + + + + +
STT
(No)
+ Tên hàng hóa, dịch vụ
+ (Name of goods and services) +
Đơn vị tính
(Unit)
Số lượng
(Quantity)
Đơn giá
(Unit price)
Thành tiền
(Amount)
{{ $i + 1 }}{{ $item->description ?? ($item->product?->name ?? 'Dịch vụ') }}{{ ($item->months && $item->months > 0) ? 'Tháng' : 'Lần' }}{{ number_format($item->quantity ?? 1, 2, ',', '.') }}{{ number_format($item->unit_price, 0, ',', '.') }}{{ number_format($item->total_price, 0, ',', '.') }}
+ Cộng tiền hàng (Total amount excl. VAT): + {{ number_format($invoice->sub_total, 0, ',', '.') }}
+ Giảm giá (Discount): + -{{ number_format($invoice->discount_amount, 0, ',', '.') }}
+ Thuế suất GTGT (VAT rate): + {{ $taxRate > 0 ? $taxRate . '%' : 'Không áp dụng' }} + Tiền thuế GTGT (VAT amount): + {{ number_format($invoice->tax, 0, ',', '.') }}
+ Tổng tiền thanh toán (Total amount): + {{ number_format($invoice->total, 0, ',', '.') }}
+ Số tiền viết bằng chữ + (Total amount in words): + {{ \App\Helpers\NumberToWords::convert((int) $invoice->total) }}. +
+
diff --git a/resources/views/pdf/invoice/seller.blade.php b/resources/views/pdf/invoice/seller.blade.php new file mode 100644 index 0000000..696d1c8 --- /dev/null +++ b/resources/views/pdf/invoice/seller.blade.php @@ -0,0 +1,39 @@ +
+

+ {{ $companyName }} +

+ +
+ Mã số thuế (Tax code): {{ $companyTaxCode }} +
+ +
+ Địa chỉ (Address): + {{ $companyAddress }} +
+ +
+ Tên cửa hàng (Shop name): +
+ + + + + + + + + + +
+ Số tài khoản (Bank account): + + 7379868 Ngân hàng TMCP Á Châu (ACB) Chi nhánh TP. Hồ Chí Minh +
+ 797379868 Ngân hàng TMCP Quân Đội (MB) Chi nhánh Phú Nhuận +
+ +
+ QR Code +
+
diff --git a/resources/views/pdf/invoice/signature.blade.php b/resources/views/pdf/invoice/signature.blade.php new file mode 100644 index 0000000..bbe35fa --- /dev/null +++ b/resources/views/pdf/invoice/signature.blade.php @@ -0,0 +1,35 @@ + + + + + + +
+
+ Người mua hàng (Buyer) +
+ +
+ (Chữ ký số nếu có)
+ (Digital signature if any) +
+ +
+ {{ $customer?->fullname ?? '' }} +
+
+
+ Người bán hàng (Seller) +
+ +
+ (Chữ ký điện tử, Chữ ký số)
+ (E-signature, Digital signature) +
+ +
+ Signature Valid
+ Ký bởi (Signed By): {{ $companyName }}
+ Ký ngày (Signing Date): +
+
diff --git a/resources/views/pdf/invoice/styles.blade.php b/resources/views/pdf/invoice/styles.blade.php new file mode 100644 index 0000000..899b544 --- /dev/null +++ b/resources/views/pdf/invoice/styles.blade.php @@ -0,0 +1,313 @@ + diff --git a/routes/api.php b/routes/api.php index 8458d0c..dbe8943 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,51 +1,7 @@ middleware('throttle:30,1'); Route::middleware('api.auth:admin')->group(function () { - Route::post('logout', [AuthController::class, 'logout']); - Route::post('refresh', [AuthController::class, 'refresh']); - Route::get('me', [AuthController::class, 'me']); - Route::put('profile', [AuthController::class, 'updateProfile']); - - // Role Permissions - Route::prefix('role-permissions')->group(function () { - // Accessible bởi mọi admin — trả về permissions hiệu lực của user hiện tại - Route::get('mine', [RolePermissionController::class, 'mine']); - // Các endpoint sau chỉ dành cho Full Administrator (kiểm tra trong controller) - Route::get('/', [RolePermissionController::class, 'index']); - Route::post('/', [RolePermissionController::class, 'store']); - Route::get('all-permissions', [RolePermissionController::class, 'allPermissions']); - Route::post('permissions', [RolePermissionController::class, 'createPermissionGroup']); - Route::delete('permissions/group/{module}', [ - RolePermissionController::class, - 'deletePermissionGroup', - ]); - Route::patch('permissions/{id}', [RolePermissionController::class, 'renamePermission']); - Route::put('{roleId}', [RolePermissionController::class, 'update']); - }); - - // Employees (module: administrator) - Route::prefix('employees')->group(function () { - Route::get('roles', [EmployeeController::class, 'roles'])->middleware('perm:administrator.view'); - Route::get('/', [EmployeeController::class, 'index'])->middleware('perm:administrator.view'); - Route::post('/', [EmployeeController::class, 'store'])->middleware('perm:administrator.create'); - Route::get('{id}', [EmployeeController::class, 'show'])->middleware('perm:administrator.view'); - Route::put('{id}', [EmployeeController::class, 'update'])->middleware('perm:administrator.edit'); - Route::delete('{id}', [EmployeeController::class, 'destroy'])->middleware( - 'perm:administrator.destroy', - ); - }); - - // Customers (module: customer) - Route::prefix('customers') - ->controller(CustomerController::class) - ->group(function () { - Route::get('trash', 'trash')->middleware('perm:customer.view'); - Route::get('vps/available', 'listAvailableVms')->middleware('perm:customer.view'); - Route::get('/', 'index')->middleware('perm:customer.view'); - Route::post('/', 'store')->middleware('perm:customer.create'); - Route::get('{uuid}', 'show')->middleware('perm:customer.view'); - Route::put('{uuid}', 'update')->middleware('perm:customer.edit'); - Route::delete('{uuid}', 'destroy')->middleware('perm:customer.destroy'); - Route::delete('{uuid}/destroy', 'destroy')->middleware('perm:customer.destroy'); - Route::post('{uuid}/reset-password', 'resetPassword')->middleware('perm:customer.edit'); - Route::post('{uuid}/send-password', 'sendPasswordEmail')->middleware('perm:customer.edit'); - Route::post('{uuid}/send-email', 'sendEmail')->middleware('perm:customer.edit'); - Route::post('{uuid}/login-as', 'loginAs')->middleware('perm:customer.edit'); - Route::post('{uuid}/add-credit', 'addCredit')->middleware('perm:customer.edit'); - Route::post('{uuid}/add-total', 'addTotal')->middleware('perm:customer.edit'); - Route::get('{uuid}/services', 'getServices')->middleware('perm:customer.view'); - Route::post('{uuid}/services', 'createService')->middleware('perm:customer.edit'); - Route::post('{uuid}/services/bulk-delete', 'bulkDeleteServices')->middleware( - 'perm:customer.edit', - ); - Route::put('{uuid}/services/{serviceId}', 'updateService')->middleware('perm:customer.edit'); - Route::post('{uuid}/services/{serviceId}/change-ip', 'changeServiceIp')->middleware('perm:customer.edit'); - Route::post('{uuid}/services/{serviceId}/change-port', 'changeServicePort')->middleware('perm:customer.edit'); - Route::post('{uuid}/services/{serviceId}/notify-renew', 'notifyServiceRenewal')->middleware('perm:customer.edit'); - Route::get('{uuid}/notes', 'getCustomerNotes')->middleware('perm:customer.view'); - Route::post('{uuid}/notes', 'storeCustomerNote')->middleware('perm:customer.edit'); - Route::delete('{uuid}/notes/{noteId}', 'destroyCustomerNote')->middleware('perm:customer.edit'); - Route::get('{uuid}/services/{serviceId}/notes', 'getServiceNotes')->middleware('perm:customer.view'); - Route::post('{uuid}/services/{serviceId}/notes', 'storeServiceNote')->middleware('perm:customer.edit'); - Route::delete('{uuid}/services/{serviceId}/notes/{noteId}', 'destroyServiceNote')->middleware( - 'perm:customer.edit', - ); - Route::get('{uuid}/orders', 'getOrders')->middleware('perm:customer.view'); - Route::put('{uuid}/orders/{orderId}', 'updateOrder')->middleware('perm:customer.edit'); - Route::get('{uuid}/invoices', 'getInvoices')->middleware('perm:customer.view'); - Route::put('{uuid}/invoices/{invoiceId}', 'updateInvoice')->middleware( - 'perm:customer.edit', - ); - Route::get('{uuid}/transactions', 'getTransactions')->middleware('perm:customer.view'); - Route::put('{uuid}/transactions/{transactionId}', 'updateTransaction')->middleware( - 'perm:customer.edit', - ); - Route::get('{uuid}/domains', 'getDomains')->middleware('perm:customer.view'); - Route::get('{uuid}/dns-domains', 'getDnsDomains')->middleware('perm:customer.view'); - Route::get('{uuid}/dns-domains/{domainId}/records', 'getDnsDomainRecords')->middleware( - 'perm:customer.view', - ); - Route::patch('{uuid}/status', 'updateStatus')->middleware('perm:customer.edit'); - Route::post('bulk-delete', 'bulkDelete')->middleware('perm:customer.destroy'); - Route::post('bulk-restore', 'bulkRestore')->middleware('perm:customer.edit'); - Route::post('bulk-force-delete', 'bulkForceDelete')->middleware('perm:customer.destroy'); - Route::post('bulk-status', 'bulkStatus')->middleware('perm:customer.edit'); - Route::post('{uuid}/restore', 'restore')->middleware('perm:customer.edit'); - Route::delete('{uuid}/force', 'forceDelete')->middleware('perm:customer.destroy'); - }); - - // Services (module: customer) - Route::prefix('services')->controller(ServiceController::class)->group(function () { - Route::get('/', 'index')->middleware('perm:customer.view'); - Route::get('{id}', 'show')->middleware('perm:customer.view'); - Route::patch('{id}/detach-customer', 'detachCustomer')->middleware('perm:customer.edit'); - Route::post('{id}/resend-credentials', 'resendCredentials')->middleware('perm:customer.edit'); - }); - - // Products (module: product-service) - Route::prefix('products') - ->controller(ProductController::class) - ->group(function () { - Route::get('export', 'export')->middleware('perm:product-service.manager'); - Route::get('/', 'index')->middleware('perm:product-service.view'); - Route::post('/', 'store')->middleware('perm:product-service.create'); - Route::post('import', 'import')->middleware('perm:product-service.create'); - Route::post('bulk-update-status', 'bulkUpdateStatus')->middleware('perm:product-service.edit'); - Route::post('bulk-destroy', 'bulkDestroy')->middleware('perm:product-service.destroy'); - - Route::controller(ProductController::class)->group(function () { - Route::get('os', 'getOsList')->middleware('perm:product-service.view'); - - Route::get('{id}', 'show')->middleware('perm:product-service.view'); - Route::get('{id}/os', 'getOs')->middleware('perm:product-service.view'); - Route::get('{id}/operating-systems', 'getOperatingSystems')->middleware( - 'perm:product-service.view', - ); - Route::put('{id}', 'update')->middleware('perm:product-service.edit'); - Route::delete('{id}', 'destroy')->middleware('perm:product-service.destroy'); - Route::post('{id}/duplicate', 'duplicate')->middleware('perm:product-service.create'); - }); - }); - - Route::prefix('product-categories') - ->controller(ProductCategoryController::class) - ->as('product-categories.') - ->group(function () { - Route::get('/', 'index')->middleware('perm:product-service.view')->name('index'); - Route::post('/', 'store')->middleware('perm:product-service.create')->name('store'); - Route::get('trashed', 'trashed')->middleware('perm:product-service.view')->name('trashed'); - Route::post('bulk-update-status', 'bulkUpdateStatus') - ->middleware('perm:product-service.edit') - ->name('bulk-update-status'); - Route::post('bulk-destroy', 'bulkDestroy') - ->middleware('perm:product-service.destroy') - ->name('bulk-destroy'); - Route::post('bulk-restore', 'bulkRestore') - ->middleware('perm:product-service.edit') - ->name('bulk-restore'); - Route::post('bulk-force-delete', 'bulkForceDelete') - ->middleware('perm:product-service.destroy') - ->name('bulk-force-delete'); - Route::get('{id}', 'show')->middleware('perm:product-service.view')->name('show'); - Route::put('{id}', 'update')->middleware('perm:product-service.edit')->name('update'); - Route::delete('{id}', 'destroy')->middleware('perm:product-service.destroy')->name('destroy'); - Route::post('{id}/restore', 'restore') - ->middleware('perm:product-service.edit') - ->name('restore'); - Route::delete('{id}/force', 'forceDelete') - ->middleware('perm:product-service.destroy') - ->name('force-delete'); - }); - - Route::prefix('product-type-fields') - ->controller(ProductTypeFieldController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:product-service.view'); - Route::post('/', 'store')->middleware('perm:product-service.create'); - Route::put('{id}', 'update')->middleware('perm:product-service.edit'); - Route::delete('{id}', 'destroy')->middleware('perm:product-service.destroy'); - }); - - // Addons (module: product-service) - Route::prefix('addons') - ->controller(AddonController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:product-service.view'); - Route::post('/', 'store')->middleware('perm:product-service.create'); - Route::post('bulk-update-status', 'bulkUpdateStatus')->middleware('perm:product-service.edit'); - Route::post('bulk-destroy', 'bulkDestroy')->middleware('perm:product-service.destroy'); - Route::get('{id}', 'show')->middleware('perm:product-service.view'); - Route::put('{id}', 'update')->middleware('perm:product-service.edit'); - Route::delete('{id}', 'destroy')->middleware('perm:product-service.destroy'); - }); - - // Báo giá thiết kế website (module: order) - Route::prefix('web-design-quotes') - ->controller(WebDesignQuoteController::class) - ->group(function () { - Route::get('platform-options', 'platformOptions')->middleware('perm:order.view'); - Route::get('/', 'index')->middleware('perm:order.view'); - Route::post('/', 'store')->middleware('perm:order.create'); - Route::get('{id}', 'show')->middleware('perm:order.view'); - Route::put('{id}', 'update')->middleware('perm:order.edit'); - Route::patch('{id}/status', 'updateStatus')->middleware('perm:order.edit'); - Route::get('{id}/pdf', 'downloadPdf')->middleware('perm:order.view'); - Route::delete('{id}', 'destroy')->middleware('perm:order.destroy'); - }); - - Route::prefix('post-categories') - ->controller(AdminPostCategoryController::class) - ->as('post-categories.') - ->group(function () { - Route::get('/', 'index')->middleware('perm:post.view')->name('index'); - Route::post('/', 'store')->middleware('perm:post.create')->name('store'); - Route::get('trashed', 'trashed')->middleware('perm:post.view')->name('trashed'); - Route::post('bulk-restore', 'bulkRestore')->middleware('perm:post.edit')->name('bulk-restore'); - Route::post('bulk-force-delete', 'bulkForceDelete') - ->middleware('perm:post.destroy') - ->name('bulk-force-delete'); - Route::post('{id}/restore', 'restore')->middleware('perm:post.edit')->name('restore'); - Route::delete('{id}/force-delete', 'forceDelete') - ->middleware('perm:post.destroy') - ->name('force-delete'); - Route::get('{id}', 'show')->middleware('perm:post.view')->name('show'); - Route::put('{id}', 'update')->middleware('perm:post.edit')->name('update'); - Route::delete('{id}', 'destroy')->middleware('perm:post.destroy')->name('destroy'); - }); - - Route::prefix('posts') - ->controller(AdminPostController::class) - ->as('posts.') - ->group(function () { - Route::get('/', 'index')->middleware('perm:post.view')->name('index'); - Route::post('/', 'store')->middleware('perm:post.create')->name('store'); - Route::post('sync-wordpress', 'syncWordpress') - ->middleware('perm:post.create') - ->name('sync-wordpress'); - Route::post('bulk-delete', 'bulkDelete')->middleware('perm:post.destroy')->name('bulk-delete'); - Route::post('bulk-update-status', 'bulkUpdateStatus') - ->middleware('perm:post.edit') - ->name('bulk-update-status'); - Route::get('trashed', 'trashed')->middleware('perm:post.view')->name('trashed'); - Route::post('bulk-restore', 'bulkRestore')->middleware('perm:post.edit')->name('bulk-restore'); - Route::post('bulk-force-delete', 'bulkForceDelete') - ->middleware('perm:post.destroy') - ->name('bulk-force-delete'); - Route::post('{id}/restore', 'restore')->middleware('perm:post.edit')->name('restore'); - Route::delete('{id}/force-delete', 'forceDelete') - ->middleware('perm:post.destroy') - ->name('force-delete'); - Route::get('{id}', 'show')->middleware('perm:post.view')->name('show'); - Route::put('{id}', 'update')->middleware('perm:post.edit')->name('update'); - Route::delete('{id}', 'destroy')->middleware('perm:post.destroy')->name('destroy'); - }); - - // Orders (module: order) - Route::prefix('orders') - ->controller(OrderController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:order.view'); - Route::post('/upload-proof', 'uploadProof')->middleware('perm:order.create'); - Route::post('/', 'store')->middleware('perm:order.create'); - Route::get('{id}', 'show')->middleware('perm:order.view'); - Route::put('{id}', 'update')->middleware('perm:order.edit'); - Route::delete('{id}', 'destroy')->middleware('perm:order.destroy'); - Route::post('{id}/accept', 'accept')->middleware('perm:order.edit'); - Route::post('{id}/cancel', 'cancel')->middleware('perm:order.edit'); - }); - - // Invoices (module: invoice) - Route::prefix('invoices') - ->controller(InvoiceController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:invoice.view'); - Route::get('/datatable', 'datatable')->middleware('perm:invoice.view'); - Route::post('/', 'store')->middleware('perm:invoice.create'); - Route::get('{id}', 'show')->middleware('perm:invoice.view'); - Route::put('{id}', 'update')->middleware('perm:invoice.edit'); - Route::delete('{id}', 'destroy')->middleware('perm:invoice.destroy'); - Route::post('{id}/paid', 'markAsPaid')->middleware('perm:invoice.edit'); - Route::post('{id}/cancel', 'cancel')->middleware('perm:invoice.edit'); - Route::post('{id}/remind', 'remind')->middleware('perm:invoice.edit'); - Route::post('{id}/refund', 'refund')->middleware('perm:invoice.edit'); - // Attachments - Route::get('{id}/attachments', 'listAttachments')->middleware('perm:invoice.view'); - Route::post('{id}/attachments', 'uploadAttachment')->middleware('perm:invoice.edit'); - Route::delete('{id}/attachments/{attachmentId}', 'deleteAttachment')->middleware( - 'perm:invoice.edit', - ); - // Send mail notification - Route::post('{id}/send-mail', 'sendMail')->middleware('perm:invoice.edit'); - }); - - // Reports / Data exports - Route::prefix('reports/export') - ->controller(ExportController::class) - ->middleware('perm:report.view') - ->group(function () { - Route::get('history', 'history'); - Route::post('preview', 'preview'); - Route::post('/', 'store'); - Route::get('{uuid}', 'show'); - Route::get('{uuid}/download', 'download'); - }); - - // Payments (module: payment) - Route::prefix('payments') - ->controller(PaymentController::class) - ->group(function () { - Route::get('for-select', 'forSelect'); - Route::get('/', 'index')->middleware('perm:payment.view'); - Route::post('/', 'store')->middleware('perm:payment.create'); - Route::get('{id}', 'show')->middleware('perm:payment.view'); - Route::put('{id}', 'update')->middleware('perm:payment.edit'); - Route::delete('{id}', 'destroy')->middleware('perm:payment.destroy'); - }); - - // OS Groups & OS Entries (module: module-vcenter) - Route::prefix('os-groups') - ->controller(OsGroupController::class) - ->as('os-groups.') - ->group(function () { - Route::get('/', 'index')->middleware('perm:module-vcenter.manager')->name('index'); - Route::post('/', 'store')->middleware('perm:module-vcenter.manager')->name('store'); - Route::get('{id}', 'show')->middleware('perm:module-vcenter.manager')->name('show'); - Route::put('{id}', 'update')->middleware('perm:module-vcenter.manager')->name('update'); - Route::delete('{id}', 'destroy')->middleware('perm:module-vcenter.manager')->name('destroy'); - }); - - Route::prefix('os') - ->controller(OsController::class) - ->as('os.') - ->group(function () { - Route::post('/', 'store')->middleware('perm:module-vcenter.manager')->name('store'); - Route::put('{id}', 'update')->middleware('perm:module-vcenter.manager')->name('update'); - Route::delete('{id}', 'destroy')->middleware('perm:module-vcenter.manager')->name('destroy'); - }); - - // VPS (module: module-vcenter) - Route::prefix('vps') - ->controller(VPSController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:module-vcenter.view'); - Route::get('{id}', 'show')->middleware('perm:module-vcenter.view'); - Route::put('{id}', 'update')->middleware('perm:module-vcenter.edit'); - Route::post('{id}/power-on', 'powerOn')->middleware('perm:module-vcenter.edit'); - Route::post('{id}/power-off', 'powerOff')->middleware('perm:module-vcenter.edit'); - Route::post('{id}/restart', 'restart')->middleware('perm:module-vcenter.edit'); - Route::get('{id}/console-ticket', 'getConsoleTicket')->middleware('perm:module-vcenter.view'); - }); - - // vCenter (module: module-vcenter) - Route::prefix('vcenter') - ->controller(VCenterController::class) - ->group(function () { - Route::get('tree', 'tree')->middleware('perm:module-vcenter.view'); - Route::get('console-ticket', 'consoleTicket')->middleware('perm:module-vcenter.view'); - Route::post('/', 'store')->middleware('perm:module-vcenter.create'); - Route::post('sync', 'sync')->middleware('perm:module-vcenter.edit'); - Route::post('node/{type}', 'storeNode')->middleware('perm:module-vcenter.create'); - Route::put('node/{type}/{id}', 'updateNode')->middleware('perm:module-vcenter.edit'); - Route::delete('node/{type}/{id}', 'destroyNode')->middleware('perm:module-vcenter.destroy'); - }); - - // DNS Manager — Domains (module: domain.manager) - Route::prefix('domains') - ->controller(DnsDomainController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:domain.manager'); - Route::post('/', 'store')->middleware('perm:domain.manager'); - Route::post('sync-dns', 'syncDns')->middleware('perm:domain.manager'); - Route::get('{id}', 'show')->middleware('perm:domain.manager'); - Route::put('{id}', 'update')->middleware('perm:domain.manager'); - Route::delete('{id}', 'destroy')->middleware('perm:domain.manager'); - // DNS Records - Route::get('{id}/dns', 'dnsIndex')->middleware('perm:domain.manager'); - Route::post('{id}/dns', 'dnsStore')->middleware('perm:domain.manager'); - Route::put('{id}/dns/{recordId}', 'dnsUpdate')->middleware('perm:domain.manager'); - Route::delete('{id}/dns/{recordId}', 'dnsDestroy')->middleware('perm:domain.manager'); - }); - - Route::get('registrars', [RegistrarController::class, 'index'])->middleware('perm:domain.manager'); - - // DNS Manager — Servers (module: domain.manager) - Route::prefix('dns-servers') - ->controller(DnsServerController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:domain.manager'); - Route::post('/', 'store')->middleware('perm:domain.manager'); - Route::get('{id}', 'show')->middleware('perm:domain.manager'); - Route::post('{id}/check-connection', 'checkConnection')->middleware('perm:domain.manager'); - Route::put('{id}', 'update')->middleware('perm:domain.manager'); - Route::delete('{id}', 'destroy')->middleware('perm:domain.manager'); - }); - - Route::prefix('dns-records') - ->controller(DnsRecordController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:domain.manager'); - Route::get('{id}', 'show')->middleware('perm:domain.manager'); - Route::put('{id}', 'update')->middleware('perm:domain.manager'); - }); - - // Hosting Manager (module: hosting) - Route::prefix('hosting-accounts') - ->controller(HostingAccountController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:hosting.view'); - Route::get('{id}', 'show')->middleware('perm:hosting.view'); - Route::post('/', 'store')->middleware('perm:hosting.create'); - Route::put('{id}', 'update')->middleware('perm:hosting.edit'); - Route::delete('{id}', 'destroy')->middleware('perm:hosting.destroy'); - Route::post('{id}/suspend', 'suspend')->middleware('perm:hosting.edit'); - Route::post('{id}/unsuspend', 'unsuspend')->middleware('perm:hosting.edit'); - Route::post('{id}/reset-ftp-password', 'resetFtpPassword')->middleware('perm:hosting.edit'); - }); - - // Service Plans — hosting package configs (module: hosting) - Route::prefix('service-plans') - ->controller(ServicePlanController::class) - ->as('service-plans.') - ->group(function () { - Route::get('/', 'index')->middleware('perm:hosting.view')->name('index'); - Route::post('/', 'store')->middleware('perm:hosting.create')->name('store'); - Route::get('{id}', 'show')->middleware('perm:hosting.view')->name('show'); - Route::put('{id}', 'update')->middleware('perm:hosting.edit')->name('update'); - Route::delete('{id}', 'destroy')->middleware('perm:hosting.destroy')->name('destroy'); - }); - - // InfraManagement (jumphost, vcenter config) - Route::prefix('infra-management') - ->controller(InfraManagementController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:module-vcenter.manager'); - Route::post('/', 'store')->middleware('perm:module-vcenter.manager'); - Route::get('{id}', 'show')->middleware('perm:module-vcenter.manager'); - Route::put('{id}', 'update')->middleware('perm:module-vcenter.manager'); - Route::delete('{id}', 'destroy')->middleware('perm:module-vcenter.manager'); - }); - - // Domain Orders (module: order) - Route::prefix('domain-orders') - ->controller(AdminDomainOrderController::class) - ->group(function () { - Route::get('pending-count', 'pendingCount')->middleware('perm:order.view'); - Route::get('/', 'index')->middleware('perm:order.view'); - Route::get('{id}', 'show')->middleware('perm:order.view'); - Route::post('{id}/confirm-registration', 'confirmRegistration')->middleware('perm:order.edit'); - Route::patch('{id}/kyc', 'updateKyc')->middleware('perm:order.edit'); - Route::patch('{id}/activate', 'activate')->middleware('perm:order.edit'); - Route::post('{id}/reject-and-refund', 'rejectAndRefund')->middleware('perm:order.edit'); - Route::get('{id}/registrant', 'getRegistrant')->middleware('perm:order.view'); - Route::patch('{id}/registrant', 'updateRegistrant')->middleware('perm:order.edit'); - Route::get('{id}/registrant/docx', 'downloadRegistrantDocx')->middleware('perm:order.view'); - }); - - // Domain Pricing (module: settings) - Route::prefix('domain-pricing') - ->controller(AdminDomainPricingController::class) - ->middleware('perm:setting.manager') - ->group(function () { - Route::get('/', 'index'); - Route::put('/', 'update'); - Route::post('/reset', 'reset'); - }); - - // Domain Tools — proxy tới iNET API (module: domain.manager) - Route::prefix('domain-tools') - ->controller(InetDomainController::class) - ->group(function () { - Route::get('whois/{domain}', 'whois')->middleware('perm:domain.manager'); - Route::get('check-available', 'checkAvailable')->middleware('perm:domain.manager'); - Route::post('bulk-check-available', 'bulkCheckAvailable')->middleware('perm:domain.manager'); - Route::post('ai-suggest', 'aiSuggest')->middleware('perm:domain.manager'); - Route::get('suffixes', 'suffixes')->middleware('perm:domain.manager'); - }); - - // Settings (module: administrator) - Route::prefix('settings') - ->controller(AdminSettingController::class) - ->middleware('perm:setting.manager') - ->group(function () { - Route::get('/', 'index'); - Route::put('/', 'update'); - Route::post('test-telegram', 'testTelegram'); - }); - - // VAT settings per service type (module: settings) - Route::prefix('vat-settings') - ->controller(AdminSettingController::class) - ->middleware('perm:setting.manager') - ->group(function () { - Route::get('/', 'vatIndex'); - Route::put('/', 'vatUpdate'); - }); - - Route::prefix('media') - ->controller(AdminMediaController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:media.view'); - Route::post('bulk-destroy', 'bulkDestroy')->middleware('perm:media.destroy'); - Route::delete('{id}', 'destroy')->middleware('perm:media.destroy'); - }); - - Route::post('media/upload', [AdminSettingController::class, 'uploadMedia']) - ->middleware('perm:media.create'); - - // Provider (active vCenter chain + provider settings — module: module-vcenter) - Route::prefix('provider')->group(function () { - Route::get('active-chain', [ - \App\Http\Controllers\Api\Admin\Provider\ActiveInfraController::class, - 'chain', - ])->middleware('perm:module-vcenter.view'); - Route::post('{type}/{id}/activate', [ - \App\Http\Controllers\Api\Admin\Provider\ActiveInfraController::class, - 'activate', - ]) - ->whereIn('type', ['vcenter', 'datacenter', 'host', 'datastore', 'network']) - ->middleware('perm:module-vcenter.edit'); - Route::post('{type}/{id}/deactivate', [ - \App\Http\Controllers\Api\Admin\Provider\ActiveInfraController::class, - 'deactivate', - ]) - ->whereIn('type', ['vcenter', 'datacenter', 'host', 'datastore', 'network']) - ->middleware('perm:module-vcenter.edit'); - - Route::get('settings', [ - \App\Http\Controllers\Api\Admin\Provider\ProviderSettingController::class, - 'index', - ])->middleware('perm:module-vcenter.view'); - Route::put('settings', [ - \App\Http\Controllers\Api\Admin\Provider\ProviderSettingController::class, - 'update', - ])->middleware('perm:module-vcenter.edit'); - }); - - // Dashboard Stats - Route::prefix('dashboard') - ->controller(\App\Http\Controllers\Api\Admin\Dashboard\DashboardController::class) - ->middleware('perm:dashboard.view') - ->group(function () { - Route::get('stats', 'stats'); - Route::get('monthly-stats', 'monthlyStats'); - Route::get('daily-stats', 'dailyStats'); - Route::get('recent-orders', 'recentOrders'); - Route::get('recent-deposits', 'recentDeposits'); - Route::get('top-services', 'topServices'); - Route::get('expiring-services', 'expiringServices'); - Route::get('activities', 'activities'); - Route::get('latest-customers', 'latestCustomers'); - }); - - // Staff Online - Route::prefix('staff-online') - ->controller(StaffOnlineController::class) - ->group(function () { - Route::get('/', 'index'); - Route::post('ping', 'ping'); - }); - - // Queue Monitor (Horizon) - Route::prefix('queue') - ->controller(QueueMonitorController::class) - ->middleware('perm:queue.manager') - ->group(function () { - Route::get('jobs/recent', 'recent'); - Route::get('jobs/pending', 'pending'); - Route::get('jobs/failed', 'failed'); - Route::post('jobs/failed/{id}/retry', 'retry'); - Route::get('jobs/metrics', 'metrics'); - }); - - // Banks — deposit bank accounts (module: settings) - Route::prefix('banks') - ->controller(BankController::class) - ->middleware('perm:setting.manager') - ->group(function () { - Route::get('/', 'index'); - Route::post('/', 'store'); - Route::put('{id}', 'update'); - Route::delete('{id}', 'destroy'); - Route::post('{id}/restore', 'restore'); - }); - - // Email Templates (module: settings) - Route::prefix('email-templates') - ->controller(EmailTemplateController::class) - ->middleware('perm:email-template.manager') - ->group(function () { - Route::get('/', 'index'); - Route::post('/', 'store'); - Route::get('scenarios', 'scenarios'); - Route::put('scenarios/{key}/label', 'updateScenarioLabel'); - Route::get('email-settings', 'getEmailSettings'); - Route::put('email-settings', 'updateEmailSettings'); - Route::get('{id}', 'show'); - Route::put('{id}', 'update'); - Route::delete('{id}', 'destroy'); - Route::get('{id}/preview-invoice', 'previewInvoiceJson'); - Route::post('{id}/preview', 'preview'); - Route::post('{id}/send-test', 'sendTest'); - }); - - // Transactions — manual deposit review (module: transaction) - Route::prefix('transactions') - ->controller(AdminTransactionController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:customer.view'); - Route::get('pending-count', 'pendingCount')->middleware('perm:customer.view'); - Route::post('{uuid}/approve', 'approve')->middleware('perm:customer.edit'); - Route::post('{uuid}/reject', 'reject')->middleware('perm:customer.edit'); - }); - - // IP Pool (module: module-vcenter) - Route::prefix('ip-pool') - ->controller(IpPoolController::class) - ->group(function () { - Route::get('networks', 'networks')->middleware('perm:module-vcenter.view'); - Route::get('used-services', 'usedServices')->middleware('perm:module-vcenter.view'); - Route::get('/', 'index')->middleware('perm:module-vcenter.view'); - Route::post('/', 'store')->middleware('perm:module-vcenter.manager'); - Route::post('sync', 'sync')->middleware('perm:module-vcenter.manager'); - Route::post('import', 'import')->middleware('perm:module-vcenter.manager'); - Route::delete('bulk', 'bulkDelete')->middleware('perm:module-vcenter.manager'); - Route::post('bulk-update-gateway', 'bulkUpdateGateway')->middleware('perm:module-vcenter.manager'); - Route::get('{id}', 'show')->middleware('perm:module-vcenter.view'); - Route::put('{id}', 'update')->middleware('perm:module-vcenter.manager'); - Route::delete('{id}', 'destroy')->middleware('perm:module-vcenter.manager'); - }); - - // vCenter Manager Power Operations (module: module-vcenter) - Route::prefix('vcenter-manager') - ->controller(VCenterManagerController::class) - ->group(function () { - Route::post('vm/power-on', 'powerOn')->middleware('perm:module-vcenter.edit'); - Route::post('vm/power-off', 'powerOff')->middleware('perm:module-vcenter.edit'); - Route::post('vm/restart', 'restart')->middleware('perm:module-vcenter.edit'); - Route::post('vm/snapshot', 'snapshot')->middleware('perm:module-vcenter.edit'); - Route::post('vm/suspend', 'suspend')->middleware('perm:module-vcenter.edit'); - Route::post('vm/update-config', 'updateConfig')->middleware('perm:module-vcenter.edit'); - Route::post('vm/delete', 'deleteVM')->middleware('perm:module-vcenter.edit'); - Route::get('vm/stats', 'stats')->middleware('perm:module-vcenter.view'); - Route::get('vm/power-state', 'powerState')->middleware('perm:module-vcenter.view'); - Route::get('vms/powered-off', 'getPoweredOffVms')->middleware('perm:module-vcenter.view'); - }); - - // Tickets (support tickets) - Route::prefix('tickets') - ->controller(AdminTicketController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:support.view'); - Route::get('{ticket_num}', 'show')->middleware('perm:support.view'); - Route::patch('{ticket_num}/status', 'updateStatus')->middleware('perm:support.edit'); - Route::post('{ticket_num}/reply', 'reply')->middleware('perm:support.edit'); - }); - - Route::prefix('notifications') - ->controller(NotificationController::class) - ->group(function () { - Route::get('/', fn() => app(NotificationController::class)->index(request(), 'admin')); - Route::get( - '{id}', - fn($id) => app(NotificationController::class)->show(request(), 'admin', (int) $id), - )->whereNumber('id'); - Route::patch( - '{id}/read', - fn($id) => app(NotificationController::class)->markRead(request(), 'admin', (int) $id), - ); - Route::patch( - 'read-all', - fn() => app(NotificationController::class)->markAllRead(request(), 'admin'), - ); - }); - - // Activity Logs - Route::prefix('activity-logs') - ->controller(ActivityLogController::class) - ->middleware('perm:system-log.manager') - ->group(function () { - Route::get('/', 'index'); - Route::get('modules', 'modules'); - Route::get('stats', 'stats'); - Route::get('{id}', 'show'); - }); - - // Email Logs - Route::prefix('email-logs') - ->controller(EmailLogController::class) - ->middleware('perm:system-log.manager') - ->group(function () { - Route::get('/', 'index'); - Route::get('{id}', 'show')->whereNumber('id'); - }); - - // Mail Campaigns (module: mail-campaign) - Route::prefix('mail-campaigns') - ->controller(MailCampaignController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:mail-campaign.view'); - Route::post('preview', 'preview')->middleware('perm:mail-campaign.create'); - Route::get('{uuid}/preview', 'previewCampaign')->middleware('perm:mail-campaign.view'); - Route::get('{uuid}', 'show')->middleware('perm:mail-campaign.view'); - Route::post('/', 'store')->middleware('perm:mail-campaign.create'); - }); - - // VM Rebuild Logs — who rebuilt, which OS, post-rebuild password - Route::prefix('rebuild-logs') - ->controller(AdminReinstallLogController::class) - ->group(function () { - Route::get('/', 'index')->middleware('perm:vps.view'); - Route::get('stats', 'stats')->middleware('perm:vps.view'); - Route::get('{id}', 'show')->middleware('perm:vps.view')->whereNumber('id'); - }); + require __DIR__.'/api/admin/auth.php'; + require __DIR__.'/api/admin/customers.php'; + require __DIR__.'/api/admin/catalog.php'; + require __DIR__.'/api/admin/content.php'; + require __DIR__.'/api/admin/billing.php'; + require __DIR__.'/api/admin/infrastructure.php'; + require __DIR__.'/api/admin/domains.php'; + require __DIR__.'/api/admin/hosting.php'; + require __DIR__.'/api/admin/system.php'; }); }); // Public branding settings — không cần token, dùng cho trang đăng nhập. Route::get('settings/public', [AdminSettingController::class, 'public']); - - Route::get('pdf-invoice', function () { - // return Pdf::loadView('pdf.invoice-pdf')->setPaper('a4', 'portrait')->stream('hoa-don-mau.pdf'); - }); }); Route::any('{any}', function () { diff --git a/routes/api/admin/auth.php b/routes/api/admin/auth.php new file mode 100644 index 0000000..45c65a1 --- /dev/null +++ b/routes/api/admin/auth.php @@ -0,0 +1,43 @@ +group(function () { + // Accessible bởi mọi admin — trả về permissions hiệu lực của user hiện tại + Route::get('mine', [RolePermissionController::class, 'mine']); + // Các endpoint sau chỉ dành cho Full Administrator (kiểm tra trong controller) + Route::get('/', [RolePermissionController::class, 'index']); + Route::post('/', [RolePermissionController::class, 'store']); + Route::get('all-permissions', [RolePermissionController::class, 'allPermissions']); + Route::post('permissions', [RolePermissionController::class, 'createPermissionGroup']); + Route::delete('permissions/group/{module}', [ + RolePermissionController::class, + 'deletePermissionGroup', + ]); + Route::patch('permissions/{id}', [RolePermissionController::class, 'renamePermission']); + Route::put('{roleId}', [RolePermissionController::class, 'update']); +}); + +// Employees (module: administrator) +Route::prefix('employees')->group(function () { + Route::get('roles', [EmployeeController::class, 'roles'])->middleware('perm:administrator.view'); + Route::get('/', [EmployeeController::class, 'index'])->middleware('perm:administrator.view'); + Route::post('/', [EmployeeController::class, 'store'])->middleware('perm:administrator.create'); + Route::get('{id}', [EmployeeController::class, 'show'])->middleware('perm:administrator.view'); + Route::put('{id}', [EmployeeController::class, 'update'])->middleware('perm:administrator.edit'); + Route::delete('{id}', [EmployeeController::class, 'destroy'])->middleware( + 'perm:administrator.destroy', + ); +}); diff --git a/routes/api/admin/billing.php b/routes/api/admin/billing.php new file mode 100644 index 0000000..6752e56 --- /dev/null +++ b/routes/api/admin/billing.php @@ -0,0 +1,92 @@ +controller(OrderController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:order.view'); + Route::post('/upload-proof', 'uploadProof')->middleware('perm:order.create'); + Route::post('/', 'store')->middleware('perm:order.create'); + Route::get('{id}', 'show')->middleware('perm:order.view'); + Route::put('{id}', 'update')->middleware('perm:order.edit'); + Route::delete('{id}', 'destroy')->middleware('perm:order.destroy'); + Route::post('{id}/accept', 'accept')->middleware('perm:order.edit'); + Route::post('{id}/cancel', 'cancel')->middleware('perm:order.edit'); + }); + +// Invoices (module: invoice) +Route::prefix('invoices') + ->controller(InvoiceController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:invoice.view'); + Route::post('/', 'store')->middleware('perm:invoice.create'); + Route::get('{id}', 'show')->middleware('perm:invoice.view'); + Route::put('{id}', 'update')->middleware('perm:invoice.edit'); + Route::delete('{id}', 'destroy')->middleware('perm:invoice.destroy'); + Route::post('{id}/paid', 'markAsPaid')->middleware('perm:invoice.edit'); + Route::post('{id}/cancel', 'cancel')->middleware('perm:invoice.edit'); + Route::post('{id}/remind', 'remind')->middleware('perm:invoice.edit'); + Route::post('{id}/refund', 'refund')->middleware('perm:invoice.edit'); + // Attachments + Route::get('{id}/attachments', 'listAttachments')->middleware('perm:invoice.view'); + Route::post('{id}/attachments', 'uploadAttachment')->middleware('perm:invoice.edit'); + Route::delete('{id}/attachments/{attachmentId}', 'deleteAttachment')->middleware( + 'perm:invoice.edit', + ); + // Send mail notification + Route::post('{id}/send-mail', 'sendMail')->middleware('perm:invoice.edit'); + }); + +// Reports / Data exports +Route::prefix('reports/export') + ->controller(ExportController::class) + ->middleware('perm:report.view') + ->group(function () { + Route::get('history', 'history'); + Route::post('preview', 'preview'); + Route::post('/', 'store'); + Route::get('{uuid}', 'show'); + Route::get('{uuid}/download', 'download'); + }); + +// Payments (module: payment) +Route::prefix('payments') + ->controller(PaymentController::class) + ->group(function () { + Route::get('for-select', 'forSelect'); + Route::get('/', 'index')->middleware('perm:payment.view'); + Route::post('/', 'store')->middleware('perm:payment.create'); + Route::get('{id}', 'show')->middleware('perm:payment.view'); + Route::put('{id}', 'update')->middleware('perm:payment.edit'); + Route::delete('{id}', 'destroy')->middleware('perm:payment.destroy'); + }); + +// Banks — deposit bank accounts (module: settings) +Route::prefix('banks') + ->controller(BankController::class) + ->middleware('perm:setting.manager') + ->group(function () { + Route::get('/', 'index'); + Route::post('/', 'store'); + Route::put('{id}', 'update'); + Route::delete('{id}', 'destroy'); + Route::post('{id}/restore', 'restore'); + }); + +// Transactions — manual deposit review (module: transaction) +Route::prefix('transactions') + ->controller(AdminTransactionController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:customer.view'); + Route::get('pending-count', 'pendingCount')->middleware('perm:customer.view'); + Route::post('{uuid}/approve', 'approve')->middleware('perm:customer.edit'); + Route::post('{uuid}/reject', 'reject')->middleware('perm:customer.edit'); + }); diff --git a/routes/api/admin/catalog.php b/routes/api/admin/catalog.php new file mode 100644 index 0000000..617875c --- /dev/null +++ b/routes/api/admin/catalog.php @@ -0,0 +1,97 @@ +controller(ProductController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:product-service.view'); + Route::post('/', 'store')->middleware('perm:product-service.create'); + Route::post('bulk-update-status', 'bulkUpdateStatus')->middleware('perm:product-service.edit'); + Route::post('bulk-destroy', 'bulkDestroy')->middleware('perm:product-service.destroy'); + + Route::controller(ProductController::class)->group(function () { + Route::get('os', 'getOsList')->middleware('perm:product-service.view'); + + Route::get('{id}', 'show')->middleware('perm:product-service.view'); + Route::get('{id}/os', 'getOs')->middleware('perm:product-service.view'); + Route::get('{id}/operating-systems', 'getOperatingSystems')->middleware( + 'perm:product-service.view', + ); + Route::put('{id}', 'update')->middleware('perm:product-service.edit'); + Route::delete('{id}', 'destroy')->middleware('perm:product-service.destroy'); + Route::post('{id}/duplicate', 'duplicate')->middleware('perm:product-service.create'); + }); + }); + +Route::prefix('product-categories') + ->controller(ProductCategoryController::class) + ->as('product-categories.') + ->group(function () { + Route::get('/', 'index')->middleware('perm:product-service.view')->name('index'); + Route::post('/', 'store')->middleware('perm:product-service.create')->name('store'); + Route::get('trashed', 'trashed')->middleware('perm:product-service.view')->name('trashed'); + Route::post('bulk-update-status', 'bulkUpdateStatus') + ->middleware('perm:product-service.edit') + ->name('bulk-update-status'); + Route::post('bulk-destroy', 'bulkDestroy') + ->middleware('perm:product-service.destroy') + ->name('bulk-destroy'); + Route::post('bulk-restore', 'bulkRestore') + ->middleware('perm:product-service.edit') + ->name('bulk-restore'); + Route::post('bulk-force-delete', 'bulkForceDelete') + ->middleware('perm:product-service.destroy') + ->name('bulk-force-delete'); + Route::get('{id}', 'show')->middleware('perm:product-service.view')->name('show'); + Route::put('{id}', 'update')->middleware('perm:product-service.edit')->name('update'); + Route::delete('{id}', 'destroy')->middleware('perm:product-service.destroy')->name('destroy'); + Route::post('{id}/restore', 'restore') + ->middleware('perm:product-service.edit') + ->name('restore'); + Route::delete('{id}/force', 'forceDelete') + ->middleware('perm:product-service.destroy') + ->name('force-delete'); + }); + +Route::prefix('product-type-fields') + ->controller(ProductTypeFieldController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:product-service.view'); + Route::post('/', 'store')->middleware('perm:product-service.create'); + Route::put('{id}', 'update')->middleware('perm:product-service.edit'); + Route::delete('{id}', 'destroy')->middleware('perm:product-service.destroy'); + }); + +// Addons (module: product-service) +Route::prefix('addons') + ->controller(AddonController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:product-service.view'); + Route::post('/', 'store')->middleware('perm:product-service.create'); + Route::post('bulk-update-status', 'bulkUpdateStatus')->middleware('perm:product-service.edit'); + Route::post('bulk-destroy', 'bulkDestroy')->middleware('perm:product-service.destroy'); + Route::get('{id}', 'show')->middleware('perm:product-service.view'); + Route::put('{id}', 'update')->middleware('perm:product-service.edit'); + Route::delete('{id}', 'destroy')->middleware('perm:product-service.destroy'); + }); + +// Báo giá thiết kế website (module: order) +Route::prefix('web-design-quotes') + ->controller(WebDesignQuoteController::class) + ->group(function () { + Route::get('platform-options', 'platformOptions')->middleware('perm:order.view'); + Route::get('/', 'index')->middleware('perm:order.view'); + Route::post('/', 'store')->middleware('perm:order.create'); + Route::get('{id}', 'show')->middleware('perm:order.view'); + Route::put('{id}', 'update')->middleware('perm:order.edit'); + Route::patch('{id}/status', 'updateStatus')->middleware('perm:order.edit'); + Route::get('{id}/pdf', 'downloadPdf')->middleware('perm:order.view'); + Route::delete('{id}', 'destroy')->middleware('perm:order.destroy'); + }); diff --git a/routes/api/admin/content.php b/routes/api/admin/content.php new file mode 100644 index 0000000..61b113a --- /dev/null +++ b/routes/api/admin/content.php @@ -0,0 +1,65 @@ +controller(AdminPostCategoryController::class) + ->as('post-categories.') + ->group(function () { + Route::get('/', 'index')->middleware('perm:post.view')->name('index'); + Route::post('/', 'store')->middleware('perm:post.create')->name('store'); + Route::get('trashed', 'trashed')->middleware('perm:post.view')->name('trashed'); + Route::post('bulk-restore', 'bulkRestore')->middleware('perm:post.edit')->name('bulk-restore'); + Route::post('bulk-force-delete', 'bulkForceDelete') + ->middleware('perm:post.destroy') + ->name('bulk-force-delete'); + Route::post('{id}/restore', 'restore')->middleware('perm:post.edit')->name('restore'); + Route::delete('{id}/force-delete', 'forceDelete') + ->middleware('perm:post.destroy') + ->name('force-delete'); + Route::get('{id}', 'show')->middleware('perm:post.view')->name('show'); + Route::put('{id}', 'update')->middleware('perm:post.edit')->name('update'); + Route::delete('{id}', 'destroy')->middleware('perm:post.destroy')->name('destroy'); + }); + +Route::prefix('posts') + ->controller(AdminPostController::class) + ->as('posts.') + ->group(function () { + Route::get('/', 'index')->middleware('perm:post.view')->name('index'); + Route::post('/', 'store')->middleware('perm:post.create')->name('store'); + Route::post('sync-wordpress', 'syncWordpress') + ->middleware('perm:post.create') + ->name('sync-wordpress'); + Route::post('bulk-delete', 'bulkDelete')->middleware('perm:post.destroy')->name('bulk-delete'); + Route::post('bulk-update-status', 'bulkUpdateStatus') + ->middleware('perm:post.edit') + ->name('bulk-update-status'); + Route::get('trashed', 'trashed')->middleware('perm:post.view')->name('trashed'); + Route::post('bulk-restore', 'bulkRestore')->middleware('perm:post.edit')->name('bulk-restore'); + Route::post('bulk-force-delete', 'bulkForceDelete') + ->middleware('perm:post.destroy') + ->name('bulk-force-delete'); + Route::post('{id}/restore', 'restore')->middleware('perm:post.edit')->name('restore'); + Route::delete('{id}/force-delete', 'forceDelete') + ->middleware('perm:post.destroy') + ->name('force-delete'); + Route::get('{id}', 'show')->middleware('perm:post.view')->name('show'); + Route::put('{id}', 'update')->middleware('perm:post.edit')->name('update'); + Route::delete('{id}', 'destroy')->middleware('perm:post.destroy')->name('destroy'); + }); + +Route::prefix('media') + ->controller(AdminMediaController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:media.view'); + Route::post('bulk-destroy', 'bulkDestroy')->middleware('perm:media.destroy'); + Route::delete('{id}', 'destroy')->middleware('perm:media.destroy'); + }); + +Route::post('media/upload', [AdminSettingController::class, 'uploadMedia']) + ->middleware('perm:media.create'); diff --git a/routes/api/admin/customers.php b/routes/api/admin/customers.php new file mode 100644 index 0000000..5de6dae --- /dev/null +++ b/routes/api/admin/customers.php @@ -0,0 +1,72 @@ +controller(CustomerController::class) + ->group(function () { + Route::get('trash', 'trash')->middleware('perm:customer.view'); + Route::get('vps/available', 'listAvailableVms')->middleware('perm:customer.view'); + Route::get('/', 'index')->middleware('perm:customer.view'); + Route::post('/', 'store')->middleware('perm:customer.create'); + Route::get('{uuid}', 'show')->middleware('perm:customer.view'); + Route::put('{uuid}', 'update')->middleware('perm:customer.edit'); + Route::delete('{uuid}', 'destroy')->middleware('perm:customer.destroy'); + Route::delete('{uuid}/destroy', 'destroy')->middleware('perm:customer.destroy'); + Route::post('{uuid}/reset-password', 'resetPassword')->middleware('perm:customer.edit'); + Route::post('{uuid}/send-password', 'sendPasswordEmail')->middleware('perm:customer.edit'); + Route::post('{uuid}/send-email', 'sendEmail')->middleware('perm:customer.edit'); + Route::post('{uuid}/login-as', 'loginAs')->middleware('perm:customer.edit'); + Route::post('{uuid}/add-credit', 'addCredit')->middleware('perm:customer.edit'); + Route::post('{uuid}/add-total', 'addTotal')->middleware('perm:customer.edit'); + Route::get('{uuid}/services', 'getServices')->middleware('perm:customer.view'); + Route::post('{uuid}/services', 'createService')->middleware('perm:customer.edit'); + Route::post('{uuid}/services/bulk-delete', 'bulkDeleteServices')->middleware( + 'perm:customer.edit', + ); + Route::put('{uuid}/services/{serviceId}', 'updateService')->middleware('perm:customer.edit'); + Route::post('{uuid}/services/{serviceId}/change-ip', 'changeServiceIp')->middleware('perm:customer.edit'); + Route::post('{uuid}/services/{serviceId}/change-port', 'changeServicePort')->middleware('perm:customer.edit'); + Route::post('{uuid}/services/{serviceId}/notify-renew', 'notifyServiceRenewal')->middleware('perm:customer.edit'); + Route::get('{uuid}/notes', 'getCustomerNotes')->middleware('perm:customer.view'); + Route::post('{uuid}/notes', 'storeCustomerNote')->middleware('perm:customer.edit'); + Route::delete('{uuid}/notes/{noteId}', 'destroyCustomerNote')->middleware('perm:customer.edit'); + Route::get('{uuid}/services/{serviceId}/notes', 'getServiceNotes')->middleware('perm:customer.view'); + Route::post('{uuid}/services/{serviceId}/notes', 'storeServiceNote')->middleware('perm:customer.edit'); + Route::delete('{uuid}/services/{serviceId}/notes/{noteId}', 'destroyServiceNote')->middleware( + 'perm:customer.edit', + ); + Route::get('{uuid}/orders', 'getOrders')->middleware('perm:customer.view'); + Route::put('{uuid}/orders/{orderId}', 'updateOrder')->middleware('perm:customer.edit'); + Route::get('{uuid}/invoices', 'getInvoices')->middleware('perm:customer.view'); + Route::put('{uuid}/invoices/{invoiceId}', 'updateInvoice')->middleware( + 'perm:customer.edit', + ); + Route::get('{uuid}/transactions', 'getTransactions')->middleware('perm:customer.view'); + Route::put('{uuid}/transactions/{transactionId}', 'updateTransaction')->middleware( + 'perm:customer.edit', + ); + Route::get('{uuid}/domains', 'getDomains')->middleware('perm:customer.view'); + Route::get('{uuid}/dns-domains', 'getDnsDomains')->middleware('perm:customer.view'); + Route::get('{uuid}/dns-domains/{domainId}/records', 'getDnsDomainRecords')->middleware( + 'perm:customer.view', + ); + Route::patch('{uuid}/status', 'updateStatus')->middleware('perm:customer.edit'); + Route::post('bulk-delete', 'bulkDelete')->middleware('perm:customer.destroy'); + Route::post('bulk-restore', 'bulkRestore')->middleware('perm:customer.edit'); + Route::post('bulk-force-delete', 'bulkForceDelete')->middleware('perm:customer.destroy'); + Route::post('bulk-status', 'bulkStatus')->middleware('perm:customer.edit'); + Route::post('{uuid}/restore', 'restore')->middleware('perm:customer.edit'); + Route::delete('{uuid}/force', 'forceDelete')->middleware('perm:customer.destroy'); + }); + +// Services (module: customer) +Route::prefix('services')->controller(ServiceController::class)->group(function () { + Route::get('/', 'index')->middleware('perm:customer.view'); + Route::get('{id}', 'show')->middleware('perm:customer.view'); + Route::patch('{id}/detach-customer', 'detachCustomer')->middleware('perm:customer.edit'); + Route::post('{id}/resend-credentials', 'resendCredentials')->middleware('perm:customer.edit'); +}); diff --git a/routes/api/admin/domains.php b/routes/api/admin/domains.php new file mode 100644 index 0000000..0eda096 --- /dev/null +++ b/routes/api/admin/domains.php @@ -0,0 +1,86 @@ +controller(DnsDomainController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:domain.manager'); + Route::post('/', 'store')->middleware('perm:domain.manager'); + Route::post('sync-dns', 'syncDns')->middleware('perm:domain.manager'); + Route::get('{id}', 'show')->middleware('perm:domain.manager'); + Route::put('{id}', 'update')->middleware('perm:domain.manager'); + Route::delete('{id}', 'destroy')->middleware('perm:domain.manager'); + // DNS Records + Route::get('{id}/dns', 'dnsIndex')->middleware('perm:domain.manager'); + Route::post('{id}/dns', 'dnsStore')->middleware('perm:domain.manager'); + Route::put('{id}/dns/{recordId}', 'dnsUpdate')->middleware('perm:domain.manager'); + Route::delete('{id}/dns/{recordId}', 'dnsDestroy')->middleware('perm:domain.manager'); + }); + +// DNS Manager — Servers (module: domain.manager) +Route::prefix('dns-servers') + ->controller(DnsServerController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:domain.manager'); + Route::post('/', 'store')->middleware('perm:domain.manager'); + Route::get('{id}', 'show')->middleware('perm:domain.manager'); + Route::post('{id}/check-connection', 'checkConnection')->middleware('perm:domain.manager'); + Route::put('{id}', 'update')->middleware('perm:domain.manager'); + Route::delete('{id}', 'destroy')->middleware('perm:domain.manager'); + }); + +Route::prefix('dns-records') + ->controller(DnsRecordController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:domain.manager'); + Route::get('{id}', 'show')->middleware('perm:domain.manager'); + Route::put('{id}', 'update')->middleware('perm:domain.manager'); + }); + +// Domain Orders (module: order) +Route::prefix('domain-orders') + ->controller(AdminDomainOrderController::class) + ->group(function () { + Route::get('pending-count', 'pendingCount')->middleware('perm:order.view'); + Route::get('/', 'index')->middleware('perm:order.view'); + Route::get('{id}', 'show')->middleware('perm:order.view'); + Route::post('{id}/confirm-registration', 'confirmRegistration')->middleware('perm:order.edit'); + Route::patch('{id}/kyc', 'updateKyc')->middleware('perm:order.edit'); + Route::patch('{id}/activate', 'activate')->middleware('perm:order.edit'); + Route::post('{id}/reject-and-refund', 'rejectAndRefund')->middleware('perm:order.edit'); + Route::get('{id}/registrant', 'getRegistrant')->middleware('perm:order.view'); + Route::patch('{id}/registrant', 'updateRegistrant')->middleware('perm:order.edit'); + Route::get('{id}/registrant/docx', 'downloadRegistrantDocx')->middleware('perm:order.view'); + }); + +// Domain Pricing (module: settings) +Route::prefix('domain-pricing') + ->controller(AdminDomainPricingController::class) + ->middleware('perm:setting.manager') + ->group(function () { + Route::get('/', 'index'); + Route::put('/', 'update'); + Route::post('/reset', 'reset'); + }); + +// Domain Tools — proxy tới iNET API (module: domain.manager) +Route::prefix('domain-tools') + ->controller(InetDomainController::class) + ->group(function () { + Route::get('whois/{domain}', 'whois')->middleware('perm:domain.manager'); + Route::get('check-available', 'checkAvailable')->middleware('perm:domain.manager'); + Route::post('bulk-check-available', 'bulkCheckAvailable')->middleware('perm:domain.manager'); + Route::post('ai-suggest', 'aiSuggest')->middleware('perm:domain.manager'); + Route::get('suffixes', 'suffixes')->middleware('perm:domain.manager'); + }); + +Route::get('registrars', [RegistrarController::class, 'index'])->middleware('perm:domain.manager'); diff --git a/routes/api/admin/hosting.php b/routes/api/admin/hosting.php new file mode 100644 index 0000000..8bde2f7 --- /dev/null +++ b/routes/api/admin/hosting.php @@ -0,0 +1,31 @@ +controller(HostingAccountController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:hosting.view'); + Route::get('{id}', 'show')->middleware('perm:hosting.view'); + Route::post('/', 'store')->middleware('perm:hosting.create'); + Route::put('{id}', 'update')->middleware('perm:hosting.edit'); + Route::delete('{id}', 'destroy')->middleware('perm:hosting.destroy'); + Route::post('{id}/suspend', 'suspend')->middleware('perm:hosting.edit'); + Route::post('{id}/unsuspend', 'unsuspend')->middleware('perm:hosting.edit'); + Route::post('{id}/reset-ftp-password', 'resetFtpPassword')->middleware('perm:hosting.edit'); + }); + +// Service Plans — hosting package configs (module: hosting) +Route::prefix('service-plans') + ->controller(ServicePlanController::class) + ->as('service-plans.') + ->group(function () { + Route::get('/', 'index')->middleware('perm:hosting.view')->name('index'); + Route::post('/', 'store')->middleware('perm:hosting.create')->name('store'); + Route::get('{id}', 'show')->middleware('perm:hosting.view')->name('show'); + Route::put('{id}', 'update')->middleware('perm:hosting.edit')->name('update'); + Route::delete('{id}', 'destroy')->middleware('perm:hosting.destroy')->name('destroy'); + }); diff --git a/routes/api/admin/infrastructure.php b/routes/api/admin/infrastructure.php new file mode 100644 index 0000000..b8a6239 --- /dev/null +++ b/routes/api/admin/infrastructure.php @@ -0,0 +1,140 @@ +controller(OsGroupController::class) + ->as('os-groups.') + ->group(function () { + Route::get('/', 'index')->middleware('perm:module-vcenter.manager')->name('index'); + Route::post('/', 'store')->middleware('perm:module-vcenter.manager')->name('store'); + Route::get('{id}', 'show')->middleware('perm:module-vcenter.manager')->name('show'); + Route::put('{id}', 'update')->middleware('perm:module-vcenter.manager')->name('update'); + Route::delete('{id}', 'destroy')->middleware('perm:module-vcenter.manager')->name('destroy'); + }); + +Route::prefix('os') + ->controller(OsController::class) + ->as('os.') + ->group(function () { + Route::post('/', 'store')->middleware('perm:module-vcenter.manager')->name('store'); + Route::put('{id}', 'update')->middleware('perm:module-vcenter.manager')->name('update'); + Route::delete('{id}', 'destroy')->middleware('perm:module-vcenter.manager')->name('destroy'); + }); + +// VPS (module: module-vcenter) +Route::prefix('vps') + ->controller(VPSController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:module-vcenter.view'); + Route::get('{id}', 'show')->middleware('perm:module-vcenter.view'); + Route::put('{id}', 'update')->middleware('perm:module-vcenter.edit'); + Route::post('{id}/power-on', 'powerOn')->middleware('perm:module-vcenter.edit'); + Route::post('{id}/power-off', 'powerOff')->middleware('perm:module-vcenter.edit'); + Route::post('{id}/restart', 'restart')->middleware('perm:module-vcenter.edit'); + Route::get('{id}/console-ticket', 'getConsoleTicket')->middleware('perm:module-vcenter.view'); + }); + +// vCenter (module: module-vcenter) +Route::prefix('vcenter') + ->controller(VCenterController::class) + ->group(function () { + Route::get('tree', 'tree')->middleware('perm:module-vcenter.view'); + Route::get('console-ticket', 'consoleTicket')->middleware('perm:module-vcenter.view'); + Route::post('/', 'store')->middleware('perm:module-vcenter.create'); + Route::post('sync', 'sync')->middleware('perm:module-vcenter.edit'); + Route::post('node/{type}', 'storeNode')->middleware('perm:module-vcenter.create'); + Route::put('node/{type}/{id}', 'updateNode')->middleware('perm:module-vcenter.edit'); + Route::delete('node/{type}/{id}', 'destroyNode')->middleware('perm:module-vcenter.destroy'); + }); + +// InfraManagement (jumphost, vcenter config) +Route::prefix('infra-management') + ->controller(InfraManagementController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:module-vcenter.manager'); + Route::post('/', 'store')->middleware('perm:module-vcenter.manager'); + Route::get('{id}', 'show')->middleware('perm:module-vcenter.manager'); + Route::put('{id}', 'update')->middleware('perm:module-vcenter.manager'); + Route::delete('{id}', 'destroy')->middleware('perm:module-vcenter.manager'); + }); + +// Provider (active vCenter chain + provider settings — module: module-vcenter) +Route::prefix('provider')->group(function () { + Route::get('active-chain', [ + \App\Http\Controllers\Api\Admin\Provider\ActiveInfraController::class, + 'chain', + ])->middleware('perm:module-vcenter.view'); + Route::post('{type}/{id}/activate', [ + \App\Http\Controllers\Api\Admin\Provider\ActiveInfraController::class, + 'activate', + ]) + ->whereIn('type', ['vcenter', 'datacenter', 'host', 'datastore', 'network']) + ->middleware('perm:module-vcenter.edit'); + Route::post('{type}/{id}/deactivate', [ + \App\Http\Controllers\Api\Admin\Provider\ActiveInfraController::class, + 'deactivate', + ]) + ->whereIn('type', ['vcenter', 'datacenter', 'host', 'datastore', 'network']) + ->middleware('perm:module-vcenter.edit'); + + Route::get('settings', [ + \App\Http\Controllers\Api\Admin\Provider\ProviderSettingController::class, + 'index', + ])->middleware('perm:module-vcenter.view'); + Route::put('settings', [ + \App\Http\Controllers\Api\Admin\Provider\ProviderSettingController::class, + 'update', + ])->middleware('perm:module-vcenter.edit'); +}); + +// IP Pool (module: module-vcenter) +Route::prefix('ip-pool') + ->controller(IpPoolController::class) + ->group(function () { + Route::get('networks', 'networks')->middleware('perm:module-vcenter.view'); + Route::get('used-services', 'usedServices')->middleware('perm:module-vcenter.view'); + Route::get('/', 'index')->middleware('perm:module-vcenter.view'); + Route::post('/', 'store')->middleware('perm:module-vcenter.manager'); + Route::post('sync', 'sync')->middleware('perm:module-vcenter.manager'); + Route::post('import', 'import')->middleware('perm:module-vcenter.manager'); + Route::delete('bulk', 'bulkDelete')->middleware('perm:module-vcenter.manager'); + Route::post('bulk-update-gateway', 'bulkUpdateGateway')->middleware('perm:module-vcenter.manager'); + Route::get('{id}', 'show')->middleware('perm:module-vcenter.view'); + Route::put('{id}', 'update')->middleware('perm:module-vcenter.manager'); + Route::delete('{id}', 'destroy')->middleware('perm:module-vcenter.manager'); + }); + +// vCenter Manager Power Operations (module: module-vcenter) +Route::prefix('vcenter-manager') + ->controller(VCenterManagerController::class) + ->group(function () { + Route::post('vm/power-on', 'powerOn')->middleware('perm:module-vcenter.edit'); + Route::post('vm/power-off', 'powerOff')->middleware('perm:module-vcenter.edit'); + Route::post('vm/restart', 'restart')->middleware('perm:module-vcenter.edit'); + Route::post('vm/snapshot', 'snapshot')->middleware('perm:module-vcenter.edit'); + Route::post('vm/suspend', 'suspend')->middleware('perm:module-vcenter.edit'); + Route::post('vm/update-config', 'updateConfig')->middleware('perm:module-vcenter.edit'); + Route::post('vm/delete', 'deleteVM')->middleware('perm:module-vcenter.edit'); + Route::get('vm/stats', 'stats')->middleware('perm:module-vcenter.view'); + Route::get('vm/power-state', 'powerState')->middleware('perm:module-vcenter.view'); + Route::get('vms/powered-off', 'getPoweredOffVms')->middleware('perm:module-vcenter.view'); + }); + +// VM Rebuild Logs — who rebuilt, which OS, post-rebuild password +Route::prefix('rebuild-logs') + ->controller(AdminReinstallLogController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:vps.view'); + Route::get('stats', 'stats')->middleware('perm:vps.view'); + Route::get('{id}', 'show')->middleware('perm:vps.view')->whereNumber('id'); + }); diff --git a/routes/api/admin/system.php b/routes/api/admin/system.php new file mode 100644 index 0000000..91b6e2f --- /dev/null +++ b/routes/api/admin/system.php @@ -0,0 +1,144 @@ +controller(AdminSettingController::class) + ->middleware('perm:setting.manager') + ->group(function () { + Route::get('/', 'index'); + Route::put('/', 'update'); + Route::post('test-telegram', 'testTelegram'); + }); + +// VAT settings per service type (module: settings) +Route::prefix('vat-settings') + ->controller(AdminSettingController::class) + ->middleware('perm:setting.manager') + ->group(function () { + Route::get('/', 'vatIndex'); + Route::put('/', 'vatUpdate'); + }); + +// Dashboard Stats +Route::prefix('dashboard') + ->controller(\App\Http\Controllers\Api\Admin\Dashboard\DashboardController::class) + ->middleware('perm:dashboard.view') + ->group(function () { + Route::get('stats', 'stats'); + Route::get('monthly-stats', 'monthlyStats'); + Route::get('daily-stats', 'dailyStats'); + Route::get('recent-orders', 'recentOrders'); + Route::get('recent-deposits', 'recentDeposits'); + Route::get('top-services', 'topServices'); + Route::get('expiring-services', 'expiringServices'); + Route::get('activities', 'activities'); + Route::get('latest-customers', 'latestCustomers'); + }); + +// Staff Online +Route::prefix('staff-online') + ->controller(StaffOnlineController::class) + ->group(function () { + Route::get('/', 'index'); + Route::post('ping', 'ping'); + }); + +// Queue Monitor (Horizon) +Route::prefix('queue') + ->controller(QueueMonitorController::class) + ->middleware('perm:queue.manager') + ->group(function () { + Route::get('jobs/recent', 'recent'); + Route::get('jobs/pending', 'pending'); + Route::get('jobs/failed', 'failed'); + Route::post('jobs/failed/{id}/retry', 'retry'); + Route::get('jobs/metrics', 'metrics'); + }); + +// Email Templates (module: settings) +Route::prefix('email-templates') + ->controller(EmailTemplateController::class) + ->middleware('perm:email-template.manager') + ->group(function () { + Route::get('/', 'index'); + Route::post('/', 'store'); + Route::get('scenarios', 'scenarios'); + Route::put('scenarios/{key}/label', 'updateScenarioLabel'); + Route::get('email-settings', 'getEmailSettings'); + Route::put('email-settings', 'updateEmailSettings'); + Route::get('{id}', 'show'); + Route::put('{id}', 'update'); + Route::delete('{id}', 'destroy'); + Route::post('{id}/preview', 'preview'); + Route::post('{id}/send-test', 'sendTest'); + }); + +// Tickets (support tickets) +Route::prefix('tickets') + ->controller(AdminTicketController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:support.view'); + Route::get('{ticket_num}', 'show')->middleware('perm:support.view'); + Route::patch('{ticket_num}/status', 'updateStatus')->middleware('perm:support.edit'); + Route::post('{ticket_num}/reply', 'reply')->middleware('perm:support.edit'); + }); + +Route::prefix('notifications') + ->controller(NotificationController::class) + ->group(function () { + Route::get('/', fn() => app(NotificationController::class)->index(request(), 'admin')); + Route::get( + '{id}', + fn($id) => app(NotificationController::class)->show(request(), 'admin', (int) $id), + )->whereNumber('id'); + Route::patch( + '{id}/read', + fn($id) => app(NotificationController::class)->markRead(request(), 'admin', (int) $id), + ); + Route::patch( + 'read-all', + fn() => app(NotificationController::class)->markAllRead(request(), 'admin'), + ); + }); + +// Activity Logs +Route::prefix('activity-logs') + ->controller(ActivityLogController::class) + ->middleware('perm:system-log.manager') + ->group(function () { + Route::get('/', 'index'); + Route::get('modules', 'modules'); + Route::get('stats', 'stats'); + Route::get('{id}', 'show'); + }); + +// Email Logs +Route::prefix('email-logs') + ->controller(EmailLogController::class) + ->middleware('perm:system-log.manager') + ->group(function () { + Route::get('/', 'index'); + Route::get('{id}', 'show')->whereNumber('id'); + }); + +// Mail Campaigns (module: mail-campaign) +Route::prefix('mail-campaigns') + ->controller(MailCampaignController::class) + ->group(function () { + Route::get('/', 'index')->middleware('perm:mail-campaign.view'); + Route::post('preview', 'preview')->middleware('perm:mail-campaign.create'); + Route::get('{uuid}/preview', 'previewCampaign')->middleware('perm:mail-campaign.view'); + Route::get('{uuid}', 'show')->middleware('perm:mail-campaign.view'); + Route::post('/', 'store')->middleware('perm:mail-campaign.create'); + });