Add lan_address support in RPC and container management
- Introduced a new `lan_address` field in the RPC response for containers, allowing for easier access to UI launch URLs based on container names. - Updated the `ContainerStatus` struct to include `lan_address`, ensuring it is initialized and passed through relevant methods in both Podman and Docker runtimes. - Enhanced the UI store to compute enriched bundled apps with their respective `lan_address`, improving the user experience for accessing containerized applications. - Modified the `ContainerApps` view to utilize the enriched data, ensuring the correct launch URLs are displayed for bundled apps.
This commit is contained in:
parent
59072bd16c
commit
d988396111
@ -322,15 +322,26 @@ impl RpcHandler {
|
|||||||
_ => "unknown",
|
_ => "unknown",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let name = c.get("Names").and_then(|v| v.as_array()).and_then(|a| a.first()).and_then(|v| v.as_str()).unwrap_or("");
|
||||||
|
|
||||||
|
// Determine lan_address based on container name
|
||||||
|
let lan_address = match name {
|
||||||
|
"bitcoin-knots" => Some("http://localhost:8334"),
|
||||||
|
"lnd" => Some("http://localhost:8081"),
|
||||||
|
"tailscale" => Some("http://localhost:8240"),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"id": c.get("Id").and_then(|v| v.as_str()).unwrap_or(""),
|
"id": c.get("Id").and_then(|v| v.as_str()).unwrap_or(""),
|
||||||
"name": c.get("Names").and_then(|v| v.as_array()).and_then(|a| a.first()).and_then(|v| v.as_str()).unwrap_or(""),
|
"name": name,
|
||||||
"state": mapped_state,
|
"state": mapped_state,
|
||||||
"image": c.get("Image").and_then(|v| v.as_str()).unwrap_or(""),
|
"image": c.get("Image").and_then(|v| v.as_str()).unwrap_or(""),
|
||||||
"created": c.get("Created").and_then(|v| v.as_str()).unwrap_or(""),
|
"created": c.get("Created").and_then(|v| v.as_str()).unwrap_or(""),
|
||||||
"ports": c.get("Ports").and_then(|v| v.as_array()).map(|a|
|
"ports": c.get("Ports").and_then(|v| v.as_array()).map(|a|
|
||||||
a.iter().filter_map(|p| p.get("hostPort").and_then(|v| v.as_u64()).map(|p| p.to_string())).collect::<Vec<_>>()
|
a.iter().filter_map(|p| p.get("hostPort").and_then(|v| v.as_u64()).map(|p| p.to_string())).collect::<Vec<_>>()
|
||||||
).unwrap_or_default(),
|
).unwrap_or_default(),
|
||||||
|
"lan_address": lan_address,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@ -23,6 +23,7 @@ pub struct ContainerStatus {
|
|||||||
pub image: String,
|
pub image: String,
|
||||||
pub created: String,
|
pub created: String,
|
||||||
pub ports: Vec<String>,
|
pub ports: Vec<String>,
|
||||||
|
pub lan_address: Option<String>, // Launch URL for UI access
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
@ -277,6 +278,7 @@ impl PodmanClient {
|
|||||||
image: parts[3].to_string(),
|
image: parts[3].to_string(),
|
||||||
created: parts[4].to_string(),
|
created: parts[4].to_string(),
|
||||||
ports: vec![], // TODO: Parse ports from parts[5]
|
ports: vec![], // TODO: Parse ports from parts[5]
|
||||||
|
lan_address: None, // Set by docker_packages scanner
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,6 +364,7 @@ impl PodmanClient {
|
|||||||
image: container["Image"].as_str().unwrap_or("").to_string(),
|
image: container["Image"].as_str().unwrap_or("").to_string(),
|
||||||
created: container["Created"].as_str().unwrap_or("").to_string(),
|
created: container["Created"].as_str().unwrap_or("").to_string(),
|
||||||
ports,
|
ports,
|
||||||
|
lan_address: None, // Set by docker_packages scanner
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -404,6 +407,7 @@ impl PodmanClient {
|
|||||||
image: container["Image"].as_str().unwrap_or("").to_string(),
|
image: container["Image"].as_str().unwrap_or("").to_string(),
|
||||||
created: container["Created"].as_str().unwrap_or("").to_string(),
|
created: container["Created"].as_str().unwrap_or("").to_string(),
|
||||||
ports,
|
ports,
|
||||||
|
lan_address: None, // Set by docker_packages scanner
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -296,6 +296,7 @@ impl ContainerRuntime for DockerRuntime {
|
|||||||
image: parts[3].to_string(),
|
image: parts[3].to_string(),
|
||||||
created: parts[4].to_string(),
|
created: parts[4].to_string(),
|
||||||
ports: vec![],
|
ports: vec![],
|
||||||
|
lan_address: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,6 +368,7 @@ impl ContainerRuntime for DockerRuntime {
|
|||||||
image: container["Image"].as_str().unwrap_or("").to_string(),
|
image: container["Image"].as_str().unwrap_or("").to_string(),
|
||||||
created: container["CreatedAt"].as_str().unwrap_or("").to_string(),
|
created: container["CreatedAt"].as_str().unwrap_or("").to_string(),
|
||||||
ports,
|
ports,
|
||||||
|
lan_address: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -96,6 +96,17 @@ export const useContainerStore = defineStore('container', () => {
|
|||||||
return container.state
|
return container.state
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Get enriched bundled apps with runtime data (like lan_address)
|
||||||
|
const enrichedBundledApps = computed(() => {
|
||||||
|
return BUNDLED_APPS.map(app => {
|
||||||
|
const container = getContainerForApp.value(app.id)
|
||||||
|
return {
|
||||||
|
...app,
|
||||||
|
lan_address: container?.lan_address
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
async function fetchContainers() {
|
async function fetchContainers() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@ -240,6 +251,7 @@ export const useContainerStore = defineStore('container', () => {
|
|||||||
getContainerForApp,
|
getContainerForApp,
|
||||||
isAppLoading,
|
isAppLoading,
|
||||||
getAppState,
|
getAppState,
|
||||||
|
enrichedBundledApps,
|
||||||
// Actions
|
// Actions
|
||||||
fetchContainers,
|
fetchContainers,
|
||||||
fetchHealthStatus,
|
fetchHealthStatus,
|
||||||
|
|||||||
@ -184,13 +184,13 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, computed } from 'vue'
|
import { onMounted, computed } from 'vue'
|
||||||
import { useContainerStore, BUNDLED_APPS, type BundledApp } from '@/stores/container'
|
import { useContainerStore, type BundledApp } from '@/stores/container'
|
||||||
import ContainerStatus from '@/components/ContainerStatus.vue'
|
import ContainerStatus from '@/components/ContainerStatus.vue'
|
||||||
|
|
||||||
const store = useContainerStore()
|
const store = useContainerStore()
|
||||||
|
|
||||||
// Expose BUNDLED_APPS to the template (prevents tree-shaking)
|
// Use enriched bundled apps with runtime data (like lan_address)
|
||||||
const bundledApps = BUNDLED_APPS
|
const bundledApps = computed(() => store.enrichedBundledApps)
|
||||||
|
|
||||||
// Get current host for launch URLs
|
// Get current host for launch URLs
|
||||||
const currentHost = computed(() => window.location.hostname)
|
const currentHost = computed(() => window.location.hostname)
|
||||||
@ -208,14 +208,14 @@ onMounted(async () => {
|
|||||||
|
|
||||||
// Containers that aren't bundled apps
|
// Containers that aren't bundled apps
|
||||||
const otherContainers = computed(() => {
|
const otherContainers = computed(() => {
|
||||||
const bundledIds = bundledApps.map(a => a.id)
|
const bundledIds = bundledApps.value.map(a => a.id)
|
||||||
return store.containers.filter(c => {
|
return store.containers.filter(c => {
|
||||||
const name = c.name.toLowerCase()
|
const name = c.name.toLowerCase()
|
||||||
return !bundledIds.some(id => name.includes(id))
|
return !bundledIds.some(id => name.includes(id))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const hasAnyApps = computed(() => bundledApps.length > 0 || store.containers.length > 0)
|
const hasAnyApps = computed(() => bundledApps.value.length > 0 || store.containers.length > 0)
|
||||||
|
|
||||||
function extractAppName(containerName: string): string {
|
function extractAppName(containerName: string): string {
|
||||||
return containerName
|
return containerName
|
||||||
@ -276,6 +276,12 @@ function getStatusText(appId: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getLaunchUrl(app: BundledApp): string {
|
function getLaunchUrl(app: BundledApp): string {
|
||||||
|
// Prefer lan_address from backend (for apps with custom UIs)
|
||||||
|
if (app.lan_address) {
|
||||||
|
return app.lan_address
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to first configured port
|
||||||
const port = app.ports[0]?.host
|
const port = app.ports[0]?.host
|
||||||
if (!port) return '#'
|
if (!port) return '#'
|
||||||
return `http://${currentHost.value}:${port}`
|
return `http://${currentHost.value}:${port}`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user