mobile-connector: merge pairing+devices into one self-service Mobile App page
Nightly Build / build (push) Successful in 7m3s
Nightly Build / build (push) Successful in 7m3s
The two admin-only console pages become a single "Mobile App" page visible to every logged-in user: connection status (with the last connection error for troubleshooting), the device list (admin sees all, others only their own), a pairing dialog with the QR, and — admin-only — a settings dialog hosting the plugin config, including a relay picker (official grayed out, test, custom URL). The generic plugin-detail config form defers to it via the new Plugin::config_in_detail_page flag. Pairing is now self-service: any user opens a window and the device auto-binds to them; revocation is admin-for-anyone, owner-for-self; (re)binding to another user stays admin-only. Binding-managed plugins (manages_own_access) now expose their non-admin pages to all users and self-scope per caller (web_pages_for). The relay client records the error that ends a WS session and clears it on reconnect.
This commit is contained in:
@@ -235,4 +235,15 @@ impl RelayClient {
|
||||
pub fn is_connected(&self) -> bool {
|
||||
self.state.is_connected()
|
||||
}
|
||||
|
||||
/// The configured relay URL ("" when not configured — the loop stays idle).
|
||||
pub fn relay_url(&self) -> String {
|
||||
self.state.relay_url()
|
||||
}
|
||||
|
||||
/// The error that ended the last WS session, if any (UI troubleshooting).
|
||||
/// Cleared on the next successful connect.
|
||||
pub fn last_error(&self) -> Option<String> {
|
||||
self.state.last_error()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,9 @@ pub(crate) struct RelayState {
|
||||
/// Derived from the seed + the client's x25519 pubkey; never persisted.
|
||||
aes_cache: Mutex<HashMap<[u8; 32], [u8; 32]>>,
|
||||
connected: AtomicBool,
|
||||
/// Last connection error that ended a WS session, for UI troubleshooting.
|
||||
/// Cleared on the next successful connect.
|
||||
last_error: Mutex<Option<String>>,
|
||||
/// Broadcast sink for [`RelayEvent`]s consumed by the application layer.
|
||||
events_tx: broadcast::Sender<RelayEvent>,
|
||||
/// Pending `open_pipe` waiters: connection_id → accept/reject delivery
|
||||
@@ -84,6 +87,7 @@ impl RelayState {
|
||||
outbound: Mutex::new(None),
|
||||
aes_cache: Mutex::new(HashMap::new()),
|
||||
connected: AtomicBool::new(false),
|
||||
last_error: Mutex::new(None),
|
||||
events_tx,
|
||||
pipe_waiters: Mutex::new(HashMap::new()),
|
||||
incoming_pipes_tx,
|
||||
@@ -115,6 +119,10 @@ impl RelayState {
|
||||
|
||||
pub(crate) fn set_connected(&self, v: bool) {
|
||||
let was = self.connected.swap(v, Ordering::Relaxed);
|
||||
if v {
|
||||
// A live connection means the previous error is resolved.
|
||||
*self.last_error.lock().unwrap() = None;
|
||||
}
|
||||
if was != v {
|
||||
self.emit(if v { RelayEvent::Connected } else { RelayEvent::Disconnected });
|
||||
}
|
||||
@@ -124,6 +132,16 @@ impl RelayState {
|
||||
self.connected.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Record the error that ended a WS session (surfaced to the UI).
|
||||
pub(crate) fn set_last_error(&self, msg: String) {
|
||||
*self.last_error.lock().unwrap() = Some(msg);
|
||||
}
|
||||
|
||||
/// The last recorded connection error, if any.
|
||||
pub(crate) fn last_error(&self) -> Option<String> {
|
||||
self.last_error.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub(crate) fn set_outbound(&self, tx: mpsc::UnboundedSender<Vec<u8>>) {
|
||||
*self.outbound.lock().unwrap() = Some(tx);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ pub(crate) async fn run_loop(
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(crate_name = "skald-relay-client", error = %e, "relay connection ended");
|
||||
state.set_last_error(e.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user