Merge claude/2

This commit is contained in:
librelad 2026-07-03 21:17:26 +01:00
commit 3675b6b34c
5 changed files with 142 additions and 1 deletions

View File

@ -52,6 +52,12 @@ cliHandleUpdaterCommands()
source "$install_scripts_dir/webui/data/generators/updater/webui_artifact_scan.sh" 2>/dev/null
fi
declare -F webuiArtifactScan >/dev/null 2>&1 && webuiArtifactScan
# Registry catalog: refresh the App Center's marketplace data
# (the type:"app" rows of the same signed index).
if ! declare -F webuiRegistryCatalogScan >/dev/null 2>&1; then
source "$install_scripts_dir/webui/data/generators/apps/webui_registry_scan.sh" 2>/dev/null
fi
declare -F webuiRegistryCatalogScan >/dev/null 2>&1 && webuiRegistryCatalogScan
if ! declare -F artifactApplyAuto >/dev/null 2>&1; then
source "$install_scripts_dir/cli/commands/artifact/cli_artifact_apply.sh" 2>/dev/null
fi

View File

@ -8,6 +8,7 @@ webui_scripts=(
"webui/data/generators/apps/webui_app_status.sh"
"webui/data/generators/apps/webui_config_patch.sh"
"webui/data/generators/apps/webui_config.sh"
"webui/data/generators/apps/webui_registry_scan.sh"
"webui/data/generators/apps/webui_services.sh"
"webui/data/generators/apps/webui_tools.sh"
"webui/data/generators/backup/webui_backup_app_status.sh"

View File

@ -943,6 +943,7 @@ declare -gA LP_FN_MAP=(
[webuiPrintInstallCard]="webui/webui_display_logins.sh"
[webuiPrintLoginBlock]="webui/webui_display_logins.sh"
[_webuiReadServiceTags]="webui/data/generators/apps/webui_config.sh"
[webuiRegistryCatalogScan]="webui/data/generators/apps/webui_registry_scan.sh"
[webuiRemoveSetupLock]="webui/data/lock/webui_remove_setup_lock.sh"
[webuiRemoveUpdateLock]="webui/data/lock/webui_remove_update_lock.sh"
[webuiRunUpdate]="update/check_update.sh"
@ -1908,6 +1909,7 @@ declare -gA LP_FN_ROOT=(
[webuiPrintInstallCard]="scripts"
[webuiPrintLoginBlock]="scripts"
[_webuiReadServiceTags]="scripts"
[webuiRegistryCatalogScan]="scripts"
[webuiRemoveSetupLock]="scripts"
[webuiRemoveUpdateLock]="scripts"
[webuiRunUpdate]="scripts"
@ -2894,6 +2896,7 @@ webuiPatchAppConfigJson() { source "${install_scripts_dir}webui/data/generators/
webuiPrintInstallCard() { source "${install_scripts_dir}webui/webui_display_logins.sh"; webuiPrintInstallCard "$@"; }
webuiPrintLoginBlock() { source "${install_scripts_dir}webui/webui_display_logins.sh"; webuiPrintLoginBlock "$@"; }
_webuiReadServiceTags() { source "${install_scripts_dir}webui/data/generators/apps/webui_config.sh"; _webuiReadServiceTags "$@"; }
webuiRegistryCatalogScan() { source "${install_scripts_dir}webui/data/generators/apps/webui_registry_scan.sh"; webuiRegistryCatalogScan "$@"; }
webuiRemoveSetupLock() { source "${install_scripts_dir}webui/data/lock/webui_remove_setup_lock.sh"; webuiRemoveSetupLock "$@"; }
webuiRemoveUpdateLock() { source "${install_scripts_dir}webui/data/lock/webui_remove_update_lock.sh"; webuiRemoveUpdateLock "$@"; }
webuiRunUpdate() { source "${install_scripts_dir}update/check_update.sh"; webuiRunUpdate "$@"; }

View File

@ -0,0 +1,124 @@
#!/bin/bash
# WebUI registry-catalog data generator (the marketplace browse data)
# ---------------------------------------------------------------------------
# Writes the read-only JSON the App Center merges into its grid as
# "Available — Add" cards:
# frontend/data/apps/generated/registry_catalog.json
#
# It fetches + verifies the signed artifact index (lpFetchIndexInto), selects
# the type:"app" / payload.kind:"bundle" envelopes, annotates each with
# `defined` (a definition exists in the install tree) and `installed` (a live
# dir exists), and mirrors each catalog icon into
# frontend/core/icons/apps/registry/<slug>.<ext>
# only when it matches the sha256 pin in the signed index — the browser is
# never pointed at a remote host (same-origin only). Written ATOMICALLY
# (temp -> validate -> one runFileWrite); on ANY fetch/verify failure the
# prior file is KEPT. Run by `libreportal updater check`.
webuiRegistryCatalogScan() {
local out_dir="${containers_dir%/}/libreportal/frontend/data/apps/generated"
local out="$out_dir/registry_catalog.json"
local icon_dir="${containers_dir%/}/libreportal/frontend/core/icons/apps/registry"
local icon_web="/core/icons/apps/registry"
local max_icon=262144
runFileOp mkdir -p "$out_dir" 2>/dev/null || true
# Lazy-loader gap: ensure the read primitives are present.
if ! declare -F lpFetchIndex >/dev/null 2>&1; then
source "$install_scripts_dir/source/fetch.sh" 2>/dev/null
source "$install_scripts_dir/source/artifacts.sh" 2>/dev/null
fi
if ! command -v jq >/dev/null 2>&1; then
isNotice "webuiRegistryCatalogScan: jq not available — keeping the prior registry_catalog.json."
return 0
fi
local index
if ! lpFetchIndexInto index; then
isNotice "webuiRegistryCatalogScan: no verified index available — keeping the prior file."
return 0
fi
local signed="false"; [[ "$LP_INDEX_SIGSTATE" == "verified" ]] && signed="true"
local serial now
serial="$(_lpJsonNum "$index" index_serial)"
now="$(date -Iseconds 2>/dev/null || date)"
# Defined apps = definition dirs in the install tree; installed = live dirs.
local defined="[]" installed="[]" d
if [[ -d "$install_containers_dir" ]]; then
defined="$(for d in "$install_containers_dir"/*/; do [[ -d "$d" ]] && basename "$d"; done | jq -R . | jq -cs .)"
fi
if [[ -d "$containers_dir" ]]; then
installed="$(for d in "$containers_dir"/*/; do [[ -d "$d" ]] && basename "$d"; done | jq -R . | jq -cs .)"
fi
# Mirror the pinned catalog icons locally (slug -> local web path). An icon
# is used only if its bytes match the sha256 the signed index pins — a
# tampered or oversized icon is simply skipped, never served.
local icons_map="{}" base rows
base="$(lpReleaseBaseUrl)"
rows="$(printf '%s' "$index" | jq -r '
.artifacts[]? | select(.type=="app" and .payload.kind=="bundle")
| select((.meta.icon // "") != "" and (.meta.icon_sha256 // "") != "")
| [(.applies_when.app // ""), .meta.icon, .meta.icon_sha256] | @tsv' 2>/dev/null)"
if [[ -n "$rows" ]]; then
runFileOp mkdir -p "$icon_dir" 2>/dev/null || true
local slug icon pin ext url tmpf dest got size
while IFS=$'\t' read -r slug icon pin; do
[[ "$slug" =~ ^[a-z0-9][a-z0-9_]{0,31}$ ]] || continue
ext="${icon##*.}"
case "$ext" in svg|png) : ;; *) continue ;; esac
dest="$icon_dir/$slug.$ext"
if [[ -f "$dest" && "$(_lpSha256 "$dest")" == "$pin" ]]; then
icons_map="$(jq -c --arg s "$slug" --arg p "$icon_web/$slug.$ext" '.[$s]=$p' <<<"$icons_map")"
continue
fi
url="$icon"; case "$url" in http*://*) : ;; *) url="$base/$url" ;; esac
tmpf="$(mktemp)"
if _lpDownload "$url" "$tmpf" 2>/dev/null; then
got="$(_lpSha256 "$tmpf")"
size="$(stat -c %s "$tmpf" 2>/dev/null || echo 0)"
if [[ "$got" == "$pin" ]] && (( size > 0 && size <= max_icon )); then
runFileWrite "$dest" < "$tmpf"
runFileOp chown "$docker_install_user":"$docker_install_user" "$dest" 2>/dev/null || true
icons_map="$(jq -c --arg s "$slug" --arg p "$icon_web/$slug.$ext" '.[$s]=$p' <<<"$icons_map")"
fi
fi
rm -f "$tmpf"
done <<<"$rows"
fi
local tmp; tmp="$(mktemp)"
printf '%s' "$index" | jq \
--arg now "$now" --arg signed "$signed" --arg serial "${serial:-0}" \
--argjson defined "$defined" --argjson installed "$installed" --argjson icons "$icons_map" '
{ generated_at: $now,
signed: ($signed=="true"),
serial: ($serial|tonumber? // 0),
apps: [ .artifacts[]? | select(.type=="app" and .payload.kind=="bundle")
| (.applies_when.app // "") as $slug | select($slug != "")
| { id,
app: $slug,
version: (.version // 1),
title: (.title // $slug),
why: (.why // ""),
publisher: (.publisher // ""),
trust: (.trust // "official"),
category: (.meta.category // ""),
description: (.meta.description // .why // ""),
long_description: (.meta.long_description // ""),
icon: ($icons[$slug] // null),
defined: (($defined | index($slug)) != null),
installed: (($installed | index($slug)) != null) } ] }' > "$tmp" 2>/dev/null
if ! jq empty "$tmp" 2>/dev/null; then
isNotice "webuiRegistryCatalogScan: generated JSON was invalid — keeping the prior file."
rm -f "$tmp"; return 1
fi
runFileWrite "$out" < "$tmp"
rm -f "$tmp"
runFileOp chown "$docker_install_user":"$docker_install_user" "$out" 2>/dev/null || true
local n; n="$(jq '.apps | length' "$out" 2>/dev/null || echo '?')"
isSuccessful "Registry catalog refreshed ($n app(s), serial=${serial:-?}, signed=$signed)."
}

View File

@ -49,6 +49,13 @@ webuiArtifactScan() {
installed="$(for d in "$containers_dir"/*/; do [[ -d "$d" ]] && basename "$d"; done | jq -R . | jq -cs .)"
fi
# The Improvements stream is HOTFIX-only. type:"app" rows belong to the
# registry catalog (webuiRegistryCatalogScan); anything else is a newer
# format this build doesn't know — skip + log, never error (§8.1 firewall).
local unknown
unknown="$(printf '%s' "$index" | jq '[.artifacts[]? | (.type // "") | select(. != "hotfix" and . != "app")] | length' 2>/dev/null || echo 0)"
[[ "$unknown" =~ ^[0-9]+$ ]] && (( unknown > 0 )) && isNotice "webuiArtifactScan: skipped $unknown artifact(s) of unrecognized type (newer format?)."
local tmp; tmp="$(mktemp)"
printf '%s' "$index" | jq \
--arg now "$now" --arg signed "$signed" --arg serial "${serial:-0}" \
@ -56,7 +63,7 @@ webuiArtifactScan() {
{ generated_at: $now,
signed: ($signed=="true"),
serial: ($serial|tonumber? // 0),
artifacts: [ .artifacts[]? | {
artifacts: [ .artifacts[]? | select(.type=="hotfix") | {
id, type,
severity: (.severity // "tweak"),
auto: (.auto // false),