212 lines
6.7 KiB
Bash
Executable file
212 lines
6.7 KiB
Bash
Executable file
#!/bin/bash
|
|
# NextNVR — Deploy & Update Script
|
|
# Usage: curl -sSLO https://git.lohmar.co.uk/cclohmar/NextNVR/raw/branch/main/deploy_nvr.sh
|
|
# chmod +x deploy_nvr.sh && ./deploy_nvr.sh
|
|
#
|
|
# Greenfield: clones repo, prompts for storage, builds config, installs service.
|
|
# Update: kills old instance, pulls latest, rebuilds, restarts (config preserved).
|
|
set -e
|
|
|
|
GIT_URL="https://git.lohmar.co.uk/cclohmar/NextNVR.git"
|
|
DEPLOY_DIR="/opt/nextnvr"
|
|
SERVICE_NAME="nextnvr"
|
|
GO_VERSION="1.24.5"
|
|
GO_ARCH="amd64"
|
|
GO2RTC_VER="1.9.14"
|
|
|
|
[ "$(uname -m)" = "aarch64" ] && GO_ARCH="arm64"
|
|
|
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
|
log() { echo -e "${GREEN}[NextNVR]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[NextNVR]${NC} $1"; }
|
|
err() { echo -e "${RED}[NextNVR]${NC} $1"; }
|
|
info() { echo -e "${CYAN}[NextNVR]${NC} $1"; }
|
|
|
|
echo ""
|
|
echo "============================================"
|
|
echo " NextNVR — Deploy / Update"
|
|
echo " $(uname -m) / $(uname -s)"
|
|
echo "============================================"
|
|
echo ""
|
|
|
|
# ── 0. Check sudo ──
|
|
if ! command -v sudo &>/dev/null; then
|
|
if [ "$(id -u)" -eq 0 ]; then
|
|
sudo() { "$@"; }
|
|
else
|
|
err "sudo is required. Install it first or run as root."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# ── 1. Kill all existing instances ──
|
|
log "Stopping any running NextNVR instances..."
|
|
sudo systemctl stop "${SERVICE_NAME}" 2>/dev/null || true
|
|
sudo pkill -9 -f "nextnvr" 2>/dev/null || true
|
|
sudo pkill -9 -f "go2rtc" 2>/dev/null || true
|
|
sudo pkill -9 -f "ffmpeg.*rec_" 2>/dev/null || true
|
|
sleep 2
|
|
|
|
if pgrep -f "nextnvr" > /dev/null 2>&1; then
|
|
err "Cannot kill existing NextNVR processes. Please stop them manually and retry."
|
|
exit 1
|
|
fi
|
|
log "All instances stopped."
|
|
|
|
# ── 2. Detect greenfield vs update ──
|
|
GREENFIELD=false
|
|
if [ ! -d "${DEPLOY_DIR}" ]; then
|
|
GREENFIELD=true
|
|
log "Greenfield deployment detected."
|
|
else
|
|
log "Existing installation detected — update mode (config preserved)."
|
|
fi
|
|
|
|
# ── 3. System dependencies ──
|
|
detect_pkg_manager() {
|
|
if command -v apt-get &>/dev/null; then PKG="sudo apt-get install -y -qq"; DISTRO="debian"
|
|
elif command -v dnf &>/dev/null; then PKG="sudo dnf install -y -q"; DISTRO="fedora"
|
|
elif command -v yum &>/dev/null; then PKG="sudo yum install -y -q"; DISTRO="rhel"
|
|
elif command -v pacman &>/dev/null; then PKG="sudo pacman -S --noconfirm --needed"; DISTRO="arch"
|
|
elif command -v apk &>/dev/null; then PKG="sudo apk add --no-cache"; DISTRO="alpine"
|
|
else err "Unsupported distro."; exit 1; fi
|
|
log "Detected: ${DISTRO}"
|
|
}
|
|
detect_pkg_manager
|
|
|
|
if ! command -v ffmpeg &>/dev/null; then
|
|
info "Installing ffmpeg..."; eval "${PKG} ffmpeg"; log "ffmpeg installed."
|
|
fi
|
|
if ! command -v git &>/dev/null; then
|
|
info "Installing git..."; eval "${PKG} git"
|
|
fi
|
|
if ! command -v curl &>/dev/null; then
|
|
info "Installing curl..."; eval "${PKG} curl"
|
|
fi
|
|
if ! command -v jq &>/dev/null; then
|
|
info "Installing jq..."; eval "${PKG} jq"
|
|
fi
|
|
|
|
# ── 4. Time sync ──
|
|
if command -v timedatectl &>/dev/null; then
|
|
sudo timedatectl set-ntp true 2>/dev/null || true
|
|
fi
|
|
log "Time sync: OK | $(date)"
|
|
|
|
# ── 5. Go ──
|
|
GO_TAR="go${GO_VERSION}.linux-${GO_ARCH}.tar.gz"
|
|
GO_HOME="$HOME/.local/go"
|
|
if [ ! -f "${GO_HOME}/bin/go" ]; then
|
|
info "Installing Go ${GO_VERSION}..."
|
|
curl -sSL "https://go.dev/dl/${GO_TAR}" -o /tmp/${GO_TAR}
|
|
mkdir -p "${GO_HOME}"
|
|
tar -C "$HOME/.local" -xzf /tmp/${GO_TAR}
|
|
rm /tmp/${GO_TAR}
|
|
fi
|
|
export PATH="${GO_HOME}/bin:$PATH"
|
|
export GOPATH="$HOME/go"
|
|
log "Go: $(${GO_HOME}/bin/go version)"
|
|
|
|
# ── 6. Clone / pull ──
|
|
if $GREENFIELD; then
|
|
sudo mkdir -p "${DEPLOY_DIR}"
|
|
sudo chown "$(whoami):$(id -gn)" "${DEPLOY_DIR}"
|
|
git clone "${GIT_URL}" "${DEPLOY_DIR}"
|
|
log "Repository cloned."
|
|
else
|
|
git -C "${DEPLOY_DIR}" pull origin main
|
|
log "Repository updated."
|
|
fi
|
|
cd "${DEPLOY_DIR}"
|
|
|
|
# ── 7. go2rtc ──
|
|
GO2RTC_BIN="${DEPLOY_DIR}/go2rtc"
|
|
if [ ! -f "${GO2RTC_BIN}" ]; then
|
|
info "Downloading go2rtc ${GO2RTC_VER}..."
|
|
curl -sSL "https://github.com/AlexxIT/go2rtc/releases/download/v${GO2RTC_VER}/go2rtc_linux_${GO_ARCH}" -o "${GO2RTC_BIN}"
|
|
chmod +x "${GO2RTC_BIN}"
|
|
log "go2rtc installed."
|
|
fi
|
|
|
|
# ── 8. Configuration (greenfield only) ──
|
|
CONFIG_FILE="${DEPLOY_DIR}/config.json"
|
|
if $GREENFIELD; then
|
|
echo ""
|
|
info "Where should recordings be stored?"
|
|
info " Examples: /mnt/recordings, /home/user/nvr_data, /media/usb"
|
|
if [ -e /dev/tty ]; then
|
|
read -p " Storage path [${HOME}/nvr_data]: " STORAGE_PATH </dev/tty
|
|
fi
|
|
STORAGE_PATH="${STORAGE_PATH:-${HOME}/nvr_data}"
|
|
STORAGE_PATH="${STORAGE_PATH%/}"
|
|
echo ""
|
|
|
|
# Build config.json from sample.
|
|
jq --arg path "${STORAGE_PATH}" \
|
|
--arg go2rtc "${GO2RTC_BIN}" \
|
|
'.storage.recordings_path = $path | .go2rtc.binary = $go2rtc' \
|
|
config.json.sample > "${CONFIG_FILE}"
|
|
|
|
sudo mkdir -p "${STORAGE_PATH}"
|
|
sudo chown "$(whoami):$(id -gn)" "${STORAGE_PATH}"
|
|
log "Config created: ${CONFIG_FILE}"
|
|
log "Storage path: ${STORAGE_PATH}"
|
|
else
|
|
log "Config preserved: ${CONFIG_FILE}"
|
|
fi
|
|
|
|
# ── 9. Build ──
|
|
log "Building NextNVR..."
|
|
go mod tidy 2>/dev/null || true
|
|
VERSION=$(cat VERSION 2>/dev/null || echo "0.0.0")
|
|
BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
CGO_ENABLED=0 go build -ldflags="-s -w -X main.Version=${VERSION} -X main.BuildTime=${BUILD_TIME}" -o nextnvr .
|
|
chmod +x nextnvr
|
|
log "Binary: nextnvr v${VERSION} ($(du -h nextnvr | cut -f1))"
|
|
|
|
# ── 10. Systemd service ──
|
|
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
|
sudo tee "${SERVICE_FILE}" > /dev/null <<EOF
|
|
[Unit]
|
|
Description=NextNVR — IP Camera Recorder
|
|
After=network.target
|
|
Wants=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=$(whoami)
|
|
WorkingDirectory=${DEPLOY_DIR}
|
|
ExecStart=${DEPLOY_DIR}/nextnvr --config ${CONFIG_FILE}
|
|
Restart=always
|
|
RestartSec=10
|
|
LimitNOFILE=65536
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable "${SERVICE_NAME}"
|
|
sudo systemctl restart "${SERVICE_NAME}"
|
|
sleep 2
|
|
|
|
# ── Done ──
|
|
echo ""
|
|
echo "============================================"
|
|
echo -e " ${GREEN}NextNVR deployment complete!${NC}"
|
|
echo "============================================"
|
|
echo ""
|
|
info " Web UI: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'localhost'):8080"
|
|
info " Config: ${CONFIG_FILE}"
|
|
info " Storage: $(jq -r .storage.recordings_path ${CONFIG_FILE} 2>/dev/null || echo 'unknown')"
|
|
echo ""
|
|
echo " Service:"
|
|
echo " sudo systemctl status ${SERVICE_NAME}"
|
|
echo " sudo systemctl restart ${SERVICE_NAME}"
|
|
echo " sudo journalctl -u ${SERVICE_NAME} -f"
|
|
echo ""
|
|
|
|
sudo systemctl status "${SERVICE_NAME}" --no-pager --lines=5 2>/dev/null || true
|
|
echo ""
|