!/bin/bash
===============================================================================
UNIT3D Auto-Installer v1.2 (Fixed)
For UNIT3D Community Edition v9.2.0 (Laravel 12)
Supports: Ubuntu 22.04 LTS / Ubuntu 24.04 LTS
===============================================================================
set -e
Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
Default values (customize these or pass as arguments)
DOMAIN=""
MYSQL_UNIT3D_PASS=""
OWNER_USERNAME="UNIT3D"
OWNER_EMAIL=""
OWNER_PASSWORD=""
TMDB_API_KEY=""
SMTP_HOST=""
SMTP_PORT="587"
SMTP_USER=""
SMTP_PASS=""
SMTP_FROM_ADDRESS=""
SMTP_FROM_NAME=""
INSTALL_PATH="/var/www/html"
PHP_VERSION=""
MEILI_KEY=""
===============================================================================
FUNCTIONS
===============================================================================
print_banner() {
echo -e "${BLUE}"
echo ""
echo " UNIT3D Auto-Installer "
echo " Private Torrent Tracker "
echo " For Ubuntu 22.04 / 24.04 LTS "
echo " v1.2 (Fixed for 9.2.0) "
echo ""
echo -e "${NC}"
}
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root"
exit 1
fi
}
check_os() {
if [[ -f /etc/os-release ]]; then
. /etc/os-release
OS=$ID
VERSION=$VERSION_ID
else
log_error "Cannot detect OS version"
exit 1
fi
}
generate_password() {
openssl rand -base64 16 | tr -dc 'a-zA-Z0-9' | head -c 20
}
interactive_setup() {
echo ""
log_info "Starting interactive setup..."
echo ""
}
install_dependencies() {
log_info "Updating system packages..."
apt update && apt upgrade -y
}
configure_mysql() {
log_info "Configuring MySQL..."
}
configure_redis() {
log_info "Configuring Redis..."
}
configure_meilisearch() {
log_info "Configuring Meilisearch..."
[Unit]
Description=Meilisearch
After=network.target
[Service]
User=root
ExecStart=/usr/local/bin/meilisearch --http-addr 127.0.0.1:7700 --master-key ${MEILI_KEY} --db-path /var/lib/meilisearch --env production
Restart=always
[Install]
WantedBy=multi-user.target
EOF
}
configure_php() {
log_info "Configuring PHP $PHP_VERSION..."
; OPcache settings for UNIT3D
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=0
opcache.validate_timestamps=0
opcache.save_comments=1
opcache.fast_shutdown=1
EOF
}
configure_nginx() {
log_info "Configuring Nginx..."
server {
listen 80;
listen [::]:80;
server_name ${DOMAIN} www.${DOMAIN};
}
EOF
}
install_unit3d() {
log_info "Installing UNIT3D..."
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | # Add safe directory for git
git config --global --add safe.directory ${INSTALL_PATH}
# Remove existing installation
rm -rf ${INSTALL_PATH}
# Clone UNIT3D Community Edition
git clone https://github.com/HDInnovations/UNIT3D-Community-Edition.git ${INSTALL_PATH}
cd ${INSTALL_PATH}
# Checkout the specific v9.2.0 release
log_info "Checking out v9.2.0..."
git checkout v9.2.0 2>/dev/null || git checkout main
log_info "Checked out UNIT3D version: $(git describe --tags 2>/dev/null || git rev-parse --short HEAD)"
# Create .env file from example
cp .env.example .env
# Configure .env - using sed to replace existing values
sed -i "s|APP_NAME=.*|APP_NAME=\"${DOMAIN}\"|" .env
sed -i "s|APP_ENV=.*|APP_ENV=prod|" .env
sed -i "s|APP_DEBUG=.*|APP_DEBUG=false|" .env
sed -i "s|APP_URL=.*|APP_URL=https://${DOMAIN}|" .env
# Database
sed -i "s|DB_CONNECTION=.*|DB_CONNECTION=mysql|" .env
sed -i "s|DB_HOST=.*|DB_HOST=127.0.0.1|" .env
sed -i "s|DB_PORT=.*|DB_PORT=3306|" .env
sed -i "s|DB_DATABASE=.*|DB_DATABASE=unit3d|" .env
sed -i "s|DB_USERNAME=.*|DB_USERNAME=unit3d|" .env
sed -i "s|DB_PASSWORD=.*|DB_PASSWORD=${MYSQL_UNIT3D_PASS}|" .env
# Echo address for chat (Vite env var)
sed -i "s|VITE_ECHO_ADDRESS=.*|VITE_ECHO_ADDRESS=https://${DOMAIN}|" .env
# Broadcasting, Cache, Session, Queue (matching .env.example variable names)
sed -i "s|BROADCAST_CONNECTION=.*|BROADCAST_CONNECTION=redis|" .env
sed -i "s|CACHE_STORE=.*|CACHE_STORE=redis|" .env
sed -i "s|SESSION_DRIVER=.*|SESSION_DRIVER=redis|" .env
sed -i "s|SESSION_CONNECTION=.*|SESSION_CONNECTION=session|" .env
sed -i "s|SESSION_SECURE_COOKIE=.*|SESSION_SECURE_COOKIE=true|" .env
sed -i "s|QUEUE_CONNECTION=.*|QUEUE_CONNECTION=redis|" .env
# Redis
sed -i "s|REDIS_HOST=.*|REDIS_HOST=127.0.0.1|" .env
sed -i "s|REDIS_PASSWORD=.*|REDIS_PASSWORD=null|" .env
sed -i "s|REDIS_PORT=.*|REDIS_PORT=6379|" .env
# Meilisearch
sed -i "s|MEILISEARCH_HOST=.*|MEILISEARCH_HOST=http://127.0.0.1:7700|" .env
sed -i "s|MEILISEARCH_KEY=.*|MEILISEARCH_KEY=${MEILI_KEY}|" .env
# Mail
sed -i "s|MAIL_MAILER=.*|MAIL_MAILER=smtp|" .env
sed -i "s|MAIL_HOST=.*|MAIL_HOST=${SMTP_HOST}|" .env
sed -i "s|MAIL_PORT=.*|MAIL_PORT=${SMTP_PORT}|" .env
sed -i "s|MAIL_USERNAME=.*|MAIL_USERNAME=${SMTP_USER}|" .env
sed -i "s|MAIL_PASSWORD=.*|MAIL_PASSWORD=${SMTP_PASS}|" .env
sed -i "s|MAIL_ENCRYPTION=.*|MAIL_ENCRYPTION=tls|" .env
sed -i "s|MAIL_FROM_ADDRESS=.*|MAIL_FROM_ADDRESS='${SMTP_FROM_ADDRESS}'|" .env
sed -i "s|MAIL_FROM_NAME=.*|MAIL_FROM_NAME='${SMTP_FROM_NAME}'|" .env
# TMDB
if [[ -n "$TMDB_API_KEY" ]]; then
sed -i "s|TMDB_API_KEY=.*|TMDB_API_KEY=${TMDB_API_KEY}|" .env
fi
# Owner credentials
sed -i "s|DEFAULT_OWNER_NAME=.*|DEFAULT_OWNER_NAME=${OWNER_USERNAME}|" .env
sed -i "s|DEFAULT_OWNER_EMAIL=.*|DEFAULT_OWNER_EMAIL=${OWNER_EMAIL}|" .env
sed -i "s|DEFAULT_OWNER_PASSWORD=.*|DEFAULT_OWNER_PASSWORD=${OWNER_PASSWORD}|" .env
# Set ownership BEFORE composer install so www-data can write
chown -R www-data:www-data ${INSTALL_PATH}
# Install Composer dependencies (as www-data)
log_info "Installing Composer dependencies..."
sudo -u www-data composer install --no-dev --optimize-autoloader --no-interaction --working-dir=${INSTALL_PATH}
# Install NPM dependencies and build assets using Bun (as www-data)
log_info "Installing NPM dependencies and building assets with Bun..."
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"
cd ${INSTALL_PATH}
sudo -E -u www-data bash -c "export PATH=$BUN_INSTALL/bin:\$PATH && cd ${INSTALL_PATH} && bun install && bun run build"
# If bun as www-data fails, fallback to running as root then fixing permissions
if [[ $? -ne 0 ]]; then
log_warn "Bun as www-data failed, running as root and fixing permissions..."
cd ${INSTALL_PATH}
bun install
bun run build
chown -R www-data:www-data ${INSTALL_PATH}
fi
# Set permissions
chmod -R 755 ${INSTALL_PATH}
chmod -R 775 ${INSTALL_PATH}/storage
chmod -R 775 ${INSTALL_PATH}/bootstrap/cache
# Generate app key using artisan (the only correct way)
log_info "Generating application key..."
sudo -u www-data php artisan key:generate --force
# Run migrations and seed
log_info "Running database migrations and seeding..."
sudo -u www-data php artisan migrate --force --seed
# Create storage link
sudo -u www-data php artisan storage:link
# Import Meilisearch indexes
log_info "Setting up Meilisearch indexes..."
sudo -u www-data php artisan scout:sync-index-settings || true
sudo -u www-data php artisan scout:import "App\Models\Torrent" || true
# Cache configuration for production
log_info "Optimizing application..."
sudo -u www-data php artisan config:cache
sudo -u www-data php artisan route:cache
sudo -u www-data php artisan view:cache
log_info "UNIT3D installed successfully"
|
}
configure_laravel_echo_server() {
log_info "Configuring Laravel Echo Server..."
{
"authHost": "https://${DOMAIN}",
"authEndpoint": "/broadcasting/auth",
"clients": [],
"database": "redis",
"databaseConfig": {
"redis": {
"host": "127.0.0.1",
"port": "6379"
}
},
"devMode": false,
"host": "127.0.0.1",
"port": "6001",
"protocol": "http",
"socketio": {},
"sslCertPath": "",
"sslKeyPath": "",
"sslCertChainPath": "",
"sslPassphrase": "",
"subscribers": {
"http": true,
"redis": true
},
"apiOriginAllow": {
"allowCors": true,
"allowOrigin": "https://${DOMAIN}",
"allowMethods": "GET, POST",
"allowHeaders": "Origin, Content-Type, X-Auth-Token, X-Requested-With, Accept, Authorization, X-CSRF-TOKEN, X-Socket-Id"
}
}
EOF
}
configure_supervisor() {
log_info "Configuring Supervisor for queue workers and chat..."
[program:unit3d-queue]
process_name=%(program_name)s_%(process_num)02d
command=php ${INSTALL_PATH}/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=${INSTALL_PATH}/storage/logs/queue.log
stopwaitsecs=3600
[program:unit3d-echo]
process_name=%(program_name)s_%(process_num)02d
command=laravel-echo-server start --dir=${INSTALL_PATH}
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=${INSTALL_PATH}/storage/logs/echo.log
EOF
}
configure_cron() {
log_info "Configuring Laravel scheduler cron..."
}
setup_ssl() {
log_info "Setting up SSL with Let's Encrypt..."
}
save_credentials() {
CREDS_FILE="/root/unit3d-credentials.txt"
===============================================================================
UNIT3D Installation Credentials
Generated: $(date)
===============================================================================
DOMAIN: ${DOMAIN}
URL: https://${DOMAIN}
OWNER LOGIN:
Username: ${OWNER_USERNAME}
Email: ${OWNER_EMAIL}
Password: ${OWNER_PASSWORD}
MYSQL:
Root: Uses socket auth (no password, run mysql as root)
UNIT3D Database: unit3d
UNIT3D User: unit3d
UNIT3D Password: ${MYSQL_UNIT3D_PASS}
MEILISEARCH:
API Key: ${MEILI_KEY}
INSTALLATION PATH: ${INSTALL_PATH}
PHP VERSION: ${PHP_VERSION}
IMPORTANT FILES:
.env: ${INSTALL_PATH}/.env
Nginx: /etc/nginx/sites-available/unit3d
Supervisor: /etc/supervisor/conf.d/unit3d.conf
Echo Server: ${INSTALL_PATH}/laravel-echo-server.json
USEFUL COMMANDS:
Restart PHP-FPM: systemctl restart php${PHP_VERSION}-fpm
Restart Nginx: systemctl restart nginx
Restart Workers: supervisorctl restart all
Clear Cache: cd ${INSTALL_PATH} && sudo -u www-data php artisan optimize:clear
Re-cache: cd ${INSTALL_PATH} && sudo -u www-data php artisan config:cache && sudo -u www-data php artisan route:cache && sudo -u www-data php artisan view:cache
Queue Status: supervisorctl status
===============================================================================
KEEP THIS FILE SECURE AND DELETE AFTER SAVING CREDENTIALS!
===============================================================================
EOF
}
print_completion() {
echo ""
echo -e "${GREEN}${NC}"
echo -e "${GREEN} UNIT3D Installation Complete! ${NC}"
echo -e "${GREEN}${NC}"
echo ""
echo -e "Your tracker is now available at: ${BLUE}https://${DOMAIN}${NC}"
echo ""
echo -e "Login with:"
echo -e " Username: ${YELLOW}${OWNER_USERNAME}${NC}"
echo -e " Password: ${YELLOW}${OWNER_PASSWORD}${NC}"
echo ""
echo -e "Credentials saved to: ${YELLOW}/root/unit3d-credentials.txt${NC}"
echo ""
echo -e "${RED}IMPORTANT: Save your credentials and delete the file!${NC}"
echo ""
}
===============================================================================
MAIN
===============================================================================
main() {
print_banner
check_root
check_os
interactive_setup
}
Run main function
main "$@"