Files
portal-api/resources/js/service/service.js
T
2025-07-24 16:18:27 +07:00

715 lines
26 KiB
JavaScript

// Global variables - NO SENSITIVE DATA (no tokens)
let invoiceId;
let vmId;
let vmHostname;
let csrfToken;
// Initialize variables from data attributes or global variables
function initializeVariables() {
// Get data from meta tags or global variables set in Blade template
const metaInvoiceId = document.querySelector('meta[name="invoice-id"]');
const metaVmId = document.querySelector('meta[name="vm-id"]');
const metaVmHostname = document.querySelector('meta[name="vm-hostname"]');
const metaCsrfToken = document.querySelector('meta[name="csrf-token"]');
invoiceId = metaInvoiceId ? metaInvoiceId.getAttribute('content') : window.invoiceId;
vmId = metaVmId ? metaVmId.getAttribute('content') : window.vmId;
vmHostname = metaVmHostname ? metaVmHostname.getAttribute('content') : window.vmHostname;
csrfToken = metaCsrfToken ? metaCsrfToken.getAttribute('content') : window.csrfToken;
}
// Cấu hình toast nếu có thể
if (typeof FuiToast !== 'undefined' && FuiToast.config) {
FuiToast.config({
position: 'top-center'
});
}
function showNotification(message, type = 'success') {
if (type === 'success') {
FuiToast.success(message);
} else {
FuiToast.error(message);
}
}
async function createSnapshot() {
const snapshotBtn = document.getElementById('createSnapshotButton');
if (snapshotBtn && snapshotBtn.disabled) return;
const requestBody = {
product_id: vmId
};
const headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': csrfToken
// NO Authorization header - using session auth
};
const snapshotPromise = fetch('/secure/create-snapshots', {
method: 'POST',
headers: headers,
body: JSON.stringify(requestBody),
credentials: 'same-origin'
}).then(async (response) => {
if (response.redirected || response.status === 401) {
throw new Error('Phiên đăng nhập đã hết hạn, vui lòng đăng nhập lại');
}
// Check if response is redirected (typical for Laravel redirect responses)
if (response.redirected) {
// Extract message from URL if possible, or show generic success
return 'Đã gửi yêu cầu tạo snapshot. Quá trình sẽ mất 3-5 phút, vui lòng chờ đợi.';
}
let data;
try {
const text = await response.text();
// Try to parse as JSON first
try {
data = JSON.parse(text);
} catch (parseError) {
// If not JSON, check for HTML with success/error messages
if (text.includes('success') || response.ok) {
setTimeout(() => {
location.reload();
}, 2000);
return 'Đã gửi yêu cầu tạo snapshot. Quá trình sẽ mất 3-5 phút, vui lòng chờ đợi.';
} else {
throw new Error('Có lỗi xảy ra khi gửi yêu cầu tạo snapshot');
}
}
} catch (parseError) {
throw new Error('Lỗi định dạng dữ liệu từ máy chủ');
}
// Handle JSON response
if (data && !data.error) {
setTimeout(() => {
location.reload();
}, 2000);
return data.message || 'Đã gửi yêu cầu tạo snapshot. Quá trình sẽ mất 3-5 phút, vui lòng chờ đợi.';
} else {
let errorMessage = 'Có lỗi xảy ra khi tạo snapshot';
if (data && data.message) {
if (typeof data.message === 'object') {
if (data.message.messages && data.message.messages.length > 0) {
errorMessage = data.message.messages[0].default_message || data.message.messages[0].message || 'Có lỗi xảy ra';
} else if (data.message.error_type) {
errorMessage = `Lỗi: ${data.message.error_type}`;
} else {
errorMessage = 'Có lỗi xảy ra từ hệ thống';
}
} else {
errorMessage = data.message;
}
} else if (data && data.value && data.value.messages && data.value.messages.length > 0) {
errorMessage = data.value.messages[0].default_message;
} else if (typeof data === 'string') {
errorMessage = data;
} else if (data && data.error) {
if (typeof data.error === 'object') {
if (data.error.message) {
errorMessage = data.error.message;
} else if (data.error.toString && data.error.toString() !== '[object Object]') {
errorMessage = data.error.toString();
} else {
errorMessage = 'Có lỗi xảy ra từ hệ thống';
}
} else {
errorMessage = data.error;
}
}
throw new Error(errorMessage);
}
});
FuiToast.promise(
snapshotPromise,
{
loading: 'Đang gửi yêu cầu tạo snapshot... (Quá trình mất 3-5 phút vui lòng giữ trang không tắt!)',
success: (data) => {
return data;
},
error: (error) => {
console.error(error);
return error.message || 'Có lỗi xảy ra khi tạo snapshot';
}
},
{
isClose: true
}
);
}
// Handle snapshot form submission with loading notification
function handleSnapshotSubmit(event) {
event.preventDefault();
const snapshotPromise = new Promise((resolve, reject) => {
const form = document.getElementById('snapshotForm');
const formData = new FormData(form);
fetch(form.action, {
method: 'POST',
body: formData,
headers: {
'X-CSRF-TOKEN': csrfToken
}
})
.then(response => {
if (response.redirected) {
resolve('Đã gửi yêu cầu tạo snapshot! Quá trình sẽ mất 3-5 phút.');
window.location.href = response.url;
} else if (response.ok) {
resolve('Đã gửi yêu cầu tạo snapshot! Quá trình sẽ mất 3-5 phút.');
setTimeout(() => {
window.location.reload();
}, 2000);
} else {
response.text().then(text => {
reject('Có lỗi xảy ra khi gửi yêu cầu tạo snapshot');
});
}
})
.catch(error => {
reject('Lỗi kết nối: ' + error.message);
});
});
if (typeof FuiToast !== 'undefined' && FuiToast.promise) {
FuiToast.promise(snapshotPromise, {
loading: 'Đang gửi yêu cầu tạo snapshot...',
success: (data) => {
return data || 'Đã gửi yêu cầu tạo snapshot!';
},
error: (error) => {
return error || 'Có lỗi xảy ra khi gửi yêu cầu tạo snapshot';
}
});
} else {
if (typeof FuiToast !== 'undefined') {
FuiToast.loading('Đang gửi yêu cầu tạo snapshot...', {
duration: 0,
});
} else {
alert('Đang gửi yêu cầu tạo snapshot...');
}
setTimeout(() => {
document.getElementById('snapshotForm').submit();
}, 500);
}
}
async function startVirtualMachine() {
const startBtn = document.getElementById('startVMButton');
if (startBtn && startBtn.disabled) return;
const requestBody = {
vm_id: vmId
};
const headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': csrfToken
// NO Authorization header - using session auth
};
const startPromise = fetch('/api/secure/start-vm', {
method: 'POST',
headers: headers,
body: JSON.stringify(requestBody),
credentials: 'same-origin'
}).then(async (response) => {
if (response.redirected || response.status === 401) {
throw new Error('Phiên đăng nhập đã hết hạn, vui lòng đăng nhập lại');
}
let data;
try {
data = await response.json();
} catch (parseError) {
throw new Error('Lỗi định dạng dữ liệu từ máy chủ');
}
if (!data.error) {
setTimeout(() => {
location.reload();
}, 2000);
return data.message;
} else {
if (data.type === 'com.vmware.vapi.std.errors.already_in_desired_state' ||
(data.message && data.message.error_type === 'ALREADY_IN_DESIRED_STATE')) {
setTimeout(() => {
location.reload();
}, 2000);
return 'Máy chủ đã được bật';
} else {
let errorMessage = 'Có lỗi xảy ra';
if (data.message) {
if (typeof data.message === 'object') {
if (data.message.messages && data.message.messages.length > 0) {
errorMessage = data.message.messages[0].default_message || data.message.messages[0]
.message || 'Có lỗi xảy ra';
} else if (data.message.error_type) {
errorMessage = `Lỗi: ${data.message.error_type}`;
} else {
errorMessage = 'Có lỗi xảy ra từ hệ thống';
}
} else {
errorMessage = data.message;
}
} else if (data.value && data.value.messages && data.value.messages.length > 0) {
errorMessage = data.value.messages[0].default_message;
} else if (typeof data === 'string') {
errorMessage = data;
} else if (data.error) {
if (typeof data.error === 'object') {
if (data.error.message) {
errorMessage = data.error.message;
} else if (data.error.toString && data.error.toString() !== '[object Object]') {
errorMessage = data.error.toString();
} else {
errorMessage = 'Có lỗi xảy ra từ hệ thống';
}
} else {
errorMessage = data.error;
}
}
throw new Error(errorMessage);
}
}
});
FuiToast.promise(
startPromise,
{
loading: 'Đang khởi động máy ảo...',
success: (data) => {
return data;
},
error: (error) => {
console.error(error);
return error.message || 'Có lỗi xảy ra khi khởi động máy ảo';
}
},
{
isClose: true
}
);
}
async function stopVirtualMachine() {
const stopBtn = document.getElementById('stopVMButton');
if (stopBtn && stopBtn.disabled) return;
const requestBody = {
vm_id: vmId
};
const headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': csrfToken
// NO Authorization header - using session auth
};
const stopPromise = fetch('/api/secure/stop-vm', {
method: 'POST',
headers: headers,
body: JSON.stringify(requestBody),
credentials: 'same-origin'
}).then(async (response) => {
if (response.redirected || response.status === 401) {
throw new Error('Phiên đăng nhập đã hết hạn, vui lòng đăng nhập lại');
}
let data;
try {
data = await response.json();
} catch (parseError) {
throw new Error('Lỗi định dạng dữ liệu từ máy chủ');
}
if (!data.error) {
setTimeout(() => {
location.reload();
}, 2000);
return data.message;
} else {
if (data.type === 'com.vmware.vapi.std.errors.already_in_desired_state' ||
(data.message && data.message.error_type === 'ALREADY_IN_DESIRED_STATE')) {
setTimeout(() => {
location.reload();
}, 2000);
return 'Máy chủ đã được tắt';
} else {
let errorMessage = 'Có lỗi xảy ra';
if (data.message) {
if (typeof data.message === 'object') {
if (data.message.messages && data.message.messages.length > 0) {
errorMessage = data.message.messages[0].default_message || data.message.messages[0]
.message || 'Có lỗi xảy ra';
} else if (data.message.error_type) {
errorMessage = `Lỗi: ${data.message.error_type}`;
} else {
errorMessage = 'Có lỗi xảy ra từ hệ thống';
}
} else {
errorMessage = data.message;
}
} else if (data.value && data.value.messages && data.value.messages.length > 0) {
errorMessage = data.value.messages[0].default_message;
} else if (typeof data === 'string') {
errorMessage = data;
} else if (data.error) {
if (typeof data.error === 'object') {
if (data.error.message) {
errorMessage = data.error.message;
} else if (data.error.toString && data.error.toString() !== '[object Object]') {
errorMessage = data.error.toString();
} else {
errorMessage = 'Có lỗi xảy ra từ hệ thống';
}
} else {
errorMessage = data.error;
}
}
throw new Error(errorMessage);
}
}
});
FuiToast.promise(
stopPromise,
{
loading: 'Đang dừng máy ảo...',
success: (data) => {
return data;
},
error: (error) => {
console.error(error);
return error.message || 'Có lỗi xảy ra khi dừng máy ảo';
}
},
{
isClose: true
}
);
}
async function restartVirtualMachine() {
const restartBtn = document.getElementById('restartVMButton');
if (restartBtn && restartBtn.disabled) return;
const restartPromise = fetch(`/service/${invoiceId}/vm/toggle`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({
action: 'restart'
})
}).then(async (response) => {
let data;
try {
data = await response.json();
} catch (parseError) {
throw new Error('Lỗi định dạng dữ liệu từ máy chủ');
}
if (data.success) {
setTimeout(() => {
location.reload();
}, 2000);
return data.message;
} else {
let errorMessage = 'Có lỗi xảy ra';
if (data.message) {
if (typeof data.message === 'object') {
if (data.message.messages && data.message.messages.length > 0) {
errorMessage = data.message.messages[0].default_message || data.message.messages[0]
.message || 'Có lỗi xảy ra';
} else if (data.message.error_type) {
errorMessage = `Lỗi: ${data.message.error_type}`;
} else {
errorMessage = 'Có lỗi xảy ra từ hệ thống';
}
} else {
errorMessage = data.message;
}
} else if (data.error) {
if (typeof data.error === 'object') {
if (data.error.message) {
errorMessage = data.error.message;
} else if (data.error.toString && data.error.toString() !== '[object Object]') {
errorMessage = data.error.toString();
} else {
errorMessage = 'Có lỗi xảy ra từ hệ thống';
}
} else {
errorMessage = data.error;
}
} else if (typeof data === 'string') {
errorMessage = data;
}
throw new Error(errorMessage);
}
});
FuiToast.promise(
restartPromise,
{
loading: 'Đang khởi động lại máy ảo...',
success: (data) => {
return data;
},
error: (error) => {
console.error(error);
return error.message || 'Có lỗi xảy ra khi khởi động lại máy ảo';
}
},
{
isClose: true
}
);
}
// DOM ready
document.addEventListener('DOMContentLoaded', function() {
// Initialize variables
initializeVariables();
const startBtn = document.getElementById('startVMButton');
const stopBtn = document.getElementById('stopVMButton');
const restartBtn = document.getElementById('restartVMButton');
const snapshotBtn = document.getElementById('createSnapshotButton');
if (startBtn) {
startBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
startVirtualMachine();
});
}
if (stopBtn) {
stopBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
stopVirtualMachine();
});
}
if (restartBtn) {
restartBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
restartVirtualMachine();
});
}
if (snapshotBtn) {
snapshotBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
createSnapshot();
});
}
});
// Snapshot Table Management
function snapshotTable() {
return {
loading: false,
snapshots: [],
error: null,
async fetchSnapshots() {
this.loading = true;
this.error = null;
const vmId = window.vmId || '';
if (!vmId) {
this.loading = false;
this.error = 'Không tìm thấy ID máy ảo';
return;
}
try {
const response = await fetch(`/secure/get-snapshots?product_id=${vmId}`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'X-CSRF-TOKEN': csrfToken,
'Cache-Control': 'no-cache'
},
credentials: 'same-origin'
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// Kiểm tra response structure từ API thực tế
if (data.error === false && data.data && data.data.snapshots) {
this.snapshots = data.data.snapshots;
} else if (data.error === true) {
this.error = data.message || 'Có lỗi xảy ra khi tải danh sách snapshot';
this.snapshots = [];
} else if (data.snapshots) {
// Fallback cho trường hợp data trực tiếp
this.snapshots = data.snapshots;
} else {
this.snapshots = [];
}
} catch (error) {
console.error('Error fetching snapshots:', error);
this.error = 'Không thể kết nối đến máy chủ';
this.snapshots = [];
} finally {
this.loading = false;
}
},
formatSize(sizeGB, sizeMB) {
// Convert to bytes first
let bytes = 0;
if (sizeGB) {
bytes = parseFloat(sizeGB) * 1024 * 1024 * 1024; // GB to bytes
} else if (sizeMB) {
bytes = parseFloat(sizeMB) * 1024 * 1024; // MB to bytes
}
if (bytes === 0) return '0 B';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
const k = 1024;
const i = Math.floor(Math.log(bytes) / Math.log(k));
const size = parseFloat((bytes / Math.pow(k, i)).toFixed(2));
return size + ' ' + units[i];
},
async deleteSnapshot(snapshotName) {
const result = await Swal.fire({
title: 'Xác nhận xóa',
text: `Bạn có chắc chắn muốn xóa snapshot "${snapshotName}"? Hành động này không thể hoàn tác.`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Xóa',
cancelButtonText: 'Hủy bỏ',
reverseButtons: true
});
if (!result.isConfirmed) return;
try {
const response = await fetch('/secure/remove-snapshot', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({
product_id: window.vmId,
snapshot_name: snapshotName
}),
credentials: 'same-origin'
});
const data = await response.json();
if (data.error === false) {
showNotification(data.message || 'Xóa snapshot thành công', 'success');
// Refresh snapshot list after successful deletion
setTimeout(() => this.fetchSnapshots(), 1000);
} else {
throw new Error(data.message || 'Có lỗi xảy ra khi xóa snapshot');
}
} catch (error) {
console.error('Error deleting snapshot:', error);
showNotification(error.message || 'Có lỗi xảy ra khi xóa snapshot', 'error');
}
},
async revertSnapshot(snapshotName) {
const result = await Swal.fire({
title: 'Xác nhận khôi phục',
text: `Bạn có chắc chắn muốn khôi phục về snapshot "${snapshotName}"? Tất cả dữ liệu hiện tại sẽ bị mất.`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Khôi phục',
cancelButtonText: 'Hủy bỏ',
reverseButtons: true
});
if (!result.isConfirmed) return;
try {
const response = await fetch('/secure/revert-snapshot', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({
product_id: window.vmId,
snapshot_name: snapshotName
}),
credentials: 'same-origin'
});
const data = await response.json();
if (data.error === false) {
showNotification(data.message || 'Khôi phục snapshot thành công', 'success');
// Refresh snapshot list after successful revert
setTimeout(() => this.fetchSnapshots(), 2000);
} else {
throw new Error(data.message || 'Có lỗi xảy ra khi khôi phục snapshot');
}
} catch (error) {
console.error('Error reverting snapshot:', error);
showNotification(error.message || 'Có lỗi xảy ra khi khôi phục snapshot', 'error');
}
},
// Helper method to refresh snapshots
refreshSnapshots() {
this.fetchSnapshots();
},
// Helper method to get snapshot count
getSnapshotCount() {
return this.snapshots.length;
},
// Helper method to check if snapshots are loading
isLoading() {
return this.loading;
},
// Helper method to check if there's an error
hasError() {
return this.error !== null;
}
}
}