mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-11-10 10:47:58 +01:00
77853ffe37
The addon, filebrowser-quantum and the FileBrowser option of homeassistant and podman-homeassistant created admin with the password community-scripts.org, on a service that runs as root over the whole filesystem. A new install now gets a random password in ~/filebrowser.creds or ~/filebrowser-quantum.creds, and an update replaces the public password on existing installs. Quantum only keeps adminPassword in its config until the admin exists, since it resets the password on every start. The Home Assistant options run the addon instead of their own copy pinned to FileBrowser 2.23.0, the service no longer forces the listen address back to 0.0.0.0, and the no-authentication prompt says what it exposes.
311 lines
10 KiB
Bash
311 lines
10 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# Copyright (c) 2021-2026 community-scripts ORG
|
|
# Author: MickLesk
|
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
# Source: https://github.com/filebrowserspace/quantum
|
|
|
|
APP="FileBrowser Quantum"
|
|
APP_TYPE="addon"
|
|
INSTALL_PATH="/usr/local/bin/filebrowser"
|
|
CONFIG_PATH="/usr/local/community-scripts/fq-config.yaml"
|
|
DEFAULT_PORT=8080
|
|
SRC_DIR="/"
|
|
|
|
if ! command -v curl &>/dev/null; then
|
|
printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2
|
|
if [[ -f /etc/alpine-release ]]; then
|
|
apk update >/dev/null 2>&1
|
|
apk add --no-cache curl >/dev/null 2>&1
|
|
else
|
|
apt-get update >/dev/null 2>&1
|
|
apt-get install -y curl >/dev/null 2>&1
|
|
fi
|
|
fi
|
|
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/core.func")
|
|
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/lib/tools.func")
|
|
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/error_handler.func")
|
|
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/api/api.func") 2>/dev/null || true
|
|
declare -f init_tool_telemetry &>/dev/null && init_tool_telemetry "filebrowser-quantum" "addon"
|
|
|
|
# Enable error handling
|
|
set -Eeuo pipefail
|
|
catch_errors
|
|
|
|
# Initialize all core functions (colors, formatting, icons, STD mode)
|
|
load_functions
|
|
|
|
header_info
|
|
get_lxc_ip
|
|
IP="$LOCAL_IP"
|
|
|
|
# Proxmox Host Warning
|
|
if [[ -d "/etc/pve" ]]; then
|
|
msg_warn "Running this addon directly on the Proxmox host is not recommended!"
|
|
msg_warn "Only the boot disk will be visible — passthrough drives will not be indexed."
|
|
msg_warn "This causes incorrect disk usage stats and incomplete file browsing."
|
|
msg_warn "Run this addon inside an LXC or VM instead and mount your drives there."
|
|
echo ""
|
|
echo -n "${TAB}Continue anyway on the Proxmox host? (y/N): "
|
|
read -r host_confirm
|
|
if [[ ! "${host_confirm,,}" =~ ^(y|yes)$ ]]; then
|
|
msg_error "Aborted."
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# OS Detection
|
|
if [[ -f "/etc/alpine-release" ]]; then
|
|
OS="Alpine"
|
|
SERVICE_PATH="/etc/init.d/filebrowser"
|
|
PKG_MANAGER="apk add --no-cache"
|
|
elif [[ -f "/etc/debian_version" ]]; then
|
|
OS="Debian"
|
|
SERVICE_PATH="/etc/systemd/system/filebrowser.service"
|
|
PKG_MANAGER="apt install -y"
|
|
else
|
|
msg_error "Unsupported OS detected. Exiting."
|
|
exit 238
|
|
fi
|
|
|
|
# Detect legacy FileBrowser installation
|
|
LEGACY_DB="/usr/local/community-scripts/filebrowser.db"
|
|
LEGACY_BIN="/usr/local/bin/filebrowser"
|
|
LEGACY_SERVICE_DEB="/etc/systemd/system/filebrowser.service"
|
|
LEGACY_SERVICE_ALP="/etc/init.d/filebrowser"
|
|
|
|
if [[ -f "$LEGACY_DB" || -f "$LEGACY_BIN" && ! -f "$CONFIG_PATH" ]]; then
|
|
msg_warn "Detected legacy FileBrowser installation."
|
|
echo -n "${TAB}Uninstall legacy FileBrowser and continue with Quantum install? (y/n): "
|
|
read -r remove_legacy
|
|
if [[ "${remove_legacy,,}" =~ ^(y|yes)$ ]]; then
|
|
msg_info "Uninstalling legacy FileBrowser"
|
|
if [[ -f "$LEGACY_SERVICE_DEB" ]]; then
|
|
systemctl disable --now filebrowser.service &>/dev/null || true
|
|
rm -f "$LEGACY_SERVICE_DEB"
|
|
elif [[ -f "$LEGACY_SERVICE_ALP" ]]; then
|
|
rc-service filebrowser stop &>/dev/null || true
|
|
rc-update del filebrowser &>/dev/null || true
|
|
rm -f "$LEGACY_SERVICE_ALP"
|
|
fi
|
|
rm -f "$LEGACY_BIN" "$LEGACY_DB"
|
|
msg_ok "Legacy FileBrowser removed"
|
|
else
|
|
msg_error "Installation aborted by user."
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# v2 rejects v1 config keys and imports the BoltDB on its first start
|
|
migrate_v1_config() {
|
|
local dir="/usr/local/community-scripts" db=0
|
|
[[ -s "$dir/database.db" && ! -s "$dir/filebrowser.sqlite" ]] && db=1
|
|
((db)) || grep -qE 'conditionals:|indexingIntervalMinutes:' "$CONFIG_PATH" || return 0
|
|
cp "$CONFIG_PATH" "${CONFIG_PATH}.v1.bak"
|
|
((db)) && mv "$dir/database.db" "$dir/database.db.old"
|
|
awk -v db="$db" '
|
|
{ match($0, /^ */); ind = RLENGTH }
|
|
/^[^ #]/ { top = $1 }
|
|
cond && ind > ci { print substr($0, 3); next }
|
|
{ cond = 0 }
|
|
/^[[:space:]]*conditionals:[[:space:]]*$/ { cond = 1; ci = ind; next }
|
|
/^[[:space:]]*indexingIntervalMinutes:/ { next }
|
|
top == "server:" && /^ port:/ { port = $2; next }
|
|
{ print }
|
|
/^server:/ && db { print " database:"; print " migrateFrom: \"database.db.old\"" }
|
|
END { if (port != "") { print "http:"; print " port: " port } }
|
|
' "${CONFIG_PATH}.v1.bak" >"$CONFIG_PATH"
|
|
}
|
|
|
|
# Existing installation
|
|
if [[ -f "$INSTALL_PATH" ]]; then
|
|
msg_warn "${APP} is already installed."
|
|
echo -n "${TAB}Uninstall ${APP}? (y/N): "
|
|
read -r uninstall_prompt
|
|
if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then
|
|
msg_info "Uninstalling ${APP}"
|
|
if [[ "$OS" == "Debian" ]]; then
|
|
systemctl disable --now filebrowser.service &>/dev/null || true
|
|
rm -f "$SERVICE_PATH"
|
|
else
|
|
rc-service filebrowser stop &>/dev/null || true
|
|
rc-update del filebrowser &>/dev/null || true
|
|
rm -f "$SERVICE_PATH"
|
|
fi
|
|
rm -f "$INSTALL_PATH" "$CONFIG_PATH" "$HOME/.filebrowser-quantum"
|
|
msg_ok "${APP} has been uninstalled."
|
|
exit 0
|
|
fi
|
|
|
|
echo -n "${TAB}Update ${APP}? (y/N): "
|
|
read -r update_prompt
|
|
if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then
|
|
# Older installs kept the public password in the config, which resets it on every start
|
|
if grep -qE '^\s*adminPassword:\s*"?community-scripts\.org"?\s*$' "$CONFIG_PATH" 2>/dev/null; then
|
|
msg_info "Replacing the default admin password"
|
|
ADMIN_PASS=$(random_password 20)
|
|
sed -i -E "s|^(\s*adminPassword:).*|\1 ${ADMIN_PASS}|" "$CONFIG_PATH"
|
|
chmod 600 "$CONFIG_PATH"
|
|
if [[ "$OS" == "Debian" ]]; then systemctl restart filebrowser.service; else rc-service filebrowser restart &>/dev/null; fi
|
|
fq_port=$(awk '/^http:/{h=1} h && /^ port:/{print $2; exit}' "$CONFIG_PATH")
|
|
for _ in {1..30}; do
|
|
[[ "$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:${fq_port:-8080}/health")" == "200" ]] && break
|
|
sleep 1
|
|
done
|
|
sed -i '/^\s*adminPassword:/d' "$CONFIG_PATH"
|
|
echo -e "FileBrowser Quantum\nUsername: admin\nPassword: ${ADMIN_PASS}" >~/filebrowser-quantum.creds
|
|
chmod 600 ~/filebrowser-quantum.creds
|
|
msg_ok "Replaced the default admin password (saved in ~/filebrowser-quantum.creds)"
|
|
fi
|
|
if check_for_gh_release "filebrowser-quantum" "gtsteffaniak/filebrowser"; then
|
|
msg_info "Updating ${APP}"
|
|
if ! command -v curl &>/dev/null; then $STD $PKG_MANAGER curl; fi
|
|
fetch_and_deploy_gh_release "filebrowser-quantum" "gtsteffaniak/filebrowser" "singlefile" "latest" "/usr/local/bin" "linux-$(arch_resolve)-filebrowser"
|
|
mv -f /usr/local/bin/filebrowser-quantum "$INSTALL_PATH"
|
|
if [[ -f "$CONFIG_PATH" ]]; then
|
|
sed -i '/^\s*disableIndexing:/d' "$CONFIG_PATH"
|
|
migrate_v1_config
|
|
fi
|
|
if [[ "$OS" == "Debian" ]]; then
|
|
systemctl restart filebrowser.service
|
|
else
|
|
rc-service filebrowser restart
|
|
fi
|
|
msg_ok "Updated ${APP}"
|
|
fi
|
|
exit 0
|
|
else
|
|
msg_warn "Update skipped. Exiting."
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
msg_warn "${APP} is not installed."
|
|
echo -n "${TAB}Enter port number (Default: ${DEFAULT_PORT}): "
|
|
read -r PORT
|
|
PORT=${PORT:-$DEFAULT_PORT}
|
|
|
|
echo -n "${TAB}Install ${APP}? (y/n): "
|
|
read -r install_prompt
|
|
if ! [[ "${install_prompt,,}" =~ ^(y|yes)$ ]]; then
|
|
msg_warn "Installation skipped. Exiting."
|
|
exit 0
|
|
fi
|
|
|
|
msg_info "Installing ${APP} on ${OS}"
|
|
$STD $PKG_MANAGER \
|
|
curl \
|
|
ffmpeg
|
|
fetch_and_deploy_gh_release "filebrowser-quantum" "gtsteffaniak/filebrowser" "singlefile" "latest" "/usr/local/bin" "linux-$(arch_resolve)-filebrowser"
|
|
mv -f /usr/local/bin/filebrowser-quantum "$INSTALL_PATH"
|
|
msg_ok "Installed ${APP}"
|
|
|
|
msg_info "Preparing configuration directory"
|
|
mkdir -p /usr/local/community-scripts
|
|
chown root:root /usr/local/community-scripts
|
|
chmod 755 /usr/local/community-scripts
|
|
msg_ok "Directory prepared"
|
|
|
|
ADMIN_PASS=$(random_password 20)
|
|
msg_warn "FileBrowser runs as root and shows the whole filesystem. Without authentication, anyone who reaches port ${PORT} can read and change every file."
|
|
echo -n "${TAB}Use No Authentication? (y/N): "
|
|
read -r noauth_prompt
|
|
|
|
# === YAML CONFIG GENERATION ===
|
|
if [[ "${noauth_prompt,,}" =~ ^(y|yes)$ ]]; then
|
|
cat <<EOF >"$CONFIG_PATH"
|
|
http:
|
|
port: $PORT
|
|
server:
|
|
sources:
|
|
- path: "$SRC_DIR"
|
|
name: "RootFS"
|
|
config:
|
|
denyByDefault: false
|
|
rules:
|
|
- neverWatchPath: "/proc"
|
|
- neverWatchPath: "/sys"
|
|
- neverWatchPath: "/dev"
|
|
- neverWatchPath: "/run"
|
|
- neverWatchPath: "/tmp"
|
|
- neverWatchPath: "/lost+found"
|
|
auth:
|
|
methods:
|
|
noauth: true
|
|
EOF
|
|
msg_ok "Configured with no authentication"
|
|
else
|
|
cat <<EOF >"$CONFIG_PATH"
|
|
http:
|
|
port: $PORT
|
|
server:
|
|
sources:
|
|
- path: "$SRC_DIR"
|
|
name: "RootFS"
|
|
config:
|
|
denyByDefault: false
|
|
rules:
|
|
- neverWatchPath: "/proc"
|
|
- neverWatchPath: "/sys"
|
|
- neverWatchPath: "/dev"
|
|
- neverWatchPath: "/run"
|
|
- neverWatchPath: "/tmp"
|
|
- neverWatchPath: "/lost+found"
|
|
auth:
|
|
adminUsername: admin
|
|
adminPassword: ${ADMIN_PASS}
|
|
EOF
|
|
chmod 600 "$CONFIG_PATH"
|
|
echo -e "FileBrowser Quantum\nUsername: admin\nPassword: ${ADMIN_PASS}" >~/filebrowser-quantum.creds
|
|
chmod 600 ~/filebrowser-quantum.creds
|
|
msg_ok "Configured admin (credentials saved in ~/filebrowser-quantum.creds)"
|
|
fi
|
|
|
|
msg_info "Creating service"
|
|
if [[ "$OS" == "Debian" ]]; then
|
|
cat <<EOF >"$SERVICE_PATH"
|
|
[Unit]
|
|
Description=FileBrowser Quantum
|
|
After=network.target
|
|
|
|
[Service]
|
|
User=root
|
|
WorkingDirectory=/usr/local/community-scripts
|
|
ExecStart=/usr/local/bin/filebrowser -c $CONFIG_PATH
|
|
Restart=always
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
systemctl enable --now filebrowser &>/dev/null
|
|
else
|
|
cat <<EOF >"$SERVICE_PATH"
|
|
#!/sbin/openrc-run
|
|
|
|
command="/usr/local/bin/filebrowser"
|
|
command_args="-c $CONFIG_PATH"
|
|
command_background=true
|
|
directory="/usr/local/community-scripts"
|
|
pidfile="/usr/local/community-scripts/pidfile"
|
|
|
|
depend() {
|
|
need net
|
|
}
|
|
EOF
|
|
chmod +x "$SERVICE_PATH"
|
|
$STD rc-update add filebrowser default
|
|
$STD rc-service filebrowser start
|
|
fi
|
|
|
|
msg_ok "Service created successfully"
|
|
|
|
# auth.adminPassword resets the password on every start, so it stays only until the admin exists
|
|
if grep -q '^\s*adminPassword:' "$CONFIG_PATH"; then
|
|
for _ in {1..30}; do
|
|
[[ "$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:${PORT}/health")" == "200" ]] && break
|
|
sleep 1
|
|
done
|
|
sed -i '/^\s*adminPassword:/d' "$CONFIG_PATH"
|
|
fi
|
|
msg_ok "${APP} is reachable at: ${BL}http://${IP}:${PORT}${CL}"
|