Compare commits
2
Commits
e26d893e5f
...
a5966f0b43
No files matched your search
+15
-11
@@ -22,28 +22,26 @@ This folder has small helpers for build, CI, and router install. Keep it simple;
|
|||||||
|
|
||||||
3. Install on the router
|
3. Install on the router
|
||||||
|
|
||||||
Use the release asset URL from your Gitea Release page:
|
Use the updater on the router to fetch the latest automatically:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
WAKEY_HOST=git.ldlda.com WAKEY_OWNER=lda WAKEY_REPO=wakey sh /etc/ldlda_help/update_wakey.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, use the release asset URL from your Gitea Release page:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
wget -O- https://<gitea>/<owner>/<repo>/releases/download/v0.1.0/wakey-rootfs-v0.1.0-armv7-unknown-linux-musleabihf.tgz | tar -xz -C /
|
wget -O- https://<gitea>/<owner>/<repo>/releases/download/v0.1.0/wakey-rootfs-v0.1.0-armv7-unknown-linux-musleabihf.tgz | tar -xz -C /
|
||||||
chmod +x /etc/init.d/wakey && /etc/init.d/wakey enable && /etc/init.d/wakey restart
|
chmod +x /etc/init.d/wakey && /etc/init.d/wakey enable && /etc/init.d/wakey restart
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, use the updater on the router to fetch the latest automatically:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
WAKEY_HOST=git.ldlda.com WAKEY_OWNER=lda WAKEY_REPO=wakey sh /etc/ldlda_help/update_wakey.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## Scripts overview
|
## Scripts overview
|
||||||
|
|
||||||
- `act_runner.ps1` — Start/seed the local Gitea runner. Use `-Attach` to see logs, `-ForceConfigure` to register non-interactively.
|
- `act_runner.ps1` — Start/seed the local Gitea runner. Use `-Attach` to see logs, `-ForceConfigure` to register non-interactively.
|
||||||
- `dev_push.ps1` — Fast dev loop: build + upload to router. Usage:
|
- `dev_push.ps1` — Fast dev loop: build + upload to router. Usage:
|
||||||
- `./scripts/dev_push.ps1 -Pass <password> [-HostName <ip>] [-RemotePath </root/.bin/wakey>] [-Restart] [-Quiet]`
|
- `./scripts/dev_push.ps1 -Pass <password> [-HostName <ip>] [-RemotePath </root/.bin/wakey>] [-Restart] [-Quiet]`
|
||||||
- Uploads to `<RemotePath>.tmp` then atomically moves into place; `-Restart` restarts the service; `-Quiet` silences MOTD.
|
- Uploads to `<RemotePath>.tmp` then atomically moves into place; `-Restart` restarts the service; `-Quiet` silences MOTD.
|
||||||
- `cross_build.ps1` — Simple wrapper for `cargo build` per target (default: armv7-unknown-linux-musleabihf).
|
|
||||||
- `package_rootfs.ps1` — Produces `dist/wakey-rootfs-<version>-<target>.tgz` with `/root/.bin/wakey` and `/etc/init.d/*`.
|
- `package_rootfs.ps1` — Produces `dist/wakey-rootfs-<version>-<target>.tgz` with `/root/.bin/wakey` and `/etc/init.d/*`.
|
||||||
- `package.ps1` — Dev bundle with binaries + init scripts (not a rootfs layout).
|
|
||||||
- `publish.ps1` — Optional version bump + build + tag (and `cargo publish` only if you pass `-Publish`).
|
- `publish.ps1` — Optional version bump + build + tag (and `cargo publish` only if you pass `-Publish`).
|
||||||
|
|
||||||
## CI (Gitea)
|
## CI (Gitea)
|
||||||
@@ -56,8 +54,8 @@ This folder has small helpers for build, CI, and router install. Keep it simple;
|
|||||||
## Local build/install (manual path)
|
## Local build/install (manual path)
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Build (requires cross installed)
|
# Build
|
||||||
./scripts/cross_build.ps1 -Release -Targets armv7-unknown-linux-musleabihf
|
cargo build --release --target armv7-unknown-linux-musleabihf
|
||||||
|
|
||||||
# Package rootfs
|
# Package rootfs
|
||||||
./scripts/package_rootfs.ps1 -Version v0.1.0 -Target armv7-unknown-linux-musleabihf
|
./scripts/package_rootfs.ps1 -Version v0.1.0 -Target armv7-unknown-linux-musleabihf
|
||||||
@@ -72,4 +70,10 @@ tar -xz -f /tmp/wakey.tgz -C /
|
|||||||
chmod +x /etc/init.d/wakey && /etc/init.d/wakey enable && /etc/init.d/wakey restart
|
chmod +x /etc/init.d/wakey && /etc/init.d/wakey enable && /etc/init.d/wakey restart
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or you can use the script:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
./scripts/dev_push.ps1 -Pass <password>
|
||||||
|
```
|
||||||
|
|
||||||
That’s it. Keep the flow: start runner → tag push → install.
|
That’s it. Keep the flow: start runner → tag push → install.
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Build release binaries using cross for Linux targets.
|
|
||||||
# Requires: cross installed (cargo install cross)
|
|
||||||
param(
|
|
||||||
[string[]]$Targets = @("armv7-unknown-linux-musleabihf"),
|
|
||||||
[switch]$Release
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
|
|
||||||
$mode = if ($Release) { "--release" } else { "" }
|
|
||||||
|
|
||||||
foreach ($t in $Targets) {
|
|
||||||
Write-Host "Building for $t..."
|
|
||||||
cross build $mode --target $t
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Done."
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
# Package a release tarball with binaries and init scripts.
|
|
||||||
# Usage: ./scripts/package.ps1 -Version 0.1.0 -OutDir dist
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory = $true)][string]$Version,
|
|
||||||
[string]$OutDir = "dist",
|
|
||||||
[string[]]$Targets = @("armv7-unknown-linux-musleabihf")
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
|
|
||||||
$root = Split-Path -Parent $MyInvocation.MyCommand.Path | Split-Path -Parent
|
|
||||||
$dist = Join-Path $root $OutDir
|
|
||||||
New-Item -ItemType Directory -Force -Path $dist | Out-Null
|
|
||||||
|
|
||||||
$pkgName = "wakey-$Version"
|
|
||||||
$pkgDir = Join-Path $dist $pkgName
|
|
||||||
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $pkgDir
|
|
||||||
New-Item -ItemType Directory -Force -Path $pkgDir | Out-Null
|
|
||||||
|
|
||||||
# Copy LICENSE/README if present
|
|
||||||
if (Test-Path "$root/README.md") { Copy-Item "$root/README.md" $pkgDir }
|
|
||||||
if (Test-Path "$root/LICENSE") { Copy-Item "$root/LICENSE" $pkgDir }
|
|
||||||
|
|
||||||
# Binaries per target
|
|
||||||
foreach ($t in $Targets) {
|
|
||||||
$binName = if ($t -like "*-windows-*") { "wakey.exe" } else { "wakey" }
|
|
||||||
$src = Join-Path $root "target/$t/release/$binName"
|
|
||||||
if (-not (Test-Path $src)) { throw "Missing binary: $src" }
|
|
||||||
$tDir = Join-Path $pkgDir "bin/$t"
|
|
||||||
New-Item -ItemType Directory -Force -Path $tDir | Out-Null
|
|
||||||
Copy-Item $src $tDir
|
|
||||||
}
|
|
||||||
|
|
||||||
# Init scripts
|
|
||||||
$initDir = Join-Path $pkgDir "init"
|
|
||||||
New-Item -ItemType Directory -Force -Path $initDir | Out-Null
|
|
||||||
Copy-Item "$root/scripts/init" $initDir -Recurse
|
|
||||||
|
|
||||||
# Systemd service (for Linux hosts)
|
|
||||||
Copy-Item "$root/scripts/systemd" $initDir -Recurse -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
# Tarball
|
|
||||||
Push-Location $dist
|
|
||||||
if (Get-Command tar -ErrorAction SilentlyContinue) {
|
|
||||||
tar -czf "$pkgName.tgz" "$pkgName"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Fallback to zip on Windows
|
|
||||||
Compress-Archive -Path "$pkgName" -DestinationPath "$pkgName.zip" -Force
|
|
||||||
}
|
|
||||||
Pop-Location
|
|
||||||
|
|
||||||
Write-Host "Packaged: $dist/$pkgName.(tgz|zip)"
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# One-shot: build, package, and print artifact location (no cross required).
|
|
||||||
# Usage: ./scripts/release.ps1 -Version 0.1.0 -Targets armv7-unknown-linux-musleabihf
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory = $true)][string]$Version,
|
|
||||||
[string[]]$Targets = @("armv7-unknown-linux-musleabihf")
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
|
|
||||||
foreach ($t in $Targets) {
|
|
||||||
rustup target add $t
|
|
||||||
cargo build --release --target $t
|
|
||||||
}
|
|
||||||
./scripts/package.ps1 -Version $Version -Targets $Targets
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Wakey WOL+ARP service
|
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
ExecStart=/usr/local/bin/wakey
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=2
|
|
||||||
User=wakey
|
|
||||||
Group=wakey
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
+10
-4
@@ -19,7 +19,7 @@ export function renderLeases(leases) {
|
|||||||
let dotClass = "dot ok";
|
let dotClass = "dot ok";
|
||||||
if (expired) {
|
if (expired) {
|
||||||
dotClass = "dot bad";
|
dotClass = "dot bad";
|
||||||
} else if (typeof l?.rank === "number") {
|
} /* rank from NUDState */ else if (typeof l?.rank === "number") {
|
||||||
if (l.rank >= 5) dotClass = "dot ok";
|
if (l.rank >= 5) dotClass = "dot ok";
|
||||||
else if (l.rank >= 2) dotClass = "dot warn";
|
else if (l.rank >= 2) dotClass = "dot warn";
|
||||||
else dotClass = "dot bad";
|
else dotClass = "dot bad";
|
||||||
@@ -34,9 +34,15 @@ export function renderLeases(leases) {
|
|||||||
const name = l.name || "";
|
const name = l.name || "";
|
||||||
tr.innerHTML = `
|
tr.innerHTML = `
|
||||||
<td><span class="${dotClass}" title="${title}"></span></td>
|
<td><span class="${dotClass}" title="${title}"></span></td>
|
||||||
<td>${ip ? `<a href="#" class="pick" data-value="${ip}" title="filter by ip">${ip}</a>` : ""}</td>
|
${[ip, mac, name]
|
||||||
<td>${mac ? `<a href="#" class="pick" data-value="${mac}" title="filter by mac">${mac}</a>` : ""}</td>
|
.map(
|
||||||
<td>${name ? `<a href="#" class="pick" data-value="${name}" title="filter by name">${name}</a>` : ""}</td>
|
(f) =>
|
||||||
|
`<td>${
|
||||||
|
f &&
|
||||||
|
`<a href="#" class="pick" data-value="${f}" title="filter by ip">${f}</a>`
|
||||||
|
}</td>`
|
||||||
|
)
|
||||||
|
.join("\n")}
|
||||||
<td><span class="tiny">${whenText}</span></td>`;
|
<td><span class="tiny">${whenText}</span></td>`;
|
||||||
tbl.appendChild(tr);
|
tbl.appendChild(tr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,12 @@ import {
|
|||||||
setPill,
|
setPill,
|
||||||
qs,
|
qs,
|
||||||
} from "./dom.js";
|
} from "./dom.js";
|
||||||
import { getName, saveName, loadName, extractHostLikeBackend } from "./utils.js";
|
import {
|
||||||
|
getName,
|
||||||
|
saveName,
|
||||||
|
loadName,
|
||||||
|
extractHostLikeBackend,
|
||||||
|
} from "./utils.js";
|
||||||
import { fetchStatus } from "./status.js";
|
import { fetchStatus } from "./status.js";
|
||||||
import { fetchLeases } from "./leases.js";
|
import { fetchLeases } from "./leases.js";
|
||||||
import { sendWake } from "./wake.js";
|
import { sendWake } from "./wake.js";
|
||||||
@@ -39,6 +44,7 @@ function pickTarget(value) {
|
|||||||
saveName(v);
|
saveName(v);
|
||||||
setLink(v);
|
setLink(v);
|
||||||
fetchStatus(v);
|
fetchStatus(v);
|
||||||
|
fetchLeases();
|
||||||
}
|
}
|
||||||
|
|
||||||
// events
|
// events
|
||||||
@@ -48,6 +54,7 @@ elCheck.addEventListener("click", () => {
|
|||||||
saveName(name);
|
saveName(name);
|
||||||
setLink(name);
|
setLink(name);
|
||||||
fetchStatus(name);
|
fetchStatus(name);
|
||||||
|
fetchLeases();
|
||||||
});
|
});
|
||||||
elWake.addEventListener("click", () => {
|
elWake.addEventListener("click", () => {
|
||||||
const name = getName(elName);
|
const name = getName(elName);
|
||||||
@@ -55,6 +62,7 @@ elWake.addEventListener("click", () => {
|
|||||||
saveName(name);
|
saveName(name);
|
||||||
setLink(name);
|
setLink(name);
|
||||||
sendWake(name);
|
sendWake(name);
|
||||||
|
fetchLeases();
|
||||||
});
|
});
|
||||||
elName.addEventListener("keydown", (e) => {
|
elName.addEventListener("keydown", (e) => {
|
||||||
if (e.key === "Enter") elCheck.click();
|
if (e.key === "Enter") elCheck.click();
|
||||||
|
|||||||
+25
-33
@@ -1,16 +1,23 @@
|
|||||||
import { elHtml, elLog, setPill, qs } from "./dom.js";
|
import { elHtml, elLog, setPill, qs } from "./dom.js";
|
||||||
import { rankState } from "./utils.js";
|
import { rankState } from "./utils.js";
|
||||||
|
|
||||||
|
const status_map = {
|
||||||
|
ip: "ip",
|
||||||
|
mac: "mac",
|
||||||
|
state: "state",
|
||||||
|
dev: "interface",
|
||||||
|
};
|
||||||
|
|
||||||
|
const filter_array = ["ip", "dev", "nud", "mac"];
|
||||||
|
|
||||||
function buildStatusUrl(name) {
|
function buildStatusUrl(name) {
|
||||||
const hasExtraFilters = ["ip", "dev", "nud", "mac"].some(
|
const hasExtraFilters = f_array.some((k) => qs.getAll(k).length);
|
||||||
(k) => qs.getAll(k).length
|
|
||||||
);
|
|
||||||
if (name && !hasExtraFilters) {
|
if (name && !hasExtraFilters) {
|
||||||
return new URL(`/api/smart/${encodeURIComponent(name)}`, location.origin);
|
return new URL(`/api/smart/${encodeURIComponent(name)}`, location.origin);
|
||||||
}
|
}
|
||||||
const u = new URL("/api/status", location.origin);
|
const u = new URL("/api/status", location.origin);
|
||||||
if (name) u.searchParams.set("name", name);
|
if (name) u.searchParams.set("name", name);
|
||||||
for (const k of ["ip", "dev", "nud", "mac"]) {
|
for (const k of Object.keys(status_map)) {
|
||||||
const vals = qs.getAll(k);
|
const vals = qs.getAll(k);
|
||||||
for (const v of vals) u.searchParams.append(k, v);
|
for (const v of vals) u.searchParams.append(k, v);
|
||||||
}
|
}
|
||||||
@@ -24,41 +31,27 @@ export function renderStatus(data) {
|
|||||||
|
|
||||||
for (const row of data.table || []) {
|
for (const row of data.table || []) {
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
tr.innerHTML = `
|
tr.innerHTML = Object.entries(status_map)
|
||||||
<td>${
|
.map(
|
||||||
row.ip
|
(field, description) =>
|
||||||
? `<a href="#" class="pick" data-value="${row.ip}" title="filter by ip">${row.ip}</a>`
|
`<td>${
|
||||||
|
row[field]
|
||||||
|
? `<a href="#" class="pick" data-value="${row[field]}" title="filter by ${description}">${row[field]}</a>`
|
||||||
: ""
|
: ""
|
||||||
}</td>
|
}</td>`
|
||||||
<td>${
|
)
|
||||||
row.mac
|
.join();
|
||||||
? `<a href="#" class="pick" data-value="${row.mac}" title="filter by mac">${row.mac}</a>`
|
|
||||||
: ""
|
|
||||||
}</td>
|
|
||||||
<td>${
|
|
||||||
row.state
|
|
||||||
? `<a href="#" class="pick" data-value="${row.state}" title="filter by state">${row.state}</a>`
|
|
||||||
: ""
|
|
||||||
}</td>
|
|
||||||
<td>${
|
|
||||||
row.dev
|
|
||||||
? `<a href="#" class="pick" data-value="${row.dev}" title="filter by interface">${row.dev}</a>`
|
|
||||||
: ""
|
|
||||||
}</td>`;
|
|
||||||
tbl.appendChild(tr);
|
tbl.appendChild(tr);
|
||||||
}
|
}
|
||||||
|
|
||||||
elHtml.innerHTML = "";
|
elHtml.innerHTML = "";
|
||||||
if (data.filters) {
|
if (data.filters) {
|
||||||
const parts = [];
|
const parts = [];
|
||||||
if (Array.isArray(data.filters.ip) && data.filters.ip.length)
|
filter_array.forEach((field) => {
|
||||||
parts.push(`ip=[${data.filters.ip.join(", ")}]`);
|
if (Array.isArray(data.filters[field]) && data.filters[field].length)
|
||||||
if (Array.isArray(data.filters.dev) && data.filters.dev.length)
|
parts.push(`${field}=[${data.filters[field].join(", ")}]`);
|
||||||
parts.push(`dev=[${data.filters.dev.join(", ")}]`);
|
});
|
||||||
if (Array.isArray(data.filters.nud) && data.filters.nud.length)
|
|
||||||
parts.push(`nud=[${data.filters.nud.join(", ")}]`);
|
|
||||||
if (Array.isArray(data.filters.mac) && data.filters.mac.length)
|
|
||||||
parts.push(`mac=[${data.filters.mac.join(", ")}]`);
|
|
||||||
if (parts.length) {
|
if (parts.length) {
|
||||||
const info = document.createElement("div");
|
const info = document.createElement("div");
|
||||||
info.className = "filters";
|
info.className = "filters";
|
||||||
@@ -85,7 +78,6 @@ export async function fetchStatus(name) {
|
|||||||
setPill("warn", "checking…");
|
setPill("warn", "checking…");
|
||||||
const u = buildStatusUrl(name);
|
const u = buildStatusUrl(name);
|
||||||
elLog.textContent = "GET " + u.pathname + u.search;
|
elLog.textContent = "GET " + u.pathname + u.search;
|
||||||
elHtml.innerHTML = "";
|
|
||||||
try {
|
try {
|
||||||
const r = await fetch(u);
|
const r = await fetch(u);
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ export function extractHostLikeBackend(input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function rankState(s) {
|
export function rankState(s) {
|
||||||
const key = String(s || "").trim().toUpperCase();
|
const key = String(s || "")
|
||||||
|
.trim()
|
||||||
|
.toUpperCase();
|
||||||
return (
|
return (
|
||||||
{
|
{
|
||||||
PERMANENT: 5,
|
PERMANENT: 5,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export async function sendWake(name) {
|
|||||||
});
|
});
|
||||||
const t = await r.text();
|
const t = await r.text();
|
||||||
elLog.textContent = t || "ok";
|
elLog.textContent = t || "ok";
|
||||||
setTimeout(() => fetchStatus(name), 1500);
|
setTimeout(() => fetchStatus(name), 800);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
elLog.textContent = "wake error: " + e;
|
elLog.textContent = "wake error: " + e;
|
||||||
setPill("bad", "error");
|
setPill("bad", "error");
|
||||||
|
|||||||
Reference in new issue
Block a user