/* ========================= DNS CACHE (mit TTL) ========================= */ const dnsCache = {} async function getDNS(domain){ const now = Date.now() const TTL = 30000 // 30 Sekunden if(dnsCache[domain] && (now - dnsCache[domain].time < TTL)){ return dnsCache[domain].ips } try{ const res = await api(API + "/dns/" + domain, {}, { showError: false }) const ips = res.ips || [] dnsCache[domain] = { ips: ips, time: now } return ips }catch(e){ dnsCache[domain] = { ips: [], time: now } return [] } } function dnsStatusMarkup(expectedIp, ips){ if(!ips.length){ return ` Kein DNS ` } const matches = expectedIp && ips.includes(expectedIp) const badgeClass = matches ? "ok" : "warn" const label = matches ? "DNS OK" : "DNS Abweichung" return ` ${label} ${ips.join(", ")} ` } function chipMarkup(label, muted = false){ return `${label}` } /* ========================= DOMAINS + SUBDOMAINS ========================= */ window.loadDomains = async function(){ const domains = await api(API + "/domains") const subs = await api(API + "/subdomains") window.domainList = domains const resources = window.resources || [] const table = document.getElementById("domains") table.innerHTML = "" for(const d of domains){ const tr = document.createElement("tr") const domainSubs = subs.filter(s => s.domain_id === d.id) const sublist = domainSubs.length ? `
${domainSubs.map(s => `
${s.subdomain}.${s.domain_name}
${s.ip_address ? chipMarkup(s.ip_address, true) : chipMarkup("Keine IP", true)}
...
`).join("")}
` : `
Noch keine Subdomains
` tr.innerHTML = `
${d.domain_name}
${d.provider ? chipMarkup(d.provider) : ""} ${d.ip_address ? chipMarkup(d.ip_address, true) : chipMarkup("Keine Ziel-IP", true)} ${d.resource_name ? chipMarkup(d.resource_name) : chipMarkup("Kein Server", true)}
${sublist} ${d.provider || "?"} ${d.ip_address || ""} ${d.resource_name || ""} ... ${d.yearly_cost || ""} ` table.appendChild(tr) /* ========================= SERVER ZUORDNUNG SUBS ========================= */ domainSubs.forEach(s => { const server = resources.find(r => Array.isArray(r.ips) && r.ips.some(ip => ip.ip === s.ip_address) ) if(server){ const el = document.getElementById("subserver-"+s.id) if(el){ el.innerHTML = chipMarkup(server.name) } } }) /* ========================= DNS CHECK DOMAIN ========================= */ try{ const ips = await getDNS(d.domain_name) document.getElementById("dns-"+d.id).innerHTML = dnsStatusMarkup(d.ip_address, ips) }catch(e){ document.getElementById("dns-"+d.id).innerHTML = `Kein DNS` } /* ========================= DNS CHECK SUBDOMAINS ========================= */ domainSubs.forEach(async s => { const full = s.subdomain + "." + s.domain_name try{ const ips = await getDNS(full) document.getElementById("subdns-"+s.id).innerHTML = dnsStatusMarkup(s.ip_address, ips) }catch(e){ document.getElementById("subdns-"+s.id).innerHTML = `Kein DNS` } }) } } /* ========================= DOMAIN CRUD ========================= */ window.saveDomain = async function(){ const id = document.getElementById("domain_id").value let cost = document.getElementById("domain_cost").value if(cost) cost = cost.replace(",", ".") const data = { domain_name: domain_name.value, provider: domain_provider.value, ip_address: domain_ip.value, yearly_cost: cost || null, notes: domain_notes.value } await runAction( async () => { if(id){ await api(API+"/domains/"+id,{ method:"PUT", headers:{'Content-Type':'application/json'}, body:JSON.stringify(data) }) }else{ await api(API+"/domains",{ method:"POST", headers:{'Content-Type':'application/json'}, body:JSON.stringify(data) }) } }, async () => { closeDomainModal() await loadDomains() await loadMapping() loadCosts() loadInfrastructure() } ) } window.deleteDomain = async function(id){ if(!confirm("Domain löschen?")) return await runAction( async () => { await api(API + "/domains/" + id, { method: "DELETE" }) }, async () => { await loadDomains() await loadMapping() loadCosts() loadInfrastructure() } ) } window.moveDomain = async function(id, direction){ await runAction( async () => { await api(API + "/domains/" + id + "/move", { method: "POST", headers: {'Content-Type':'application/json'}, body: JSON.stringify({ direction }) }) }, async () => { await loadDomains() await loadMapping() loadInfrastructure() } ) } /* ========================= SUBDOMAIN CRUD ========================= */ window.saveSubdomain = async function(){ const id = document.getElementById("sub_id").value const domain_id = document.getElementById("sub_domain_id").value const subdomain = document.getElementById("sub_name").value const ip_address = document.getElementById("sub_ip").value const data = { domain_id, subdomain, ip_address } await runAction( async () => { if(id){ await api(API+"/subdomains/"+id,{ method:"PUT", headers:{'Content-Type':'application/json'}, body:JSON.stringify(data) }) }else{ await api(API+"/subdomains",{ method:"POST", headers:{'Content-Type':'application/json'}, body:JSON.stringify(data) }) } }, async () => { closeSubModal() await loadDomains() await loadMapping() loadInfrastructure() } ) } window.deleteSub = async function(id){ if(!confirm("Subdomain löschen?")) return await runAction( async () => { await api(API + "/subdomains/" + id, { method: "DELETE" }) }, async () => { await loadDomains() await loadMapping() loadInfrastructure() } ) } /* ========================= DOMAIN MAPPING ========================= */ window.loadMapping = async function(){ const data = await api(API + "/domainmap") const table = document.getElementById("mapping") table.innerHTML = "" data.forEach(m => { const tr = document.createElement("tr") tr.innerHTML = ` ${m.domain_name} ${m.ip_address} ${m.server_name || ""} ` table.appendChild(tr) }) }