193 lines
9.3 KiB
Bash
Executable File
193 lines
9.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Mnemosyne install script — idempotent; safe to re-run.
|
|
# Does NOT clobber an existing database or config file.
|
|
#
|
|
# Usage (as root or with sudo):
|
|
# bash scripts/install.sh [--app-dir /opt/mnemosyne] [--config-dir /etc/mnemosyne]
|
|
# [--data-dir /var/lib/mnemosyne]
|
|
#
|
|
# After running:
|
|
# 1. Edit /etc/mnemosyne/mnemosyne.conf (fill in token, secret, URL, chat ID)
|
|
# 2. Copy and adjust nginx/mnemosyne.conf.example → /etc/nginx/conf.d/mnemosyne.conf
|
|
# 3. Obtain a Let's Encrypt cert: certbot --nginx -d your.subdomain.com
|
|
# 4. Register the Telegram webhook: /opt/mnemosyne/bin/mnemosyne-webhook --config /etc/mnemosyne/mnemosyne.conf
|
|
# 5. Start the bot: systemctl start mnemosyne-bot
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Defaults ────────────────────────────────────────────────────────────────
|
|
APP_DIR="/opt/mnemosyne"
|
|
CONFIG_DIR="/etc/mnemosyne"
|
|
DATA_DIR="/var/lib/mnemosyne"
|
|
SERVICE_USER="mnemosyne"
|
|
SERVICE_GROUP="mnemosyne"
|
|
SYSTEMD_DIR="/etc/systemd/system"
|
|
CRON_FILE="/etc/cron.d/mnemosyne"
|
|
|
|
# ── Argument parsing ─────────────────────────────────────────────────────────
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--app-dir) APP_DIR="$2"; shift 2 ;;
|
|
--config-dir) CONFIG_DIR="$2"; shift 2 ;;
|
|
--data-dir) DATA_DIR="$2"; shift 2 ;;
|
|
*) echo "Unknown argument: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# ── Must run as root ─────────────────────────────────────────────────────────
|
|
if [[ $EUID -ne 0 ]]; then
|
|
echo "Error: this script must be run as root." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ── Detect source directory (where this script lives) ───────────────────────
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
echo "==> Mnemosyne installer"
|
|
echo " Source: $REPO_DIR"
|
|
echo " App: $APP_DIR"
|
|
echo " Config: $CONFIG_DIR"
|
|
echo " Data: $DATA_DIR"
|
|
echo ""
|
|
|
|
# ── Detect distro family ─────────────────────────────────────────────────────
|
|
detect_distro() {
|
|
if command -v dnf &>/dev/null || command -v yum &>/dev/null; then
|
|
echo "rhel"
|
|
elif command -v apt-get &>/dev/null; then
|
|
echo "debian"
|
|
else
|
|
echo "unknown"
|
|
fi
|
|
}
|
|
DISTRO="$(detect_distro)"
|
|
|
|
# ── 1. System packages ───────────────────────────────────────────────────────
|
|
echo "==> Installing system packages (distro: $DISTRO)"
|
|
case "$DISTRO" in
|
|
rhel)
|
|
PKG_MGR="dnf"
|
|
command -v yum &>/dev/null && ! command -v dnf &>/dev/null && PKG_MGR="yum"
|
|
$PKG_MGR install -y perl perl-App-cpanminus perl-DBI sqlite perl-local-lib 2>&1 | grep -E '(Installed|already|Error)' || true
|
|
# DBD::SQLite needs C compiler for XS build
|
|
$PKG_MGR install -y gcc make perl-devel 2>&1 | grep -E '(Installed|already|Error)' || true
|
|
;;
|
|
debian)
|
|
apt-get install -y perl cpanminus libdbi-perl sqlite3 gcc make 2>&1 | grep -E '(Setting up|already|Error)' || true
|
|
;;
|
|
*)
|
|
echo " Warning: unknown distro. Install Perl, cpanminus, DBI, and SQLite manually." >&2
|
|
;;
|
|
esac
|
|
|
|
# ── 2. CPAN dependencies ─────────────────────────────────────────────────────
|
|
echo "==> Installing CPAN dependencies"
|
|
# Install into system Perl (running as root) or local::lib if preferred
|
|
cpanm --notest --quiet --installdeps "$REPO_DIR" || {
|
|
echo " Warning: cpanm reported errors. Check output above." >&2
|
|
}
|
|
|
|
# ── 3. System user/group ─────────────────────────────────────────────────────
|
|
echo "==> Creating system user: $SERVICE_USER"
|
|
if ! getent group "$SERVICE_GROUP" &>/dev/null; then
|
|
groupadd --system "$SERVICE_GROUP"
|
|
echo " Created group $SERVICE_GROUP"
|
|
fi
|
|
if ! id "$SERVICE_USER" &>/dev/null; then
|
|
useradd --system --gid "$SERVICE_GROUP" --no-create-home \
|
|
--home-dir "$DATA_DIR" --shell /sbin/nologin "$SERVICE_USER"
|
|
echo " Created user $SERVICE_USER"
|
|
else
|
|
echo " User $SERVICE_USER already exists — skipping"
|
|
fi
|
|
|
|
# ── 4. Directories ───────────────────────────────────────────────────────────
|
|
echo "==> Creating directories"
|
|
install -d -m 755 "$APP_DIR"
|
|
install -d -m 750 -o "$SERVICE_USER" -g "$SERVICE_GROUP" "$DATA_DIR"
|
|
install -d -m 750 "$CONFIG_DIR"
|
|
|
|
# ── 5. Copy application files ────────────────────────────────────────────────
|
|
echo "==> Installing application to $APP_DIR"
|
|
# Use rsync if available (preserves structure, skips .git), otherwise cp
|
|
if command -v rsync &>/dev/null; then
|
|
rsync -a --exclude='.git' --exclude='config/mnemosyne.conf' \
|
|
"$REPO_DIR/" "$APP_DIR/"
|
|
else
|
|
cp -rp "$REPO_DIR/." "$APP_DIR/"
|
|
rm -rf "$APP_DIR/.git"
|
|
rm -f "$APP_DIR/config/mnemosyne.conf"
|
|
fi
|
|
chmod +x "$APP_DIR/bin/"*
|
|
chown -R root:"$SERVICE_GROUP" "$APP_DIR"
|
|
chmod -R o-rwx "$APP_DIR"
|
|
|
|
# ── 6. Config file ───────────────────────────────────────────────────────────
|
|
echo "==> Config"
|
|
if [[ -f "$CONFIG_DIR/mnemosyne.conf" ]]; then
|
|
echo " $CONFIG_DIR/mnemosyne.conf already exists — not overwriting"
|
|
else
|
|
install -m 640 -o root -g "$SERVICE_GROUP" \
|
|
"$REPO_DIR/config/mnemosyne.conf.example" \
|
|
"$CONFIG_DIR/mnemosyne.conf"
|
|
echo ""
|
|
echo " *** ACTION REQUIRED ***"
|
|
echo " Edit $CONFIG_DIR/mnemosyne.conf and fill in:"
|
|
echo " bot.token — from @BotFather"
|
|
echo " bot.webhook_secret — run: openssl rand -hex 32"
|
|
echo " bot.webhook_url — your public HTTPS webhook URL"
|
|
echo " bot.allowed_chat_ids — your Telegram chat ID (@userinfobot)"
|
|
echo " db.path — default: $DATA_DIR/mnemosyne.db"
|
|
echo " app.timezone — IANA timezone (e.g. America/New_York)"
|
|
echo ""
|
|
fi
|
|
|
|
# ── 7. Systemd service ───────────────────────────────────────────────────────
|
|
echo "==> Installing systemd service"
|
|
# Rewrite paths in the service template
|
|
sed "s|/opt/mnemosyne|$APP_DIR|g; s|/etc/mnemosyne|$CONFIG_DIR|g" \
|
|
"$REPO_DIR/systemd/mnemosyne-bot.service" \
|
|
> "$SYSTEMD_DIR/mnemosyne-bot.service"
|
|
chmod 644 "$SYSTEMD_DIR/mnemosyne-bot.service"
|
|
systemctl daemon-reload
|
|
systemctl enable mnemosyne-bot
|
|
echo " Enabled mnemosyne-bot.service (not started — configure first)"
|
|
|
|
# ── 8. Cron entry for digest ─────────────────────────────────────────────────
|
|
echo "==> Installing cron job for digest"
|
|
cat > "$CRON_FILE" <<CRON
|
|
# Mnemosyne morning digest — fires every 5 min; the script checks digest_time itself.
|
|
*/5 * * * * $SERVICE_USER $APP_DIR/bin/mnemosyne-digest --config $CONFIG_DIR/mnemosyne.conf 2>&1 | logger -t mnemosyne-digest
|
|
CRON
|
|
chmod 644 "$CRON_FILE"
|
|
echo " Installed $CRON_FILE"
|
|
|
|
# ── 9. Backup cron (optional) ────────────────────────────────────────────────
|
|
echo "==> Installing daily backup cron"
|
|
BACKUP_DIR="/var/backups/mnemosyne"
|
|
install -d -m 750 -o "$SERVICE_USER" -g "$SERVICE_GROUP" "$BACKUP_DIR"
|
|
cat >> "$CRON_FILE" <<CRON
|
|
# Daily backup at 02:15
|
|
15 2 * * * $SERVICE_USER MNEMOSYNE_BACKUP_DIR=$BACKUP_DIR $APP_DIR/scripts/backup.sh --config $CONFIG_DIR/mnemosyne.conf 2>&1 | logger -t mnemosyne-backup
|
|
CRON
|
|
echo " Backup cron added (target: $BACKUP_DIR)"
|
|
|
|
# ── Done ─────────────────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "==> Installation complete."
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Edit $CONFIG_DIR/mnemosyne.conf"
|
|
echo " 2. Copy nginx/mnemosyne.conf.example → /etc/nginx/conf.d/mnemosyne.conf"
|
|
echo " and adjust server_name + port"
|
|
echo " 3. Obtain TLS cert: certbot --nginx -d your.subdomain.example.com"
|
|
echo " 4. Reload nginx: systemctl reload nginx"
|
|
echo " 5. Register webhook: $APP_DIR/bin/mnemosyne-webhook --config $CONFIG_DIR/mnemosyne.conf"
|
|
echo " 6. Start bot: systemctl start mnemosyne-bot"
|
|
echo " 7. Check logs: journalctl -u mnemosyne-bot -f"
|
|
echo ""
|
|
echo " Note (RHEL/AlmaLinux): if SELinux is enforcing, you may need:"
|
|
echo " setsebool -P httpd_can_network_connect 1"
|
|
echo " (allows nginx to proxy to the local bot port)"
|