IP Manager überarbeitet

This commit is contained in:
ecki
2026-03-27 14:50:03 +01:00
parent 0329c67904
commit 89ce86eb4a
7 changed files with 275 additions and 13 deletions
+41
View File
@@ -122,3 +122,44 @@ color:#555;
margin-left:30px;
color:#555;
}
.clickable{
cursor:pointer;
}
.clickable:hover{
color:#1e3a8a;
text-decoration:underline;
}
#lastUpdate{
font-size:13px;
margin-bottom:10px;
}
#ipList div{
display:flex;
align-items:center;
margin-bottom:6px;
}
#ipList .ip{
flex:1;
}
#ipList button{
margin-left:6px;
font-size:11px;
}
#ipForm.edit-mode{
border:2px solid #3b82f6;
padding:10px;
border-radius:6px;
background:#eef4ff;
}
.ip.public{
background:#22c55e;
color:white;
font-weight:bold;
}
+20 -3
View File
@@ -13,6 +13,10 @@
<h1>Ressourcen Verwaltung</h1>
<div id="lastUpdate" style="margin-bottom:10px;color:#555;">
Letztes Update: -
</div>
<section>
<h2>Kosten Dashboard</h2>
@@ -220,10 +224,9 @@ Domains jährlich: <span id="costDomain">0</span> €<br>
<div id="ipList"></div>
<hr>
<input id="new_ip" placeholder="IP">
<input id="new_type" placeholder="type">
<input id="new_comment" placeholder="comment">
<input id="new_type" placeholder="public / private">
<input id="new_comment" placeholder="Beschreibung">
<button onclick="saveIP()">Add</button>
@@ -233,6 +236,20 @@ Domains jährlich: <span id="costDomain">0</span> €<br>
</div>
<div id="ipForm">
<input id="new_ip" placeholder="IP">
<input id="new_type" placeholder="public / private">
<input id="new_comment" placeholder="Beschreibung">
<button id="ipSaveBtn" onclick="saveIP()">Add</button>
<button id="ipCancelBtn" onclick="cancelIPEdit()" style="display:none">
Cancel
</button>
</div>
<div id="domainModal" class="modal">
<h3 id="domainModalTitle">Domain</h3>
+18 -4
View File
@@ -9,7 +9,12 @@ window.loadInfrastructure = async function(){
resources.forEach(r => {
let html = `<div class="infra-server"><b>${r.name}</b></div>`
let html = `
<div class="infra-server clickable"
onclick='openServerDetail(${JSON.stringify(r)})'>
<b>${r.name}</b>
</div>
`
if(Array.isArray(r.ips)){
@@ -23,8 +28,12 @@ window.loadInfrastructure = async function(){
domains.forEach(d => {
html += `<div class="infra-domain"> └ 🌐 ${d.domain_name}</div>`
html += `
<div class="infra-domain clickable"
onclick='openDomainEdit(${JSON.stringify(d)})'>
🌐 ${d.domain_name}
</div>
`
const sublist = subs.filter(s =>
s.domain_id == d.domain_id || s.domain_name == d.domain_name
)
@@ -33,7 +42,12 @@ window.loadInfrastructure = async function(){
if(s.ip_address == ip.ip){
html += `<div class="infra-sub">   └ ${s.subdomain}.${s.domain_name}</div>`
html += `
<div class="infra-sub clickable"
onclick='openSubEdit(${JSON.stringify(s)})'>
${s.subdomain}.${s.domain_name}
</div>
`
}
+20 -1
View File
@@ -8,7 +8,6 @@ document.addEventListener("DOMContentLoaded", async () => {
})
let refreshing = false
async function refreshAll(){
@@ -16,6 +15,7 @@ async function refreshAll(){
if(refreshing) return
refreshing = true
try{
await loadResources()
@@ -26,16 +26,35 @@ async function refreshAll(){
loadInfrastructure()
loadCosts()
// Zeit setzen
updateLastUpdate()
}catch(e){
console.error("Refresh error", e)
document.getElementById("lastUpdate").innerText = "🔴 Fehler beim Update"
}
refreshing = false
}
function clearDNSCache(){
for(const key in dnsCache){
delete dnsCache[key]
}
}
function updateLastUpdate(){
const now = new Date()
const time = now.toLocaleTimeString()
// Lade-Indikator
document.getElementById("lastUpdate").innerText =
"🟢 Letztes Update: " + time
}
+4 -2
View File
@@ -233,9 +233,11 @@ document.getElementById("subdomainModal").style.display="block"
window.openIPManager = function(resourceId){
document.getElementById("ip_resource_id").value=resourceId
document.getElementById("ip_resource_id").value = resourceId
document.getElementById("ipModal").style.display="block"
document.getElementById("ipModal").style.display="block"
loadIPs(resourceId) // 🔥 wichtig!
}
+149 -3
View File
@@ -15,9 +15,17 @@ window.loadResources = async function(){
let ips = ""
if(Array.isArray(r.ips)){
ips = r.ips.map(ip =>
"<span class='ip'>" + (ip.type || "") + " " + ip.ip + "</span>"
).join("")
ips = r.ips.map(ip => {
const cls = (ip.type || "").toLowerCase().includes("public")
? "ip public"
: "ip"
return "<span class='" + cls + "'>" +
(ip.type || "") + " " + ip.ip +
"</span>"
}).join("")
}
let domains = mappings
@@ -163,3 +171,141 @@ window.loadCancelled = async function(){
})
}
window.saveIP = async function(){
const resourceId = document.getElementById("ip_resource_id").value
const ipField = document.getElementById("new_ip")
const id = ipField.dataset.editId
const ip = ipField.value
const type = document.getElementById("new_type").value
const comment = document.getElementById("new_comment").value
if(id){
// 🔥 UPDATE
await api(API + "/ips/" + id, {
method: "PUT",
headers: {'Content-Type':'application/json'},
body: JSON.stringify({ip, type, comment})
})
delete ipField.dataset.editId
}else{
// 🔥 CREATE
try{
const result = await api(API + "/ipcheck/" + ip)
if(result.length){
if(!confirm("IP existiert bereits. Trotzdem speichern?")){
return
}
}
}catch(e){}
await api(API + "/resources/" + resourceId + "/ips", {
method: "POST",
headers: {'Content-Type':'application/json'},
body: JSON.stringify({ip, type, comment})
})
}
// Felder reset
ipField.value = ""
document.getElementById("new_type").value = ""
document.getElementById("new_comment").value = ""
loadIPs(resourceId)
loadResources()
cancelIPEdit()
}
window.deleteIP = async function(id, resourceId){
if(!confirm("IP löschen?")) return
await api(API + "/ips/" + id, {
method: "DELETE"
})
loadIPs(resourceId) // Modal neu laden
loadResources() // Tabelle aktualisieren
}
window.loadIPs = async function(resourceId){
const ips = await api(API + "/resources/" + resourceId + "/ips")
const container = document.getElementById("ipList")
container.innerHTML = ""
ips.forEach(ip => {
// ✅ HIER rein!
const cls = (ip.type || "").toLowerCase().includes("public")
? "ip public"
: "ip"
const div = document.createElement("div")
div.innerHTML = `
<span class="${cls}">
${ip.ip} (${ip.type || ""}) ${ip.comment || ""}
</span>
<button onclick="editIP(${ip.id}, '${ip.ip}', '${ip.type || ""}', '${ip.comment || ""}')">
Edit
</button>
<button onclick="deleteIP(${ip.id}, ${resourceId})">
Delete
</button>
`
container.appendChild(div)
})
}
window.editIP = function(id, ip, type, comment){
document.getElementById("new_ip").value = ip
document.getElementById("new_type").value = type
document.getElementById("new_comment").value = comment
document.getElementById("new_ip").dataset.editId = id
// 👇 UX Highlight + Fokus
document.getElementById("ipForm").classList.add("edit-mode")
document.getElementById("ipSaveBtn").innerText = "Update"
document.getElementById("ipCancelBtn").style.display = "inline-block"
document.getElementById("new_ip").focus() // 👈 HIER
}
window.cancelIPEdit = function(){
const ipField = document.getElementById("new_ip")
delete ipField.dataset.editId
ipField.value = ""
document.getElementById("new_type").value = ""
document.getElementById("new_comment").value = ""
document.getElementById("ipForm").classList.remove("edit-mode")
document.getElementById("ipSaveBtn").innerText = "Add"
document.getElementById("ipCancelBtn").style.display = "none"
}
+23
View File
@@ -11,4 +11,27 @@ router.post("/resources/:id/ips", controller.create);
/* delete IP */
router.delete("/ips/:id", controller.remove);
router.put("/ips/:id", async (req, res) => {
try{
const {ip, type, comment} = req.body
await db.query(
"UPDATE resource_ips SET ip=?, type=?, comment=? WHERE id=?",
[ip, type, comment, req.params.id]
)
res.json({success:true})
}catch(e){
console.error("UPDATE IP error:", e)
res.status(500).json({error:"DB error"})
}
})
module.exports = router;