Compare commits

1 Commits
main ... next

Author SHA1 Message Date
Laptop-Luis
b6c752ce79 added db and list of servers 2026-02-16 20:57:40 +01:00
9 changed files with 1992 additions and 82 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules

81
dbMgr.js Normal file
View File

@@ -0,0 +1,81 @@
import sqlite3 from 'sqlite3'
import { open } from 'sqlite'
let db = null
let error = null
const dbInit = open({
filename: './sqlite.db',
driver: sqlite3.Database
})
.then((database) => {
db = database
console.log('Connected to the database.')
})
.catch((err) => {
console.error('Error opening database: ' + err.message)
error = err
console.log(err)
})
const getDb = async () => {
await dbInit
if (!db) throw new Error('Database not initialized.')
return db
}
const getServers = async (db) => {
return await db.all('SELECT * FROM servers')
}
const getServerByCol = async (db, col, val) => {
const data = await db.all('SELECT * FROM servers WHERE ? = ?', [col, val])
if(!data[0]) throw 'NOTFOUND'
return data[0]
}
export { getDb, getServers, getServerByCol }
// const getProducts = async () => {
// const db = await getDb()
// return await db.all('SELECT * FROM Product')
// }
// const getProductById = async (id) => {
// const db = await getDb()
// const data = await db.all('SELECT * FROM Product WHERE id = ?', [id])
// if (!data[0]) throw 'NOTFOUND'
// return data[0]
// }
// const updateProduct = async (
// id,
// code,
// price,
// name
// ) => {
// const db = await getDb()
// await db.run('UPDATE Product SET code = ?, price = ?, name = ? WHERE id = ?', [
// code,
// price,
// name,
// id
// ])
// }
// const addProduct = async (code, price, name) => {
// const db = await getDb()
// await db.run('INSERT INTO Product (code, price, name) VALUES (?, ?, ?)', [code, price, name])
// }
// const removeProduct = async (id) => {
// const db = await getDb()
// await db.run('DELETE FROM Product WHERE id = ?', [id])
// }
// const getProductByCode = async (code) => {
// const db = await getDb()
// const data = await db.all('SELECT * FROM Product WHERE code = ?', [code])
// if (!data[0]) throw 'NO_DATA'
// return data[0]
// }

View File

