fix: container install flow, filebrowser auth, AppCard enrichment
Some checks failed
Build Archipelago ISO / build-iso (push) Has been cancelled
Some checks failed
Build Archipelago ISO / build-iso (push) Has been cancelled
- Fix .198-style fresh installs: systemd service ExecStartPre creates /run/user/1000, enable podman.socket, chmod 644 /etc/hosts - Filebrowser: add /data volume for database (fixes read-only crash), secure auth with random password via backend RPC (no more admin/admin) - AppCard: enrich installing state with marketplace metadata (icon, title, description, tier badge, author, version) - Registry: btcpayserver 1.13.5 → 1.13.7, images mirrored - ReadWritePaths: add home container paths for rootless podman Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,7 @@ impl RpcHandler {
|
||||
"package.stop" => self.handle_package_stop(params).await,
|
||||
"package.restart" => self.handle_package_restart(params).await,
|
||||
"package.uninstall" => self.handle_package_uninstall(params).await,
|
||||
"app.filebrowser-token" => self.handle_filebrowser_token().await,
|
||||
|
||||
// Bundled app management (for pre-loaded container images)
|
||||
"bundled-app-start" => self.handle_bundled_app_start(params).await,
|
||||
|
||||
@@ -562,10 +562,18 @@ pub(super) async fn get_app_config(
|
||||
.unwrap_or(8083);
|
||||
(
|
||||
vec![format!("{}:80", host_port)],
|
||||
vec!["/var/lib/archipelago/filebrowser:/srv".to_string()],
|
||||
vec![
|
||||
"/var/lib/archipelago/filebrowser:/srv".to_string(),
|
||||
"/var/lib/archipelago/filebrowser-data:/data".to_string(),
|
||||
],
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
Some(vec![
|
||||
"--database=/data/database.db".to_string(),
|
||||
"--root=/srv".to_string(),
|
||||
"--address=0.0.0.0".to_string(),
|
||||
"--port=80".to_string(),
|
||||
]),
|
||||
)
|
||||
}
|
||||
"nginx-proxy-manager" => (
|
||||
|
||||
@@ -404,6 +404,67 @@ printtoconsole=1\n",
|
||||
|
||||
/// Run post-install hooks (Nextcloud trusted domains, Bitcoin UI container).
|
||||
async fn run_post_install_hooks(&self, package_id: &str) {
|
||||
if package_id == "filebrowser" {
|
||||
tokio::spawn(async move {
|
||||
// Wait for filebrowser to start and initialize its database
|
||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||
|
||||
// Generate a random password (32 bytes, hex-encoded)
|
||||
let mut buf = [0u8; 32];
|
||||
rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut buf);
|
||||
let password = hex::encode(buf);
|
||||
|
||||
// Get a JWT token with default credentials
|
||||
let login_res = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.build()
|
||||
.unwrap_or_default()
|
||||
.post("http://127.0.0.1:8083/api/login")
|
||||
.json(&serde_json::json!({"username": "admin", "password": "admin"}))
|
||||
.send()
|
||||
.await;
|
||||
|
||||
let token = match login_res {
|
||||
Ok(resp) if resp.status().is_success() => {
|
||||
resp.text().await.unwrap_or_default().trim_matches('"').to_string()
|
||||
}
|
||||
_ => {
|
||||
tracing::warn!("FileBrowser not ready for password change — keeping default");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Change admin password via filebrowser API
|
||||
let change_res = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.build()
|
||||
.unwrap_or_default()
|
||||
.put("http://127.0.0.1:8083/api/users/1")
|
||||
.header("X-Auth", &token)
|
||||
.json(&serde_json::json!({"password": password}))
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match change_res {
|
||||
Ok(resp) if resp.status().is_success() => {
|
||||
let secret_dir = "/var/lib/archipelago/secrets/filebrowser";
|
||||
let _ = tokio::fs::create_dir_all(secret_dir).await;
|
||||
let _ = tokio::fs::write(
|
||||
format!("{}/password", secret_dir),
|
||||
&password,
|
||||
).await;
|
||||
info!("FileBrowser admin password secured (default credentials replaced)");
|
||||
}
|
||||
Ok(resp) => {
|
||||
tracing::warn!("FileBrowser password change failed: {}", resp.status());
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!("FileBrowser password change error: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if package_id == "nextcloud" {
|
||||
let host_ip = self.config.host_ip.clone();
|
||||
tokio::spawn(async move {
|
||||
@@ -464,4 +525,36 @@ printtoconsole=1\n",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a fresh FileBrowser JWT token for the frontend.
|
||||
/// Reads the stored random password and authenticates to filebrowser's API.
|
||||
pub(in crate::api::rpc) async fn handle_filebrowser_token(
|
||||
&self,
|
||||
) -> Result<serde_json::Value> {
|
||||
let secret_path = "/var/lib/archipelago/secrets/filebrowser/password";
|
||||
let password = tokio::fs::read_to_string(secret_path)
|
||||
.await
|
||||
.unwrap_or_else(|_| "admin".to_string());
|
||||
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.build()
|
||||
.unwrap_or_default();
|
||||
|
||||
let resp = client
|
||||
.post("http://127.0.0.1:8083/api/login")
|
||||
.json(&serde_json::json!({"username": "admin", "password": password}))
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to connect to FileBrowser")?;
|
||||
|
||||
if !resp.status().is_success() {
|
||||
return Err(anyhow::anyhow!("FileBrowser login failed ({})", resp.status()));
|
||||
}
|
||||
|
||||
let token = resp.text().await.unwrap_or_default();
|
||||
let token = token.trim_matches('"');
|
||||
|
||||
Ok(serde_json::json!({ "token": token }))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user