Template
feat: per-site SSL email and error display
This commit is contained in:
@@ -74,6 +74,10 @@ const baseCSS = `
|
||||
.badge.ssl-pending { background: #713f12; color: #fde68a; }
|
||||
.badge.ssl-active { background: #14532d; color: #bbf7d0; }
|
||||
.badge.ssl-error { background: #7f1d1d; color: #fecaca; }
|
||||
.ssl-err-text { margin-top: .35rem; font-size: .75rem; color: #fca5a5; line-height: 1.3; max-width: 220px; }
|
||||
.ssl-cell { min-width: 120px; }
|
||||
input.email-sm { padding: .4rem .5rem; margin: 0 0 .35rem; font-size: .85rem; }
|
||||
.actions-cell { min-width: 140px; }
|
||||
`
|
||||
|
||||
const setupPageHTML = `<!DOCTYPE html>
|
||||
@@ -125,7 +129,8 @@ func dashboardPageHTML(username string) string {
|
||||
<div><label>Имя сайта</label><input id="name" required pattern="[a-z0-9][a-z0-9-]{1,62}[a-z0-9]" placeholder="mysite"></div>
|
||||
<div><label>Домен</label><input id="domain" required placeholder="example.com"></div>
|
||||
</div><label>PHP версия</label><select id="php_version" required></select>
|
||||
<label class="check"><input type="checkbox" id="issue_ssl" checked> Выпустить SSL (Let's Encrypt)</label>
|
||||
<div id="sslBlock"><label>Email для SSL</label><input id="ssl_email" type="email" placeholder="admin@example.com">
|
||||
<label class="check"><input type="checkbox" id="issue_ssl" checked> Выпустить SSL (Let's Encrypt)</label></div>
|
||||
<button type="submit" id="createBtn">Создать сайт</button></form>
|
||||
<div id="formMsg" class="msg"></div></div>
|
||||
<div class="card"><h2>Сайты</h2>
|
||||
@@ -133,31 +138,39 @@ func dashboardPageHTML(username string) string {
|
||||
<tbody id="sites"></tbody></table></div>
|
||||
</div>
|
||||
<script>
|
||||
function esc(s){return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/"/g,'"');}
|
||||
async function loadPHP(){const fallback=['8.1','8.2','8.3','8.4'];let versions=fallback;try{const r=await fetch('/api/v1/php-versions');const d=await r.json();if(r.ok&&d.versions&&d.versions.length)versions=d.versions;}catch(e){}const s=document.getElementById('php_version');s.innerHTML='';versions.forEach(v=>{const o=document.createElement('option');o.value=v;o.textContent='PHP '+v;s.appendChild(o);});if(versions.length)s.value=versions[versions.length-1];}
|
||||
async function loadSites(){const r=await fetch('/api/v1/sites');const d=await r.json();const tb=document.getElementById('sites');tb.innerHTML='';
|
||||
(d.sites||[]).forEach(site=>{const tr=document.createElement('tr');const ssl=site.ssl_status||'none';
|
||||
const sslCls=ssl==='active'?'ssl-active':(ssl==='error'?'ssl-error':(ssl==='pending'?'ssl-pending':'ssl-none'));
|
||||
const needSSL=ssl!=='active';
|
||||
const actions=needSSL?'<button class="secondary btn-ssl" type="button">Выпустить SSL</button>':'';
|
||||
tr.innerHTML='<td>'+site.name+'</td><td>'+site.primary_domain+'</td><td>'+(site.php_version||'-')+'</td><td><span class="badge '+sslCls+'">'+ssl+'</span></td><td><span class="badge">'+site.status+'</span></td><td><code>'+site.document_root+'</code></td><td>'+actions+'</td>';
|
||||
if(needSSL){const btn=tr.querySelector('.btn-ssl');btn.onclick=async()=>{btn.disabled=true;btn.textContent='Выпускается...';const res=await fetch('/api/v1/sites/'+site.id+'/ssl/issue',{method:'POST'});if(!res.ok){const err=await res.json().catch(()=>({}));btn.textContent=err.error||'Ошибка';btn.disabled=false;return;}setTimeout(loadSites,1500);};}
|
||||
const errHtml=site.ssl_error?'<div class="ssl-err-text">'+esc(site.ssl_error)+'</div>':'';
|
||||
const emailVal=esc(site.ssl_email||'');
|
||||
const actions=needSSL?'<input type="email" class="email-sm ssl-email" placeholder="email@..." value="'+emailVal+'"><button class="secondary btn-ssl" type="button">Выпустить SSL</button>':'';
|
||||
tr.innerHTML='<td>'+esc(site.name)+'</td><td>'+esc(site.primary_domain)+'</td><td>'+(site.php_version||'-')+'</td><td class="ssl-cell"><span class="badge '+sslCls+'">'+ssl+'</span>'+errHtml+'</td><td><span class="badge">'+site.status+'</span></td><td><code>'+esc(site.document_root)+'</code></td><td class="actions-cell">'+actions+'</td>';
|
||||
if(needSSL){const btn=tr.querySelector('.btn-ssl');const emailIn=tr.querySelector('.ssl-email');btn.onclick=async()=>{const em=emailIn.value.trim();if(!em){alert('Укажите email для SSL');return;}btn.disabled=true;btn.textContent='Выпускается...';const res=await fetch('/api/v1/sites/'+site.id+'/ssl/issue',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ssl_email:em})});if(!res.ok){const err=await res.json().catch(()=>({}));alert(err.error||'Ошибка');btn.textContent='Выпустить SSL';btn.disabled=false;return;}setTimeout(loadSites,1500);};}
|
||||
tb.appendChild(tr);});}
|
||||
document.getElementById('issue_ssl').onchange=()=>{document.getElementById('ssl_email').required=document.getElementById('issue_ssl').checked;};
|
||||
document.getElementById('ssl_email').required=true;
|
||||
document.getElementById('logout').onclick=async()=>{await fetch('/api/v1/auth/logout',{method:'POST'});location.reload();};
|
||||
document.getElementById('siteForm').onsubmit=async e=>{
|
||||
e.preventDefault();
|
||||
const btn=document.getElementById('createBtn'),msg=document.getElementById('formMsg');
|
||||
const siteName=document.getElementById('name'),siteDomain=document.getElementById('domain'),phpSelect=document.getElementById('php_version');
|
||||
const issueSSL=document.getElementById('issue_ssl').checked,sslEmail=document.getElementById('ssl_email').value.trim();
|
||||
if(issueSSL&&!sslEmail){msg.className='msg error';msg.textContent='Укажите email для SSL';return;}
|
||||
btn.disabled=true;msg.className='msg';msg.textContent='';
|
||||
try{
|
||||
const res=await fetch('/api/v1/sites',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:siteName.value.trim().toLowerCase(),domain:siteDomain.value.trim().toLowerCase(),php_version:phpSelect.value,issue_ssl:document.getElementById('issue_ssl').checked})});
|
||||
const res=await fetch('/api/v1/sites',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:siteName.value.trim().toLowerCase(),domain:siteDomain.value.trim().toLowerCase(),php_version:phpSelect.value,issue_ssl:issueSSL,ssl_email:sslEmail})});
|
||||
const d=await res.json().catch(()=>({}));
|
||||
if(!res.ok){msg.className='msg error';msg.textContent=d.error||'Ошибка';btn.disabled=false;return;}
|
||||
msg.className='msg ok';msg.textContent='Сайт создан';
|
||||
document.getElementById('siteForm').reset();
|
||||
document.getElementById('siteForm').reset();document.getElementById('issue_ssl').checked=true;document.getElementById('ssl_email').required=true;
|
||||
await loadPHP();loadSites();
|
||||
}catch(err){msg.className='msg error';msg.textContent='Сетевая ошибка';}
|
||||
btn.disabled=false;
|
||||
};
|
||||
loadPHP();loadSites();
|
||||
setInterval(()=>{if(document.querySelector('.badge.ssl-pending'))loadSites();},5000);
|
||||
</script></body></html>`, baseCSS, username)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user