Compare commits

..

8 Commits

7 changed files with 147 additions and 5 deletions

2
.gitignore vendored
View File

@@ -139,3 +139,5 @@ dist
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.vite/
targets.json

View File

@@ -6,7 +6,14 @@ Wake-on-LAN API in Node.js
1. `npm install`
2. `npm run start`
## Custom targets
1. Copy `targets.example.json` to `targets.json`
2. Edit the file and add your targets
## Usage
Navigate to `http://localhost:9999` and just click the target you want to wake up.
Or:
### POST to `/wake`
```bash
@@ -19,3 +26,18 @@ curl -X POST -H "Content-Type: application/json" -d '{
```bash
curl http://localhost:9999/wake?mac=00:AA:BB:CC:DD:EE
```
### GET to `/targets`
```bash
curl http://localhost:9999/targets
```
### GET to `/wake/:name`
```bash
curl http://localhost:9999/wake/<target name>
```
### Get to `/status/:name`
```bash
curl http://localhost:9999/status/<target name>
```

41
api.js
View File

@@ -1,13 +1,23 @@
import express from 'express';
import cors from 'cors';
import { sendWolPacket } from './wol.js';
import { ping, sendWolPacket } from './wol.js';
import targets from "./targets.json" with { type: "json" };
import { fileURLToPath } from "node:url";
import path from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const port = 9999;
app.use(cors()).use(express.json());
app.post('/wake', (req, res) => {
app.get("/targets", (req, res) => {
res.json(targets);
});
app.post("/wake", (req, res) => {
const { mac } = req.body;
if(!mac) {
@@ -22,7 +32,7 @@ app.post('/wake', (req, res) => {
});
});
app.get('/wake', (req, res) => {
app.get("/wake", (req, res) => {
const mac = req.query.mac;
if(!mac) {
@@ -37,6 +47,31 @@ app.get('/wake', (req, res) => {
});
});
app.get("/wake/:name", (req, res) => {
const target = targets[req.params.name];
if (!target?.mac) {
return res.status(404).json({ status: "error", message: "Unknown target" });
}
sendWolPacket(target.mac)
.then(() => res.json({ status: "ok" }))
.catch(err => {
console.error(err);
res.status(500).json({ status: "error", message: "Failed to send WOL packet" });
});
});
app.get("/status/:name", (req, res) => {
const target = targets[req.params.name];
if (!target?.ip) {
return res.json({ isUp: null });
}
ping(target.ip).then(isUp => res.json({ isUp }));
});
app.use(express.static(path.join(__dirname, 'public')));
app.listen(port, () => {
console.log('WOL server running on port ' + port);
});

View File

@@ -1,6 +1,6 @@
{
"name": "wol-api",
"version": "1.0.0",
"version": "2.0.0",
"description": "Wake-on-LAN API in Node.js",
"keywords": [
"node",

64
public/index.html Normal file
View File

@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WOL</title>
<style>
body {
background: #fff;
color: #000;
}
@media (prefers-color-scheme: dark) {
body {
background: #111;
color: #eee;
}
a {
color: #7295f6;
}
}
</style>
</head>
<body>
<h2>Targets</h2>
<table border="1" cellpadding="6">
<tr>
<th>Name</th>
<th>MAC</th>
<th>Status</th>
<th></th>
</tr>
<tbody id="targets"></tbody>
</body>
<script>
async function loadTargets() {
const res = await fetch("/targets");
const targets = await res.json();
const tbody = document.getElementById("targets");
for (const [name, t] of Object.entries(targets)) {
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${name}</td>
<td>${t.mac}</td>
<td id="status-${name}">checking...</td>
<td><a href="#" onclick="fetch('/wake/${name}')">Send WOL</a></td>
`;
tbody.appendChild(tr);
const checkStatus = async () => {
fetch("/status/" + name)
.then(res => res.ok ? res.json() : Promise.reject())
.then(json => document.getElementById("status-" + name).textContent = json.isUp ? "online" : "offline")
.catch(() => document.getElementById("status-" + name).textContent = "unknown");
}
checkStatus();
setInterval(checkStatus, 5000);
}
}
loadTargets();
</script>
</html>

10
targets.example.json Normal file
View File

@@ -0,0 +1,10 @@
{
"pc": {
"mac": "00:AA:BB:CC:DD:EE",
"ip": "192.168.0.1"
},
"server": {
"mac": "01:23:45:67:89:00",
"ip": "192.168.0.2"
}
}

9
wol.js
View File

@@ -1,4 +1,5 @@
import dgram from "node:dgram"
import { exec } from "node:child_process";
export function createWolPacket(mac) {
const parts = mac.split(/[:-]/);
@@ -39,3 +40,11 @@ export async function sendWolPacket(mac, broadcast = "255.255.255.255", port = 9
});
});
}
export function ping(ip) {
return new Promise(resolve => {
exec(`ping -c 1 -W 1 ${ip}`, err => {
resolve(!err);
});
});
}