feat: script de instalacion automatizado para Debian/Ubuntu/RPi
Adds install.sh and uninstall.sh for one-command deployment of Nexus POS on Debian 12+, Ubuntu 22.04+, and Raspberry Pi OS. The installer handles PostgreSQL setup, tenant provisioning, systemd service, and nginx reverse proxy with interactive business configuration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
513
install.sh
Executable file
513
install.sh
Executable file
@@ -0,0 +1,513 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================
|
||||||
|
# Nexus Autoparts POS — Automated Installer
|
||||||
|
# Works on: Debian 12+, Ubuntu 22.04+, Raspberry Pi OS (64-bit)
|
||||||
|
# Usage: curl -s https://raw.githubusercontent.com/.../install.sh | bash
|
||||||
|
# ============================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ----- Colors & helpers -----
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
INSTALL_DIR="/opt/nexus-pos"
|
||||||
|
SERVICE_NAME="nexus-pos"
|
||||||
|
DB_USER="nexus"
|
||||||
|
DB_PASS="nexus_autoparts_2026"
|
||||||
|
DB_NAME="nexus_autoparts"
|
||||||
|
POS_PORT=5001
|
||||||
|
LOG_FILE="/var/log/nexus-pos-install.log"
|
||||||
|
|
||||||
|
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
|
||||||
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
err() { echo -e "${RED}[ERROR]${NC} $*"; }
|
||||||
|
fatal() { err "$*"; exit 1; }
|
||||||
|
|
||||||
|
banner() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${CYAN}"
|
||||||
|
echo " ========================================"
|
||||||
|
echo " Nexus Autoparts POS — Installer v1.0"
|
||||||
|
echo " ========================================"
|
||||||
|
echo -e "${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"; }
|
||||||
|
|
||||||
|
cleanup_on_error() {
|
||||||
|
err "Installation failed. Check $LOG_FILE for details."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
trap cleanup_on_error ERR
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 1. CHECK PREREQUISITES
|
||||||
|
# ============================================================
|
||||||
|
check_prerequisites() {
|
||||||
|
info "Checking prerequisites..."
|
||||||
|
|
||||||
|
# Must be Linux
|
||||||
|
if [[ "$(uname -s)" != "Linux" ]]; then
|
||||||
|
fatal "This installer only supports Linux (Debian/Ubuntu/Raspberry Pi OS)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Must be root
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
fatal "This script must be run as root. Use: sudo bash install.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check distro
|
||||||
|
if [[ -f /etc/os-release ]]; then
|
||||||
|
. /etc/os-release
|
||||||
|
info "Detected OS: ${PRETTY_NAME:-$ID}"
|
||||||
|
log "OS: ${PRETTY_NAME:-$ID}"
|
||||||
|
else
|
||||||
|
warn "Could not detect OS version. Proceeding anyway."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect Raspberry Pi
|
||||||
|
IS_RPI=false
|
||||||
|
if grep -qi "raspberry" /proc/cpuinfo 2>/dev/null || grep -qi "raspberry" /sys/firmware/devicetree/base/model 2>/dev/null; then
|
||||||
|
IS_RPI=true
|
||||||
|
info "Raspberry Pi detected."
|
||||||
|
log "Raspberry Pi detected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check architecture
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
info "Architecture: $ARCH"
|
||||||
|
if [[ "$ARCH" != "x86_64" && "$ARCH" != "aarch64" && "$ARCH" != "armv7l" ]]; then
|
||||||
|
warn "Untested architecture: $ARCH. Proceeding with caution."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check internet
|
||||||
|
if ! ping -c 1 -W 3 8.8.8.8 &>/dev/null; then
|
||||||
|
fatal "No internet connection detected. Please connect and retry."
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok "Prerequisites check passed."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 2. INSTALL SYSTEM PACKAGES
|
||||||
|
# ============================================================
|
||||||
|
install_packages() {
|
||||||
|
info "Updating package lists..."
|
||||||
|
apt-get update -qq >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
PACKAGES=(
|
||||||
|
python3
|
||||||
|
python3-pip
|
||||||
|
python3-venv
|
||||||
|
postgresql
|
||||||
|
postgresql-client
|
||||||
|
git
|
||||||
|
nginx
|
||||||
|
libpq-dev
|
||||||
|
gcc
|
||||||
|
python3-dev
|
||||||
|
curl
|
||||||
|
)
|
||||||
|
|
||||||
|
# On Raspberry Pi, add some extras for lxml
|
||||||
|
if [[ "$IS_RPI" == true ]]; then
|
||||||
|
PACKAGES+=(libxml2-dev libxslt1-dev zlib1g-dev)
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Installing system packages: ${PACKAGES[*]}"
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "${PACKAGES[@]}" >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
ok "System packages installed."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 3. CONFIGURE POSTGRESQL
|
||||||
|
# ============================================================
|
||||||
|
configure_postgresql() {
|
||||||
|
info "Configuring PostgreSQL..."
|
||||||
|
|
||||||
|
# Ensure PostgreSQL is running
|
||||||
|
systemctl enable postgresql >> "$LOG_FILE" 2>&1
|
||||||
|
systemctl start postgresql >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
# Create user if not exists
|
||||||
|
if sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${DB_USER}'" | grep -q 1; then
|
||||||
|
info "PostgreSQL user '${DB_USER}' already exists."
|
||||||
|
else
|
||||||
|
sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASS}' CREATEDB;" >> "$LOG_FILE" 2>&1
|
||||||
|
ok "PostgreSQL user '${DB_USER}' created."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create master database if not exists
|
||||||
|
if sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='${DB_NAME}'" | grep -q 1; then
|
||||||
|
info "Database '${DB_NAME}' already exists."
|
||||||
|
else
|
||||||
|
sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};" >> "$LOG_FILE" 2>&1
|
||||||
|
ok "Database '${DB_NAME}' created."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Grant CREATEDB to user (idempotent)
|
||||||
|
sudo -u postgres psql -c "ALTER USER ${DB_USER} CREATEDB;" >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
# Ensure pg_hba.conf allows md5 auth for local connections
|
||||||
|
PG_HBA=$(sudo -u postgres psql -tAc "SHOW hba_file" 2>/dev/null | head -1)
|
||||||
|
if [[ -n "$PG_HBA" ]] && ! grep -q "nexus" "$PG_HBA" 2>/dev/null; then
|
||||||
|
# Add md5 auth line for nexus user before the first local line
|
||||||
|
sed -i "/^# TYPE/a local all ${DB_USER} md5" "$PG_HBA" 2>/dev/null || true
|
||||||
|
systemctl reload postgresql >> "$LOG_FILE" 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok "PostgreSQL configured."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 4. CLONE REPOSITORY
|
||||||
|
# ============================================================
|
||||||
|
clone_repo() {
|
||||||
|
info "Setting up application in ${INSTALL_DIR}..."
|
||||||
|
|
||||||
|
if [[ -d "${INSTALL_DIR}" ]]; then
|
||||||
|
warn "${INSTALL_DIR} already exists."
|
||||||
|
echo -en "${YELLOW} Overwrite? [y/N]: ${NC}"
|
||||||
|
read -r overwrite
|
||||||
|
if [[ "${overwrite,,}" == "y" ]]; then
|
||||||
|
rm -rf "${INSTALL_DIR}"
|
||||||
|
else
|
||||||
|
info "Keeping existing installation. Will update in place."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "${INSTALL_DIR}" ]]; then
|
||||||
|
# If running from the repo itself, copy it
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
if [[ -f "${SCRIPT_DIR}/pos/app.py" ]]; then
|
||||||
|
info "Copying from local source: ${SCRIPT_DIR}"
|
||||||
|
cp -a "${SCRIPT_DIR}" "${INSTALL_DIR}"
|
||||||
|
else
|
||||||
|
info "Cloning from GitHub..."
|
||||||
|
git clone https://github.com/consultoria-as/nexus-autoparts.git "${INSTALL_DIR}" >> "$LOG_FILE" 2>&1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok "Application files ready at ${INSTALL_DIR}."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 5. INSTALL PYTHON DEPENDENCIES
|
||||||
|
# ============================================================
|
||||||
|
install_python_deps() {
|
||||||
|
info "Creating Python virtual environment..."
|
||||||
|
|
||||||
|
python3 -m venv "${INSTALL_DIR}/venv" >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
info "Installing Python dependencies..."
|
||||||
|
"${INSTALL_DIR}/venv/bin/pip" install --upgrade pip >> "$LOG_FILE" 2>&1
|
||||||
|
"${INSTALL_DIR}/venv/bin/pip" install -r "${INSTALL_DIR}/pos/requirements.txt" >> "$LOG_FILE" 2>&1
|
||||||
|
"${INSTALL_DIR}/venv/bin/pip" install gunicorn >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
ok "Python dependencies installed."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 6. INTERACTIVE SETUP
|
||||||
|
# ============================================================
|
||||||
|
interactive_setup() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${CYAN}--- Business Setup ---${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Business name
|
||||||
|
echo -en "${BOLD} Business name${NC} (e.g., Refaccionaria Lopez): "
|
||||||
|
read -r BUSINESS_NAME
|
||||||
|
if [[ -z "$BUSINESS_NAME" ]]; then
|
||||||
|
BUSINESS_NAME="Mi Refaccionaria"
|
||||||
|
warn "Using default: ${BUSINESS_NAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# RFC
|
||||||
|
echo -en "${BOLD} RFC${NC} (optional, press Enter to skip): "
|
||||||
|
read -r BUSINESS_RFC
|
||||||
|
if [[ -z "$BUSINESS_RFC" ]]; then
|
||||||
|
BUSINESS_RFC=""
|
||||||
|
info "RFC skipped."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Owner name
|
||||||
|
echo -en "${BOLD} Owner name${NC}: "
|
||||||
|
read -r OWNER_NAME
|
||||||
|
if [[ -z "$OWNER_NAME" ]]; then
|
||||||
|
OWNER_NAME="Administrador"
|
||||||
|
warn "Using default: ${OWNER_NAME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Owner PIN
|
||||||
|
while true; do
|
||||||
|
echo -en "${BOLD} Owner PIN${NC} (4 digits): "
|
||||||
|
read -rs OWNER_PIN
|
||||||
|
echo ""
|
||||||
|
if [[ "$OWNER_PIN" =~ ^[0-9]{4}$ ]]; then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
warn "PIN must be exactly 4 digits. Try again."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Domain/IP
|
||||||
|
DEFAULT_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
|
echo -en "${BOLD} Domain or IP${NC} for access [${DEFAULT_IP:-localhost}]: "
|
||||||
|
read -r ACCESS_HOST
|
||||||
|
if [[ -z "$ACCESS_HOST" ]]; then
|
||||||
|
ACCESS_HOST="${DEFAULT_IP:-localhost}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD} Summary:${NC}"
|
||||||
|
echo " Business: ${BUSINESS_NAME}"
|
||||||
|
echo " RFC: ${BUSINESS_RFC:-N/A}"
|
||||||
|
echo " Owner: ${OWNER_NAME}"
|
||||||
|
echo " PIN: ****"
|
||||||
|
echo " Access: http://${ACCESS_HOST}"
|
||||||
|
echo ""
|
||||||
|
echo -en "${BOLD} Proceed? [Y/n]: ${NC}"
|
||||||
|
read -r confirm
|
||||||
|
if [[ "${confirm,,}" == "n" ]]; then
|
||||||
|
fatal "Installation cancelled by user."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 7. PROVISION TENANT
|
||||||
|
# ============================================================
|
||||||
|
provision_tenant() {
|
||||||
|
info "Provisioning tenant database..."
|
||||||
|
|
||||||
|
cd "${INSTALL_DIR}/pos"
|
||||||
|
|
||||||
|
# Build a small Python script to avoid quoting issues in bash
|
||||||
|
cat > /tmp/_nexus_provision.py << PYEOF
|
||||||
|
import sys, os
|
||||||
|
sys.path.insert(0, '${INSTALL_DIR}/pos')
|
||||||
|
os.chdir('${INSTALL_DIR}/pos')
|
||||||
|
from services.tenant_manager import provision_tenant
|
||||||
|
|
||||||
|
rfc_val = os.environ.get('NX_RFC') or None
|
||||||
|
result = provision_tenant(
|
||||||
|
name=os.environ['NX_BUSINESS'],
|
||||||
|
rfc=rfc_val,
|
||||||
|
owner_name=os.environ['NX_OWNER'],
|
||||||
|
owner_pin=os.environ['NX_PIN']
|
||||||
|
)
|
||||||
|
print(f"Tenant created: id={result['tenant_id']}, db={result['db_name']}")
|
||||||
|
PYEOF
|
||||||
|
|
||||||
|
NX_BUSINESS="$BUSINESS_NAME" \
|
||||||
|
NX_RFC="$BUSINESS_RFC" \
|
||||||
|
NX_OWNER="$OWNER_NAME" \
|
||||||
|
NX_PIN="$OWNER_PIN" \
|
||||||
|
"${INSTALL_DIR}/venv/bin/python3" /tmp/_nexus_provision.py >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
rm -f /tmp/_nexus_provision.py
|
||||||
|
|
||||||
|
ok "Tenant '${BUSINESS_NAME}' provisioned."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 8. APPLY MIGRATIONS (v1.1)
|
||||||
|
# ============================================================
|
||||||
|
apply_migrations() {
|
||||||
|
info "Applying database migrations..."
|
||||||
|
|
||||||
|
cd "${INSTALL_DIR}/pos"
|
||||||
|
|
||||||
|
"${INSTALL_DIR}/venv/bin/python3" -c "
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, '${INSTALL_DIR}/pos')
|
||||||
|
from migrations.runner import run_migrations
|
||||||
|
run_migrations()
|
||||||
|
" >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
ok "Migrations applied."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 9. CREATE SYSTEMD SERVICE
|
||||||
|
# ============================================================
|
||||||
|
create_systemd_service() {
|
||||||
|
info "Creating systemd service..."
|
||||||
|
|
||||||
|
cat > /etc/systemd/system/${SERVICE_NAME}.service << SERVICEEOF
|
||||||
|
[Unit]
|
||||||
|
Description=Nexus Autoparts POS
|
||||||
|
After=network.target postgresql.service
|
||||||
|
Requires=postgresql.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=www-data
|
||||||
|
Group=www-data
|
||||||
|
WorkingDirectory=${INSTALL_DIR}/pos
|
||||||
|
Environment=PATH=${INSTALL_DIR}/venv/bin:/usr/bin:/bin
|
||||||
|
Environment=MASTER_DB_URL=postgresql://${DB_USER}:${DB_PASS}@localhost/${DB_NAME}
|
||||||
|
Environment=TENANT_DB_URL_TEMPLATE=postgresql://${DB_USER}:${DB_PASS}@localhost/{db_name}
|
||||||
|
ExecStart=${INSTALL_DIR}/venv/bin/gunicorn --bind 127.0.0.1:${POS_PORT} --workers 3 --timeout 120 "app:create_app()"
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
SERVICEEOF
|
||||||
|
|
||||||
|
# Set ownership
|
||||||
|
chown -R www-data:www-data "${INSTALL_DIR}"
|
||||||
|
|
||||||
|
systemctl daemon-reload >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
|
ok "Systemd service created: ${SERVICE_NAME}.service"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 10. CONFIGURE NGINX
|
||||||
|
# ============================================================
|
||||||
|
configure_nginx() {
|
||||||
|
info "Configuring nginx reverse proxy..."
|
||||||
|
|
||||||
|
# Remove default site
|
||||||
|
rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true
|
||||||
|
|
||||||
|
cat > /etc/nginx/sites-available/nexus-pos << NGINXEOF
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name ${ACCESS_HOST};
|
||||||
|
|
||||||
|
client_max_body_size 20M;
|
||||||
|
|
||||||
|
# POS application
|
||||||
|
location /pos/ {
|
||||||
|
proxy_pass http://127.0.0.1:${POS_PORT}/pos/;
|
||||||
|
proxy_set_header Host \$host;
|
||||||
|
proxy_set_header X-Real-IP \$remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API endpoints
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:${POS_PORT}/api/;
|
||||||
|
proxy_set_header Host \$host;
|
||||||
|
proxy_set_header X-Real-IP \$remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Redirect root to POS login
|
||||||
|
location = / {
|
||||||
|
return 302 /pos/login;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
location /health {
|
||||||
|
proxy_pass http://127.0.0.1:${POS_PORT}/pos/health;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NGINXEOF
|
||||||
|
|
||||||
|
ln -sf /etc/nginx/sites-available/nexus-pos /etc/nginx/sites-enabled/nexus-pos
|
||||||
|
|
||||||
|
# Test nginx config
|
||||||
|
if nginx -t >> "$LOG_FILE" 2>&1; then
|
||||||
|
ok "Nginx configuration valid."
|
||||||
|
else
|
||||||
|
err "Nginx configuration test failed. Check $LOG_FILE."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 11. START SERVICES
|
||||||
|
# ============================================================
|
||||||
|
start_services() {
|
||||||
|
info "Starting services..."
|
||||||
|
|
||||||
|
systemctl enable nginx >> "$LOG_FILE" 2>&1
|
||||||
|
systemctl restart nginx >> "$LOG_FILE" 2>&1
|
||||||
|
ok "Nginx started."
|
||||||
|
|
||||||
|
systemctl enable "${SERVICE_NAME}" >> "$LOG_FILE" 2>&1
|
||||||
|
systemctl start "${SERVICE_NAME}" >> "$LOG_FILE" 2>&1
|
||||||
|
ok "Nexus POS service started."
|
||||||
|
|
||||||
|
# Wait a moment and verify
|
||||||
|
sleep 2
|
||||||
|
if systemctl is-active --quiet "${SERVICE_NAME}"; then
|
||||||
|
ok "Service is running."
|
||||||
|
else
|
||||||
|
warn "Service may not have started correctly. Check: journalctl -u ${SERVICE_NAME}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 12. PRINT SUCCESS
|
||||||
|
# ============================================================
|
||||||
|
print_success() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${GREEN}"
|
||||||
|
echo " ========================================"
|
||||||
|
echo " Installation Complete!"
|
||||||
|
echo " ========================================"
|
||||||
|
echo -e "${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BOLD}Access URL:${NC} http://${ACCESS_HOST}/pos/login"
|
||||||
|
echo -e " ${BOLD}Business:${NC} ${BUSINESS_NAME}"
|
||||||
|
echo -e " ${BOLD}Owner:${NC} ${OWNER_NAME}"
|
||||||
|
echo -e " ${BOLD}PIN:${NC} **** (the 4-digit PIN you entered)"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BOLD}Service:${NC} systemctl status ${SERVICE_NAME}"
|
||||||
|
echo -e " ${BOLD}Logs:${NC} journalctl -u ${SERVICE_NAME} -f"
|
||||||
|
echo -e " ${BOLD}Install log:${NC} ${LOG_FILE}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BOLD}Database:${NC}"
|
||||||
|
echo " Host: localhost"
|
||||||
|
echo " User: ${DB_USER}"
|
||||||
|
echo " Master DB: ${DB_NAME}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BOLD}Files:${NC} ${INSTALL_DIR}/"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}To uninstall:${NC} sudo bash ${INSTALL_DIR}/uninstall.sh"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# MAIN
|
||||||
|
# ============================================================
|
||||||
|
main() {
|
||||||
|
banner
|
||||||
|
|
||||||
|
# Init log
|
||||||
|
mkdir -p "$(dirname "$LOG_FILE")"
|
||||||
|
echo "=== Nexus POS Install started at $(date) ===" > "$LOG_FILE"
|
||||||
|
|
||||||
|
check_prerequisites
|
||||||
|
install_packages
|
||||||
|
configure_postgresql
|
||||||
|
clone_repo
|
||||||
|
install_python_deps
|
||||||
|
interactive_setup
|
||||||
|
provision_tenant
|
||||||
|
apply_migrations
|
||||||
|
create_systemd_service
|
||||||
|
configure_nginx
|
||||||
|
start_services
|
||||||
|
print_success
|
||||||
|
|
||||||
|
log "Installation completed successfully."
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
190
uninstall.sh
Executable file
190
uninstall.sh
Executable file
@@ -0,0 +1,190 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================
|
||||||
|
# Nexus Autoparts POS — Uninstaller
|
||||||
|
# Cleanly removes all Nexus POS components
|
||||||
|
# Usage: sudo bash /opt/nexus-pos/uninstall.sh
|
||||||
|
# ============================================================
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# ----- Colors & helpers -----
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
INSTALL_DIR="/opt/nexus-pos"
|
||||||
|
SERVICE_NAME="nexus-pos"
|
||||||
|
DB_USER="nexus"
|
||||||
|
DB_NAME="nexus_autoparts"
|
||||||
|
|
||||||
|
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
|
||||||
|
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
err() { echo -e "${RED}[ERROR]${NC} $*"; }
|
||||||
|
|
||||||
|
# Must be root
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
err "This script must be run as root. Use: sudo bash uninstall.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${RED}"
|
||||||
|
echo " ========================================"
|
||||||
|
echo " Nexus Autoparts POS — Uninstaller"
|
||||||
|
echo " ========================================"
|
||||||
|
echo -e "${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${YELLOW}WARNING: This will remove:${NC}"
|
||||||
|
echo " - Nexus POS systemd service"
|
||||||
|
echo " - Nginx site configuration"
|
||||||
|
echo " - Application files at ${INSTALL_DIR}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BOLD}Database removal is optional (asked separately).${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -en " ${BOLD}${RED}Continue with uninstall? [y/N]: ${NC}"
|
||||||
|
read -r confirm
|
||||||
|
if [[ "${confirm,,}" != "y" ]]; then
|
||||||
|
info "Uninstall cancelled."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 1. STOP AND REMOVE SYSTEMD SERVICE
|
||||||
|
# ============================================================
|
||||||
|
echo ""
|
||||||
|
info "Stopping ${SERVICE_NAME} service..."
|
||||||
|
if systemctl is-active --quiet "${SERVICE_NAME}" 2>/dev/null; then
|
||||||
|
systemctl stop "${SERVICE_NAME}" 2>/dev/null
|
||||||
|
ok "Service stopped."
|
||||||
|
else
|
||||||
|
info "Service was not running."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if systemctl is-enabled --quiet "${SERVICE_NAME}" 2>/dev/null; then
|
||||||
|
systemctl disable "${SERVICE_NAME}" 2>/dev/null
|
||||||
|
ok "Service disabled."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "/etc/systemd/system/${SERVICE_NAME}.service" ]]; then
|
||||||
|
rm -f "/etc/systemd/system/${SERVICE_NAME}.service"
|
||||||
|
systemctl daemon-reload 2>/dev/null
|
||||||
|
ok "Service file removed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 2. REMOVE NGINX CONFIGURATION
|
||||||
|
# ============================================================
|
||||||
|
info "Removing nginx configuration..."
|
||||||
|
if [[ -f /etc/nginx/sites-enabled/nexus-pos ]]; then
|
||||||
|
rm -f /etc/nginx/sites-enabled/nexus-pos
|
||||||
|
fi
|
||||||
|
if [[ -f /etc/nginx/sites-available/nexus-pos ]]; then
|
||||||
|
rm -f /etc/nginx/sites-available/nexus-pos
|
||||||
|
fi
|
||||||
|
if command -v nginx &>/dev/null; then
|
||||||
|
# Restore default site if it exists
|
||||||
|
if [[ -f /etc/nginx/sites-available/default ]]; then
|
||||||
|
ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
nginx -t &>/dev/null && systemctl reload nginx 2>/dev/null
|
||||||
|
ok "Nginx configuration removed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 3. REMOVE APPLICATION FILES
|
||||||
|
# ============================================================
|
||||||
|
info "Removing application files..."
|
||||||
|
if [[ -d "${INSTALL_DIR}" ]]; then
|
||||||
|
rm -rf "${INSTALL_DIR}"
|
||||||
|
ok "Removed ${INSTALL_DIR}"
|
||||||
|
else
|
||||||
|
info "${INSTALL_DIR} not found (already removed?)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 4. DATABASE REMOVAL (OPTIONAL)
|
||||||
|
# ============================================================
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BOLD}${YELLOW}Database removal${NC}"
|
||||||
|
echo " This will drop ALL tenant databases, the master database,"
|
||||||
|
echo " and the PostgreSQL user. All data will be permanently lost."
|
||||||
|
echo ""
|
||||||
|
echo -en " ${BOLD}${RED}Remove databases and PostgreSQL user? [y/N]: ${NC}"
|
||||||
|
read -r drop_db
|
||||||
|
if [[ "${drop_db,,}" == "y" ]]; then
|
||||||
|
echo -en " ${BOLD}${RED}Type 'DELETE' to confirm: ${NC}"
|
||||||
|
read -r final_confirm
|
||||||
|
if [[ "$final_confirm" == "DELETE" ]]; then
|
||||||
|
info "Dropping databases..."
|
||||||
|
|
||||||
|
# Get list of tenant databases
|
||||||
|
TENANT_DBS=$(sudo -u postgres psql -tAc "SELECT db_name FROM tenants" "${DB_NAME}" 2>/dev/null || echo "")
|
||||||
|
TEMPLATE_DB=$(sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='tenant_template'" 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
# Drop tenant databases
|
||||||
|
if [[ -n "$TENANT_DBS" ]]; then
|
||||||
|
while IFS= read -r tdb; do
|
||||||
|
if [[ -n "$tdb" ]]; then
|
||||||
|
sudo -u postgres psql -c "DROP DATABASE IF EXISTS \"${tdb}\";" 2>/dev/null
|
||||||
|
ok "Dropped tenant database: ${tdb}"
|
||||||
|
fi
|
||||||
|
done <<< "$TENANT_DBS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Drop template database
|
||||||
|
if [[ -n "$TEMPLATE_DB" ]]; then
|
||||||
|
sudo -u postgres psql -c "DROP DATABASE IF EXISTS tenant_template;" 2>/dev/null
|
||||||
|
ok "Dropped template database."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Drop master database
|
||||||
|
sudo -u postgres psql -c "DROP DATABASE IF EXISTS ${DB_NAME};" 2>/dev/null
|
||||||
|
ok "Dropped master database: ${DB_NAME}"
|
||||||
|
|
||||||
|
# Drop user
|
||||||
|
sudo -u postgres psql -c "DROP USER IF EXISTS ${DB_USER};" 2>/dev/null
|
||||||
|
ok "Dropped PostgreSQL user: ${DB_USER}"
|
||||||
|
else
|
||||||
|
warn "Database removal cancelled (confirmation did not match)."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
info "Databases preserved. You can remove them manually later."
|
||||||
|
echo " sudo -u postgres psql -c \"DROP DATABASE ${DB_NAME};\""
|
||||||
|
echo " sudo -u postgres psql -c \"DROP USER ${DB_USER};\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 5. REMOVE LOG FILE
|
||||||
|
# ============================================================
|
||||||
|
if [[ -f /var/log/nexus-pos-install.log ]]; then
|
||||||
|
rm -f /var/log/nexus-pos-install.log
|
||||||
|
ok "Install log removed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# DONE
|
||||||
|
# ============================================================
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${GREEN}"
|
||||||
|
echo " ========================================"
|
||||||
|
echo " Uninstall Complete"
|
||||||
|
echo " ========================================"
|
||||||
|
echo -e "${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BOLD}Removed:${NC}"
|
||||||
|
echo " - Systemd service (${SERVICE_NAME})"
|
||||||
|
echo " - Nginx site configuration"
|
||||||
|
echo " - Application files (${INSTALL_DIR})"
|
||||||
|
if [[ "${drop_db,,}" == "y" && "$final_confirm" == "DELETE" ]]; then
|
||||||
|
echo " - All databases and PostgreSQL user"
|
||||||
|
else
|
||||||
|
echo -e " - Databases: ${YELLOW}NOT removed${NC} (kept by user request)"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo " Note: System packages (python3, postgresql, nginx, git)"
|
||||||
|
echo " were NOT removed. Remove them manually if desired:"
|
||||||
|
echo " apt-get remove --purge postgresql nginx"
|
||||||
|
echo ""
|
||||||
Reference in New Issue
Block a user