feat: reboot button in Settings with password confirmation

- system.reboot RPC endpoint requires password re-verification
- Uses systemd path unit pattern (tor-helper.sh) for privilege escalation
- 2-second delay before reboot to allow RPC response to reach client
- Clean UI: password input modal, loading state, error feedback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-20 10:48:06 +00:00
co-authored by Claude Opus 4.6
parent 1f3c86687d
commit adf0aa465f
4 changed files with 104 additions and 0 deletions
+1
View File
@@ -705,6 +705,7 @@ impl RpcHandler {
"system.detect-usb-devices" => self.handle_system_detect_usb_devices().await,
"system.disk-status" => self.handle_system_disk_status().await,
"system.disk-cleanup" => self.handle_system_disk_cleanup().await,
"system.reboot" => self.handle_system_reboot(params).await,
"system.factory-reset" => self.handle_system_factory_reset(params).await,
// Opt-in anonymous analytics
+32
View File
@@ -642,6 +642,38 @@ async fn read_temperatures() -> Result<Vec<serde_json::Value>> {
impl RpcHandler {
/// system.factory-reset — Wipe all user data, remove containers, and restart.
/// Only preserves the data_dir itself (recreated empty on restart).
/// system.reboot — Reboot the machine. Requires password re-verification.
pub(super) async fn handle_system_reboot(
&self,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let password = params
.as_ref()
.and_then(|p| p.get("password"))
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing password — re-authentication required"))?;
let valid = self.auth_manager.verify_password(password).await?;
if !valid {
return Err(anyhow::anyhow!("Password incorrect"));
}
info!("System reboot initiated by user");
// Schedule reboot in 2 seconds (gives time for the RPC response to reach the client)
// Uses the tor-helper path unit pattern (writes action file, systemd triggers root service)
tokio::spawn(async {
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
let action = serde_json::json!({"action": "reboot"});
let _ = tokio::fs::write(
"/var/lib/archipelago/tor-config/tor-action",
serde_json::to_string(&action).unwrap_or_default(),
).await;
});
Ok(serde_json::json!({ "rebooting": true }))
}
pub(super) async fn handle_system_factory_reset(
&self,
params: Option<serde_json::Value>,