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>
This commit is contained in:
MasterDraco 2025-03-07 11:38:14 +00:00
parent 83222078d9
commit 6dc2df3cee
8 changed files with 268 additions and 112 deletions

View File

@ -401,8 +401,14 @@ fi
# Step 7: Set up update script
log "INFO" "Setting up update script..."
mkdir -p "${SCRIPT_DIR}/scripts"
cp "${SCRIPT_DIR}/scripts/update.sh" "${SCRIPT_DIR}/scripts/update.sh" 2>/dev/null || {
# If copy fails, it probably doesn't exist, so we'll create it
# 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
@ -491,6 +497,8 @@ 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

View File

@ -41,9 +41,13 @@ function create_config_files() {
# Create initial config.json
echo "Creating config.json..."
mkdir -p "$CONFIG_DIR"
# Get version from package.json dynamically
VERSION=$(grep -oP '"version": "\K[^"]+' "${SCRIPT_DIR}/package.json" 2>/dev/null || echo "2.0.9")
cat > $CONFIG_DIR/config.json << EOF
{
"version": "2.0.6",
"version": "$VERSION",
"installPath": "$INSTALL_DIR",
"transmissionConfig": {
"host": "$TRANSMISSION_HOST",
@ -301,12 +305,13 @@ app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
//==============================
// Get the version from package.json
let appVersion = '2.0.6'; // Default fallback version
let appVersion;
try {
const packageJson = require('./package.json');
appVersion = packageJson.version;
} catch (err) {
console.warn('Could not read version from package.json, using default');
appVersion = '2.0.9'; // Default fallback version aligned with package.json
}
// Server status API

View File

@ -27,21 +27,19 @@ class RssFeedManager {
// Log the data path for debugging
console.log(`Data directory path set to: ${this.dataPath}`);
}
// Create the data directory synchronously to make sure it exists
// We'll always ensure the data directory exists regardless of where it's set
// Use synchronous operation to ensure directory exists immediately upon construction
try {
// Check if fs.mkdirSync is available
if (fs.mkdirSync) {
const fsSync = require('fs');
if (!fsSync.existsSync(this.dataPath)) {
fsSync.mkdirSync(this.dataPath, { recursive: true });
console.log(`Created data directory synchronously: ${this.dataPath}`);
}
}
} catch (err) {
console.warn(`Warning: Could not create data directory synchronously: ${err.message}`);
// Will try again asynchronously in ensureDataDirectory
}
// Will try again asynchronously in ensureDataDirectory when start() is called
}
// Maximum items to keep in memory to prevent memory leaks
@ -158,10 +156,13 @@ class RssFeedManager {
console.log(`Updating feed: ${feed.name || 'Unnamed'} (${feed.url})`);
try {
// Get version from package.json if available, fallback to environment or hardcoded
const version = process.env.APP_VERSION || require('../package.json').version || '2.0.9';
const response = await fetch(feed.url, {
timeout: 30000, // 30 second timeout
headers: {
'User-Agent': 'Transmission-RSS-Manager/2.0.6'
'User-Agent': `Transmission-RSS-Manager/${version}`
}
});
@ -507,11 +508,16 @@ class RssFeedManager {
}
}
/**
* Ensures the data directory exists, using a consistent approach across the application
* @returns {Promise<boolean>} true if directory exists or was created
*/
async ensureDataDirectory() {
try {
// Create data directory with recursive option (creates all parent directories if they don't exist)
await fs.mkdir(this.dataPath, { recursive: true });
console.log(`Ensured data directory exists at: ${this.dataPath}`);
return true;
} catch (error) {
// Log the error details for debugging
console.error(`Error creating data directory ${this.dataPath}:`, error);
@ -521,9 +527,10 @@ class RssFeedManager {
const { execSync } = require('child_process');
execSync(`mkdir -p "${this.dataPath}"`);
console.log(`Created data directory using fallback method: ${this.dataPath}`);
return true;
} catch (fallbackError) {
console.error('Fallback method for creating data directory also failed:', fallbackError);
throw error; // Throw the original error
console.error('All methods for creating data directory failed:', fallbackError);
throw new Error(`Failed to create data directory: ${this.dataPath}. Original error: ${error.message}`);
}
}
}

View File

@ -125,6 +125,15 @@ class TransmissionClient {
*/
async addTorrent(url, options = {}) {
try {
// Verify client is initialized
if (!this.client) {
await this.initializeConnection();
if (!this.client) {
throw new Error("Failed to initialize Transmission client");
}
}
const downloadDir = options.downloadDir || null;
const result = await this.client.addUrl(url, {
"download-dir": downloadDir,

View File

@ -101,6 +101,29 @@ function create_dir_if_not_exists() {
function ensure_npm_packages() {
local install_dir=$1
# First ensure the installation directory exists
if [ ! -d "$install_dir" ]; then
log "INFO" "Creating installation directory: $install_dir"
mkdir -p "$install_dir" || {
log "ERROR" "Failed to create installation directory: $install_dir"
return 1
}
}
# Ensure data directory exists
if [ ! -d "$install_dir/data" ]; then
log "INFO" "Creating data directory: $install_dir/data"
mkdir -p "$install_dir/data" || {
log "ERROR" "Failed to create data directory: $install_dir/data"
return 1
}
# Initialize empty data files
echo "[]" > "$install_dir/data/rss-feeds.json"
echo "[]" > "$install_dir/data/rss-items.json"
log "INFO" "Initialized empty data files"
fi
# Ensure package.json exists in the installation directory
if [ ! -f "$install_dir/package.json" ]; then
log "INFO" "Copying package.json to installation directory..."
@ -108,7 +131,7 @@ function ensure_npm_packages() {
log "ERROR" "Failed to copy package.json to installation directory"
return 1
}
fi
}
# Install NPM packages if not already installed or if it's an update
if [ ! -d "$install_dir/node_modules" ] || [ "$IS_UPDATE" = "true" ]; then
@ -199,9 +222,12 @@ function finalize_setup() {
# Make sure CONFIG_DIR exists
mkdir -p "$CONFIG_DIR"
# Get version from package.json dynamically
VERSION=$(grep -oP '"version": "\K[^"]+' "${SCRIPT_DIR}/package.json" 2>/dev/null || echo "2.0.9")
cat > $CONFIG_DIR/config.json << EOF
{
"version": "2.0.6",
"version": "$VERSION",
"transmissionConfig": {
"host": "${TRANSMISSION_HOST}",
"port": ${TRANSMISSION_PORT},

View File

@ -1,49 +1,78 @@
#!/bin/bash
# Create bi-directional symlinks for module compatibility
# Script to create symlinks for all modules in different naming styles
# This ensures compatibility with different module import styles
MODULES_DIR="$(dirname "$(dirname "$0")")/modules"
APP_DIR="$(dirname "$(dirname "$(readlink -f "$0")")")"
MODULE_DIR="$APP_DIR/modules"
echo "Creating module symlinks in $MODULES_DIR..."
echo "Creating module symlinks for compatibility..."
echo "Module directory: $MODULE_DIR"
# Check if modules directory exists
if [ ! -d "$MODULES_DIR" ]; then
echo "Error: Modules directory not found: $MODULES_DIR"
exit 1
fi
# Create a function to make bidirectional symlinks
create_module_symlinks() {
if [ -d "$MODULE_DIR" ]; then
# Create symlinks for hyphenated modules
for module in "$MODULE_DIR"/*-*.js; do
if [ -f "$module" ]; then
# Convert hyphenated to camelCase
BASE_NAME=$(basename "$module")
CAMEL_NAME=$(echo "$BASE_NAME" | sed -E 's/-([a-z])/\U\1/g')
# Create bidirectional symlinks
create_bidirectional_links() {
local hyphenated="$1"
local camelCase="$2"
# Create camelCase symlink if needed
if [ ! -f "$MODULE_DIR/$CAMEL_NAME" ] && [ ! -L "$MODULE_DIR/$CAMEL_NAME" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$CAMEL_NAME"
echo "Created symlink: $CAMEL_NAME -> $BASE_NAME"
fi
# Check if hyphenated version exists
if [ -f "$MODULES_DIR/$hyphenated.js" ]; then
# Create camelCase symlink
ln -sf "$hyphenated.js" "$MODULES_DIR/$camelCase.js"
echo "Created symlink: $camelCase.js -> $hyphenated.js"
# Create extension-less symlink for both versions
NO_EXT_BASE="${BASE_NAME%.js}"
if [ ! -f "$MODULE_DIR/$NO_EXT_BASE" ] && [ ! -L "$MODULE_DIR/$NO_EXT_BASE" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$NO_EXT_BASE"
echo "Created symlink: $NO_EXT_BASE -> $BASE_NAME"
fi
# Create symlinks without extension
ln -sf "$hyphenated.js" "$MODULES_DIR/$hyphenated"
ln -sf "$hyphenated.js" "$MODULES_DIR/$camelCase"
echo "Created extension-less symlinks: $hyphenated, $camelCase -> $hyphenated.js"
# Check if camelCase version exists
elif [ -f "$MODULES_DIR/$camelCase.js" ]; then
# Create hyphenated symlink
ln -sf "$camelCase.js" "$MODULES_DIR/$hyphenated.js"
echo "Created symlink: $hyphenated.js -> $camelCase.js"
NO_EXT_CAMEL="${CAMEL_NAME%.js}"
if [ ! -f "$MODULE_DIR/$NO_EXT_CAMEL" ] && [ ! -L "$MODULE_DIR/$NO_EXT_CAMEL" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$NO_EXT_CAMEL"
echo "Created symlink: $NO_EXT_CAMEL -> $BASE_NAME"
fi
fi
done
# Create symlinks without extension
ln -sf "$camelCase.js" "$MODULES_DIR/$hyphenated"
ln -sf "$camelCase.js" "$MODULES_DIR/$camelCase"
echo "Created extension-less symlinks: $hyphenated, $camelCase -> $camelCase.js"
# Create symlinks for camelCase modules (only non-symlinked files)
for module in "$MODULE_DIR"/[a-z]*[A-Z]*.js; do
if [ -f "$module" ] && [ ! -L "$module" ]; then
# Convert camelCase to hyphenated
BASE_NAME=$(basename "$module")
HYPHEN_NAME=$(echo "$BASE_NAME" | sed -E 's/([a-z])([A-Z])/\1-\L\2/g')
# Create hyphenated symlink if needed
if [ ! -f "$MODULE_DIR/$HYPHEN_NAME" ] && [ ! -L "$MODULE_DIR/$HYPHEN_NAME" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$HYPHEN_NAME"
echo "Created symlink: $HYPHEN_NAME -> $BASE_NAME"
fi
# Create extension-less symlink for both versions
NO_EXT_BASE="${BASE_NAME%.js}"
if [ ! -f "$MODULE_DIR/$NO_EXT_BASE" ] && [ ! -L "$MODULE_DIR/$NO_EXT_BASE" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$NO_EXT_BASE"
echo "Created symlink: $NO_EXT_BASE -> $BASE_NAME"
fi
NO_EXT_HYPHEN="${HYPHEN_NAME%.js}"
if [ ! -f "$MODULE_DIR/$NO_EXT_HYPHEN" ] && [ ! -L "$MODULE_DIR/$NO_EXT_HYPHEN" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$NO_EXT_HYPHEN"
echo "Created symlink: $NO_EXT_HYPHEN -> $BASE_NAME"
fi
fi
done
echo "Module symlinks created successfully"
else
echo "Warning: Neither $hyphenated.js nor $camelCase.js exists in $MODULES_DIR"
echo "Error: Module directory not found at $MODULE_DIR"
exit 1
fi
}
# Create symlinks for all modules
create_bidirectional_links "rss-feed-manager" "rssFeedManager"
create_bidirectional_links "transmission-client" "transmissionClient"
create_bidirectional_links "post-processor" "postProcessor"
echo "Module symlinks created successfully."
# Execute the symlink creation function
create_module_symlinks

View File

@ -25,17 +25,25 @@ if [ ! -d "$DATA_DIR" ]; then
fi
# Set permissions
chmod -R 755 "$DATA_DIR"
chmod -R 755 "$DATA_DIR" || {
echo "Warning: Failed to set permissions on data directory"
}
# Check for RSS files
if [ ! -f "$DATA_DIR/rss-feeds.json" ]; then
echo "Creating initial empty rss-feeds.json file"
echo "[]" > "$DATA_DIR/rss-feeds.json"
echo "[]" > "$DATA_DIR/rss-feeds.json" || {
echo "ERROR: Failed to create rss-feeds.json file"
exit 1
}
fi
if [ ! -f "$DATA_DIR/rss-items.json" ]; then
echo "Creating initial empty rss-items.json file"
echo "[]" > "$DATA_DIR/rss-items.json"
echo "[]" > "$DATA_DIR/rss-items.json" || {
echo "ERROR: Failed to create rss-items.json file"
exit 1
}
fi
# Find the node executable path
@ -57,12 +65,74 @@ if [ -z "$NODE_PATH" ]; then
fi
# Create module symlinks to ensure compatibility
if [ -f "$APP_DIR/scripts/create-module-links.sh" ]; then
echo "Creating module symlinks for compatibility..."
bash "$APP_DIR/scripts/create-module-links.sh"
else
echo "Warning: Module symlink script not found"
fi
echo "Creating module symlinks for compatibility..."
MODULE_DIR="$APP_DIR/modules"
# Create a function to make bidirectional symlinks
create_module_symlinks() {
if [ -d "$MODULE_DIR" ]; then
# Create symlinks for hyphenated modules
for module in "$MODULE_DIR"/*-*.js; do
if [ -f "$module" ]; then
# Convert hyphenated to camelCase
BASE_NAME=$(basename "$module")
CAMEL_NAME=$(echo "$BASE_NAME" | sed -E 's/-([a-z])/\U\1/g')
# Create camelCase symlink if needed
if [ ! -f "$MODULE_DIR/$CAMEL_NAME" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$CAMEL_NAME"
echo "Created symlink: $CAMEL_NAME -> $BASE_NAME"
fi
# Create extension-less symlink for both versions
NO_EXT_BASE="${BASE_NAME%.js}"
if [ ! -f "$MODULE_DIR/$NO_EXT_BASE" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$NO_EXT_BASE"
echo "Created symlink: $NO_EXT_BASE -> $BASE_NAME"
fi
NO_EXT_CAMEL="${CAMEL_NAME%.js}"
if [ ! -f "$MODULE_DIR/$NO_EXT_CAMEL" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$NO_EXT_CAMEL"
echo "Created symlink: $NO_EXT_CAMEL -> $BASE_NAME"
fi
fi
done
# Create symlinks for camelCase modules
for module in "$MODULE_DIR"/[a-z]*[A-Z]*.js; do
if [ -f "$module" ]; then
# Convert camelCase to hyphenated
BASE_NAME=$(basename "$module")
HYPHEN_NAME=$(echo "$BASE_NAME" | sed -E 's/([a-z])([A-Z])/\1-\L\2/g')
# Create hyphenated symlink if needed
if [ ! -f "$MODULE_DIR/$HYPHEN_NAME" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$HYPHEN_NAME"
echo "Created symlink: $HYPHEN_NAME -> $BASE_NAME"
fi
# Create extension-less symlink for both versions
NO_EXT_BASE="${BASE_NAME%.js}"
if [ ! -f "$MODULE_DIR/$NO_EXT_BASE" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$NO_EXT_BASE"
echo "Created symlink: $NO_EXT_BASE -> $BASE_NAME"
fi
NO_EXT_HYPHEN="${HYPHEN_NAME%.js}"
if [ ! -f "$MODULE_DIR/$NO_EXT_HYPHEN" ]; then
ln -sf "$BASE_NAME" "$MODULE_DIR/$NO_EXT_HYPHEN"
echo "Created symlink: $NO_EXT_HYPHEN -> $BASE_NAME"
fi
fi
done
else
echo "Warning: Module directory not found at $MODULE_DIR"
fi
}
# Execute the symlink creation function
create_module_symlinks
# Start the application
cd "$APP_DIR" || { echo "Failed to change to application directory"; exit 1; }

View File

@ -21,60 +21,62 @@ const semver = require('semver'); // For semantic version comparison
// Import custom modules
let RssFeedManager, TransmissionClient, PostProcessor;
// Helper function to try multiple module paths
function tryRequire(modulePaths) {
/**
* Helper function to try multiple module paths
* This function tries to require a module using different naming conventions
* to work around issues with module resolution in different Node.js environments
*
* @param {string} baseName - The base module name without extension or path
* @returns {Object} The loaded module
* @throws {Error} If module cannot be loaded from any path
*/
function loadModule(baseName) {
// Generate all possible module paths
const paths = [
`./modules/${baseName}.js`, // With extension
`./modules/${baseName}`, // Without extension
// Convert hyphenated to camelCase
`./modules/${baseName.replace(/-([a-z])/g, (_, c) => c.toUpperCase())}.js`,
`./modules/${baseName.replace(/-([a-z])/g, (_, c) => c.toUpperCase())}`,
// Convert camelCase to hyphenated
`./modules/${baseName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}.js`,
`./modules/${baseName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}`
];
let lastError = null;
for (const modulePath of modulePaths) {
for (const modulePath of paths) {
try {
return require(modulePath);
} catch (err) {
console.log(`Attempted to load module from ${modulePath}, but got error: ${err.message}`);
if (err.code !== 'MODULE_NOT_FOUND' || !err.message.includes(modulePath)) {
// This is a real error in the module, not just not finding it
throw err;
}
lastError = err;
}
}
throw lastError;
// If we get here, we couldn't load the module from any path
const error = new Error(`Could not load module '${baseName}' from any path. Original error: ${lastError.message}`);
error.original = lastError;
throw error;
}
// Try loading modules with various namings (with and without .js extension, with hyphens or camelCase)
// Try loading modules with improved error reporting
try {
const rssPaths = [
'./modules/rss-feed-manager.js',
'./modules/rssFeedManager.js',
'./modules/rss-feed-manager',
'./modules/rssFeedManager'
];
RssFeedManager = tryRequire(rssPaths);
RssFeedManager = loadModule('rss-feed-manager');
console.log('Successfully loaded RssFeedManager module');
} catch (err) {
console.error('Failed to load RssFeedManager module:', err);
process.exit(1);
}
try {
const transmissionPaths = [
'./modules/transmission-client.js',
'./modules/transmissionClient.js',
'./modules/transmission-client',
'./modules/transmissionClient'
];
TransmissionClient = tryRequire(transmissionPaths);
TransmissionClient = loadModule('transmission-client');
console.log('Successfully loaded TransmissionClient module');
} catch (err) {
console.error('Failed to load TransmissionClient module:', err);
process.exit(1);
}
try {
const processorPaths = [
'./modules/post-processor.js',
'./modules/postProcessor.js',
'./modules/post-processor',
'./modules/postProcessor'
];
PostProcessor = tryRequire(processorPaths);
PostProcessor = loadModule('post-processor');
console.log('Successfully loaded PostProcessor module');
} catch (err) {
console.error('Failed to load PostProcessor module:', err);
console.error('Fatal error loading required module:', err.message);
console.error('Please make sure all module files exist and are valid JavaScript');
process.exit(1);
}
@ -456,7 +458,7 @@ app.get('/api/status', authenticateJWT, async (req, res) => {
res.json({
success: true,
status: 'running',
version: '2.0.6',
version: APP_VERSION, // Use the dynamic APP_VERSION from package.json
transmissionConnected: transmissionStatus.connected,
transmissionVersion: transmissionStatus.version,
transmissionStats: {