added db and list of servers
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
81
dbMgr.js
Normal file
81
dbMgr.js
Normal 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]
|
||||||
|
// }
|
||||||
136
index.html
136
index.html
@@ -17,28 +17,17 @@
|
|||||||
<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>
|
||||||
|
|
||||||
<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>
|
||||||
|
<div class="server-info">
|
||||||
|
<p><strong>MAC:</strong> ${server.mac}</p>
|
||||||
|
<p><strong>IP:</strong> ${server.ip}</p>
|
||||||
|
</div>
|
||||||
|
<div class="button-group">
|
||||||
|
<button id="wake-${index}" type="button">Wake Server</button>
|
||||||
|
<button id="ping-${index}" type="button">Ping Server</button>
|
||||||
|
</div>
|
||||||
|
<div class="status-container">
|
||||||
|
Status:
|
||||||
|
<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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(async () => {
|
// document.getElementById('wake').addEventListener('click', () => {
|
||||||
status.innerText = 'pinging';
|
// showSpinner();
|
||||||
let booted = false;
|
// fetch('/wake?mac=9C:7B:EF:A7:F2:F6') // replace with the MAC-Address of your PC
|
||||||
let trys = 0;
|
// .then(response => response.json())
|
||||||
while (!booted && trys < 10) {
|
// .then(data => {
|
||||||
trys++;
|
// status.innerText = 'waiting for boot';
|
||||||
await fetch('/ping?host=192.168.178.41') // set static IP in your Router to make Ping work, then enter IP-Address
|
// status.className = "status-text";
|
||||||
.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) {
|
// setTimeout(async () => {
|
||||||
status.innerText = 'booted';
|
// status.innerText = 'pinging';
|
||||||
status.className = "status-text green";
|
// let booted = false;
|
||||||
} else {
|
// let trys = 0;
|
||||||
status.innerText = 'failed, did not respond to ping';
|
// while (!booted && trys < 10) {
|
||||||
status.className = "status-text red";
|
// trys++;
|
||||||
}
|
// await fetch('/ping?host=192.168.178.41') // set static IP in your Router to make Ping work, then enter IP-Address
|
||||||
hideSpinner();
|
// .then(response => response.json())
|
||||||
}, 25000);
|
// .then(data => {
|
||||||
})
|
// if (data.status == 'success' && data.message === true) {
|
||||||
.catch(error => {
|
// booted = true;
|
||||||
status.innerText = 'failed, unknown error';
|
// }
|
||||||
status.className = "status-text red";
|
// })
|
||||||
hideSpinner();
|
// .catch(error => {
|
||||||
});
|
// status.innerText = 'failed, unknown error';
|
||||||
});
|
// status.className = "status-text red";
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
document.getElementById('ping').addEventListener('click', ping);
|
// if (booted) {
|
||||||
ping();
|
// 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>
|
||||||
|
|||||||
30
index.js
30
index.js
@@ -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
1712
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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": {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user