From b780ac1e1f2183d8127cef91e81749485d94d414 Mon Sep 17 00:00:00 2001 From: cclohmar Date: Sat, 30 May 2026 14:23:42 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20interactive=20install.sh=20=E2=80=94?= =?UTF-8?q?=20prompts=20user=20for=20credentials,=20installs=20to=20/opt/r?= =?UTF-8?q?x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - install.sh copies binary + assets to /opt/rx/ - Prompts for SMTP, Gemini, port, URL interactively - Creates .env file with chmod 600 - Creates systemd service pointing to /opt/rx/ - Handles both pre-built and dist/ binaries --- install.sh | 212 ++++++++++++++++++++++++++--------------------------- 1 file changed, 106 insertions(+), 106 deletions(-) diff --git a/install.sh b/install.sh index 25a4b09..f5e5a13 100644 --- a/install.sh +++ b/install.sh @@ -1,129 +1,135 @@ #!/usr/bin/env bash # -# install.sh — ReceiptNext one-command installer for bare-metal servers +# install.sh — ReceiptNext installer # -# Usage: -# curl -sL https://git.lohmar.co.uk/cclohmar/ReceiptNext/releases/download/v1.0.0/install.sh | bash +# Copies the binary and assets to /opt/rx/, prompts for configuration, +# and sets up a systemd service. # -# Or from a local clone: -# ./install.sh +# Usage: sudo ./install.sh # -# What it does: -# 1. Creates receiptnext user if missing -# 2. Creates /opt/receiptnext with templates/, static/, storage/ -# 3. Installs binary to /usr/local/bin/receiptnext -# 4. Creates /etc/receiptnext/.env from .env.example (if not exists) -# 5. Installs systemd service -# 6. Starts the service -# 7. Shows status and instructions set -euo pipefail -BINARY="receiptnext" -INSTALL_DIR="/opt/${BINARY}" -BIN_DIR="/usr/local/bin" -ETC_DIR="/etc/${BINARY}" -SERVICE_FILE="contrib/${BINARY}.service" -LOG_FILE="/var/log/${BINARY}.log" +INSTALL_DIR="/opt/rx" +BIN_PATH="/opt/rx/receiptnext" +SERVICE_NAME="receiptnext" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' -NC='\033[0m' # No Color +CYAN='\033[0;36m' +NC='\033[0m' info() { echo -e "${GREEN}[✓]${NC} $1"; } warn() { echo -e "${YELLOW}[!]${NC} $1"; } -error() { echo -e "${RED}[✗]${NC} $1"; } +prompt() { echo -en "${CYAN}→${NC} $1"; } -# Check if running as root if [ "$EUID" -ne 0 ]; then - error "Please run as root: sudo ./install.sh" + echo -e "${RED}Please run as root: sudo ./install.sh${NC}" exit 1 fi echo "" echo " ╔═══════════════════════════════════════╗" -echo " ║ ReceiptNext Installer ║" -echo " ║ AI-Powered Expense Tracker ║" +echo " ║ ReceiptNext Installer ║" echo " ╚═══════════════════════════════════════╝" echo "" -# ── Step 1: Build or download binary ────────────────────────────── -if [ -f "./${BINARY}" ]; then - info "Binary found locally" -elif [ -f "go.mod" ]; then - info "Building from source..." - if ! command -v go &>/dev/null; then - error "Go is required to build from source. Install go 1.22+ and re-run." - exit 1 +# ── Detect binary ──────────────────────────────────────────────── +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +BINARY="" +for f in "$SCRIPT_DIR/receiptnext" "$SCRIPT_DIR/dist/receiptnext-linux-amd64" "$SCRIPT_DIR/dist/receiptnext-linux-arm64"; do + if [ -f "$f" ] && [ -x "$f" ]; then + BINARY="$f" + break fi - go build -ldflags="-s -w" -o "${BINARY}" . - info "Build complete" -else - # Try to download the latest release - TAG=$(curl -s https://git.lohmar.co.uk/api/v1/repos/cclohmar/ReceiptNext/releases/latest \ - | python3 -c "import json,sys; print(json.load(sys.stdin).get('tag_name','v1.0.0'))" 2>/dev/null || echo "v1.0.0") - ARCH=$(uname -m) - case "$ARCH" in - x86_64) ARCH="amd64" ;; - aarch64) ARCH="arm64" ;; - *) error "Unsupported architecture: $ARCH"; exit 1 ;; - esac - URL="https://git.lohmar.co.uk/cclohmar/ReceiptNext/releases/download/${TAG}/${BINARY}-linux-${ARCH}" - info "Downloading ${URL}..." - curl -sL -o "${BINARY}" "${URL}" - info "Download complete" +done + +if [ -z "$BINARY" ]; then + echo -e "${RED}No binary found. Build it first: go build -o receiptnext .${NC}" + exit 1 fi -chmod +x "${BINARY}" +BINARY_NAME=$(basename "$BINARY") +info "Using binary: $BINARY" -# ── Step 2: Create system user ──────────────────────────────────── -if ! id -u ${BINARY} &>/dev/null 2>&1; then - useradd -r -s /bin/false -d "${INSTALL_DIR}" ${BINARY} - info "Created system user '${BINARY}'" -fi +# ── Create install directory ───────────────────────────────────── +mkdir -p "$INSTALL_DIR"/{templates,static/css,static/icons,storage} +info "Created $INSTALL_DIR" -# ── Step 3: Create install directory ────────────────────────────── -mkdir -p "${INSTALL_DIR}"/{templates,static/css,static/icons,storage} +# ── Copy binary and assets ─────────────────────────────────────── +cp "$BINARY" "$BIN_PATH" +chmod +x "$BIN_PATH" +cp -r "$SCRIPT_DIR/templates/." "$INSTALL_DIR/templates/" +cp -r "$SCRIPT_DIR/static/." "$INSTALL_DIR/static/" +info "Copied binary + assets to $INSTALL_DIR" -# Copy templates and static assets -cp -r templates/* "${INSTALL_DIR}/templates/" -cp -r static/* "${INSTALL_DIR}/static/" -cp -r storage/. "${INSTALL_DIR}/storage/" 2>/dev/null || true +# ── Prompt for configuration ───────────────────────────────────── +echo "" +echo " ── Configuration ──" +echo " (Press Enter to skip any field)" +echo "" -# ── Step 4: Install binary ──────────────────────────────────────── -cp "${BINARY}" "${BIN_DIR}/${BINARY}" -chmod +x "${BIN_DIR}/${BINARY}" -info "Installed binary to ${BIN_DIR}/${BINARY}" +read -p " SMTP host [$SMTP_HOST]: " SMTP_HOST_IN +SMTP_HOST="${SMTP_HOST_IN:-$SMTP_HOST}" +read -p " SMTP port [${SMTP_PORT:-587}]: " SMTP_PORT_IN +SMTP_PORT="${SMTP_PORT_IN:-${SMTP_PORT:-587}}" +read -p " SMTP user [$SMTP_USER]: " SMTP_USER_IN +SMTP_USER="${SMTP_USER_IN:-$SMTP_USER}" +read -p " SMTP password [$SMTP_PASS]: " SMTP_PASS_IN +SMTP_PASS="${SMTP_PASS_IN:-$SMTP_PASS}" +read -p " Gemini API key [$GEMINI_API_KEY]: " GEMINI_IN +GEMINI_API_KEY="${GEMINI_IN:-$GEMINI_API_KEY}" +read -p " Public server URL [${BASE_URL:-http://localhost:8080}]: " BASE_URL_IN +BASE_URL="${BASE_URL_IN:-${BASE_URL:-http://localhost:8080}}" +read -p " Port for web server [${PORT:-8080}]: " PORT_IN +PORT="${PORT_IN:-${PORT:-8080}}" -# ── Step 5: Create config directory ─────────────────────────────── -mkdir -p "${ETC_DIR}" -if [ ! -f "${ETC_DIR}/.env" ]; then - if [ -f ".env.example" ]; then - cp .env.example "${ETC_DIR}/.env" - info "Created ${ETC_DIR}/.env — edit it with your credentials" - else - warn "No .env.example found — create ${ETC_DIR}/.env manually" - fi -else - info "${ETC_DIR}/.env already exists — keeping existing config" -fi +# ── Write .env ─────────────────────────────────────────────────── +cat > "$INSTALL_DIR/.env" << ENVEOF +# ReceiptNext Configuration +# Created by install.sh on $(date) -# ── Step 6: Install systemd service ─────────────────────────────── -if [ -f "${SERVICE_FILE}" ]; then - cp "${SERVICE_FILE}" /etc/systemd/system/ - systemctl daemon-reload - systemctl enable ${BINARY} - info "Systemd service installed and enabled" -fi +SMTP_HOST=${SMTP_HOST} +SMTP_PORT=${SMTP_PORT} +SMTP_USER=${SMTP_USER} +SMTP_PASS=${SMTP_PASS} +GEMINI_API_KEY=${GEMINI_API_KEY} +BASE_URL=${BASE_URL} +PORT=${PORT} +ENVEOF -# ── Step 7: Set permissions ─────────────────────────────────────── -chown -R ${BINARY}:${BINARY} "${INSTALL_DIR}" -chown ${BINARY}:${BINARY} "${ETC_DIR}/.env" 2>/dev/null || true -touch "${LOG_FILE}" && chown ${BINARY}:${BINARY} "${LOG_FILE}" +chmod 600 "$INSTALL_DIR/.env" +info "Created $INSTALL_DIR/.env" -# ── Step 8: Start service ───────────────────────────────────────── -systemctl start ${BINARY} || warn "Service failed to start — check 'systemctl status ${BINARY}'" +# ── Create systemd service ─────────────────────────────────────── +cat > "/etc/systemd/system/${SERVICE_NAME}.service" << SERVEOF +[Unit] +Description=ReceiptNext — AI-Powered Expense Tracker +Documentation=https://git.lohmar.co.uk/cclohmar/ReceiptNext +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=${INSTALL_DIR} +ExecStart=${BIN_PATH} +Restart=always +RestartSec=5 +EnvironmentFile=${INSTALL_DIR}/.env +StandardOutput=append:/var/log/receiptnext.log +StandardError=append:/var/log/receiptnext.log + +[Install] +WantedBy=multi-user.target +SERVEOF + +systemctl daemon-reload +systemctl enable "${SERVICE_NAME}" +info "Systemd service created: ${SERVICE_NAME}" + +# ── Start service ──────────────────────────────────────────────── +systemctl start "${SERVICE_NAME}" 2>/dev/null || true sleep 2 echo "" @@ -131,23 +137,17 @@ echo " ╔═══════════════════════ echo " ║ Installation Complete! ║" echo " ╚═══════════════════════════════════════╝" echo "" -echo " Config: ${ETC_DIR}/.env" -echo " Binary: ${BIN_DIR}/${BINARY}" -echo " Data: ${INSTALL_DIR}" -echo " Logs: ${LOG_FILE}" -echo " Service: systemctl status ${BINARY}" -echo " Restart: systemctl restart ${BINARY}" -echo " Log tail: journalctl -u ${BINARY} -f" +echo " Install dir: $INSTALL_DIR" +echo " Config: $INSTALL_DIR/.env" +echo " Binary: $BIN_PATH" +echo " Service: systemctl status $SERVICE_NAME" +echo " Logs: journalctl -u $SERVICE_NAME -f" echo "" -if systemctl is-active --quiet ${BINARY}; then - info "ReceiptNext is running!" - echo " Open http://YOUR_SERVER_IP:8080 in your browser" - echo "" - echo " First steps:" - echo " 1. Edit ${ETC_DIR}/.env with your SMTP + AI credentials" - echo " 2. Restart: systemctl restart ${BINARY}" - echo "" +if systemctl is-active --quiet "${SERVICE_NAME}"; then + info "ReceiptNext is running on port ${PORT}" + echo " Open http://localhost:${PORT} (or your server IP)" else - warn "Service is not running. Check: systemctl status ${BINARY}" + warn "Service did not start. Check: systemctl status ${SERVICE_NAME}" + echo " Logs: journalctl -u ${SERVICE_NAME} -n 50 --no-pager" fi