@@ -17,21 +17,9 @@
<link rel="manifest" href="/icon/site.webmanifest" /> <link rel="manifest" href="/icon/site.webmanifest" />
</head> </head>
<body> <body>
<div class="container"> <div class="main-container">
<h1>Wake-on-LAN Interface</h1> <h1>Wake-on-LAN Interface</h1>
<div class="button-group"> <div class="servers-grid" id="serverlist">
<button id="wake" type="button">Wake Server</button>
<button id="ping" type="button">Ping Server</button>
</div>
<div class="status-container">
Status:
<span id="status" class="status-text">Ready</span>
<svg id="spinner" class="spinner" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" >
<circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="4"/>
</svg>
</div> </div>
</div> </div>
@@ -39,6 +27,7 @@
<script> <script>
const status = document.getElementById('status'); const status = document.getElementById('status');
const spinner = document.getElementById('spinner'); const spinner = document.getElementById('spinner');
const serverlist = document.getElementById('serverlist')
function showSpinner() { function showSpinner() {
spinner.classList.add('visible'); spinner.classList.add('visible');
@@ -71,52 +60,83 @@
}); });
} }
document.getElementById('wake').addEventListener('click', () => { const getServers = async () => {
showSpinner(); try {
fetch('/wake?mac=9C:7B:EF:A7:F2:F6') // replace with the MAC-Address of your PC let response = await fetch('/api/server/getall')
.then(response => response.json()) let result = await response.json()
.then(data => { if(result.status == 'success') {
status.innerText = 'waiting for boot'; serverlist.innerHTML = result.data.map((server, index) => `
status.className = "status-text"; <div class="server-card">
<h2>Server ${index + 1}: ${server.name}</h2>
setTimeout(async () => { <div class="server-info">
status.innerText = 'pinging'; <p><strong>MAC:</strong> ${server.mac}</p>
let booted = false; <p><strong>IP:</strong> ${server.ip}</p>
let trys = 0; </div>
while (!booted && trys < 10) { <div class="button-group">
trys++; <button id="wake-${index}" type="button">Wake Server</button>
await fetch('/ping?host=192.168.178.41') // set static IP in your Router to make Ping work, then enter IP-Address <button id="ping-${index}" type="button">Ping Server</button>
.then(response => response.json()) </div>
.then(data => { <div class="status-container">
if (data.status == 'success' && data.message === true) { Status:
booted = true; <span id="status-${index}" class="status-text">Ready</span>
<svg id="spinner-${index}" class="spinner" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" >
<circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="4"/>
</svg>
</div>
</div>`).join('')
} else throw 'DB-Error'
} catch (error) {
console.error(error)
} }
})
.catch(error => {
status.innerText = 'failed, unknown error';
status.className = "status-text red";
});
} }
if (booted) { // document.getElementById('wake').addEventListener('click', () => {
status.innerText = 'booted'; // showSpinner();
status.className = "status-text green"; // fetch('/wake?mac=9C:7B:EF:A7:F2:F6') // replace with the MAC-Address of your PC
} else { // .then(response => response.json())
status.innerText = 'failed, did not respond to ping'; // .then(data => {
status.className = "status-text red"; // status.innerText = 'waiting for boot';
} // status.className = "status-text";
hideSpinner();
}, 25000);
})
.catch(error => {
status.innerText = 'failed, unknown error';
status.className = "status-text red";
hideSpinner();
});
});
document.getElementById('ping').addEventListener('click', ping); // setTimeout(async () => {
ping(); // status.innerText = 'pinging';
// let booted = false;
// let trys = 0;
// while (!booted && trys < 10) {
// trys++;
// await fetch('/ping?host=192.168.178.41') // set static IP in your Router to make Ping work, then enter IP-Address
// .then(response => response.json())
// .then(data => {
// if (data.status == 'success' && data.message === true) {
// booted = true;
// }
// })
// .catch(error => {
// status.innerText = 'failed, unknown error';
// status.className = "status-text red";
// });
// }
// if (booted) {
// status.innerText = 'booted';
// status.className = "status-text green";
// } else {
// status.innerText = 'failed, did not respond to ping';
// status.className = "status-text red";
// }
// hideSpinner();
// }, 25000);
// })
// .catch(error => {
// status.innerText = 'failed, unknown error';
// status.className = "status-text red";
// hideSpinner();
// });
// });
// document.getElementById('ping').addEventListener('click', ping);
// ping();
getServers()
</script> </script>
</body> </body>
</html> </html>

View File

