��<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>�W T��fN�{t</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <style> /* ���gΘ </head> <body> <div class="main-container"> <!-- 4Y� --> <div class="header"> <h1><i class="fas fa-shield-alt me-2"></i>�W T��fN�{t</h1> <div id="userInfoContainer"> <div class="user-info" id="userInfo"> <div class="row text-center"> <div class="col-6 col-md-3"> <div><strong>(u7b</strong></div> <div id="username">�R}�-N...</div> </div> <div class="col-6 col-md-3"> <div><strong>���{</strong></div> <div id="email">�R}�-N...</div> </div> <div class="col-6 col-md-3"> <div><strong>IP</strong></div> <div id="lastLoginIp">�R}�-N...</div> </div> <div class="col-6 col-md-3"> <div><strong>{vU_�e��</strong></div> <div id="last_login_time">�R}�-N...</div> </div> </div> </div> </div> </div> <!-- d"}:S�W --> <div class="search-section"> <div class="search-card"> <form id="domainForm"> <div class="row align-items-end"> <div class="col-md-8"> <label class="form-label">�W T�g�</label> <textarea class="form-control" id="domains" rows="3" placeholder="��eQ�W T ��kL�N*N&#10;example.com&#10;test.com"></textarea> </div> <div class="col-md-4 mt-3 mt-md-0"> <button type="submit" class="btn btn-primary w-100"> <i class="fas fa-search me-2"></i>_�Y�g� </button> <button type="button" class="btn btn-outline-secondary w-100 mt-2" onclick="clearResults()"> <i class="fas fa-trash me-2"></i>nzz�~�g </button> </div> </div> </form> </div> </div> <!-- �d\O c��:S�W --> <div class="action-buttons"> <div class="d-flex justify-content-between align-items-center"> <div class="selected-count"> �] ��b <span id="selectedCount" class="fw-bold text-primary">0</span> *N�W T </div> <div class="btn-group-compact"> <button class="btn btn-outline-info btn-xs" onclick="showSelectedInfo()"> <i class="fas fa-info-circle me-1"></i>�g w��` </button> <button class="btn btn-primary btn-xs" onclick="showCertificateModal()"> <i class="fas fa-certificate me-1"></i>3u����fN </button> <button class="btn btn-outline-success btn-xs" onclick="batchOpenDomains()"> <i class="fas fa-external-link-alt me-1"></i>ybϑSb_ </button> <button class="btn btn-outline-warning btn-xs" onclick="exportSelectedDomains()"> <i class="fas fa-download me-1"></i>�[�QCSV </button> <button class="btn btn-outline-danger btn-xs" onclick="clearSelection()"> <i class="fas fa-times me-1"></i>nzz ��b </button> </div> </div> </div> <!-- �g��~�g --> <div class="results-section"> <div class="table-container"> <div class="table-responsive"> <table class="table"> <thead> <tr> <th style="width: 50px;"> <input type="checkbox" class="form-check-input" id="selectAll" onchange="toggleSelectAll()"> </th> <th>�z�pID</th> <th>�W TID</th> <th>�W T</th> <th>��fN�r`</th> <th>0Rg�e��</th> <th class="sortable" onclick="sortByExpirationDays()">iRYO)Ype</th> <th>{|�W</th> <th>�~g</th> <th>�~�</th> <th>(u7b</th> </tr> </thead> <tbody id="resultsBody"> </tbody> </table> </div> </div> </div> </div> <!-- ��fN3u��!j`Fh --> <div class="certificate-modal" id="certificateModal" style="display: none;" onclick="closeCertificateModalOnOverlay(event)"> <div class="certificate-form-compact" onclick="event.stopPropagation()"> <div class="certificate-header-compact"> <h6 class="mb-0"><i class="fas fa-certificate me-2"></i>��fN3u��</h6> <button class="btn-close-compact" onclick="closeCertificateModal()">�</button> </div> <div class="certificate-content"> <div class="row g-3"> <!-- �]�OM�n:S�W --> <div class="col-7"> <!-- ACME{|�W --> <div class="config-section"> <label class="config-label">��fN�c�OFU</label> <div class="option-row"> <div class="option-compact selected" onclick="selectAcmeType('letsencrypt')"> <input type="radio" name="acmeType" value="letsencrypt" id="acmeLetsencrypt" checked> <span class="option-icon">=��</span> <span class="option-text">Let's Encrypt</span> </div> <div class="option-compact" onclick="selectAcmeType('zerossl')"> <input type="radio" name="acmeType" value="zerossl" id="acmeZerossl"> <span class="option-icon">=����</span> <span class="option-text">ZeroSSL</span> </div> </div> </div> <!-- �����e_ --> <div class="config-section"> <label class="config-label">�����e_</label> <div class="option-row"> <div class="option-compact selected" onclick="selectVerification('http')"> <input type="radio" name="verification" value="http" id="verificationHttp" checked> <span class="option-icon"><��</span> <span class="option-text">HTTP����</span> </div> <div class="option-compact" onclick="selectVerification('dns')"> <input type="radio" name="verification" value="dns" id="verificationDns"> <span class="option-icon">=���</span> <span class="option-text">DNS����</span> </div> </div> </div> <!-- DNS&��S ��b --> <div class="config-section" id="dnsAccountGroup" style="display: none;"> <label class="config-label">DNS&��S</label> <select class="form-select form-select-sm" id="dnsAccountSelect"> <option value="">�R}�-N...</option> </select> </div> <!-- 3u�� �y� --> <div class="config-section"> <div class="form-check form-switch"> <input class="form-check-input" type="checkbox" id="independentApplication"> <label class="form-check-label small" for="independentApplication"> �r�z3u����fN </label> </div> </div> </div> <!-- �S�O�W T��ȉ:S�W --> <div class="col-5"> <div class="domains-section"> <label class="config-label"> �-N�W T <span id="selectedCountModal" class="badge bg-primary ms-1">0</span> </label> <div class="selected-domains-compact" id="selectedDomainsPreview"> <div class="empty-state"> <i class="fas fa-info-circle text-muted"></i> <div class="small text-muted">��(W;Nu�b� ��b�W T</div> </div> </div> </div> </div> </div> <!-- �^� c�� --> <div class="certificate-footer"> <button class="btn btn-outline-secondary btn-sm" onclick="closeCertificateModal()">�S�m</button> <button class="btn btn-primary btn-sm" id="submitButton" onclick="submitCertificateApplication()"> <i class="fas fa-paper-plane me-1"></i>�c�N3u�� </button> </div> </div> </div> </div> <script> let tableData = []; let isLoggedIn = false; let dnsAccounts = []; let sortOrder = 'none'; // 'none', 'asc', 'desc' document.addEventListener('DOMContentLoaded', function() { checkLoginStatus(); updateSelectedDisplay(); // �m�RESC.�sQ�!j`Fh document.addEventListener('keydown', function(event) { if (event.key === 'Escape') { closeCertificateModal(); } }); }); async function checkLoginStatus() { try { const response = await axios.get('token.php'); if (response.data.code === 200) { isLoggedIn = true; displayUserInfo(response.data.data); } else { showLoginPrompt(); } } catch (error) { console.error('�h�g{vU_�r`1Y%�:', error); showLoginPrompt(); } } function displayUserInfo(userData) { document.getElementById('username').textContent = userData.username || '*g�w'; document.getElementById('email').textContent = userData.email || '*g�w'; document.getElementById('lastLoginIp').textContent = userData.last_login_ip || '*g�w'; document.getElementById('last_login_time').textContent = userData.last_login_time || '*g�w'; } function showLoginPrompt() { const userInfoContainer = document.getElementById('userInfoContainer'); userInfoContainer.innerHTML = ` <div class="user-info text-center"> <div class="text-warning mb-2"> <i class="fas fa-exclamation-triangle fs-4"></i> </div> <div>{vU_�]Ǐg ��|�~ck(WꁨR{vU_...</div> <button class="btn btn-light btn-sm mt-2" onclick="checkLoginStatus()">͑�e�h�g</button> </div> `; } function cleanAndValidateDomain(domain) { if (!domain) return null; domain = domain.trim().replace(/\s+/g, '').replace(/^(https?:\/\/)/, '').replace(/\/.*$/, '').toLowerCase(); if (!/^[a-z0-9.-]+$/.test(domain) || domain.startsWith('.') || domain.endsWith('.') || domain.includes('..')) { return null; } return domain; } document.getElementById('domainForm').addEventListener('submit', function(event) { event.preventDefault(); if (!isLoggedIn) { alert('��I{�_{vU_�[b'); checkLoginStatus(); return; } let domains = document.getElementById('domains').value.trim().split('\n') .map(domain => cleanAndValidateDomain(domain)) .filter(domain => domain !== null); if (domains.length === 0) { alert('����eQ gHe�v�W T�'); return; } clearResults(); domains.forEach(domain => { insertPendingResult(domain); queryDomain(domain); }); }); function insertPendingResult(domain) { const row = { selected: false, siteId: '�g�-N...', domainId: '�g�-N...', domainName: domain, certificateStatus: '�g�-N...', certificateExpiration: '�g�-N...', expirationDays: '�g�-N...', certificateType: '�g�-N...', autoRenewal: false, line: '�g�-N...', cname: '�g�-N...', user: '�g�-N...', sources: '�g�-N...', expirationStatus: '', searchDomain: domain, certificateId: '�g�-N...' }; tableData.push(row); renderTable(); } function queryDomain(domainName) { axios.get('api.php', { params: { domain_name: domainName } }) .then(response => { const data = response.data; if (data.code === 200 && data.data && data.data.items && data.data.items.length > 0) { updateResult(domainName, data.data.items[0]); } else if (data.code === 406) { showNoResult(domainName, '{vU_Ǐg'); checkLoginStatus(); } else { showNoResult(domainName, '*g~b0R'); } }) .catch(error => { console.error('�g���:', error); showNoResult(domainName, '�g�1Y%�'); }); } function updateResult(domain, data) { tableData = tableData.filter(row => row.searchDomain !== domain); const sources = data.sources || []; const lineInfo = data.line_group_info?.name || data.order_info?.line_group_name || data.product_info?.name || '-'; const userInfo = data.client_info?.username || data.client_info?.email || '-'; const cnameInfo = data.dcname || data.cname || (data.line_group_info?.cname ? `${data.line_group_info.cname}.ttggui.com` : '-'); const sourceInfo = sources.map(s => s.content).join(', ') || '-'; if (data.domain_bindings && Array.isArray(data.domain_bindings)) { data.domain_bindings.forEach(binding => { const certificateExpiration = binding.certificate_info?.not_after || '-'; const expirationDays = calculateExpirationDays(certificateExpiration); const certificateStatus = binding.certificate_info?.status || 'unknown'; const certificateType = binding.certificate_info?.type || '-'; const autoRenewal = binding.certificate_info?.auto_renewal || false; const row = { selected: false, siteId: data.id, domainId: binding.id, domainName: binding.domain_name, certificateStatus: certificateStatus, certificateExpiration: certificateExpiration, expirationDays: expirationDays, certificateType: certificateType, autoRenewal: autoRenewal, line: lineInfo, cname: cnameInfo, user: userInfo, sources: sourceInfo, expirationStatus: getRowClass(expirationDays), searchDomain: domain, certificateId: binding.certificate_id || '-' }; tableData.push(row); }); } else { const row = { selected: false, siteId: data.id, domainId: '-', domainName: Array.isArray(data.domain_name) ? data.domain_name.join(', ') : data.domain_name || domain, certificateStatus: '�e��fN', certificateExpiration: '-', expirationDays: '-', certificateType: '-', autoRenewal: false, line: lineInfo, cname: cnameInfo, user: userInfo, sources: sourceInfo, expirationStatus: '', searchDomain: domain, certificateId: '-' }; tableData.push(row); } renderTable(); } function showNoResult(domain, errorMsg = ' NX[(W') { tableData = tableData.filter(row => row.searchDomain !== domain); const row = { selected: false, siteId: '-', domainId: '-', domainName: domain, certificateStatus: errorMsg, certificateExpiration: '-', expirationDays: '-', certificateType: '-', autoRenewal: false, line: '-', cname: '-', user: '-', sources: '-', expirationStatus: '', searchDomain: domain, certificateId: '-' }; tableData.push(row); renderTable(); } function sortByExpirationDays() { // Rbc�c�^�r` if (sortOrder === 'none' || sortOrder === 'desc') { sortOrder = 'asc'; } else { sortOrder = 'desc'; } // �f�eh�4Y7h_ const sortableHeader = document.querySelector('.sortable'); sortableHeader.className = 'sortable ' + sortOrder; // ͑�e2n�gh� { group.domains.sort((a, b) => { const aValue = getNumericExpirationDays(a.expirationDays); const bValue = getNumericExpirationDays(b.expirationDays); if (sortOrder === 'asc') { return aValue - bValue; } else { return bValue - aValue; } }); }); } groupedData.forEach((group) => { group.domains.forEach((row, domainIndex) => { const tr = document.createElement('tr'); tr.className = row.expirationStatus; const isFirstRowInGroup = domainIndex === 0; const rowSpan = group.domains.length; // �h�gte*N�~/f&T gHe� gw�[�W TID � const validDomainsInGroup = group.domains.filter(d => d.domainId !== '-' && d.domainId !== '�g�-N...'); const hasValidDomains = validDomainsInGroup.length > 0; // �h�gte*N�~/f&ThQ� �-N const allGroupSelected = hasValidDomains && validDomainsInGroup.every(d => d.selected); const someGroupSelected = hasValidDomains && validDomainsInGroup.some(d => d.selected); let html = ''; // �S(W,{NL�>f:yTv^�v Y �Fh if (isFirstRowInGroup) { if (hasValidDomains) { const checkboxState = allGroupSelected ? 'checked' : ''; const indeterminate = someGroupSelected && !allGroupSelected ? 'data-indeterminate="true"' : ''; html += `<td rowspan="${rowSpan}" class="site-group checkbox-cell"> <input type="checkbox" class="form-check-input site-checkbox" ${checkboxState} ${indeterminate} onchange="toggleSiteSelection('${row.siteId}')"> </td>`; } else { html += `<td rowspan="${rowSpan}" class="site-group checkbox-cell"> <span class="text-muted">-</span> </td>`; } html += `<td rowspan="${rowSpan}" class="site-group"><div class="site-id">${escapeHTML(row.siteId)}</div></td>`; } const statusBadge = getStatusBadge(row.certificateStatus); const autoRenewalDisplay = row.autoRenewal ? ''' : 'L''; const expirationDisplay = row.expirationDays !== '-' && row.expirationDays !== '�g�-N...' ? `${row.expirationDays})Y` : row.expirationDays; html += ` <td><strong>${escapeHTML(row.domainId)}</strong></td> <td>${escapeHTML(row.domainName)}</td> <td>${statusBadge}</td> <td><small>${escapeHTML(row.certificateExpiration)}</small></td> <td><strong>${escapeHTML(expirationDisplay)}</strong></td> <td><small>${escapeHTML(row.certificateType)}</small></td> <td>${autoRenewalDisplay}</td> `; if (isFirstRowInGroup) { html += ` <td rowspan="${rowSpan}" class="site-group"><small>${escapeHTML(row.line)}</small></td> <td rowspan="${rowSpan}" class="site-group"><small>${escapeHTML(row.user)}</small></td> `; } tr.innerHTML = html; tbody.appendChild(tr); }); }); updateSelectAllCheckbox(); updateIndeterminateCheckboxes(); } function groupBySiteId(data) { const groups = {}; data.forEach((row, index) => { row.originalIndex = index; const siteId = row.siteId; if (!groups[siteId]) { groups[siteId] = { siteId: siteId, domains: [] }; } groups[siteId].domains.push(row); }); return Object.values(groups).sort((a, b) => { if (a.siteId === '�g�-N...' || a.siteId === '-') return 1; if (b.siteId === '�g�-N...' || b.siteId === '-') return -1; const aNum = parseInt(a.siteId) || 0; const bNum = parseInt(b.siteId) || 0; return aNum - bNum; }); } function calculateExpirationDays(expirationDate) { if (!expirationDate || expirationDate === '-' || expirationDate === '�e��fN�Oo`' || expirationDate === '*g/T(uHTTPS') return '-'; const expDate = new Date(expirationDate); if (isNaN(expDate.getTime())) return '-'; const currentDate = new Date(); const timeDiff = expDate.getTime() - currentDate.getTime(); return Math.ceil(timeDiff / (1000 * 3600 * 24)); } function getRowClass(days) { if (days === '-') return ''; if (days < 0) return 'row-expired'; if (days <= 7) return 'row-expiring'; if (days <= 30) return 'row-warning'; return ''; } function getNumericExpirationDays(days) { // \yr�k${escapeHTML(status)}</span>`; } function toggleRowSelection(index) { if (tableData[index]) { tableData[index].selected = !tableData[index].selected; updateSelectAllCheckbox(); updateSelectedDisplay(); } } function toggleSiteSelection(siteId) { // ���S��z�p N�v@b g gHe�W T const siteRows = tableData.filter(row => row.siteId === siteId && row.domainId !== '-' && row.domainId !== '�g�-N...' ); if (siteRows.length === 0) return; // �h�gS_MR/f&ThQ� �-N const allSelected = siteRows.every(row => row.selected); // Rbc ��b�r`��Y�ghQ� �-NR�S�mhQ� �&TR �-NhQ� const newState = !allSelected; siteRows.forEach(row => { row.selected = newState; }); renderTable(); updateSelectedDisplay(); } function updateIndeterminateCheckboxes() { // �f�e�z�p�~ Y �Fh�v indeterminate �r` document.querySelectorAll('.site-checkbox').forEach(checkbox => { const isIndeterminate = checkbox.getAttribute('data-indeterminate') === 'true'; checkbox.indeterminate = isIndeterminate; }); } function toggleSelectAll() { const selectAll = document.getElementById('selectAll'); const isChecked = selectAll.checked; tableData.forEach(row => { if (row.domainId !== '-' && row.domainId !== '�g�-N...') { row.selected = isChecked; } }); renderTable(); updateSelectedDisplay(); } function updateSelectAllCheckbox() { const selectAll = document.getElementById('selectAll'); const validRows = tableData.filter(row => row.domainId !== '-' && row.domainId !== '�g�-N...'); const selectedRows = validRows.filter(row => row.selected); if (validRows.length === 0) { selectAll.checked = false; selectAll.indeterminate = false; } else if (selectedRows.length === validRows.length) { selectAll.checked = true; selectAll.indeterminate = false; } else if (selectedRows.length > 0) { selectAll.checked = false; selectAll.indeterminate = true; } else { selectAll.checked = false; selectAll.indeterminate = false; } } function getSelectedDomains() { return tableData.filter(row => row.selected).map(row => ({ siteId: row.siteId, domainId: row.domainId, domainName: row.domainName, certificateId: row.certificateId, certificateExpiration: row.certificateExpiration, expirationDays: row.expirationDays })); } function updateSelectedDisplay() { const selectedDomains = getSelectedDomains(); const selectedCount = document.getElementById('selectedCount'); const selectedCountModal = document.getElementById('selectedCountModal'); if (selectedCount) { selectedCount.textContent = selectedDomains.length; } if (selectedCountModal) { selectedCountModal.textContent = selectedDomains.length; } } function showSelectedInfo() { const selectedDomains = getSelectedDomains(); if (selectedDomains.length === 0) { alert('��HQ ��b�W T'); return; } let info = `�] ��b ${selectedDomains.length} *N�W T:\n\n`; selectedDomains.forEach((domain, index) => { info += `${index + 1}. ${domain.domainName}\n �z�pID: ${domain.siteId}\n �W TID: ${domain.domainId}\n ��fN0Rg: ${domain.certificateExpiration}\n\n`; }); alert(info); } function exportSelectedDomains() { const selectedDomains = getSelectedDomains(); if (selectedDomains.length === 0) { alert('��HQ ��b�W T'); return; } let csvContent = "�W T,�z�pID,�W TID,��fNID,��fN0Rg�e��,iRYO)Ype\n"; selectedDomains.forEach(domain => { csvContent += `"${domain.domainName}","${domain.siteId}","${domain.domainId}","${domain.certificateId}","${domain.certificateExpiration}","${domain.expirationDays}"\n`; }); const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = `domains_${new Date().getTime()}.csv`; link.click(); } function clearSelection() { tableData.forEach(row => { row.selected = false; }); renderTable(); updateSelectedDisplay(); } function batchOpenDomains() { const selectedDomains = getSelectedDomains(); if (selectedDomains.length === 0) { alert('��HQ ��b��Sb_�v�W T'); return; } // c�z�pR�~ �nx�O�k*N�z�p�v@�W T�T*�l㉐g�W T����Yt const siteGroups = {}; selectedDomains.forEach(domain => { if (!siteGroups[domain.siteId]) { siteGroups[domain.siteId] = []; } siteGroups[domain.siteId].push(domain); }); // ub��Sb_�vURLRh� ��S͑Yt const urlsToOpen = new Set(); Object.values(siteGroups).forEach(siteGroup => { siteGroup.forEach(domain => { const urls = generateDomainUrls(domain.domainName); urls.forEach(url => urlsToOpen.add(url)); }); }); const totalUrls = urlsToOpen.size; if (totalUrls > 10) { if (!confirm(`\��Sb_ ${totalUrls} *Nh~{u��S+T@�W T�T�l㉐g�W T �0nx�[���~�~T�`)) { return; } } let openedCount = 0; const delay = 500; // �k*NURLSb_����500ms ��MQOmȉhV;�bk const urlArray = Array.from(urlsToOpen); urlArray.forEach((url, index) => { setTimeout(() => { try { window.open(url, '_blank'); openedCount++; // gTN*NURLSb_T>f:y�c:y if (openedCount === totalUrls) { showNotification(`�]Sb_ ${openedCount} *N�W T`, 'success'); } } catch (error) { console.error('Sb_URL1Y%�:', url, error); } }, index * delay); }); showNotification(`ck(WybϑSb_ ${totalUrls} *N�W T...`, 'info'); } function generateDomainUrls(domainName) { const urls = []; // Yt�l㉐g�W T *.example.com if (domainName.startsWith('*.')) { const baseDomain = domainName.substring(2); // �yd� *. // :N�l㉐g�W Tub�Q*N8^(u�vP[�W TURL const subdomains = ['www', 'app', 'api', 'admin']; subdomains.forEach(subdomain => { urls.push(`https://${subdomain}.${baseDomain}`); }); // T�e_N�m�R�W@x�W T�@��U_ � urls.push(`https://${baseDomain}`); } else { // nf��W T ��SSb_N*NURL urls.push(`https://${domainName}`); } return urls; } function generateDomainUrl(domainName) { // �OYu�S�Qpe(u�NvQ�N0W�e�v|Q�['` const urls = generateDomainUrls(domainName); return urls[0]; // ԏ�V,{N*NURL } function showNotification(message, type = 'info') { // R�^��wCQ } const notification = document.createElement('div'); notification.className = `notification notification-${type}`; notification.innerHTML = ` <div class="notification-content"> <i class="fas fa-${getNotificationIcon(type)} me-2"></i> ${message} </div> `; // �m�R0Ru�b� document.body.appendChild(notification); // >f:y�R;u setTimeout(() => { notification.classList.add('show'); }, 100); // ꁨR��υ setTimeout(() => { notification.classList.remove('show'); setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }, 3000); } function getNotificationIcon(type) { switch (type) { case 'success': return 'check-circle'; case 'error': return 'exclamation-circle'; case 'warning': return 'exclamation-triangle'; default: return 'info-circle'; } } function clearResults() { tableData = []; sortOrder = 'none'; // ͑nh�4Y7h_ const sortableHeader = document.querySelector('.sortable'); if (sortableHeader) { sortableHeader.className = 'sortable'; } document.getElementById('resultsBody').innerHTML = ''; updateSelectedDisplay(); } function escapeHTML(str) { if (str === null || str === undefined) return ''; return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;'); } function showCertificateModal() { const selectedDomains = getSelectedDomains(); if (selectedDomains.length === 0) { alert('��HQ ��b���3u����fN�v�W T'); return; } // >f:y �-N�v�W T displaySelectedDomainsPreview(selectedDomains); // �R}�DNS&��SRh� loadDnsAccounts(); // ͑nh�US�r` resetModalForm(); // >f:y!j`Fh document.getElementById('certificateModal').style.display = 'block'; } function resetModalForm() { // ͑nACME{|�W ��b document.querySelectorAll('.option-compact').forEach(option => { if (option.querySelector('input[name="acmeType"]')) { option.classList.remove('selected'); } }); document.querySelector('.option-compact').classList.add('selected'); document.getElementById('acmeLetsencrypt').checked = true; // ͑n�����e_ ��b document.querySelectorAll('.option-compact').forEach(option => { if (option.querySelector('input[name="verification"]')) { option.classList.remove('selected'); } }); document.querySelector('.option-compact:has(input[name="verification"])').classList.add('selected'); document.getElementById('verificationHttp').checked = true; // ��υDNS&��S ��b document.getElementById('dnsAccountGroup').style.display = 'none'; // ͑n�r�z3u�� �y� document.getElementById('independentApplication').checked = false; } function closeCertificateModal() { document.getElementById('certificateModal').style.display = 'none'; } function closeCertificateModalOnOverlay(event) { if (event.target === event.currentTarget) { closeCertificateModal(); } } function displaySelectedDomainsPreview(domains) { const preview = document.getElementById('selectedDomainsPreview'); const selectedCountModal = document.getElementById('selectedCountModal'); // �f�epeϑ>f:y if (selectedCountModal) { selectedCountModal.textContent = domains.length; } let html = ''; domains.forEach(domain => { html += ` <div class="domain-preview-item"> <div class="domain-name">${domain.domainName}</div> <div class="domain-info">�z�p: ${domain.siteId} | ID: ${domain.domainId}</div> </div> `; }); preview.innerHTML = html; } async function loadDnsAccounts() { try { const response = await axios.get('dns_accounts.php'); if (response.data.code === 200) { dnsAccounts = response.data.data.items; populateDnsAccountSelect(); } else { console.error('�R}�DNS&��S1Y%�:', response.data.message); const select = document.getElementById('dnsAccountSelect'); select.innerHTML = '<option value="">�R}�1Y%�</option>'; } } catch (error) { console.error('�R}�DNS&��S1Y%�:', error); const select = document.getElementById('dnsAccountSelect'); select.innerHTML = '<option value="">�R}�1Y%�</option>'; } } function populateDnsAccountSelect() { const select = document.getElementById('dnsAccountSelect'); let html = '<option value="">�� ��bDNS&��S</option>'; dnsAccounts.forEach(account => { html += `<option value="${account.id}">${account.name} (${account.type})</option>`; }); select.innerHTML = html; } function selectAcmeType(type) { document.querySelectorAll('.option-compact').forEach(option => { if (option.querySelector('input[name="acmeType"]')) { option.classList.remove('selected'); } }); event.currentTarget.classList.add('selected'); document.getElementById(`acme${type.charAt(0).toUpperCase() + type.slice(1)}`).checked = true; } function selectVerification(type) { document.querySelectorAll('.option-compact').forEach(option => { if (option.querySelector('input[name="verification"]')) { option.classList.remove('selected'); } }); event.currentTarget.classList.add('selected'); document.getElementById(`verification${type.charAt(0).toUpperCase() + type.slice(1)}`).checked = true; // >f:y/��υDNS&��S ��b const dnsGroup = document.getElementById('dnsAccountGroup'); if (type === 'dns') { dnsGroup.style.display = 'block'; } else { dnsGroup.style.display = 'none'; } } async function submitCertificateApplication() { const selectedDomains = getSelectedDomains(); if (selectedDomains.length === 0) { alert('��HQ ��b�W T'); return; } // >f:y�R}��r` const submitButton = document.getElementById('submitButton'); const originalText = submitButton.innerHTML; submitButton.disabled = true; submitButton.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>3u��-N...'; try { // ���Sh�USpenc const acmeType = document.querySelector('input[name="acmeType"]:checked').value; const verificationType = document.querySelector('input[name="verification"]:checked').value; const independentApplication = document.getElementById('independentApplication').checked; let dnsAccountId = null; if (verificationType === 'dns') { dnsAccountId = document.getElementById('dnsAccountSelect').value; if (!dnsAccountId) { alert('�� ��bDNS&��S'); return; } } // c�z�pIDR�~Yt const groupedBySite = {}; selectedDomains.forEach(domain => { if (!groupedBySite[domain.siteId]) { groupedBySite[domain.siteId] = []; } groupedBySite[domain.siteId].push(domain.domainId); }); // �c�N3u�� const results = []; for (const [siteId, domainIds] of Object.entries(groupedBySite)) { try { const requestData = { siteId: siteId, acme_type: acmeType, not_certificate: false, independent_application: independentApplication, enable_dns: verificationType === 'dns', domain_name_ids: domainIds.map(id => parseInt(id)) }; if (verificationType === 'dns') { requestData.dns_account_id = parseInt(dnsAccountId); } const response = await axios.post('certificate_apply.php', requestData, { headers: { 'Content-Type': 'application/json' } }); results.push({ siteId: siteId, success: response.data.code === 200, message: response.data.message || '3u��b�R' }); } catch (error) { console.error(`�z�p ${siteId} 3u��1Y%�:`, error); results.push({ siteId: siteId, success: false, message: error.response?.data?.message || '3u��1Y%�' }); } } // >f:y�~�g showApplicationResults(results); closeCertificateModal(); } catch (error) { console.error('�c�N3u��1Y%�:', error); alert('�c�N3u��1Y%� ��� zT͑Ջ'); } finally { // b` Y c���r` submitButton.disabled = false; submitButton.innerHTML = originalText; } } function showApplicationResults(results) { let message = '��fN3u���~�g:\n\n'; let successCount = 0; let failCount = 0; results.forEach(result => { if (result.success) { message += `' �z�p ${result.siteId}: ${result.message}\n`; successCount++; } else { message += `L' �z�p ${result.siteId}: ${result.message}\n`; failCount++; } }); message += `\nb�R: ${successCount} *N�z�p, 1Y%�: ${failCount} *N�z�p`; alert(message); // �Y�g gb�R�v3u�� ��S�N7R�epenc if (successCount > 0) { setTimeout(() => { // ͑�e�g��] �-N�v�W T const selectedDomains = getSelectedDomains(); selectedDomains.forEach(domain => { queryDomain(domain.domainName); }); }, 2000); } } // getAuthToken�Qpe�]�yd� ��s(W1uPHPT�z�~NYttoken </script> </body> </html>