715 lines
26 KiB
JavaScript
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;
|
|
}
|
|
}
|
|
} |