@@ -3,21 +3,39 @@ const express = require('express');
const app = express(); const app = express();
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const path = require('path'); const path = require('path');
const ping = require('ping') const ping = require('ping');
const { getDb, getServers, getServerByCol } = require('./dbMgr');
const port = process.env.PORT || 3000; const port = process.env.PORT || 3000;
// Middleware
app.use(bodyParser.json()); app.use(bodyParser.json());
app.use(express.static('public')); app.use(express.static('public'));
// Serve HTML
app.get('/', (req, res) => { app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html')); res.sendFile(path.join(__dirname, 'index.html'));
}); });
// Wake endpoint app.get('/api/server/getAll', async (req, res) => {
app.get('/wake', (req, res) => { try {
const db = await getDb()
res.json({ status: 'success', data: await getServers(db) })
} catch (error) {
console.log(error)
res.json({ status: 'error', error: error })
}
})
app.get('/api/server/getAll', async (req, res) => {
try {
const db = await getDb()
res.json({ status: 'success', data: await getServerByCol(db, req.query.col, req.query.val) })
} catch (error) {
console.log(error)
res.json({ status: 'error', error: error })
}
})
app.get('/api/wake', (req, res) => {
const mac = req.query.mac; const mac = req.query.mac;
if (mac && mac !== '') { if (mac && mac !== '') {
@@ -33,7 +51,7 @@ app.get('/wake', (req, res) => {
} }
}); });
app.get('/ping', (req, res) => { app.get('/api/ping', (req, res) => {
const host = req.query.host const host = req.query.host
if(host && host != ''){ if(host && host != ''){
ping.sys.probe(host, (isAlive) => { ping.sys.probe(host, (isAlive) => {

1712
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,8 @@
"express": "^5.1.0", "express": "^5.1.0",
"path": "^0.12.7", "path": "^0.12.7",
"ping": "^0.4.4", "ping": "^0.4.4",
"sqlite": "^5.1.1",
"sqlite3": "^5.1.7",
"wakeonlan": "^0.1.0" "wakeonlan": "^0.1.0"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -12,25 +12,67 @@ body {
padding: 0; padding: 0;
} }
.container { .main-container {
max-width: 800px; max-width: 1200px;
margin: 60px auto; margin: 0 auto;
background: #1E1E1E; padding: 40px 20px;
padding: 40px;
border-radius: 24px;
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
border: 1px solid rgba(255, 255, 255, 0.05);
transition: background-color 0.3s ease;
} }
h1 { h1 {
font-size: 2.4em; font-size: 2.4em;
margin-bottom: 32px; margin-bottom: 40px;
text-align: center; text-align: center;
color: #FFFFFF; color: #FFFFFF;
font-weight: 500; font-weight: 500;
} }
.servers-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 24px;
}
.server-card {
background: #1E1E1E;
padding: 32px;
border-radius: 24px;
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
border: 1px solid rgba(255, 255, 255, 0.05);
transition: all 0.3s ease;
}
.server-card:hover {
box-shadow: 0 8px 24px rgba(0,0,0,0.5);
border-color: rgba(255, 255, 255, 0.1);
transform: translateY(-2px);
}
.server-card h2 {
font-size: 1.3em;
margin-top: 0;
margin-bottom: 24px;
text-align: center;
color: #FFFFFF;
font-weight: 500;
}
.server-info {
margin-bottom: 20px;
font-size: 0.9em;
text-align: center;
}
.server-info p {
margin: 4px 0;
font-family: 'Courier New', monospace;
color: #B0B0B0;
}
.server-info strong {
color: #E3E3E3;
font-weight: 600;
}
.button-group { .button-group {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@@ -13,22 +13,56 @@ body {
text-align: center; text-align: center;
} }
.container { .main-container {
padding: 20px; padding: 16px;
background-color: #1E1E1E;
border-radius: 24px;
margin: 12px;
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
border: 1px solid rgba(255, 255, 255, 0.05);
} }
h1 { h1 {
font-size: 2em; font-size: 2em;
margin-bottom: 28px; margin-bottom: 24px;
font-weight: 500; font-weight: 500;
color: #FFFFFF; color: #FFFFFF;
} }
.servers-grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
.server-card {
padding: 20px;
background-color: #1E1E1E;
border-radius: 24px;
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
border: 1px solid rgba(255, 255, 255, 0.05);
}
.server-card h2 {
font-size: 1.2em;
margin-top: 0;
margin-bottom: 16px;
font-weight: 500;
color: #FFFFFF;
}
.server-info {
margin-bottom: 16px;
font-size: 0.85em;
text-align: center;
}
.server-info p {
margin: 3px 0;
font-family: 'Courier New', monospace;
color: #B0B0B0;
}
.server-info strong {
color: #E3E3E3;
font-weight: 600;
}
.button-group { .button-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

BIN
sqlite.db Normal file

Binary file not shown.