inboxer/install.sh
cclohmar 01a58156a1 Consolidate all secrets into config.yaml — remove .env entirely
All configuration (including secrets) now lives in a single file:
bin/config.yaml. The separate .env file has been eliminated.

Changes:
- config.go: Added SMTPSettings struct + AI.APIKey to Config; removed
  godotenv import, Environment struct, and all os.Getenv() calls
- config.yaml: Added smtp section (host/port/username/password) and
  ai.api_key field with placeholder values
- main.go: Reads SMTP and API key from cfg instead of env
- smtp.go: Changed Port field from string to int
- otp_test.go: Updated Port values to int
- .env.example: Deleted (all config is in config.yaml)
- .gitignore: Removed .env.example; kept .env for safety
- go.mod/go.sum: Removed github.com/joho/godotenv dependency
- install.sh: No longer creates .env or uses EnvironmentFile;
  warns about placeholder values in config.yaml instead
2026-04-23 20:06:16 +00:00

219 lines
8.7 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
#
# inBOXER Install Script
# ======================
# Deploys the inBOXER binary and configuration to /opt/inboxer
# Creates a systemd service for production deployment.
# Supports Debian (apt) and RHEL (yum/dnf) families.
#
# Usage: sudo ./install.sh
#
set -euo pipefail
# ─── Configuration ───────────────────────────────────────────────────────────
INSTALL_DIR="/opt/inboxer"
SERVICE_USER="inboxer"
SERVICE_GROUP="inboxer"
SERVICE_NAME="inboxer"
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
BIN_DIR="${INSTALL_DIR}/bin"
DATA_DIR="${INSTALL_DIR}/data"
LOGS_DIR="${INSTALL_DIR}/logs"
# Detect script/repo location
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ "$(basename "${SCRIPT_DIR}")" == "bin" ]]; then
REPO_DIR="$(dirname "${SCRIPT_DIR}")"
else
REPO_DIR="${SCRIPT_DIR}"
fi
# ─── Terminal colours ────────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
# ─── Pre-flight checks ───────────────────────────────────────────────────────
if [[ $EUID -ne 0 ]]; then
error "This script must be run as root (use sudo)."
exit 1
fi
if ! command -v systemctl &>/dev/null; then
error "systemd is required but not found on this system."
exit 1
fi
REQUIRED_FILES=(
"${REPO_DIR}/bin/inboxer"
"${REPO_DIR}/bin/config.yaml"
"${REPO_DIR}/bin/prompt.txt"
)
for f in "${REQUIRED_FILES[@]}"; do
if [[ ! -f "$f" ]]; then
error "Required file not found: $f"
error "Run this script from the inBOXER repository root."
exit 1
fi
done
# ─── OS Detection (informational) ───────────────────────────────────────────
if [[ -f /etc/os-release ]]; then
# shellcheck source=/dev/null
. /etc/os-release
info "Detected OS: ${NAME} ${VERSION_ID}"
else
info "Could not detect OS version (no /etc/os-release)."
fi
# ─── Create system user & group ──────────────────────────────────────────────
info "Creating system user '${SERVICE_USER}'..."
if getent group "${SERVICE_GROUP}" &>/dev/null; then
info "Group '${SERVICE_GROUP}' already exists."
else
groupadd --system "${SERVICE_GROUP}"
info "Group '${SERVICE_GROUP}' created."
fi
if getent passwd "${SERVICE_USER}" &>/dev/null; then
info "User '${SERVICE_USER}' already exists."
else
useradd --system \
--no-create-home \
--gid "${SERVICE_GROUP}" \
--shell /sbin/nologin \
--comment "inBOXER Service User" \
"${SERVICE_USER}"
info "User '${SERVICE_USER}' created."
fi
# ─── Create directory structure ──────────────────────────────────────────────
info "Creating directories under ${INSTALL_DIR}..."
mkdir -p "${BIN_DIR}" "${DATA_DIR}" "${LOGS_DIR}"
# ─── Install binary & config files ───────────────────────────────────────────
info "Installing binary..."
install -m 755 "${REPO_DIR}/bin/inboxer" "${BIN_DIR}/inboxer"
info "Installing configuration..."
install -m 644 "${REPO_DIR}/bin/config.yaml" "${BIN_DIR}/config.yaml"
install -m 644 "${REPO_DIR}/bin/prompt.txt" "${BIN_DIR}/prompt.txt"
# Adjust config paths for /opt/inboxer deployment
info "Adjusting configuration paths for deployment..."
sed -i 's|path: "bin/db.sqlite"|path: "'"${DATA_DIR}"'/db.sqlite"|' "${BIN_DIR}/config.yaml"
sed -i 's|file: "bin/inboxer.log"|file: "'"${LOGS_DIR}"'/inboxer.log"|' "${BIN_DIR}/config.yaml"
# prompt_file: "bin/prompt.txt" works as-is relative to the working directory,
# since WorkingDirectory=/opt/inboxer resolves it to /opt/inboxer/bin/prompt.txt
# ─── Create systemd service unit ─────────────────────────────────────────────
info "Creating systemd service unit at ${SERVICE_FILE} ..."
cat > "${SERVICE_FILE}" << UNITEOF
[Unit]
Description=inBOXER AI-Powered Email Classifier
Documentation=https://github.com/cclohmar/inboxer
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=${SERVICE_USER}
Group=${SERVICE_GROUP}
WorkingDirectory=${INSTALL_DIR}
ExecStart=${BIN_DIR}/inboxer
Restart=on-failure
RestartSec=10
# Logging
StandardOutput=append:${LOGS_DIR}/stdout.log
StandardError=append:${LOGS_DIR}/stderr.log
# Security hardening
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
UNITEOF
# ─── Set file permissions ────────────────────────────────────────────────────
info "Setting file ownership and permissions..."
chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "${INSTALL_DIR}"
# Directory permissions
chmod 755 "${BIN_DIR}"
chmod 750 "${DATA_DIR}" # database file is sensitive
chmod 750 "${LOGS_DIR}"
# config.yaml contains secrets so restrict access
chmod 640 "${BIN_DIR}/config.yaml"
# ─── Register & start service ───────────────────────────────────────────────
info "Reloading systemd daemon..."
systemctl daemon-reload
info "Enabling ${SERVICE_NAME} service (starts on boot)..."
systemctl enable "${SERVICE_NAME}"
info "Starting ${SERVICE_NAME} service..."
systemctl start "${SERVICE_NAME}"
# Brief pause so the service can initialise
sleep 2
# ─── Verify ──────────────────────────────────────────────────────────────────
if systemctl is-active --quiet "${SERVICE_NAME}"; then
info "Service '${SERVICE_NAME}' is running."
systemctl status "${SERVICE_NAME}" --no-pager
else
warn "Service '${SERVICE_NAME}' did not start. Check logs:"
warn " journalctl -u ${SERVICE_NAME} --no-pager"
systemctl status "${SERVICE_NAME}" --no-pager || true
fi
# ─── Summary ─────────────────────────────────────────────────────────────────
echo ""
info "═════════════════════════════════════════════════"
info " inBOXER Installation Complete"
info "═════════════════════════════════════════════════"
echo ""
info " Install directory: ${INSTALL_DIR}"
info " Binary: ${BIN_DIR}/inboxer"
info " Configuration: ${BIN_DIR}/config.yaml"
info " Prompt file: ${BIN_DIR}/prompt.txt"
info " Data (SQLite): ${DATA_DIR}/"
info " Logs: ${LOGS_DIR}/"
echo ""
info " ⚙ Edit config.yaml with your credentials before first start:"
info " sudo nano ${BIN_DIR}/config.yaml"
info ""
info " Required settings:"
info " - ai.api_key (DeepSeek API key)"
info " - smtp.host/port (SMTP server for OTP emails)"
info " - smtp.username (SMTP login)"
info " - smtp.password (SMTP password)"
info " - server.session_secret (change from the default)"
echo ""
if grep -q "your_deepseek_api_key_here\|your.smtp.host\|change-me-in-production" "${BIN_DIR}/config.yaml" 2>/dev/null; then
warn " ⚠ config.yaml still contains placeholder values!"
warn " Edit ${BIN_DIR}/config.yaml before the service will function."
echo ""
fi
info " Service management:"
info " sudo systemctl status ${SERVICE_NAME}"
info " sudo systemctl restart ${SERVICE_NAME}"
info " sudo systemctl stop ${SERVICE_NAME}"
info " sudo journalctl -u ${SERVICE_NAME} -f"
echo ""
info " Web interface: http://$(hostname -s 2>/dev/null || echo "localhost"):8080"
info "═════════════════════════════════════════════════"
echo ""