transmission-rss-manager/main-installer.sh
MasterDraco 6dc2df3cee Fix code consistency and reliability issues
This commit addresses multiple code consistency and reliability issues across the codebase:

1. Version consistency - use package.json version (2.0.9) throughout
2. Improved module loading with better error handling and consistent symlinks
3. Enhanced data directory handling with better error checking
4. Fixed redundant code in main-installer.sh
5. Improved error handling in transmission-client.js
6. Added extensive module symlink creation
7. Better file path handling and permission checks
8. Enhanced API response handling

💡 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-03-07 11:38:14 +00:00

539 lines
19 KiB
Bash
Executable File

#!/bin/bash
# Transmission RSS Manager Modular Installer
# Modified to work with the git-based approach
# Set script to exit on error
set -e
# Load installation environment variables if they exist
if [ -f "$(dirname "$0")/.env.install" ]; then
source "$(dirname "$0")/.env.install"
echo "Loaded TRANSMISSION_REMOTE=$TRANSMISSION_REMOTE from environment file"
fi
# Text formatting
BOLD='\033[1m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Get current directory
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
# Source the utils module first to make the log function available
source "${SCRIPT_DIR}/modules/utils-module.sh"
# Print header
echo -e "${BOLD}==================================================${NC}"
echo -e "${BOLD} Transmission RSS Manager Installer ${NC}"
VERSION=$(grep -oP '"version": "\K[^"]+' "${SCRIPT_DIR}/package.json" 2>/dev/null || echo "Unknown")
echo -e "${BOLD} Version ${VERSION} - Git Edition ${NC}"
echo -e "${BOLD}==================================================${NC}"
echo
# Check if script is run with sudo
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Please run as root (use sudo)${NC}"
exit 1
fi
# Check for installation type
IS_UPDATE=false
INSTALLATION_DETECTED=false
# Check for config.json file (primary indicator)
if [ -f "${SCRIPT_DIR}/config.json" ]; then
INSTALLATION_DETECTED=true
fi
# Check for service file (secondary indicator)
if [ -f "/etc/systemd/system/transmission-rss-manager.service" ]; then
INSTALLATION_DETECTED=true
# Check if the service points to /opt/trans-install
if grep -q "/opt/trans-install" "/etc/systemd/system/transmission-rss-manager.service"; then
# Set INSTALL_DIR to match existing service file
INSTALL_DIR="/opt/trans-install"
log "INFO" "Found existing installation at /opt/trans-install - using this as installation directory"
export INSTALL_DIR
fi
fi
# Check for data directory (tertiary indicator)
if [ -d "${SCRIPT_DIR}/data" ] && [ "$(ls -A "${SCRIPT_DIR}/data" 2>/dev/null)" ]; then
INSTALLATION_DETECTED=true
fi
if [ "$INSTALLATION_DETECTED" = true ]; then
IS_UPDATE=true
echo -e "${YELLOW}Existing installation detected. Running in update mode.${NC}"
echo -e "${GREEN}Your existing configuration will be preserved.${NC}"
echo -e "${GREEN}Only application files will be updated.${NC}"
else
echo -e "${GREEN}Fresh installation. Will create new configuration.${NC}"
fi
export IS_UPDATE
# Check if required module files exist
REQUIRED_MODULES=(
"${SCRIPT_DIR}/modules/config-module.sh"
"${SCRIPT_DIR}/modules/utils-module.sh"
"${SCRIPT_DIR}/modules/dependencies-module.sh"
"${SCRIPT_DIR}/modules/service-setup-module.sh"
"${SCRIPT_DIR}/modules/file-creator-module.sh"
)
for module in "${REQUIRED_MODULES[@]}"; do
if [ ! -f "$module" ]; then
echo -e "${RED}Error: Required module file not found: $module${NC}"
echo -e "${YELLOW}The module files should be included in the git repository.${NC}"
exit 1
fi
done
# Source the remaining module files
source "${SCRIPT_DIR}/modules/config-module.sh"
source "${SCRIPT_DIR}/modules/dependencies-module.sh"
# Check if the updated service module exists, use it if available
if [ -f "${SCRIPT_DIR}/modules/service-setup-module-updated.sh" ]; then
log "INFO" "Using updated service setup module"
source "${SCRIPT_DIR}/modules/service-setup-module-updated.sh"
else
log "INFO" "Using standard service setup module"
source "${SCRIPT_DIR}/modules/service-setup-module.sh"
fi
source "${SCRIPT_DIR}/modules/file-creator-module.sh"
# Function to handle cleanup on error
function cleanup_on_error() {
log "ERROR" "Installation failed: $1"
log "INFO" "Cleaning up..."
# Add any cleanup steps here if needed
log "INFO" "You can try running the installer again after fixing the issues."
exit 1
}
# Set trap for error handling
trap 'cleanup_on_error "$BASH_COMMAND"' ERR
# Execute the installation steps in sequence
log "INFO" "Starting installation process..."
# Set defaults for key variables
export TRANSMISSION_REMOTE=false
export CONFIG_DIR=${CONFIG_DIR:-"/etc/transmission-rss-manager"}
export USER=${USER:-$(logname || echo $SUDO_USER)}
if [ "$IS_UPDATE" = true ]; then
log "INFO" "Running in update mode - preserving existing configuration..."
# First, let's check if we already have this value from the environment
# This allows for non-interactive usage in scripts
if [ -n "$TRANSMISSION_REMOTE" ]; then
is_remote=$([ "$TRANSMISSION_REMOTE" = true ] && echo "Remote" || echo "Local")
log "INFO" "Using Transmission mode from environment: $is_remote"
# Set the input_remote variable based on the environment variable
# This ensures consistent behavior with the rest of the script
if [ "$TRANSMISSION_REMOTE" = true ]; then
input_remote="y"
else
input_remote="n"
fi
else
# Directly ask about Transmission
# This is a direct approach that bypasses any potential sourcing issues
log "INFO" "Configuring Transmission connection..."
echo -e "${BOLD}Transmission Configuration:${NC}"
echo -e "Configure connection to your Transmission client:"
echo
# If stdin is not a terminal (pipe or redirect), assume default
if [ ! -t 0 ]; then
input_remote="n" # Default to no
log "INFO" "Non-interactive mode detected, using default: local Transmission"
else
read -p "Is Transmission running on a remote server? (y/n) [n]: " input_remote
fi
log "INFO" "DEBUG: Input received for remote: '$input_remote'"
fi
# More explicit check for "y" or "Y" input
if [ "$input_remote" = "y" ] || [ "$input_remote" = "Y" ]; then
export TRANSMISSION_REMOTE=true
log "INFO" "Remote Transmission selected."
# Update the config file directly to set remote mode
if [ -f "$CONFIG_DIR/config.json" ]; then
log "INFO" "Updating configuration file for remote Transmission..."
# Log all environment variables we have for debugging
log "INFO" "DEBUG: Environment variables for remote configuration:"
log "INFO" "DEBUG: TRANSMISSION_HOST=${TRANSMISSION_HOST:-'not set'}"
log "INFO" "DEBUG: TRANSMISSION_PORT=${TRANSMISSION_PORT:-'not set'}"
log "INFO" "DEBUG: REMOTE_DOWNLOAD_DIR=${REMOTE_DOWNLOAD_DIR:-'not set'}"
log "INFO" "DEBUG: LOCAL_DOWNLOAD_DIR=${LOCAL_DOWNLOAD_DIR:-'not set'}"
# Check if we already have the remote configuration details from the environment
if [ -n "$TRANSMISSION_HOST" ] && [ -n "$TRANSMISSION_PORT" ] && [ -n "$REMOTE_DOWNLOAD_DIR" ] && [ -n "$LOCAL_DOWNLOAD_DIR" ]; then
log "INFO" "Using remote Transmission configuration from environment"
# Values are already set from the environment, no need to ask again
else
# Get and validate hostname
read -p "Remote Transmission host [localhost]: " input_trans_host
TRANSMISSION_HOST=${input_trans_host:-"localhost"}
# Get and validate port
read -p "Remote Transmission port [9091]: " input_trans_port
TRANSMISSION_PORT=${input_trans_port:-9091}
# Get credentials
read -p "Remote Transmission username []: " input_trans_user
TRANSMISSION_USER=${input_trans_user:-""}
# Use read -s for password to avoid showing it on screen
read -s -p "Remote Transmission password []: " input_trans_pass
echo # Add a newline after the password input
TRANSMISSION_PASS=${input_trans_pass:-""}
read -p "Remote Transmission RPC path [/transmission/rpc]: " input_trans_path
TRANSMISSION_RPC_PATH=${input_trans_path:-"/transmission/rpc"}
# Configure directory mapping for remote setup
echo
echo -e "${YELLOW}Directory Mapping Configuration${NC}"
echo -e "When using a remote Transmission server, you need to map paths between servers."
echo -e "For each directory on the remote server, specify the corresponding local directory."
echo
# Get remote download directory
read -p "Remote Transmission download directory [/var/lib/transmission-daemon/downloads]: " REMOTE_DOWNLOAD_DIR
REMOTE_DOWNLOAD_DIR=${REMOTE_DOWNLOAD_DIR:-"/var/lib/transmission-daemon/downloads"}
# Get local directory that corresponds to remote download directory
read -p "Local directory that corresponds to the remote download directory [/mnt/transmission-downloads]: " LOCAL_DOWNLOAD_DIR
LOCAL_DOWNLOAD_DIR=${LOCAL_DOWNLOAD_DIR:-"/mnt/transmission-downloads"}
fi
# Create mapping JSON
TRANSMISSION_DIR_MAPPING=$(cat <<EOF
{
"$REMOTE_DOWNLOAD_DIR": "$LOCAL_DOWNLOAD_DIR"
}
EOF
)
# Create the local directory
mkdir -p "$LOCAL_DOWNLOAD_DIR"
chown -R $USER:$USER "$LOCAL_DOWNLOAD_DIR"
# Update the config file with the new remote settings
log "INFO" "Updating configuration file with remote Transmission settings..."
# Backup the original config file
cp "$CONFIG_DIR/config.json" "$CONFIG_DIR/config.json.bak.$(date +%Y%m%d%H%M%S)"
# Update the isRemote setting
sed -i 's/"isRemote": false/"isRemote": true/' "$CONFIG_DIR/config.json"
# Update the host setting
sed -i "s/\"host\": \"[^\"]*\"/\"host\": \"$TRANSMISSION_HOST\"/" "$CONFIG_DIR/config.json"
# Update the port setting
sed -i "s/\"port\": [0-9]*/\"port\": $TRANSMISSION_PORT/" "$CONFIG_DIR/config.json"
# Update the username setting
sed -i "s/\"username\": \"[^\"]*\"/\"username\": \"$TRANSMISSION_USER\"/" "$CONFIG_DIR/config.json"
# Update the password setting
sed -i "s/\"password\": \"[^\"]*\"/\"password\": \"$TRANSMISSION_PASS\"/" "$CONFIG_DIR/config.json"
# Update the RPC path setting
sed -i "s|\"path\": \"[^\"]*\"|\"path\": \"$TRANSMISSION_RPC_PATH\"|" "$CONFIG_DIR/config.json"
# Update the directory mapping
# Use a more complex approach since it's a JSON object
# This is a simplification and might need improvement for complex JSON handling
sed -i "/\"directoryMapping\":/c\\ \"directoryMapping\": $TRANSMISSION_DIR_MAPPING" "$CONFIG_DIR/config.json"
log "INFO" "Configuration updated for remote Transmission."
fi
else
export TRANSMISSION_REMOTE=false
log "INFO" "Local Transmission selected."
# Update the config file directly to set local mode
if [ -f "$CONFIG_DIR/config.json" ]; then
log "INFO" "Updating configuration file for local Transmission..."
# Backup the original config file
cp "$CONFIG_DIR/config.json" "$CONFIG_DIR/config.json.bak.$(date +%Y%m%d%H%M%S)"
# Update the isRemote setting
sed -i 's/"isRemote": true/"isRemote": false/' "$CONFIG_DIR/config.json"
# Update the host setting
sed -i 's/"host": "[^"]*"/"host": "localhost"/' "$CONFIG_DIR/config.json"
log "INFO" "Configuration updated for local Transmission."
fi
fi
# Step 1: Check dependencies (but don't reconfigure)
log "INFO" "Checking dependencies..."
install_dependencies || {
log "ERROR" "Dependency check failed"
exit 1
}
# Step the service configuration (will preserve existing settings)
log "INFO" "Updating service configuration..."
setup_service || {
log "ERROR" "Service update failed"
exit 1
}
# Install npm dependencies using our common function
ensure_npm_packages "$INSTALL_DIR" || {
log "ERROR" "NPM installation failed"
exit 1
}
# Copy JavaScript module files during update as well
log "INFO" "Copying JavaScript module files..."
copy_module_files || {
log "ERROR" "Failed to copy JavaScript module files"
exit 1
}
else
# This is a fresh installation - run all steps
# Step 1: First, let's check if we already have this value from the environment
# This allows for non-interactive usage in scripts
if [ -n "$TRANSMISSION_REMOTE" ]; then
is_remote=$([ "$TRANSMISSION_REMOTE" = true ] && echo "Remote" || echo "Local")
log "INFO" "Using Transmission mode from environment: $is_remote"
# Set the input_remote variable based on the environment variable
# This ensures consistent behavior with the rest of the script
if [ "$TRANSMISSION_REMOTE" = true ]; then
input_remote="y"
else
input_remote="n"
fi
else
# Directly ask about Transmission
# This is a direct approach that bypasses any potential sourcing issues
log "INFO" "Configuring Transmission connection..."
echo -e "${BOLD}Transmission Configuration:${NC}"
echo -e "Configure connection to your Transmission client:"
echo
# If stdin is not a terminal (pipe or redirect), assume default
if [ ! -t 0 ]; then
input_remote="n" # Default to no
log "INFO" "Non-interactive mode detected, using default: local Transmission"
else
read -p "Is Transmission running on a remote server? (y/n) [n]: " input_remote
fi
log "INFO" "DEBUG: Input received for remote: '$input_remote'"
fi
# More explicit check for "y" or "Y" input
if [ "$input_remote" = "y" ] || [ "$input_remote" = "Y" ]; then
export TRANSMISSION_REMOTE=true
log "INFO" "Remote Transmission selected."
else
export TRANSMISSION_REMOTE=false
log "INFO" "Local Transmission selected."
fi
# Now gather the rest of the configuration
log "INFO" "Gathering remaining configuration..."
gather_configuration || {
log "ERROR" "Configuration gathering failed"
exit 1
}
# Debug: Verify TRANSMISSION_REMOTE is set
log "INFO" "After configuration gathering, TRANSMISSION_REMOTE=$TRANSMISSION_REMOTE"
# Step 2: Install dependencies
log "INFO" "Installing dependencies..."
install_dependencies || {
log "ERROR" "Dependency installation failed"
exit 1
}
# Step 3: Create installation directories
log "INFO" "Creating directories..."
# Make sure CONFIG_DIR is set and exported
export CONFIG_DIR=${CONFIG_DIR:-"/etc/transmission-rss-manager"}
# Call our new create_directories function
create_directories || {
log "ERROR" "Directory creation failed"
exit 1
}
# Step 4: Create configuration files only (no application files since they're from git)
log "INFO" "Creating configuration files..."
create_config_files || {
log "ERROR" "Configuration file creation failed"
exit 1
}
# Step 5: Create service files and install the service
log "INFO" "Setting up service..."
setup_service || {
log "ERROR" "Service setup failed"
exit 1
}
# Step 6: Install npm dependencies using our common function
ensure_npm_packages "$INSTALL_DIR" || {
log "ERROR" "NPM installation failed"
exit 1
}
fi
# Step 7: Set up update script
log "INFO" "Setting up update script..."
mkdir -p "${SCRIPT_DIR}/scripts"
# Check if update script exists - don't copy it to itself
if [ ! -f "${SCRIPT_DIR}/scripts/update.sh" ]; then
# First, check if we have an update script to copy
if [ -f "${SCRIPT_DIR}/update.sh" ]; then
cp "${SCRIPT_DIR}/update.sh" "${SCRIPT_DIR}/scripts/update.sh"
log "INFO" "Copied update script from root to scripts directory"
else
# Create the update script since it doesn't exist
cat > "${SCRIPT_DIR}/scripts/update.sh" << 'EOL'
#!/bin/bash
# Transmission RSS Manager - Update Script
# This script pulls the latest version from git and runs necessary updates
# Color and formatting
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
BOLD='\033[1m'
# Installation directory (should be current directory)
INSTALL_DIR=$(pwd)
# Check if we're in the right directory
if [ ! -f "$INSTALL_DIR/package.json" ] || [ ! -d "$INSTALL_DIR/modules" ]; then
echo -e "${RED}Error: This script must be run from the installation directory.${NC}"
exit 1
fi
# Get the current version
CURRENT_VERSION=$(grep -oP '"version": "\K[^"]+' package.json)
echo -e "${YELLOW}Current version: ${BOLD}$CURRENT_VERSION${NC}"
# Check for git repository
if [ ! -d ".git" ]; then
echo -e "${RED}Error: This installation was not set up using git.${NC}"
echo -e "Please use the bootstrap installer to perform a fresh installation."
exit 1
fi
# Stash any local changes
echo -e "${YELLOW}Backing up any local configuration changes...${NC}"
git stash -q
# Pull the latest changes
echo -e "${YELLOW}Pulling latest updates from git...${NC}"
git pull
if [ $? -ne 0 ]; then
echo -e "${RED}Failed to pull updates. Restoring original state...${NC}"
git stash pop -q
exit 1
fi
# Get the new version
NEW_VERSION=$(grep -oP '"version": "\K[^"]+' package.json)
echo -e "${GREEN}New version: ${BOLD}$NEW_VERSION${NC}"
# Check if update is needed
if [ "$CURRENT_VERSION" == "$NEW_VERSION" ]; then
echo -e "${GREEN}You already have the latest version.${NC}"
exit 0
fi
# Install any new npm dependencies
echo -e "${YELLOW}Installing dependencies...${NC}"
npm install
# Apply any local configuration changes
if git stash list | grep -q "stash@{0}"; then
echo -e "${YELLOW}Restoring local configuration changes...${NC}"
git stash pop -q
# Handle conflicts if any
if [ $? -ne 0 ]; then
echo -e "${RED}There were conflicts when restoring your configuration.${NC}"
echo -e "Please check the files and resolve conflicts manually."
echo -e "Your original configuration is saved in .git/refs/stash"
fi
fi
# Restart the service
echo -e "${YELLOW}Restarting service...${NC}"
if command -v systemctl &> /dev/null; then
sudo systemctl restart transmission-rss-manager
else
echo -e "${RED}Could not restart service automatically.${NC}"
echo -e "Please restart the service manually."
fi
# Update complete
echo -e "${GREEN}${BOLD}Update complete!${NC}"
echo -e "Updated from version $CURRENT_VERSION to $NEW_VERSION"
echo -e "Changes will take effect immediately."
EOL
chmod +x "${SCRIPT_DIR}/scripts/update.sh"
log "INFO" "Created update script: ${SCRIPT_DIR}/scripts/update.sh"
fi
}
# Step 8: Final setup and permissions
log "INFO" "Finalizing setup..."
finalize_setup || {
log "ERROR" "Setup finalization failed"
exit 1
}
# Installation complete
echo
echo -e "${BOLD}${GREEN}==================================================${NC}"
if [ "$IS_UPDATE" = true ]; then
echo -e "${BOLD}${GREEN} Update Complete! ${NC}"
else
echo -e "${BOLD}${GREEN} Installation Complete! ${NC}"
fi
echo -e "${BOLD}${GREEN}==================================================${NC}"
echo -e "You can access the web interface at: ${BOLD}http://localhost:$PORT${NC} or ${BOLD}http://your-server-ip:$PORT${NC}"
echo -e "You may need to configure your firewall to allow access to port $PORT"
echo
echo -e "${BOLD}Useful Commands:${NC}"
echo -e " To check the service status: ${YELLOW}systemctl status $SERVICE_NAME${NC}"
echo -e " To view logs: ${YELLOW}journalctl -u $SERVICE_NAME${NC}"
echo -e " To restart the service: ${YELLOW}systemctl restart $SERVICE_NAME${NC}"
echo -e " To update the application: ${YELLOW}Use the Update button in the System Status section${NC}"
echo
if [ "$IS_UPDATE" = true ]; then
echo -e "Thank you for updating Transmission RSS Manager!"
echo -e "The service has been restarted with the new version."
else
echo -e "Thank you for installing Transmission RSS Manager!"
fi
echo -e "${BOLD}==================================================${NC}"