pkiapi/webui/main.js

199 lines
8.1 KiB
JavaScript

(function(){
const API_BASE = '/api/v1';
let token = localStorage.getItem('pki_token') || '';
function $(id){ return document.getElementById(id); }
function show(el){ el.classList.remove('hidden'); }
function hide(el){ el.classList.add('hidden'); }
function showMessage(msg, isError){
const m = $('message');
m.textContent = msg;
m.className = 'card';
if(isError) m.classList.add('error');
show(m);
setTimeout(()=>{ hide(m); }, 7000);
}
function apiFetch(path, opts={}){
opts.headers = opts.headers || {};
opts.headers['Content-Type'] = 'application/json';
if(token) opts.headers['Authorization'] = 'Bearer ' + token;
return fetch(API_BASE + path, opts).then(async res => {
const ct = res.headers.get('content-type') || '';
if(!res.ok){
let body = await res.text();
throw new Error(body || res.statusText);
}
if(ct.includes('application/json')) return res.json();
return res.blob();
});
}
function renderTable(items, type){
const list = $('list');
list.innerHTML = '';
const h = document.createElement('h3');
h.textContent = type === 'ca' ? 'Autorités de Certification' : 'Certificats';
list.appendChild(h);
const table = document.createElement('table');
table.className = 'table';
const thead = document.createElement('thead');
const headRow = document.createElement('tr');
['ID','Sujet','Émetteur','Not After','Actions'].forEach(t=>{ const th=document.createElement('th'); th.textContent=t; headRow.appendChild(th); });
thead.appendChild(headRow);
table.appendChild(thead);
const tbody = document.createElement('tbody');
items.forEach(it=>{
const tr = document.createElement('tr');
const idCell = document.createElement('td'); idCell.textContent = it.id || it.ID || it.Id || '';
const subj = document.createElement('td'); subj.textContent = it.subject || it.Subject || it.common_name || '-';
const issuer = document.createElement('td'); issuer.textContent = it.issuer || it.issuer || '-';
const na = document.createElement('td'); na.textContent = it.not_after || it.notAfter || it.notAfter || '-';
const actions = document.createElement('td');
const btnView = document.createElement('button'); btnView.textContent = 'Voir';
btnView.onclick = ()=> viewDetails(it.id || it.ID || it.Id);
actions.appendChild(btnView);
if(type === 'cert'){
const btnExport = document.createElement('button'); btnExport.textContent = 'Export PEM';
btnExport.onclick = ()=> exportCertPEM(it.id || it.ID || it.Id);
actions.appendChild(btnExport);
const btnRevoke = document.createElement('button'); btnRevoke.textContent = 'Révoquer';
btnRevoke.onclick = ()=> revokeCert(it.id || it.ID || it.Id);
actions.appendChild(btnRevoke);
}
tr.appendChild(idCell); tr.appendChild(subj); tr.appendChild(issuer); tr.appendChild(na); tr.appendChild(actions);
tbody.appendChild(tr);
});
table.appendChild(tbody);
list.appendChild(table);
show(list);
}
function setTokenFromInput(){
const t = $('token').value.trim();
token = t;
localStorage.setItem('pki_token', token);
showMessage('Token enregistré.');
}
async function loadCAs(){
try{
const res = await apiFetch('/ca');
const cas = res.cas || res.list || res;
renderTable(cas, 'ca');
}catch(e){ showMessage('Erreur loadCAs: '+e.message, true); }
}
async function loadCerts(){
try{
const res = await apiFetch('/certificates');
const certs = res.certificates || res.list || res;
renderTable(certs, 'cert');
}catch(e){ showMessage('Erreur loadCerts: '+e.message, true); }
}
function showCreateCAForm(){
const f = $('form'); f.innerHTML = '';
const h = document.createElement('h3'); h.textContent = 'Créer une CA'; f.appendChild(h);
const subject = document.createElement('input'); subject.placeholder='CN=Root CA,O=Example,C=FR'; subject.id='ca_subject';
const days = document.createElement('input'); days.placeholder='validity_days'; days.type='number'; days.id='ca_days'; days.value=3650;
const btn = document.createElement('button'); btn.textContent='Créer'; btn.onclick = async ()=>{
try{
const body = { subject: subject.value, validity_days: parseInt(days.value||0,10) };
const res = await apiFetch('/ca',{ method:'POST', body: JSON.stringify(body) });
showMessage('CA créée: '+(res.ca && res.ca.id));
loadCAs();
}catch(e){ showMessage('Erreur création CA: '+e.message, true); }
};
f.appendChild(subject); f.appendChild(document.createElement('br'));
f.appendChild(days); f.appendChild(document.createElement('br'));
f.appendChild(btn);
show(f);
}
function showCreateCertForm(){
const f = $('form'); f.innerHTML = '';
const h = document.createElement('h3'); h.textContent = 'Créer un Certificat (auto-signé)'; f.appendChild(h);
const subject = document.createElement('input'); subject.placeholder='CN=server.example.com,O=Example,C=FR'; subject.id='cert_subject';
const days = document.createElement('input'); days.placeholder='validity_days'; days.type='number'; days.id='cert_days'; days.value=365;
const btn = document.createElement('button'); btn.textContent='Créer'; btn.onclick = async ()=>{
try{
const body = { subject: subject.value, validity_days: parseInt(days.value||0,10) };
const res = await apiFetch('/certificates',{ method:'POST', body: JSON.stringify(body) });
showMessage('Certificat créé: '+(res.certificate && res.certificate.id));
loadCerts();
}catch(e){ showMessage('Erreur création cert: '+e.message, true); }
};
f.appendChild(subject); f.appendChild(document.createElement('br'));
f.appendChild(days); f.appendChild(document.createElement('br'));
f.appendChild(btn);
show(f);
}
async function viewDetails(id){
try{
const res = await apiFetch('/certificates/'+id);
const cert = res.certificate || res;
const d = $('details');
d.innerHTML = '';
const h = document.createElement('h3'); h.textContent = 'Détails Certificat'; d.appendChild(h);
const pre = document.createElement('pre'); pre.textContent = JSON.stringify(cert, null, 2);
d.appendChild(pre);
show(d);
}catch(e){
// peut-être une CA
try{
const res = await apiFetch('/ca/'+id);
const ca = res.ca || res;
const d = $('details'); d.innerHTML=''; d.appendChild(document.createElement('h3')).textContent='Détails CA';
const pre = document.createElement('pre'); pre.textContent = JSON.stringify(ca, null, 2); d.appendChild(pre); show(d);
}catch(er){ showMessage('Erreur viewDetails: '+er.message, true); }
}
}
async function revokeCert(id){
if(!confirm('Confirmer la révocation du certificat '+id+' ?')) return;
try{
const body = { certificate_id: id, reason: 'Revoked via UI' };
await apiFetch('/revoke',{ method:'POST', body: JSON.stringify(body) });
showMessage('Certificat révoqué: '+id);
loadCerts();
}catch(e){ showMessage('Erreur revoke: '+e.message, true); }
}
async function exportCertPEM(id){
try{
const blob = await apiFetch('/certificates/'+id+'/export/pem');
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a'); a.href = url; a.download = id + '.pem'; document.body.appendChild(a); a.click(); a.remove();
window.URL.revokeObjectURL(url);
}catch(e){ showMessage('Erreur export: '+e.message, true); }
}
function attachEvents(){
$('saveToken').onclick = setTokenFromInput;
$('btnCAs').onclick = loadCAs;
$('btnCerts').onclick = loadCerts;
$('btnCreateCA').onclick = showCreateCAForm;
$('btnCreateCert').onclick = showCreateCertForm;
// prefills
$('token').value = token;
}
// init
document.addEventListener('DOMContentLoaded', ()=>{
attachEvents();
if(token) showMessage('Token chargé depuis localStorage.');
});
// export functions for debugging
window.pkiUI = { loadCAs, loadCerts, viewDetails };
})();