Compare commits
2
Commits
a4ae375617
...
be06b3ce2b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be06b3ce2b | ||
|
|
f3d96ae2ee |
@@ -135,7 +135,7 @@ impl RpcHandler {
|
||||
// not /usr/bin/tollgate-module-basic-go — that's only the opkg/apk
|
||||
// *package* name, never an on-disk filename.
|
||||
let tollgate_installed = router
|
||||
.run("/usr/bin/opkg list-installed 2>/dev/null | grep -q '^tollgate-module-basic-go ' || \
|
||||
.run("opkg list-installed 2>/dev/null | grep -q '^tollgate-module-basic-go ' || \
|
||||
test -f /usr/bin/tollgate-wrt 2>/dev/null")
|
||||
.map(|(_, code)| code == 0)
|
||||
.unwrap_or(false);
|
||||
|
||||
+19
-15
@@ -15,25 +15,32 @@ pub enum PkgManager {
|
||||
impl Router {
|
||||
/// Detect which package manager is available.
|
||||
///
|
||||
/// - If `/usr/bin/opkg` exists → `PkgManager::Opkg` (nothing to do).
|
||||
/// - If `/usr/bin/apk` exists → run `apk update` (switching repos to HTTP
|
||||
/// Looks up `opkg`/`apk` via the router's `$PATH` (`command -v`) rather
|
||||
/// than a hardcoded `/usr/bin/<tool>` — official OpenWrt images don't all
|
||||
/// symlink `/bin` into `/usr/bin` (e.g. the `glinet_gl-mt3000` 24.10.2
|
||||
/// build keeps them as separate real directories with `opkg` living in
|
||||
/// `/bin`), so a fixed absolute path silently misses a perfectly normal
|
||||
/// install and reports "no package management" (archy-x250-pa3, 2026-09-05).
|
||||
///
|
||||
/// - If `opkg` is on PATH → `PkgManager::Opkg` (nothing to do).
|
||||
/// - If `apk` is on PATH → run `apk update` (switching repos to HTTP
|
||||
/// first to work around missing CA bundle on fresh images), then try
|
||||
/// `apk add opkg`. If opkg is in the repos → `Opkg`. If not (OpenWrt
|
||||
/// 25.x) → `ApkNative`.
|
||||
/// - Neither found → error.
|
||||
pub fn opkg_check(&self) -> Result<PkgManager> {
|
||||
let (_, code) = self.run("test -x /usr/bin/opkg")?;
|
||||
let (_, code) = self.run("command -v opkg >/dev/null 2>&1")?;
|
||||
if code == 0 {
|
||||
return Ok(PkgManager::Opkg);
|
||||
}
|
||||
|
||||
let (_, apk_code) = self.run("test -x /usr/bin/apk")?;
|
||||
let (_, apk_code) = self.run("command -v apk >/dev/null 2>&1")?;
|
||||
if apk_code == 0 {
|
||||
info!("[{}] opkg not found — using apk (OpenWrt 25.x+)", self.host);
|
||||
// Fresh images ship without a CA bundle; switch repos to HTTP so
|
||||
// apk's wget can reach the package index without TLS verification.
|
||||
self.run_ok("sed -i 's|https://|http://|g' /etc/apk/repositories 2>/dev/null || true")?;
|
||||
let (update_out, update_code) = self.run("/usr/bin/apk update 2>&1")?;
|
||||
let (update_out, update_code) = self.run("apk update 2>&1")?;
|
||||
if update_code != 0 {
|
||||
anyhow::bail!(
|
||||
"apk update failed (exit {}) — router may have no internet access. \
|
||||
@@ -43,7 +50,7 @@ impl Router {
|
||||
);
|
||||
}
|
||||
// Try to install opkg (only available on some 25.x builds).
|
||||
let (add_out, add_code) = self.run("/usr/bin/apk add opkg 2>&1")?;
|
||||
let (add_out, add_code) = self.run("apk add opkg 2>&1")?;
|
||||
if add_code == 0 {
|
||||
return Ok(PkgManager::Opkg);
|
||||
}
|
||||
@@ -62,7 +69,7 @@ impl Router {
|
||||
}
|
||||
|
||||
anyhow::bail!(
|
||||
"opkg not found at /usr/bin/opkg — this router's firmware may not \
|
||||
"Neither opkg nor apk found on this router's $PATH — its firmware may not \
|
||||
support package management (TollGate requires a standard OpenWrt build)"
|
||||
);
|
||||
}
|
||||
@@ -70,31 +77,28 @@ impl Router {
|
||||
/// `opkg update` — refresh package lists.
|
||||
pub fn opkg_update(&self) -> Result<()> {
|
||||
info!("[{}] opkg update", self.host);
|
||||
self.run_ok("/usr/bin/opkg update")?;
|
||||
self.run_ok("opkg update")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Install a package, skipping if already installed.
|
||||
pub fn opkg_install(&self, package: &str) -> Result<()> {
|
||||
// Check if already installed to avoid unnecessary network traffic.
|
||||
let (_, code) = self.run(&format!(
|
||||
"/usr/bin/opkg list-installed | grep -q '^{} '",
|
||||
package
|
||||
))?;
|
||||
let (_, code) = self.run(&format!("opkg list-installed | grep -q '^{} '", package))?;
|
||||
if code == 0 {
|
||||
info!("[{}] {} already installed", self.host, package);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("[{}] opkg install {}", self.host, package);
|
||||
self.run_ok(&format!("/usr/bin/opkg install {}", package))?;
|
||||
self.run_ok(&format!("opkg install {}", package))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a package.
|
||||
pub fn opkg_remove(&self, package: &str) -> Result<()> {
|
||||
info!("[{}] opkg remove {}", self.host, package);
|
||||
self.run_ok(&format!("/usr/bin/opkg remove {}", package))?;
|
||||
self.run_ok(&format!("opkg remove {}", package))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -121,7 +125,7 @@ impl Router {
|
||||
}
|
||||
|
||||
info!("[{}] apk add {}", self.host, package);
|
||||
self.run_ok(&format!("/usr/bin/apk add {}", package))?;
|
||||
self.run_ok(&format!("apk add {}", package))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ pub fn install_tollgate(router: &Router) -> Result<()> {
|
||||
|
||||
// Package not in any feed — download the .ipk directly.
|
||||
let arch = router
|
||||
.run_ok("/usr/bin/opkg print-architecture | grep -v all | grep -v noarch | tail -1 | awk '{print $2}'")?;
|
||||
.run_ok("opkg print-architecture | grep -v all | grep -v noarch | tail -1 | awk '{print $2}'")?;
|
||||
let arch = arch.trim();
|
||||
|
||||
let url = ipk_url(arch).ok_or_else(|| {
|
||||
@@ -88,7 +88,7 @@ pub fn install_tollgate_apk_native(router: &Router) -> Result<()> {
|
||||
". /etc/openwrt_release 2>/dev/null \
|
||||
&& a=\"${DISTRIB_ARCH:-${OPENWRT_ARCH:-}}\" \
|
||||
&& [ -n \"$a\" ] && echo \"$a\" \
|
||||
|| /usr/bin/apk --print-arch 2>/dev/null \
|
||||
|| apk --print-arch 2>/dev/null \
|
||||
|| uname -m",
|
||||
)?;
|
||||
// Normalise: uname -m returns bare "mipsel"/"mips"; map to 24kc variant
|
||||
|
||||
@@ -115,6 +115,24 @@ const showConnectForm = ref(false)
|
||||
const connecting = ref(false)
|
||||
const connectedParams = ref<Record<string, string> | null>(null)
|
||||
|
||||
// Every action below (install/edit TollGate, WiFi scan, WAN configure) needs
|
||||
// host/ssh_user/ssh_password to reach the router. `connectedParams` only gets
|
||||
// set when the Connect form was actually submitted this session (WR-03 above)
|
||||
// — on a normal page load the router reconnects via the server-persisted
|
||||
// config instead, so `sshPassword`/`sshUser`/`host` (the Connect form's own
|
||||
// local refs) sit at their untouched defaults ('', 'root', ''). Falling back
|
||||
// to those refs here used to send an explicit-but-empty ssh_password, which
|
||||
// the backend treats as "the caller provided this" and never falls back to
|
||||
// the real saved password — a real router password then fails auth on every
|
||||
// action even though the status poll (which sends no params at all) keeps
|
||||
// working fine (archy-x250-pa3, 2026-09-05: dropbear logged one bad-password
|
||||
// attempt at the exact moment "Install TollGate" was clicked). Omitting the
|
||||
// fields entirely when there's no explicit connectedParams lets the backend's
|
||||
// own saved-config fallback do the right thing, same as the status poll.
|
||||
function authParams(): Record<string, string> {
|
||||
return connectedParams.value ?? {}
|
||||
}
|
||||
|
||||
const detecting = ref(false)
|
||||
const detectError = ref('')
|
||||
const detectedCandidates = ref<string[]>([])
|
||||
@@ -271,11 +289,7 @@ async function provisionTollgate() {
|
||||
provisionError.value = ''
|
||||
provisionSuccess.value = false
|
||||
try {
|
||||
const params: Record<string, unknown> = {
|
||||
host: connectedParams.value?.host ?? status.value?.host,
|
||||
ssh_user: connectedParams.value?.ssh_user ?? sshUser.value,
|
||||
ssh_password: connectedParams.value?.ssh_password ?? sshPassword.value,
|
||||
}
|
||||
const params: Record<string, unknown> = { ...authParams() }
|
||||
await rpcClient.call({ method: 'openwrt.provision-tollgate', params, timeout: 300000 })
|
||||
provisionSuccess.value = true
|
||||
await load(connectedParams.value ?? undefined)
|
||||
@@ -302,9 +316,7 @@ async function saveTollgateConfig() {
|
||||
updateTollgateError.value = ''
|
||||
try {
|
||||
const params: Record<string, unknown> = {
|
||||
host: connectedParams.value?.host ?? status.value?.host,
|
||||
ssh_user: connectedParams.value?.ssh_user ?? sshUser.value,
|
||||
ssh_password: connectedParams.value?.ssh_password ?? sshPassword.value,
|
||||
...authParams(),
|
||||
price_sats: editPriceSats.value,
|
||||
step_size_ms: editStepSizeMin.value * 60_000,
|
||||
min_steps: editMinSteps.value,
|
||||
@@ -336,11 +348,7 @@ async function scanWifi() {
|
||||
wanStep.value = 'scanning'
|
||||
wanError.value = ''
|
||||
try {
|
||||
const params: Record<string, unknown> = {
|
||||
host: connectedParams.value?.host ?? status.value?.host,
|
||||
ssh_user: connectedParams.value?.ssh_user ?? sshUser.value,
|
||||
ssh_password: connectedParams.value?.ssh_password ?? sshPassword.value,
|
||||
}
|
||||
const params: Record<string, unknown> = { ...authParams() }
|
||||
const result = await rpcClient.call<{ networks: ScannedNetwork[] }>({
|
||||
method: 'openwrt.scan-wifi',
|
||||
params,
|
||||
@@ -367,9 +375,7 @@ async function configureWan() {
|
||||
wanError.value = ''
|
||||
try {
|
||||
const params: Record<string, unknown> = {
|
||||
host: connectedParams.value?.host ?? status.value?.host,
|
||||
ssh_user: connectedParams.value?.ssh_user ?? sshUser.value,
|
||||
ssh_password: connectedParams.value?.ssh_password ?? sshPassword.value,
|
||||
...authParams(),
|
||||
ssid: selectedNetwork.value.ssid,
|
||||
password: wanPassword.value,
|
||||
encryption: selectedNetwork.value.encryption,
|
||||
|
||||
Reference in New Issue
Block a user