Fix WebUI service URLs and Traefik flag; add app icon to instance modal
Found while installing two LAN-only Bookstack instances — both in the same no-domain path as the previous commit: - apps-services.json advertised every app at http://localhost:<port>. The CFG_SERVER_IP override it reads is defined in no config file, so the lookup always fell through to the "localhost" default — a URL that only resolves for someone browsing on the server itself. Now falls back to $local_ip_v4, the same host APP_URL is stamped with. - traefikManaged was inferred from `access == public`, a stated placeholder. Public only means the port is published on the host; it says nothing about a router. It reported true for both new instances despite their compose having traefik.enable:false. Now read from the port's own traefik column, gated on the app's domain actually being set — resolved per-app here rather than from $domain_full, which this generator never populates. - The "New instance" modal led with bare text. It now shows the type's icon in the same .app-card-icon holder the grid cards use, so it's visually tied to the app the user clicked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
48c024f69b
commit
6aa6eb81a1
@ -634,9 +634,21 @@
|
||||
}
|
||||
.lp-instance-head h3 {
|
||||
margin: 0;
|
||||
/* Takes the slack so the close button stays pinned right once the icon is
|
||||
added ahead of the title — space-between alone would centre the heading. */
|
||||
flex: 1;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
/* Reuses .app-card-icon (the grid card's holder) for identical framing, scaled
|
||||
down to sit on a heading line rather than a card. */
|
||||
.lp-instance-icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
min-width: 44px;
|
||||
padding: 8px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.lp-instance-x {
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
||||
@ -33,12 +33,24 @@ class InstanceManager {
|
||||
}
|
||||
}
|
||||
|
||||
// The type's entry in the apps list — source of both the heading and the icon.
|
||||
_typeApp(typeSlug) {
|
||||
return (window.apps || []).find(x => (x.command || '').split(' ').pop() === typeSlug);
|
||||
}
|
||||
|
||||
// The type's display title, for the modal heading.
|
||||
_typeTitle(typeSlug) {
|
||||
const a = (window.apps || []).find(x => (x.command || '').split(' ').pop() === typeSlug);
|
||||
const a = this._typeApp(typeSlug);
|
||||
return a ? (a.name || typeSlug).split(' - ')[0].trim() : typeSlug;
|
||||
}
|
||||
|
||||
// The type's icon. Same source and same default the grid cards use, so the
|
||||
// modal shows the exact artwork the user just clicked on.
|
||||
_typeIcon(typeSlug) {
|
||||
const a = this._typeApp(typeSlug);
|
||||
return (a && a.icon) || '/core/icons/apps/default.svg';
|
||||
}
|
||||
|
||||
// user text -> [a-z0-9] id (mirrors the backend's instanceIdPart)
|
||||
_idPart(raw) {
|
||||
return String(raw || '').toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
@ -59,6 +71,7 @@ class InstanceManager {
|
||||
async openCreateModal(typeSlug) {
|
||||
if (document.getElementById('lp-instance-modal')) return;
|
||||
const title = this._typeTitle(typeSlug);
|
||||
const icon = this._typeIcon(typeSlug);
|
||||
this.domains = await this._loadDomains();
|
||||
|
||||
// With no CFG_DOMAIN_n set, a subdomain has nothing to attach to — the backend
|
||||
@ -76,6 +89,9 @@ class InstanceManager {
|
||||
overlay.innerHTML = `
|
||||
<div class="lp-instance-modal" role="dialog" aria-modal="true" aria-label="New ${this._esc(title)} instance">
|
||||
<div class="lp-instance-head">
|
||||
<div class="app-card-icon lp-instance-icon">
|
||||
<img src="${this._esc(icon)}" alt="" onerror="this.onerror=null; this.src='/core/icons/apps/default.svg'"/>
|
||||
</div>
|
||||
<h3>New ${this._esc(title)} instance</h3>
|
||||
<button type="button" class="lp-instance-x" aria-label="Close">×</button>
|
||||
</div>
|
||||
|
||||
@ -47,6 +47,17 @@ EOF
|
||||
local install_date=$(sqlite3 "$docker_dir/$db_file" "SELECT install_date, install_time FROM apps WHERE name = '$app_name';" 2>/dev/null)
|
||||
local installed_date="${install_date}|"
|
||||
|
||||
# This app's domain, resolved the same way initializeAppVariables does
|
||||
# (CFG_<APP>_DOMAIN picks a CFG_DOMAIN_<n> slot). Resolved here rather than
|
||||
# read from $domain_full — this generator never calls initializeAppVariables,
|
||||
# so that global is either unset or left over from an unrelated app. Empty
|
||||
# means no Traefik router exists for any of this app's ports, whatever their
|
||||
# traefik column says.
|
||||
local _dom_idx_var="CFG_${app_name^^}_DOMAIN"
|
||||
local _dom_idx="${!_dom_idx_var:-1}"
|
||||
local _dom_var="CFG_DOMAIN_${_dom_idx}"
|
||||
local app_domain_full="${!_dom_var:-}"
|
||||
|
||||
# Get all Docker services for this app
|
||||
local docker_services=$(sqlite3 "$docker_dir/$db_file" "SELECT service_name, resource_value FROM network_resources WHERE app_name = '$app_name' AND resource_type = 'ip' AND status = 'active' ORDER BY service_name;" 2>/dev/null)
|
||||
|
||||
@ -63,18 +74,28 @@ EOF
|
||||
local access_type=$(echo "$port_mapping" | cut -d':' -f3)
|
||||
local protocol=$(echo "$port_mapping" | cut -d':' -f4)
|
||||
|
||||
# Get server IP (from system config or default)
|
||||
# Host for the "Open" links. CFG_SERVER_IP stays an explicit
|
||||
# override, but no config file actually defines it today, so
|
||||
# this always fell through to "localhost" — a URL that only
|
||||
# works for someone browsing ON the server. $local_ip_v4 (the
|
||||
# source IP of the default route) is what LAN/VPN clients dial,
|
||||
# and matches the host APP_URL is stamped with.
|
||||
local server_ip=""
|
||||
if [[ -f "${containers_dir}libreportal/config/generated/configs.json" ]]; then
|
||||
server_ip=$(grep -o '"CFG_SERVER_IP":[[:space:]]*"[^"]*"' "${containers_dir}libreportal/config/generated/configs.json" | cut -d'"' -f4)
|
||||
fi
|
||||
[[ -z "$server_ip" ]] && server_ip="${local_ip_v4:-localhost}"
|
||||
[[ -z "$server_ip" ]] && server_ip="localhost"
|
||||
|
||||
# Determine if Traefik managed (check if service has Traefik labels)
|
||||
|
||||
# Whether this port really has a Traefik router. Read from the
|
||||
# port's own traefik column below (field 7), not inferred from
|
||||
# the access type — "public" only means the port is published
|
||||
# on the host, and plenty of public ports carry no router. The
|
||||
# old assumption reported traefikManaged:true for apps whose
|
||||
# compose had traefik.enable:false, including every app on a
|
||||
# box with no domain configured.
|
||||
local traefik_managed="false"
|
||||
# For now, assume public access means Traefik managed
|
||||
[[ "$access_type" == "public" ]] && traefik_managed="true"
|
||||
|
||||
|
||||
# Per-port URL path (10th col of CFG_<APP>_PORT_N).
|
||||
# Set when the matching port row is found below; left
|
||||
# empty for ports without a web UI (DNS, etc.).
|
||||
@ -106,6 +127,14 @@ EOF
|
||||
local config_button_enabled
|
||||
local config_button_text
|
||||
local config_url_path=""
|
||||
# Field 7 = the traefik column. Absent on the 8-col
|
||||
# legacy layout, where field 6 held it instead.
|
||||
local config_traefik="false"
|
||||
if [[ "$field_count" -ge 9 ]]; then
|
||||
config_traefik=$(echo "$var_value" | cut -d'|' -f7)
|
||||
else
|
||||
config_traefik=$(echo "$var_value" | cut -d'|' -f6)
|
||||
fi
|
||||
if [[ "$field_count" -ge 10 ]]; then
|
||||
config_login_required=$(echo "$var_value" | cut -d'|' -f6)
|
||||
config_button_enabled=$(echo "$var_value" | cut -d'|' -f8)
|
||||
@ -129,6 +158,12 @@ EOF
|
||||
button_text="$config_button_text"
|
||||
login_required="$config_login_required"
|
||||
port_url_path="$config_url_path"
|
||||
# A router also needs a domain to attach to —
|
||||
# mirrors the guard in initializeAppVariables, so
|
||||
# the WebUI and the generated compose agree.
|
||||
if [[ "$config_traefik" == "true" && -n "$app_domain_full" ]]; then
|
||||
traefik_managed="true"
|
||||
fi
|
||||
fi
|
||||
done < "$app_config_file"
|
||||
fi
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user