775 lines
26 KiB
JavaScript
775 lines
26 KiB
JavaScript
// Global variables - NO SENSITIVE DATA (no tokens)
|
|
let invoiceId;
|
|
let invoiceOrdernum;
|
|
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 metaInvoiceOrdernum = document.querySelector('meta[name="invoice-ordernum"]');
|
|
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;
|
|
invoiceOrdernum = metaInvoiceOrdernum
|
|
? metaInvoiceOrdernum.getAttribute("content")
|
|
: window.invoiceOrdernum;
|
|
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,
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
// 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() {
|
|
// Constants
|
|
const API_ENDPOINTS = {
|
|
GET_SNAPSHOTS: "/dich-vu-dang-ky/secure/get-snapshots",
|
|
REMOVE_SNAPSHOT: "/dich-vu-dang-ky/secure/remove-snapshot",
|
|
REVERT_SNAPSHOT: "/dich-vu-dang-ky/secure/revert-snapshot",
|
|
};
|
|
|
|
const MESSAGES = {
|
|
VM_ID_NOT_FOUND: "Không tìm thấy ID máy ảo",
|
|
CONNECTION_ERROR: "Không thể kết nối đến máy chủ",
|
|
GENERIC_ERROR: "Có lỗi xảy ra",
|
|
DELETE_SUCCESS: "Xóa snapshot thành công",
|
|
REVERT_SUCCESS: "Khôi phục snapshot thành công",
|
|
};
|
|
|
|
return {
|
|
loading: false,
|
|
snapshots: [],
|
|
error: null,
|
|
|
|
// Cache để tránh fetch lại không cần thiết
|
|
_lastFetchTime: 0,
|
|
_cacheTimeout: 30000, // 30 seconds
|
|
|
|
async fetchSnapshots(forceRefresh = false) {
|
|
// Check cache
|
|
const now = Date.now();
|
|
if (
|
|
!forceRefresh &&
|
|
this.snapshots.length > 0 &&
|
|
now - this._lastFetchTime < this._cacheTimeout
|
|
) {
|
|
return;
|
|
}
|
|
|
|
this.loading = true;
|
|
this.error = null;
|
|
|
|
const vmId = window.vmId?.trim();
|
|
if (!vmId) {
|
|
this._setError(MESSAGES.VM_ID_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const data = await this._makeRequest(
|
|
`${
|
|
API_ENDPOINTS.GET_SNAPSHOTS
|
|
}?product_id=${encodeURIComponent(vmId)}`
|
|
);
|
|
|
|
this._processSnapshotsResponse(data);
|
|
this._lastFetchTime = now;
|
|
} catch (error) {
|
|
this._handleError("fetching snapshots", error);
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
formatSize(sizeGB, sizeMB) {
|
|
const bytes = this._convertToBytes(sizeGB, sizeMB);
|
|
if (bytes === 0) return "0 B";
|
|
|
|
return this._formatBytes(bytes);
|
|
},
|
|
|
|
async deleteSnapshot(snapshotName) {
|
|
if (
|
|
!(await this._confirmAction(
|
|
"Xác nhận xóa",
|
|
`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.`,
|
|
"Xóa"
|
|
))
|
|
)
|
|
return;
|
|
|
|
await this._executeSnapshotAction(
|
|
API_ENDPOINTS.REMOVE_SNAPSHOT,
|
|
snapshotName,
|
|
MESSAGES.DELETE_SUCCESS,
|
|
"deleting snapshot"
|
|
);
|
|
},
|
|
|
|
async revertSnapshot(snapshotName) {
|
|
if (
|
|
!(await this._confirmAction(
|
|
"Xác nhận khôi phục",
|
|
`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.`,
|
|
"Khôi phục"
|
|
))
|
|
)
|
|
return;
|
|
|
|
await this._executeSnapshotAction(
|
|
API_ENDPOINTS.REVERT_SNAPSHOT,
|
|
snapshotName,
|
|
MESSAGES.REVERT_SUCCESS,
|
|
"reverting snapshot",
|
|
2000
|
|
);
|
|
},
|
|
|
|
// Public helper methods
|
|
refreshSnapshots() {
|
|
return this.fetchSnapshots(true);
|
|
},
|
|
|
|
getSnapshotCount() {
|
|
return this.snapshots.length;
|
|
},
|
|
|
|
isLoading() {
|
|
return this.loading;
|
|
},
|
|
|
|
hasError() {
|
|
return this.error !== null;
|
|
},
|
|
|
|
// Private helper methods
|
|
_setError(message) {
|
|
this.loading = false;
|
|
this.error = message;
|
|
this.snapshots = [];
|
|
},
|
|
|
|
async _makeRequest(url, options = {}) {
|
|
const defaultOptions = {
|
|
method: "GET",
|
|
headers: {
|
|
Accept: "application/json",
|
|
"X-CSRF-TOKEN": csrfToken,
|
|
"Cache-Control": "no-cache",
|
|
},
|
|
credentials: "same-origin",
|
|
};
|
|
|
|
const response = await fetch(url, {
|
|
...defaultOptions,
|
|
...options,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
},
|
|
|
|
_processSnapshotsResponse(data) {
|
|
// Handle different response structures
|
|
if (data.error === false && data.data?.snapshots) {
|
|
this.snapshots = data.data.snapshots;
|
|
} else if (data.error === true) {
|
|
throw new Error(
|
|
data.message || "Có lỗi xảy ra khi tải danh sách snapshot"
|
|
);
|
|
} else if (data.snapshots) {
|
|
this.snapshots = data.snapshots;
|
|
} else {
|
|
this.snapshots = [];
|
|
}
|
|
},
|
|
|
|
_convertToBytes(sizeGB, sizeMB) {
|
|
if (sizeGB) {
|
|
return parseFloat(sizeGB) * 1024 ** 3; // GB to bytes
|
|
} else if (sizeMB) {
|
|
return parseFloat(sizeMB) * 1024 ** 2; // MB to bytes
|
|
}
|
|
return 0;
|
|
},
|
|
|
|
_formatBytes(bytes) {
|
|
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 _confirmAction(title, text, confirmText) {
|
|
const result = await Swal.fire({
|
|
title,
|
|
text,
|
|
icon: "warning",
|
|
showCancelButton: true,
|
|
confirmButtonColor: title.includes("xóa") ? "#d33" : "#3085d6",
|
|
cancelButtonColor: title.includes("xóa") ? "#3085d6" : "#d33",
|
|
confirmButtonText: confirmText,
|
|
cancelButtonText: "Hủy bỏ",
|
|
reverseButtons: true,
|
|
});
|
|
|
|
return result.isConfirmed;
|
|
},
|
|
|
|
async _executeSnapshotAction(
|
|
endpoint,
|
|
snapshotName,
|
|
successMessage,
|
|
errorContext,
|
|
delay = 1000
|
|
) {
|
|
try {
|
|
const data = await this._makeRequest(endpoint, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
"X-CSRF-TOKEN": csrfToken,
|
|
},
|
|
body: JSON.stringify({
|
|
product_id: window.vmId,
|
|
snapshot_name: snapshotName,
|
|
}),
|
|
});
|
|
|
|
if (data.error === false) {
|
|
showNotification(data.message || successMessage, "success");
|
|
setTimeout(() => this.refreshSnapshots(), delay);
|
|
} else {
|
|
throw new Error(
|
|
data.message ||
|
|
`${MESSAGES.GENERIC_ERROR} khi ${errorContext}`
|
|
);
|
|
}
|
|
} catch (error) {
|
|
this._handleError(errorContext, error);
|
|
}
|
|
},
|
|
|
|
_handleError(context, error) {
|
|
console.error(`Error ${context}:`, error);
|
|
const message = error.message || MESSAGES.CONNECTION_ERROR;
|
|
|
|
if (context.includes("fetching")) {
|
|
this._setError(message);
|
|
} else {
|
|
showNotification(message, "error");
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
// Make snapshotTable available globally for Alpine.js
|
|
window.snapshotTable = snapshotTable;
|