feat(apps): backend-only services classify as services with no Launch button
Demo images / Build & push demo images (push) Has been cancelled
Demo images / Build & push demo images (push) Has been cancelled
A published port no longer implies a web UI. The package scanner used to synthesize interfaces.main.ui="true" for any container with a port or onion address, so headless backends — including self-deployed compose stacks like podsteadr — showed up as launchable apps. New ui_detection module decides instead: a manifest interfaces declaration (catalog overlay first, disk second) is definitive; undeclared apps get a short HTTP probe of the launch port (HTML page, redirect, or browser auth wall = UI; JSON APIs, raw TCP, dead ports = service), with cached verdicts and probes gated on running containers. Frontend canLaunch now refuses curated services outright and only treats a bare runtime address as launchable for curated known apps. Works identically for manifest apps and containers deployed by hand outside the orchestrator. ui_detection tests 6/6, frontend suite 696/696. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
d7c5d39747
commit
da14c135e4
@@ -212,9 +212,21 @@ impl DockerPackageScanner {
|
||||
website: lan_address.clone(),
|
||||
tier: Some(metadata.tier.to_string()),
|
||||
interfaces: if lan_address.is_some() || tor_address.is_some() {
|
||||
// `ui` is no longer implied by a published port: a
|
||||
// headless backend with an exposed port is a service,
|
||||
// not a launchable app. ui_detection consults the
|
||||
// manifest declaration first, then HTTP-probes the
|
||||
// port. Addresses stay present either way so the
|
||||
// Services tab can still show where a backend lives.
|
||||
let has_ui = super::ui_detection::has_web_ui(
|
||||
&app_id,
|
||||
lan_address.as_deref(),
|
||||
package_state == PackageState::Running,
|
||||
)
|
||||
.await;
|
||||
Some(Interfaces {
|
||||
main: Some(MainInterface {
|
||||
ui: Some("true".to_string()),
|
||||
ui: has_ui.then(|| "true".to_string()),
|
||||
tor_config: tor_address.clone(),
|
||||
lan_config: None,
|
||||
}),
|
||||
@@ -729,7 +741,7 @@ async fn reachable_lan_address(app_id: &str, candidate: Option<String>) -> Optio
|
||||
/// `rsplit(':')` then yields `"8096/"`, which fails to parse and silently
|
||||
/// drops a reachable launch URL. Reading digits after the final colon mirrors
|
||||
/// `port_from_url` in the RPC layer and tolerates a trailing path.
|
||||
fn launch_url_port(url: &str) -> Option<u16> {
|
||||
pub(crate) fn launch_url_port(url: &str) -> Option<u16> {
|
||||
let after_colon = url.rsplit_once(':')?.1;
|
||||
after_colon
|
||||
.chars()
|
||||
|
||||
Reference in New Issue
Block a user