feat(install): native bare-metal install path, no Docker
Adds scripts/install-baremetal.sh: installs Homelable natively on a Debian/Ubuntu host — Python venv plus a homelable systemd unit for the backend on 127.0.0.1:8000, the built frontend served by nginx on :3000. Modeled on scripts/lxc-mcp-install.sh, with the install steps taken from the community-scripts/ProxmoxVE recipe so the two stay recognisably the same install. The script clones into INSTALL_DIR when empty, creates the service user, builds the venv and the frontend, generates backend/.env with a random SECRET_KEY and a bcrypt hash, writes the systemd unit and the nginx site, then waits on /api/v1/health. Re-running is safe and is the upgrade path: an existing .env is kept, everything else is rebuilt. Every prompt has an environment-variable override, so a non-interactive install is one line. Two details worth calling out: - JSON values in the generated .env are single-quoted. systemd's EnvironmentFile parser strips bare double quotes, which would hand pydantic [http://...] instead of ["http://..."] and fail startup. - The admin password reaches Python through the environment rather than argv, which is world-readable in ps. The unit runs unprivileged, so nmap falls back to a TCP connect scan; AmbientCapabilities=CAP_NET_RAW is shipped commented out with the trade-off spelled out in the unit and in the docs. INSTALLATION.md gains a "Bare metal — no Docker" section covering the quick start, the upgrade, the option table, the non-root scan trade-off and the host-nginx blocks for anyone bringing their own reverse proxy. README links it from the install line. No test suite: shell installers have none in this repo, and CI's lint-scripts job shellchecks scripts/. Shellcheck is clean. Closes #333 ha-relevant: no
This commit is contained in:
committed by
Pouzor - Rémy Jardient
parent
39cad9efc7
commit
9998b3784f
+146
@@ -89,6 +89,152 @@ bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/Proxmo
|
||||
|
||||
---
|
||||
|
||||
## Bare metal — no Docker
|
||||
|
||||
`scripts/install-baremetal.sh` installs Homelable natively on a Debian 12+ /
|
||||
Ubuntu 22.04+ host (physical, VM or LXC): a Python venv and a `homelable`
|
||||
systemd unit for the backend on `127.0.0.1:8000`, the built frontend served by
|
||||
nginx on port 3000.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Pouzor/homelable.git /opt/homelable
|
||||
sudo bash /opt/homelable/scripts/install-baremetal.sh
|
||||
```
|
||||
|
||||
The script can also clone for you — run it from anywhere and it fetches the repo
|
||||
into `INSTALL_DIR` if that directory is empty:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/scripts/install-baremetal.sh \
|
||||
| sudo bash
|
||||
```
|
||||
|
||||
It prompts for the admin password and the CIDR range to scan, then writes
|
||||
`backend/.env` with a generated `SECRET_KEY` and bcrypt hash. Open
|
||||
**http://\<host-ip\>:3000**.
|
||||
|
||||
Re-running is safe and is how you upgrade — an existing `backend/.env` is kept
|
||||
untouched, everything else is rebuilt:
|
||||
|
||||
```bash
|
||||
cd /opt/homelable && git pull
|
||||
sudo bash scripts/install-baremetal.sh
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
Every setting is an environment variable, so a non-interactive install is one line:
|
||||
|
||||
```bash
|
||||
sudo HTTP_PORT=8080 ADMIN_PASSWORD=hunter2 SCANNER_RANGES='["10.0.0.0/24"]' \
|
||||
bash scripts/install-baremetal.sh
|
||||
```
|
||||
|
||||
| Variable | Default | What |
|
||||
|---|---|---|
|
||||
| `INSTALL_DIR` | `/opt/homelable` | Repo root |
|
||||
| `REPO_URL` / `REPO_REF` | upstream / `main` | Used only when `INSTALL_DIR` is empty |
|
||||
| `SERVICE_USER` | `homelable` | systemd `User=` |
|
||||
| `BACKEND_PORT` | `8000` | uvicorn port, bound to loopback |
|
||||
| `HTTP_PORT` | `3000` | nginx port |
|
||||
| `SERVER_NAME` | `_` | nginx `server_name` |
|
||||
| `ADMIN_PASSWORD` | prompt (`admin`) | Initial password for user `admin` |
|
||||
| `SCANNER_RANGES` | prompt (guessed) | JSON array of CIDRs |
|
||||
| `SKIP_NGINX=1` | off | Do not install or touch nginx |
|
||||
|
||||
### Afterwards
|
||||
|
||||
```bash
|
||||
systemctl status homelable
|
||||
journalctl -u homelable -f
|
||||
```
|
||||
|
||||
- Config: `/opt/homelable/backend/.env` — every other option (OIDC, MCP,
|
||||
Proxmox, Zigbee, Z-Wave, live view) is documented in `.env.example`.
|
||||
`systemctl restart homelable` after an edit.
|
||||
- Data: `/opt/homelable/data` — SQLite DB and uploads. Back up this folder.
|
||||
- nginx site: `/etc/nginx/sites-available/homelable`.
|
||||
|
||||
Change the password later:
|
||||
|
||||
```bash
|
||||
/opt/homelable/backend/.venv/bin/python -c \
|
||||
'import bcrypt; print(bcrypt.hashpw(b"newpassword", bcrypt.gensalt()).decode())'
|
||||
# put it in backend/.env as AUTH_PASSWORD_HASH='$2b$12$...' (keep the single quotes)
|
||||
systemctl restart homelable
|
||||
```
|
||||
|
||||
⚠️ Keep JSON values in `backend/.env` single-quoted — `CORS_ORIGINS='["http://…"]'`.
|
||||
systemd's `EnvironmentFile` parser strips bare double quotes, which breaks the
|
||||
JSON before the backend parses it. This does not apply to the Docker install.
|
||||
|
||||
### Scanning as a non-root service
|
||||
|
||||
The unit runs as `homelable`, not root, so nmap has no raw sockets and silently
|
||||
falls back to a TCP connect scan: hosts and open ports are still found, OS
|
||||
detection (`-O`) and SYN scan (`-sS`) are not. To grant them, uncomment in
|
||||
`/etc/systemd/system/homelable.service`:
|
||||
|
||||
```ini
|
||||
AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN
|
||||
CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN
|
||||
```
|
||||
|
||||
then `systemctl daemon-reload && systemctl restart homelable`.
|
||||
|
||||
### Your own reverse proxy
|
||||
|
||||
With `SKIP_NGINX=1` the script leaves the front end to you: serve
|
||||
`/opt/homelable/frontend/dist` as a static SPA and proxy the API to the backend.
|
||||
The nginx translation of `docker/nginx.conf` — what the script writes, with
|
||||
`backend:8000` replaced by `127.0.0.1:8000`:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 3000;
|
||||
server_name _;
|
||||
root /opt/homelable/frontend/dist;
|
||||
index index.html;
|
||||
|
||||
client_max_body_size 20M;
|
||||
|
||||
# WebSocket — must come before /api/ to take priority
|
||||
location /api/v1/status/ws/ {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# Legacy /ws/ path
|
||||
location /ws/ {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Terminating TLS in front of it means adding your own hostname to
|
||||
`CORS_ORIGINS` in `backend/.env` (`https://homelable.example`) and restarting
|
||||
the service.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
All configuration is done via `.env` (copied from `.env.example`):
|
||||
|
||||
@@ -65,7 +65,7 @@ Every feature, with how to turn it on and use it, is described in **[FEATURES.md
|
||||
|
||||
## Installation
|
||||
|
||||
Docker (from the **[pre-built GHCR images](./INSTALLATION.md#pre-built-docker-images)**), Proxmox LXC, build from source, configuration, and development setup are all covered in **[INSTALLATION.md](./INSTALLATION.md)**.
|
||||
Docker (from the **[pre-built GHCR images](./INSTALLATION.md#pre-built-docker-images)**), Proxmox LXC, **[bare metal without Docker](./INSTALLATION.md#bare-metal--no-docker)** (`sudo bash scripts/install-baremetal.sh` — systemd unit plus nginx on a Debian/Ubuntu host), build from source, configuration, and development setup are all covered in **[INSTALLATION.md](./INSTALLATION.md)**.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Executable
+300
@@ -0,0 +1,300 @@
|
||||
#!/bin/bash
|
||||
# Install Homelable (backend + frontend) natively on a Debian/Ubuntu host — no Docker.
|
||||
#
|
||||
# Run interactively as root on any Debian 12+/Ubuntu 22.04+ host, VM or LXC.
|
||||
# Installs:
|
||||
# - a Python venv + systemd unit for the FastAPI backend on 127.0.0.1:8000
|
||||
# - the built Vite frontend, served by nginx on $HTTP_PORT with /api and /ws
|
||||
# proxied to the backend
|
||||
#
|
||||
# Idempotent: re-running is safe. An existing backend/.env is kept untouched;
|
||||
# only the venv, the frontend build, the systemd unit and the nginx site are
|
||||
# refreshed. Use it to upgrade: pull (or re-clone) then re-run.
|
||||
#
|
||||
# Optional env vars (override defaults / skip the matching prompt):
|
||||
# INSTALL_DIR repo root (default: /opt/homelable)
|
||||
# REPO_URL clone URL if $INSTALL_DIR is empty (default: https://github.com/Pouzor/homelable.git)
|
||||
# REPO_REF branch/tag/commit when cloning (default: main)
|
||||
# SERVICE_USER systemd User= (default: homelable)
|
||||
# BACKEND_PORT uvicorn listen port, loopback only (default: 8000)
|
||||
# HTTP_PORT nginx listen port (default: 3000)
|
||||
# SERVER_NAME nginx server_name (default: _)
|
||||
# ADMIN_PASSWORD initial admin password (default: prompt, "admin" on empty)
|
||||
# SCANNER_RANGES JSON array of CIDRs to scan (default: prompt, guessed from
|
||||
# the primary interface)
|
||||
# SKIP_NGINX=1 do not install or touch nginx (bring your own reverse proxy)
|
||||
set -euo pipefail
|
||||
|
||||
INSTALL_DIR="${INSTALL_DIR:-/opt/homelable}"
|
||||
REPO_URL="${REPO_URL:-https://github.com/Pouzor/homelable.git}"
|
||||
REPO_REF="${REPO_REF:-main}"
|
||||
SERVICE_USER="${SERVICE_USER:-homelable}"
|
||||
SERVICE_NAME="homelable"
|
||||
BACKEND_PORT="${BACKEND_PORT:-8000}"
|
||||
HTTP_PORT="${HTTP_PORT:-3000}"
|
||||
SERVER_NAME="${SERVER_NAME:-_}"
|
||||
SKIP_NGINX="${SKIP_NGINX:-0}"
|
||||
NODE_MAJOR=20
|
||||
|
||||
log() { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[1;33m!!\033[0m %s\n' "$*" >&2; }
|
||||
fail() { printf '\033[1;31mxx\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
[[ $EUID -eq 0 ]] || fail "Run as root (sudo bash $0)."
|
||||
command -v apt-get >/dev/null || fail "This script targets Debian/Ubuntu (apt-get not found)."
|
||||
|
||||
log "Installing OS dependencies"
|
||||
apt-get update -qq
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
||||
git curl ca-certificates python3 python3-venv python3-pip \
|
||||
build-essential nmap iputils-ping iproute2 openssl >/dev/null
|
||||
|
||||
if [[ "$SKIP_NGINX" != "1" ]]; then
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq nginx >/dev/null
|
||||
fi
|
||||
|
||||
# Node 20+ — Debian 12 ships 18, too old for Vite 7 / React 19.
|
||||
node_major="$(node --version 2>/dev/null | sed 's/^v\([0-9]*\).*/\1/')"
|
||||
if [[ -z "$node_major" || "$node_major" -lt "$NODE_MAJOR" ]]; then
|
||||
log "Installing Node.js $NODE_MAJOR (found: ${node_major:-none})"
|
||||
curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash - >/dev/null
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq nodejs >/dev/null
|
||||
fi
|
||||
|
||||
BACKEND_DIR="$INSTALL_DIR/backend"
|
||||
FRONTEND_DIR="$INSTALL_DIR/frontend"
|
||||
|
||||
if [[ ! -d "$BACKEND_DIR" ]]; then
|
||||
log "Cloning $REPO_URL ($REPO_REF) → $INSTALL_DIR"
|
||||
mkdir -p "$(dirname "$INSTALL_DIR")"
|
||||
git clone --depth 1 --branch "$REPO_REF" "$REPO_URL" "$INSTALL_DIR"
|
||||
fi
|
||||
[[ -f "$BACKEND_DIR/requirements.txt" ]] || fail "Missing $BACKEND_DIR/requirements.txt — repo layout unexpected."
|
||||
[[ -f "$FRONTEND_DIR/package.json" ]] || fail "Missing $FRONTEND_DIR/package.json — repo layout unexpected."
|
||||
|
||||
for port in "$BACKEND_PORT" "$HTTP_PORT"; do
|
||||
if ss -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]${port}$"; then
|
||||
warn "Port $port already in use. Fine if it's a previous Homelable instance; otherwise abort and free it."
|
||||
fi
|
||||
done
|
||||
|
||||
if ! id -u "$SERVICE_USER" >/dev/null 2>&1; then
|
||||
log "Creating service user '$SERVICE_USER'"
|
||||
useradd --system --home "$INSTALL_DIR" --shell /usr/sbin/nologin "$SERVICE_USER"
|
||||
fi
|
||||
|
||||
DATA_DIR="$INSTALL_DIR/data"
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
VENV="$BACKEND_DIR/.venv"
|
||||
if [[ ! -d "$VENV" ]]; then
|
||||
log "Creating venv at $VENV"
|
||||
python3 -m venv "$VENV"
|
||||
fi
|
||||
log "Installing Python deps (this takes a few minutes)"
|
||||
"$VENV/bin/pip" install --quiet --upgrade pip
|
||||
"$VENV/bin/pip" install --quiet -r "$BACKEND_DIR/requirements.txt"
|
||||
|
||||
ENV_FILE="$BACKEND_DIR/.env"
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
log ".env already present at $ENV_FILE — keeping existing values"
|
||||
else
|
||||
[[ -f "$INSTALL_DIR/.env.example" ]] || fail "Missing $INSTALL_DIR/.env.example"
|
||||
log "No .env found — generating one (press Enter to accept defaults)"
|
||||
|
||||
admin_password="${ADMIN_PASSWORD:-}"
|
||||
if [[ -z "$admin_password" ]]; then
|
||||
read -rsp "Initial admin password [default: admin]: " admin_password; echo
|
||||
admin_password="${admin_password:-admin}"
|
||||
fi
|
||||
|
||||
scanner_ranges="${SCANNER_RANGES:-}"
|
||||
if [[ -z "$scanner_ranges" ]]; then
|
||||
guess="$(ip -o -f inet addr show scope global 2>/dev/null \
|
||||
| awk '{print $4}' | head -n1 \
|
||||
| awk -F/ '{split($1,o,"."); print o[1]"."o[2]"."o[3]".0/"$2}')"
|
||||
guess="${guess:-192.168.1.0/24}"
|
||||
read -rp "CIDR range to scan [$guess]: " scanner_ranges
|
||||
scanner_ranges="[\"${scanner_ranges:-$guess}\"]"
|
||||
fi
|
||||
|
||||
secret_key="$(openssl rand -hex 32)"
|
||||
# Passed through the environment, not argv — argv is world-readable in ps.
|
||||
password_hash="$(HL_PW="$admin_password" "$VENV/bin/python" -c \
|
||||
'import os, bcrypt; print(bcrypt.hashpw(os.environ["HL_PW"].encode(), bcrypt.gensalt()).decode())')"
|
||||
|
||||
host_ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||||
origins="[\"http://localhost:${HTTP_PORT}\""
|
||||
[[ -n "$host_ip" ]] && origins="${origins},\"http://${host_ip}:${HTTP_PORT}\""
|
||||
origins="${origins}]"
|
||||
|
||||
umask 077
|
||||
cat >"$ENV_FILE" <<EOF
|
||||
# Generated by scripts/install-baremetal.sh
|
||||
# JSON values are single-quoted: systemd's EnvironmentFile parser strips
|
||||
# bare double quotes, which would break the JSON before pydantic sees it.
|
||||
SECRET_KEY=$secret_key
|
||||
SQLITE_PATH=$DATA_DIR/homelab.db
|
||||
|
||||
# Set this to the URL(s) you use to reach Homelable in your browser.
|
||||
# Behind a TLS reverse proxy, replace these with https://homelable.example.
|
||||
CORS_ORIGINS='$origins'
|
||||
|
||||
AUTH_MODE=local
|
||||
AUTH_USERNAME=admin
|
||||
AUTH_PASSWORD_HASH='$password_hash'
|
||||
|
||||
SCANNER_RANGES='$scanner_ranges'
|
||||
SCANNER_HTTP_RANGES='[]'
|
||||
SCANNER_HTTP_PROBE_ENABLED=false
|
||||
SCANNER_HTTP_VERIFY_TLS=false
|
||||
STATUS_CHECKER_INTERVAL=60
|
||||
EOF
|
||||
umask 022
|
||||
log "Wrote $ENV_FILE (mode 600)"
|
||||
warn "Every other setting (OIDC, MCP, Proxmox, Zigbee, Z-Wave, live view) is documented in .env.example."
|
||||
fi
|
||||
|
||||
log "Building the frontend"
|
||||
if [[ -f "$FRONTEND_DIR/package-lock.json" ]]; then
|
||||
( cd "$FRONTEND_DIR" && npm ci --silent )
|
||||
else
|
||||
( cd "$FRONTEND_DIR" && npm install --silent )
|
||||
fi
|
||||
( cd "$FRONTEND_DIR" && npm run build )
|
||||
[[ -d "$FRONTEND_DIR/dist" ]] || fail "Frontend build produced no $FRONTEND_DIR/dist."
|
||||
|
||||
chown -R "$SERVICE_USER":"$SERVICE_USER" "$INSTALL_DIR"
|
||||
chmod 600 "$ENV_FILE"
|
||||
# nginx (www-data) needs to traverse the tree down to dist/.
|
||||
chmod o+x "$INSTALL_DIR" "$FRONTEND_DIR"
|
||||
|
||||
UNIT="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
log "Writing $UNIT"
|
||||
cat >"$UNIT" <<EOF
|
||||
[Unit]
|
||||
Description=Homelable backend
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$SERVICE_USER
|
||||
WorkingDirectory=$BACKEND_DIR
|
||||
EnvironmentFile=$ENV_FILE
|
||||
ExecStart=$VENV/bin/uvicorn app.main:app --host 127.0.0.1 --port $BACKEND_PORT
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
# The scanner shells out to nmap. Without raw sockets nmap silently falls back
|
||||
# to a TCP connect scan: hosts and open ports are still found, but OS detection
|
||||
# (-O) and SYN scan (-sS) do not work. Uncomment to grant them.
|
||||
#AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN
|
||||
#CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now "$SERVICE_NAME"
|
||||
systemctl restart "$SERVICE_NAME"
|
||||
|
||||
if [[ "$SKIP_NGINX" != "1" ]]; then
|
||||
SITE="/etc/nginx/sites-available/homelable"
|
||||
log "Writing $SITE"
|
||||
cat >"$SITE" <<EOF
|
||||
server {
|
||||
listen $HTTP_PORT;
|
||||
server_name $SERVER_NAME;
|
||||
root $FRONTEND_DIR/dist;
|
||||
index index.html;
|
||||
|
||||
client_max_body_size 20M;
|
||||
|
||||
# WebSocket (must come before /api/ to take priority)
|
||||
location /api/v1/status/ws/ {
|
||||
proxy_pass http://127.0.0.1:$BACKEND_PORT;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:$BACKEND_PORT;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
}
|
||||
|
||||
# Legacy /ws/ path
|
||||
location /ws/ {
|
||||
proxy_pass http://127.0.0.1:$BACKEND_PORT;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host \$host;
|
||||
}
|
||||
|
||||
# SPA fallback
|
||||
location / {
|
||||
try_files \$uri \$uri/ /index.html;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
ln -sf "$SITE" /etc/nginx/sites-enabled/homelable
|
||||
if [[ "$HTTP_PORT" == "80" ]]; then rm -f /etc/nginx/sites-enabled/default; fi
|
||||
nginx -t
|
||||
systemctl reload nginx || systemctl restart nginx
|
||||
fi
|
||||
|
||||
log "Waiting for the backend on :$BACKEND_PORT"
|
||||
ok=0
|
||||
for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
|
||||
if curl -fsS "http://127.0.0.1:${BACKEND_PORT}/api/v1/health" >/dev/null 2>&1; then
|
||||
ok=1; break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if [[ "$ok" -ne 1 ]]; then
|
||||
warn "Backend did not answer /api/v1/health within 15s. Check: journalctl -u $SERVICE_NAME -n 50"
|
||||
else
|
||||
log "Backend is up."
|
||||
fi
|
||||
|
||||
HOST_IP="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||||
|
||||
cat <<EOF
|
||||
|
||||
----------------------------------------------------------------
|
||||
Homelable installed.
|
||||
|
||||
Service: $SERVICE_NAME (systemctl status $SERVICE_NAME)
|
||||
Backend: http://127.0.0.1:$BACKEND_PORT (loopback only)
|
||||
Env file: $ENV_FILE
|
||||
Data: $DATA_DIR (SQLite DB + uploads)
|
||||
Logs: journalctl -u $SERVICE_NAME -f
|
||||
EOF
|
||||
if [[ "$SKIP_NGINX" != "1" ]]; then
|
||||
cat <<EOF
|
||||
Web UI: http://${HOST_IP:-<host-ip>}:${HTTP_PORT}
|
||||
nginx site: /etc/nginx/sites-available/homelable
|
||||
EOF
|
||||
else
|
||||
cat <<EOF
|
||||
nginx: skipped — proxy your own front end to 127.0.0.1:$BACKEND_PORT
|
||||
and serve $FRONTEND_DIR/dist. See INSTALLATION.md.
|
||||
EOF
|
||||
fi
|
||||
cat <<EOF
|
||||
|
||||
Log in as 'admin' with the password you set. Change it later by regenerating
|
||||
AUTH_PASSWORD_HASH in $ENV_FILE:
|
||||
|
||||
$VENV/bin/python -c "import bcrypt; print(bcrypt.hashpw(b'newpassword', bcrypt.gensalt()).decode())"
|
||||
systemctl restart $SERVICE_NAME
|
||||
|
||||
Upgrade: git -C $INSTALL_DIR pull && bash $INSTALL_DIR/scripts/install-baremetal.sh
|
||||
----------------------------------------------------------------
|
||||
EOF
|
||||
Reference in New Issue
Block a user