IP Manager überarbeitet
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
`
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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!
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user