feat: bitcoin-ui CSS fix, HTTPS proxy support, deploy script improvements

Bitcoin UI:
- Replace cdn.tailwindcss.com with locally bundled tailwind.css (CSP blocks external scripts)
- Make all asset paths relative for nginx proxy compatibility
- Add bitcoin-ui build/deploy to deploy-to-target.sh (was missing entirely)
- Use --network host (bitcoin-ui proxies Bitcoin RPC at 127.0.0.1:8332)

HTTPS mixed content fix:
- Add HTTPS_PROXY_PATHS in AppSession.vue — when parent page is HTTPS,
  iframe loads through nginx proxy instead of direct HTTP port
- Prevents browser blocking HTTP iframes inside HTTPS pages
- All Tailscale servers use HTTPS, this was breaking all app iframes

Deploy & first-boot improvements:
- first-boot-containers.sh auto-detects disk size for pruning vs txindex
- first-boot-containers.sh checks fallback source path for UI containers
- Added mempool-electrs to APP_PORTS mapping
- ElectrumX container creation in first-boot
- Podman doctor/fix/uptime skills added

Also includes: session persistence, identity management, LND transactions,
ElectrumX status UI, nostr-provider improvements, Web5 enhancements

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-16 12:58:35 +00:00
co-authored by Claude Opus 4.6
parent 07e46dce56
commit 30164fd12a
49 changed files with 6180 additions and 495 deletions
+29 -29
View File
@@ -58,13 +58,13 @@ impl RpcHandler {
})
};
let has_bitcoin = is_running(&["bitcoin-knots", "bitcoin-core", "bitcoin"]);
let has_electrs = is_running(&["mempool-electrs", "electrs"]);
let has_electrumx = is_running(&["electrumx", "mempool-electrs", "electrs"]);
has_lnd = is_running(&["lnd"]);
match package_id {
"mempool-electrs" | "electrs" if !has_bitcoin => {
"electrumx" | "mempool-electrs" | "electrs" if !has_bitcoin => {
return Err(anyhow::anyhow!(
"Electrs requires a running Bitcoin node (Bitcoin Knots). Please install and start Bitcoin Knots first."
"ElectrumX requires a running Bitcoin node (Bitcoin Knots). Please install and start Bitcoin Knots first."
));
}
"lnd" if !has_bitcoin => {
@@ -77,10 +77,10 @@ impl RpcHandler {
"BTCPay Server requires a running Bitcoin node (Bitcoin Knots). Please install and start Bitcoin Knots first."
));
}
"mempool" | "mempool-web" if !has_bitcoin || !has_electrs => {
"mempool" | "mempool-web" if !has_bitcoin || !has_electrumx => {
let mut missing = vec![];
if !has_bitcoin { missing.push("Bitcoin Knots"); }
if !has_electrs { missing.push("Electrs"); }
if !has_electrumx { missing.push("ElectrumX"); }
return Err(anyhow::anyhow!(
"Mempool requires {} to be running. Please install and start {} first.",
missing.join(" and "),
@@ -179,8 +179,11 @@ impl RpcHandler {
debug!("Using local image: {}", docker_image);
}
// Normalize container name: "electrs" alias -> "mempool-electrs"
let container_name = if package_id == "electrs" { "mempool-electrs" } else { package_id };
// Normalize container name: legacy "electrs"/"mempool-electrs" aliases -> "electrumx"
let container_name = match package_id {
"electrs" | "mempool-electrs" => "electrumx",
_ => package_id,
};
// Create and start container with security constraints
let mut run_args = vec![
@@ -234,7 +237,7 @@ impl RpcHandler {
package_id,
"bitcoin-knots" | "bitcoin" | "bitcoin-core"
| "lnd"
| "mempool" | "mempool-web" | "mempool-api" | "mempool-electrs" | "electrs" | "mysql-mempool" | "archy-mempool-db" | "archy-mempool-web"
| "mempool" | "mempool-web" | "mempool-api" | "electrumx" | "mempool-electrs" | "electrs" | "mysql-mempool" | "archy-mempool-db" | "archy-mempool-web"
| "btcpay-server" | "btcpayserver" | "archy-btcpay-db" | "archy-nbxplorer" | "nbxplorer"
| "fedimint" | "fedimint-gateway"
);
@@ -669,10 +672,10 @@ printtoconsole=1\n";
vec![format!("archy-{}", package_id)]
} else {
let order: &[&str] = match package_id {
"mempool" | "mempool-web" => &["archy-mempool-db", "mysql-mempool", "mempool-electrs", "mempool-api", "archy-mempool-api", "archy-mempool-web", "mempool"],
"mempool" | "mempool-web" => &["archy-mempool-db", "mysql-mempool", "electrumx", "mempool-electrs", "mempool-api", "archy-mempool-api", "archy-mempool-web", "mempool"],
"immich" => &["immich_postgres", "immich_redis", "immich_server"],
"penpot" | "penpot-frontend" => &["penpot-postgres", "penpot-valkey", "penpot-backend", "penpot-exporter", "penpot-frontend"],
_ => &["archy-mempool-db", "mysql-mempool", "mempool-electrs", "mempool-api", "archy-mempool-api", "archy-mempool-web", "mempool"],
_ => &["archy-mempool-db", "mysql-mempool", "electrumx", "mempool-electrs", "mempool-api", "archy-mempool-api", "archy-mempool-web", "mempool"],
};
let mut sorted = containers;
sorted.sort_by_key(|c| order.iter().position(|o| *o == c).unwrap_or(99));
@@ -1114,6 +1117,7 @@ async fn get_containers_for_app(package_id: &str) -> Result<Vec<String>> {
let patterns: Vec<String> = match package_id {
"mempool" | "mempool-web" => {
vec![
"electrumx".into(),
"mempool-electrs".into(),
"mempool-api".into(),
"archy-mempool-api".into(),
@@ -1160,6 +1164,7 @@ fn get_data_dirs_for_app(package_id: &str) -> Vec<String> {
"mempool" | "mempool-web" => vec![
format!("{}/mempool", base),
format!("{}/mysql-mempool", base),
format!("{}/electrumx", base),
format!("{}/mempool-electrs", base),
],
"fedimint" => vec![format!("{}/fedimint", base), format!("{}/fedimint-gateway", base)],
@@ -1300,6 +1305,7 @@ fn is_readonly_compatible(app_id: &str) -> bool {
"searxng"
| "grafana"
| "filebrowser"
| "electrumx"
| "mempool-electrs"
| "electrs"
| "nostr-rs-relay"
@@ -1332,8 +1338,8 @@ fn get_health_check_args(app_id: &str) -> Vec<String> {
"curl -sf http://localhost:8080/ || exit 1",
"30s", "3",
),
"mempool-electrs" | "electrs" => (
"curl -sf http://localhost:50001/ || exit 1",
"electrumx" | "mempool-electrs" | "electrs" => (
"curl -sf http://localhost:8000/ || exit 1",
"60s", "3",
),
"nextcloud" => (
@@ -1420,7 +1426,7 @@ fn get_memory_limit(app_id: &str) -> &'static str {
"ollama" => "4g",
// Medium apps
"lnd" => "512m",
"mempool-electrs" | "electrs" => "1g",
"electrumx" | "mempool-electrs" | "electrs" => "1g",
"nextcloud" => "1g",
"immich_server" | "immich" => "1g",
"btcpay-server" | "btcpayserver" => "1g",
@@ -1507,7 +1513,7 @@ fn get_app_config(
vec!["/var/lib/archipelago/mempool:/data".to_string()],
vec![
"MEMPOOL_BACKEND=electrum".to_string(),
"ELECTRUM_HOST=mempool-electrs".to_string(),
"ELECTRUM_HOST=electrumx".to_string(),
"ELECTRUM_PORT=50001".to_string(),
"ELECTRUM_TLS_ENABLED=false".to_string(),
format!("CORE_RPC_HOST={}", host_ip),
@@ -1523,26 +1529,20 @@ fn get_app_config(
None,
None,
),
"mempool-electrs" | "electrs" => {
"electrumx" | "mempool-electrs" | "electrs" => {
// Detect which bitcoin container is running for archy-net DNS resolution
let bitcoin_host = detect_bitcoin_container_name();
(
vec!["50001:50001".to_string()],
vec!["/var/lib/archipelago/mempool-electrs:/data".to_string()],
vec![],
vec!["/var/lib/archipelago/electrumx:/data".to_string()],
vec![
format!("DAEMON_URL=http://archipelago:archipelago123@{}:8332/", bitcoin_host),
"COIN=Bitcoin".to_string(),
"DB_DIRECTORY=/data".to_string(),
"SERVICES=tcp://:50001,rpc://0.0.0.0:8000".to_string(),
],
None,
None,
Some(vec![
"--daemon-rpc-addr".to_string(),
format!("{}:8332", bitcoin_host),
"--cookie".to_string(),
"archipelago:archipelago123".to_string(),
"--jsonrpc-import".to_string(),
"--electrum-rpc-addr".to_string(),
"0.0.0.0:50001".to_string(),
"--db-dir".to_string(),
"/data".to_string(),
"--lightmode".to_string(),
]),
)
},
"mysql-mempool" => (