Files
portal-api/resources/js/service/service.js
T
2025-08-03 23:06:08 +07:00

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;