feat: Tích hợp Bản quyền License AES-256 (OTH.Adapter), Biên bản giao ban A4, Biểu đồ tròn và Tài liệu triển khai demo

This commit is contained in:
ongtrieuhau
2026-10-27 02:04:23 -07:00
parent 6d54ca20f6
commit 358cfbfde1
13 changed files with 2149 additions and 32 deletions
+117
View File
@@ -16,7 +16,11 @@ document.addEventListener('DOMContentLoaded', () => {
checkUpdate();
} else if (hash === '#tab-logs') {
switchTab('tab-logs');
} else if (hash === '#tab-license') {
switchTab('tab-license');
}
initLicenseManager();
});
function showToast(msg) {
@@ -446,3 +450,116 @@ function escapeHtml(str) {
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// -------------------------------------------------------------------
// LICENSE MANAGEMENT
// -------------------------------------------------------------------
const SAMPLE_LICENSE_KEY = "U2FsdGVkX18Zsq52FPbB7FS1QM7CwWh+t1Esi96gUMFILJD0vZXM69uQBC4sP7TjLVOiJPZt0KIq17iql+hXehLCVSbCEhwKKm+zNab+u2Swah63A52hz0KMJXddLRZ6vanmP2goLTzY7749HsFVRDq+3bdbi8fdUZCpAsSTEiqC2cRTpF5WVh0SHt1wX/i42YSQgOYPeXQtg+lRx/URk4p7lYSVQOYWXglp99kO+Va1ADoNcTgrfz3aaAxAkj0S";
function initLicenseManager() {
loadLicenseStatus();
const btnPaste = document.getElementById('btnPasteSampleKey');
if (btnPaste) {
btnPaste.addEventListener('click', () => {
const txt = document.getElementById('txtLicenseKey');
if (txt) {
txt.value = SAMPLE_LICENSE_KEY;
showToast('Đã dán mã bản quyền mẫu');
}
});
}
const btnActivate = document.getElementById('btnActivateLicense');
if (btnActivate) {
btnActivate.addEventListener('click', activateLicense);
}
}
async function loadLicenseStatus() {
try {
const res = await fetch(`${API_BASE}/api/license/status`);
const json = await res.json();
if (json.success && json.data) {
renderLicenseInfo(json.data);
}
} catch (err) {
console.error('Lỗi lấy thông tin bản quyền:', err);
}
}
function renderLicenseInfo(lic) {
const badge = document.getElementById('licenseStatusBadge');
const alertBox = document.getElementById('licenseAlertBox');
const alertMsg = document.getElementById('licenseAlertMsg');
document.getElementById('licTenBV').innerText = lic.TenBV || 'Chưa xác định';
document.getElementById('licMabvbh').innerText = lic.mabvbh || '--';
document.getElementById('licDateExpires').innerText = lic.DateExpires || '--';
document.getElementById('licTitle').innerText = lic.title || 'Sử dụng Web giao ban';
document.getElementById('licKey').innerText = lic.key || 'web_giao_ban';
document.getElementById('licDoiTac').innerText = lic['Đối tác'] || '--';
document.getElementById('licGhiChu').innerText = lic['Ghi chú'] || 'Không có';
if (lic.is_valid) {
badge.className = 'text-xs px-2.5 py-1 rounded-full font-bold bg-emerald-100 text-emerald-800 border border-emerald-300 flex items-center gap-1.5';
badge.innerHTML = '<i class="fa-solid fa-circle-check text-emerald-600"></i> Hợp Lệ';
document.getElementById('licRemainingDays').innerHTML = `<span class="text-emerald-600 font-bold"><i class="fa-solid fa-clock"></i> Còn ${lic.remaining_days} ngày</span>`;
alertBox.className = 'mt-4 p-3.5 rounded-xl border text-xs flex items-center gap-2.5 bg-emerald-50 border-emerald-200 text-emerald-800';
alertBox.innerHTML = `<i class="fa-solid fa-circle-check text-emerald-600 text-base"></i><span>${escapeHtml(lic.status_message || 'Bản quyền đang hoạt động bình thường.')}</span>`;
} else {
badge.className = 'text-xs px-2.5 py-1 rounded-full font-bold bg-rose-100 text-rose-800 border border-rose-300 flex items-center gap-1.5';
badge.innerHTML = '<i class="fa-solid fa-triangle-exclamation text-rose-600"></i> Chưa Hợp Lệ';
document.getElementById('licRemainingDays').innerHTML = `<span class="text-rose-600 font-bold"><i class="fa-solid fa-triangle-exclamation"></i> Không khả dụng</span>`;
alertBox.className = 'mt-4 p-3.5 rounded-xl border text-xs flex items-center gap-2.5 bg-rose-50 border-rose-200 text-rose-800';
alertBox.innerHTML = `<i class="fa-solid fa-circle-xmark text-rose-600 text-base"></i><span>${escapeHtml(lic.status_message || 'Chưa cập nhật key bản quyền hợp lệ.')}</span>`;
}
// Pre-fill textarea if key exists and textarea is empty
const txt = document.getElementById('txtLicenseKey');
if (txt && !txt.value && lic.raw_encrypted) {
txt.value = lic.raw_encrypted;
}
}
async function activateLicense() {
const txt = document.getElementById('txtLicenseKey');
if (!txt) return;
const keyVal = txt.value.trim();
if (!keyVal) {
alert('Vui lòng nhập chuỗi key bản quyền (bắt đầu bằng U2FsdGVkX1...)');
txt.focus();
return;
}
const btn = document.getElementById('btnActivateLicense');
btn.disabled = true;
btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin mr-1"></i> Đang xác thực...';
try {
const res = await fetch(`${API_BASE}/api/license/activate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: keyVal })
});
const json = await res.json();
if (json.success && json.data) {
showToast(json.message);
renderLicenseInfo(json.data);
alert('Kích hoạt thành công!\n\n' + json.message + '\nĐơn vị: ' + json.data.TenBV + '\nHạn sử dụng: ' + json.data.DateExpires);
} else {
alert('Kích hoạt không thành công: ' + (json.message || 'Lỗi không xác định'));
loadLicenseStatus();
}
} catch (err) {
alert('Lỗi kết nối máy chủ: ' + err.message);
} finally {
btn.disabled = false;
btn.innerHTML = '<i class="fa-solid fa-check-double"></i> <span>Kích Hoạt Key Bản Quyền</span>';
}
}