Enable multi-instance on vaultwarden and searxng
Both now clear every guard: services are <type>-prefixed, and their host ports
became random in the previous commit.
Enabling them surfaced a real bug that would have made vaultwarden instances
fail to start, found by dry-running the clone path before trusting the flag.
Eight apps define an app-specific compose-tags hook named with the app as a
SUFFIX — appSetupComposeTags_vaultwarden — and docker_config_setup_data.sh
dispatches it as appSetupComposeTags_${app_name}. The tools rewrite only
renamed the <type>_ PREFIX form, so a clone kept the base name: it defined a
function nobody calls (colliding with the base app's), its ADMIN_TOKEN and
SIGNUPS_ALLOWED tags were never filled, and the pre-start guard would have
refused to launch the instance. Now renamed, anchored on the () of a definition
so only real function names are touched.
The same hooks pass tag NAMES as strings ("VAULTWARDEN_ADMIN_TOKEN_1_TAG"),
invisible to the lowercase renames, while the cloned compose had already moved
to <SLUG>_..._TAG. Those are rewritten too, mirroring compose rule 4. Verified:
the tags the cloned hook sets now match the cloned compose exactly.
Also affects matrix, nextcloud, speedtest, pihole, gluetun and wireguard, which
ship the same hook shape — latent for those, since none are enabled.
WebUI: the instance bar on app details rendered nothing at all for apps without
instance support, which reads as "this build has no instance feature" and sends
people hunting for a setting that isn't missing. It now states the reason where
the pills would be, and names the blocking ports when it can — the port rows
are in the config the frontend already holds, so it mirrors
_instanceCheckPortsInstanceable (skipping disabled and random rows). The other
blocker lives in the compose, which the frontend never sees, so that case is
left unexplained rather than guessed at.
Bookstack's rewritten compose and tool tree remain byte-identical to the
running instances.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
39dad00455
commit
e9fceeab99
@ -542,6 +542,24 @@
|
||||
border: 1px solid var(--border-subtle, var(--border-color));
|
||||
border-radius: 12px;
|
||||
}
|
||||
/* The "not available" variant of the bar: same frame, quieter, since it states a
|
||||
fact rather than offering an action. */
|
||||
.instance-family-bar-off {
|
||||
background: transparent;
|
||||
border-style: dashed;
|
||||
}
|
||||
.instance-family-note {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.45;
|
||||
}
|
||||
.instance-family-note code {
|
||||
font-size: 12px;
|
||||
padding: 1px 5px;
|
||||
border-radius: 5px;
|
||||
background: rgba(var(--text-rgb), 0.08);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.instance-family-label {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
|
||||
@ -331,7 +331,41 @@ Object.assign(AppsManager.prototype, {
|
||||
const type = isInstance ? myCfg[`CFG_${U(cleanAppName)}_INSTANCE_OF`] : cleanAppName;
|
||||
const typeCfg = cfgOf(type);
|
||||
const isCapable = String(typeCfg[`CFG_${U(type)}_MULTI_INSTANCE`]).toLowerCase() === 'true';
|
||||
if (!isCapable) return '';
|
||||
|
||||
const typeAppEarly = apps.find(x => slugOf(x) === type);
|
||||
const typeTitleEarly = typeAppEarly ? (typeAppEarly.name || type).split(' - ')[0].trim() : type;
|
||||
|
||||
// Not instanceable: say so where the pills would have been, rather than
|
||||
// rendering nothing. An empty space reads as "this page has no instance
|
||||
// feature", which sends people looking for a setting that isn't missing.
|
||||
//
|
||||
// Where the reason is knowable from config, give it. A pinned host port is
|
||||
// the common blocker and the only one visible client-side: the port rows are
|
||||
// right here, so this mirrors _instanceCheckPortsInstanceable exactly —
|
||||
// skip `disabled` rows (they publish nothing) and `random` ones (already
|
||||
// per-instance). Anything else is left unexplained rather than guessed at,
|
||||
// since the remaining blocker lives in the compose, which the frontend
|
||||
// never sees.
|
||||
if (!isCapable) {
|
||||
const pinned = [];
|
||||
for (let i = 1; i <= 20; i++) {
|
||||
const row = typeCfg[`CFG_${U(type)}_PORT_${i}`];
|
||||
if (!row) continue;
|
||||
const f = String(row).split('|');
|
||||
const ext = (f[2] || '').split(':')[0];
|
||||
if (!ext || ext === 'random' || f[3] === 'disabled') continue;
|
||||
if (!pinned.includes(ext)) pinned.push(ext);
|
||||
}
|
||||
const why = pinned.length
|
||||
? `${esc(typeTitleEarly)} publishes on fixed host port${pinned.length > 1 ? 's' : ''} `
|
||||
+ `<code>${esc(pinned.join(', '))}</code>, which a second copy could not bind.`
|
||||
: `${esc(typeTitleEarly)} isn't enabled for multiple instances.`;
|
||||
return `
|
||||
<div class="instance-family-bar instance-family-bar-off">
|
||||
<span class="instance-family-label">Instances</span>
|
||||
<span class="instance-family-note">Not available — ${why}</span>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const instances = apps
|
||||
.map(slugOf)
|
||||
|
||||
@ -11,6 +11,11 @@
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
#
|
||||
CFG_SEARXNG_APP_NAME=searxng
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
CFG_SEARXNG_MULTI_INSTANCE=true
|
||||
CFG_SEARXNG_BACKUP=false
|
||||
CFG_SEARXNG_BACKUP_STRATEGY=auto
|
||||
CFG_SEARXNG_UPDATE_TYPE=auto
|
||||
|
||||
@ -12,6 +12,11 @@
|
||||
# MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed)
|
||||
#
|
||||
CFG_VAULTWARDEN_APP_NAME=vaultwarden
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
CFG_VAULTWARDEN_MULTI_INSTANCE=true
|
||||
CFG_VAULTWARDEN_BACKUP=true
|
||||
CFG_VAULTWARDEN_BACKUP_STRATEGY=auto
|
||||
CFG_VAULTWARDEN_UPDATE_TYPE=auto
|
||||
|
||||
@ -312,6 +312,8 @@ _instanceRewriteCompose() {
|
||||
_instanceRewriteTools() {
|
||||
local type="$1" slug="$2" dir="$3" src_compose="$4"
|
||||
local d f base
|
||||
local type_u="${type^^}" slug_u="${slug^^}"
|
||||
type_u="${type_u//-/_}"; slug_u="${slug_u//-/_}"
|
||||
|
||||
# Container identities as they appear in hook/tool code. The per-token rename
|
||||
# below is what catches `docker exec -u git gitea-service …`: the docker-verb
|
||||
@ -361,6 +363,24 @@ _instanceRewriteTools() {
|
||||
# names and prose.
|
||||
sed -i -E "s/(\b(container|container_name)=\")${type}(\")/\1${slug}\3/g" "$f"
|
||||
|
||||
# Function names carrying the app as a SUFFIX. The prefix rule above
|
||||
# only matches <type>_, so appSetupComposeTags_vaultwarden survived
|
||||
# untouched — and docker_config_setup_data.sh dispatches that hook as
|
||||
# appSetupComposeTags_${app_name}, i.e. ..._vaultwarden_work for an
|
||||
# instance. The clone therefore defined a function nobody calls (under
|
||||
# a name that collides with the base app's), its compose tags were
|
||||
# never filled, and the pre-start guard refused to launch the
|
||||
# instance. Anchored on the () of a definition so only real function
|
||||
# names are touched. Affects 8 apps that ship this hook shape.
|
||||
sed -i -E "s/\b([A-Za-z_][A-Za-z0-9_]*)_${type}(\(\))/\1_${slug}\2/g" "$f"
|
||||
|
||||
# The uppercase tag namespace, mirroring rule 4 of the compose rewrite.
|
||||
# These hooks pass tag NAMES as strings ("VAULTWARDEN_ADMIN_TOKEN_1_TAG"),
|
||||
# which the lowercase renames above cannot see. The cloned compose has
|
||||
# already moved to <SLUG>_..._TAG, so leaving these behind would update
|
||||
# a tag that no longer exists in the file.
|
||||
sed -i -E "s/\b${type_u}_([A-Z0-9_]*_TAG)\b/${slug_u}_\1/g" "$f"
|
||||
|
||||
# The app's own deployed directory. Hooks that build it from
|
||||
# "${containers_dir}<type>/..." instead of "$containers_dir$app_name/..."
|
||||
# read and WRITE the base app's files — adguard's auth adapter edits
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user