diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..a15235f --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,368 @@ +# VidRip Production Deployment Guide + +This guide covers deploying VidRip in a production environment. + +## Quick Start (Simple Deployment) + +The simplest way to run VidRip in production: + +```bash +# Make scripts executable (if not already) +chmod +x start-production.sh stop-production.sh + +# Start the service +./start-production.sh + +# Stop the service +./stop-production.sh +``` + +The `start-production.sh` script will: +- Check system requirements (Node.js 18+, yt-dlp) +- Install dependencies +- Build backend and frontend +- Start the backend server in the background +- Create log files in `logs/` directory + +## Production Deployment (Recommended) + +For a robust production setup, use systemd for process management: + +### 1. Install System Requirements + +```bash +# Install Node.js 18+ (if not installed) +curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - +sudo apt-get install -y nodejs + +# Install yt-dlp +sudo apt-get install -y python3-pip +pip3 install yt-dlp +# OR +sudo wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O /usr/local/bin/yt-dlp +sudo chmod a+rx /usr/local/bin/yt-dlp +``` + +### 2. Deploy Application Files + +```bash +# Create application directory +sudo mkdir -p /var/www/vidrip +sudo chown $USER:$USER /var/www/vidrip + +# Copy files to production location +cp -r /path/to/vidrip/* /var/www/vidrip/ + +# Create required directories +sudo mkdir -p /var/log/vidrip +sudo chown www-data:www-data /var/log/vidrip +``` + +### 3. Build the Application + +```bash +cd /var/www/vidrip + +# Install dependencies +npm run install:all + +# Build backend +cd backend +npm run build + +# Build frontend +cd ../frontend +npm run build +``` + +### 4. Configure Backend (Optional) + +Create `/var/www/vidrip/backend/.env`: + +```env +NODE_ENV=production +PORT=3001 +``` + +### 5. Setup Systemd Service + +```bash +# Copy service file +sudo cp /var/www/vidrip/vidrip.service /etc/systemd/system/ + +# Edit the service file to match your paths and user +sudo nano /etc/systemd/system/vidrip.service + +# Reload systemd +sudo systemctl daemon-reload + +# Enable service to start on boot +sudo systemctl enable vidrip + +# Start service +sudo systemctl start vidrip + +# Check status +sudo systemctl status vidrip +``` + +### 6. Setup Nginx Reverse Proxy + +Create `/etc/nginx/sites-available/vidrip`: + +```nginx +server { + listen 80; + server_name your-domain.com; + + # Serve frontend static files + location / { + root /var/www/vidrip/frontend/dist; + try_files $uri $uri/ /index.html; + } + + # Proxy API requests to backend + location /api/ { + proxy_pass http://localhost:3001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Serve downloaded video files + location /downloads/ { + alias /var/www/vidrip/backend/downloads/; + add_header Content-Type video/mp4; + } + + # Increase upload size if needed + client_max_body_size 100M; +} +``` + +Enable the site: + +```bash +sudo ln -s /etc/nginx/sites-available/vidrip /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl reload nginx +``` + +### 7. Setup SSL with Let's Encrypt (Recommended) + +```bash +sudo apt-get install certbot python3-certbot-nginx +sudo certbot --nginx -d your-domain.com +``` + +## Systemd Service Management + +```bash +# Start service +sudo systemctl start vidrip + +# Stop service +sudo systemctl stop vidrip + +# Restart service +sudo systemctl restart vidrip + +# View status +sudo systemctl status vidrip + +# View logs +sudo journalctl -u vidrip -f + +# View application logs +tail -f /var/log/vidrip/vidrip.log +tail -f /var/log/vidrip/vidrip-error.log +``` + +## PM2 Alternative (Node.js Process Manager) + +If you prefer PM2 over systemd: + +```bash +# Install PM2 globally +sudo npm install -g pm2 + +# Start application +cd /var/www/vidrip/backend +pm2 start dist/server.js --name vidrip + +# Save PM2 configuration +pm2 save + +# Setup PM2 to start on boot +pm2 startup +# Follow the instructions shown + +# View logs +pm2 logs vidrip + +# Monitor +pm2 monit + +# Restart +pm2 restart vidrip +``` + +## Environment Variables + +Configure in `/var/www/vidrip/backend/.env`: + +```env +NODE_ENV=production +PORT=3001 +``` + +## Monitoring and Maintenance + +### Check Service Health + +```bash +# Check if backend is running +curl http://localhost:3001/api/config + +# Check disk space (downloads can fill up) +df -h /var/www/vidrip/backend/downloads +``` + +### Log Rotation + +Create `/etc/logrotate.d/vidrip`: + +``` +/var/log/vidrip/*.log { + daily + rotate 14 + compress + delaycompress + notifempty + create 0640 www-data www-data + sharedscripts + postrotate + systemctl reload vidrip > /dev/null 2>&1 || true + endscript +} +``` + +### Backup Strategy + +```bash +# Backup database +cp /var/www/vidrip/backend/data.db /backups/vidrip-$(date +%Y%m%d).db + +# Backup downloads (optional, can be large) +rsync -av /var/www/vidrip/backend/downloads/ /backups/downloads/ +``` + +## Firewall Configuration + +```bash +# Allow HTTP/HTTPS +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp + +# Backend should NOT be exposed directly +# Only accessible via nginx proxy +``` + +## Troubleshooting + +### Service won't start + +```bash +# Check logs +sudo journalctl -u vidrip -n 50 + +# Check if port is already in use +sudo netstat -tlnp | grep 3001 + +# Check file permissions +ls -la /var/www/vidrip/backend/dist/server.js +``` + +### Database locked errors + +```bash +# Check database permissions +ls -la /var/www/vidrip/backend/data.db + +# Ensure only one process is accessing database +ps aux | grep vidrip +``` + +### yt-dlp issues + +```bash +# Update yt-dlp +pip3 install --upgrade yt-dlp + +# Check if it's in PATH +which yt-dlp + +# Test manually +yt-dlp --version +``` + +## Security Checklist + +- [ ] Run backend as non-root user (www-data) +- [ ] Enable firewall (ufw) +- [ ] Setup SSL/TLS certificates +- [ ] Restrict database file permissions +- [ ] Configure nginx security headers +- [ ] Enable log rotation +- [ ] Regular yt-dlp updates +- [ ] Monitor disk space for downloads +- [ ] Setup fail2ban (optional) +- [ ] Regular security updates + +## Performance Tuning + +### Download Directory Management + +Monitor download directory size: + +```bash +# Check total size +du -sh /var/www/vidrip/backend/downloads + +# Find large files +du -h /var/www/vidrip/backend/downloads/* | sort -rh | head -20 +``` + +### Database Optimization + +```bash +# Vacuum database periodically +sqlite3 /var/www/vidrip/backend/data.db "VACUUM;" +``` + +## Updating the Application + +```bash +# Pull latest code +cd /var/www/vidrip +git pull + +# Install dependencies +npm run install:all + +# Rebuild backend +cd backend +npm run build + +# Rebuild frontend +cd ../frontend +npm run build + +# Restart service +sudo systemctl restart vidrip +``` diff --git a/backend/src/db/database.ts b/backend/src/db/database.ts index 1f49b3d..f83cfd1 100644 --- a/backend/src/db/database.ts +++ b/backend/src/db/database.ts @@ -153,11 +153,9 @@ export const videoOperations = { }, updateStatus: (id: number, status: string, error?: string) => { - if (error !== undefined) { - return db.prepare('UPDATE videos SET status = ?, error = ? WHERE id = ?') - .run(status, error, id); - } - return db.prepare('UPDATE videos SET status = ? WHERE id = ?').run(status, id); + const errorValue = error !== undefined ? error : null; + return db.prepare('UPDATE videos SET status = ?, error = ? WHERE id = ?') + .run(status, errorValue, id); }, markCompleted: (id: number, filePath: string, fileSize: number) => { diff --git a/start-production.sh b/start-production.sh new file mode 100755 index 0000000..f081e29 --- /dev/null +++ b/start-production.sh @@ -0,0 +1,264 @@ +#!/bin/bash + +################################################################################ +# VidRip Production Startup Script +################################################################################ +# This script builds and runs the VidRip service in production mode. +# It handles dependency checks, builds both frontend and backend, and starts +# the backend server with proper process management. +################################################################################ + +set -e # Exit on error +set -u # Exit on undefined variable + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# Configuration +BACKEND_DIR="$SCRIPT_DIR/backend" +FRONTEND_DIR="$SCRIPT_DIR/frontend" +LOG_DIR="$SCRIPT_DIR/logs" +PID_FILE="$SCRIPT_DIR/vidrip.pid" +LOG_FILE="$LOG_DIR/vidrip.log" +ERROR_LOG_FILE="$LOG_DIR/vidrip-error.log" + +################################################################################ +# Helper Functions +################################################################################ + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +check_command() { + if ! command -v "$1" &> /dev/null; then + log_error "$1 is not installed or not in PATH" + return 1 + fi + log_success "$1 is installed" + return 0 +} + +cleanup_on_exit() { + log_info "Cleaning up..." + if [ -f "$PID_FILE" ]; then + rm -f "$PID_FILE" + fi +} + +################################################################################ +# Pre-flight Checks +################################################################################ + +log_info "Starting VidRip production deployment..." +echo "" + +log_info "Checking system requirements..." + +# Check for Node.js +if ! check_command node; then + log_error "Node.js is required but not installed" + exit 1 +fi + +NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1) +if [ "$NODE_VERSION" -lt 18 ]; then + log_error "Node.js version 18 or higher is required (found v$NODE_VERSION)" + exit 1 +fi +log_success "Node.js version: $(node --version)" + +# Check for npm +if ! check_command npm; then + log_error "npm is required but not installed" + exit 1 +fi + +# Check for yt-dlp +if ! check_command yt-dlp; then + log_warning "yt-dlp is not installed or not in PATH" + log_warning "Install with: pip install yt-dlp OR brew install yt-dlp" + log_warning "The application will not be able to download videos without yt-dlp" +else + log_success "yt-dlp version: $(yt-dlp --version)" +fi + +echo "" + +################################################################################ +# Check for existing process +################################################################################ + +if [ -f "$PID_FILE" ]; then + OLD_PID=$(cat "$PID_FILE") + if ps -p "$OLD_PID" > /dev/null 2>&1; then + log_error "VidRip is already running (PID: $OLD_PID)" + log_info "Stop it first with: kill $OLD_PID" + exit 1 + else + log_warning "Stale PID file found, removing..." + rm -f "$PID_FILE" + fi +fi + +################################################################################ +# Create necessary directories +################################################################################ + +log_info "Creating necessary directories..." +mkdir -p "$LOG_DIR" +mkdir -p "$BACKEND_DIR/downloads" +mkdir -p "$BACKEND_DIR/dist" +log_success "Directories created" + +echo "" + +################################################################################ +# Install Dependencies +################################################################################ + +log_info "Installing dependencies..." + +# Backend dependencies +log_info "Installing backend dependencies..." +cd "$BACKEND_DIR" +if [ ! -d "node_modules" ]; then + npm install --production=false +else + log_info "Backend node_modules already exists, skipping install" +fi +log_success "Backend dependencies installed" + +# Frontend dependencies +log_info "Installing frontend dependencies..." +cd "$FRONTEND_DIR" +if [ ! -d "node_modules" ]; then + npm install --production=false +else + log_info "Frontend node_modules already exists, skipping install" +fi +log_success "Frontend dependencies installed" + +cd "$SCRIPT_DIR" +echo "" + +################################################################################ +# Build Applications +################################################################################ + +log_info "Building applications..." + +# Build backend +log_info "Building backend (TypeScript compilation)..." +cd "$BACKEND_DIR" +npm run build +if [ ! -f "dist/server.js" ]; then + log_error "Backend build failed - dist/server.js not found" + exit 1 +fi +log_success "Backend built successfully" + +# Build frontend +log_info "Building frontend (Vite production build)..." +cd "$FRONTEND_DIR" +npm run build +if [ ! -d "dist" ]; then + log_error "Frontend build failed - dist directory not found" + exit 1 +fi +log_success "Frontend built successfully" + +cd "$SCRIPT_DIR" +echo "" + +################################################################################ +# Start Backend Server +################################################################################ + +log_info "Starting VidRip backend server..." + +# Setup environment +export NODE_ENV=production + +# Start the backend server +cd "$BACKEND_DIR" + +# Run in background with output redirected to log files +nohup node dist/server.js > "$LOG_FILE" 2> "$ERROR_LOG_FILE" & +SERVER_PID=$! + +# Save PID +echo "$SERVER_PID" > "$PID_FILE" + +# Wait a moment and check if process is still running +sleep 2 +if ! ps -p "$SERVER_PID" > /dev/null 2>&1; then + log_error "Server failed to start" + log_error "Check logs at:" + log_error " - $LOG_FILE" + log_error " - $ERROR_LOG_FILE" + rm -f "$PID_FILE" + exit 1 +fi + +log_success "VidRip backend started successfully!" +echo "" +log_info "Process ID: $SERVER_PID" +log_info "PID file: $PID_FILE" +log_info "Log file: $LOG_FILE" +log_info "Error log: $ERROR_LOG_FILE" +echo "" + +# Check for port in logs (default 3001) +sleep 1 +if grep -q "Server running" "$LOG_FILE" 2>/dev/null; then + PORT_INFO=$(grep "Server running" "$LOG_FILE" | tail -1) + log_success "$PORT_INFO" +else + log_info "Server should be running on port 3001 (or PORT from .env)" +fi + +echo "" +log_info "===================================================================" +log_info "VidRip is now running in production mode" +log_info "===================================================================" +echo "" +log_info "To stop the service:" +log_info " ./stop-production.sh" +echo "" +log_info "To view logs:" +log_info " tail -f $LOG_FILE" +echo "" +log_info "To view errors:" +log_info " tail -f $ERROR_LOG_FILE" +echo "" +log_warning "IMPORTANT: In production, you'll need to:" +log_warning " 1. Serve the frontend build (frontend/dist) via nginx/Apache" +log_warning " 2. Setup reverse proxy from frontend to backend API" +log_warning " 3. Configure proper firewall rules" +echo "" + +# Register cleanup on script exit +trap cleanup_on_exit EXIT + +exit 0 diff --git a/stop-production.sh b/stop-production.sh new file mode 100755 index 0000000..fd5b95d --- /dev/null +++ b/stop-production.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +################################################################################ +# VidRip Production Stop Script +################################################################################ +# This script safely stops the VidRip production service +################################################################################ + +set -e # Exit on error + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +PID_FILE="$SCRIPT_DIR/vidrip.pid" + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +################################################################################ +# Stop Service +################################################################################ + +if [ ! -f "$PID_FILE" ]; then + log_error "No PID file found at $PID_FILE" + log_error "VidRip may not be running or was started manually" + exit 1 +fi + +PID=$(cat "$PID_FILE") + +if ! ps -p "$PID" > /dev/null 2>&1; then + log_error "Process $PID is not running" + log_info "Removing stale PID file..." + rm -f "$PID_FILE" + exit 1 +fi + +log_info "Stopping VidRip (PID: $PID)..." + +# Try graceful shutdown first +kill "$PID" + +# Wait up to 10 seconds for graceful shutdown +COUNTER=0 +while ps -p "$PID" > /dev/null 2>&1 && [ $COUNTER -lt 10 ]; do + sleep 1 + COUNTER=$((COUNTER + 1)) +done + +# Force kill if still running +if ps -p "$PID" > /dev/null 2>&1; then + log_info "Process did not stop gracefully, forcing shutdown..." + kill -9 "$PID" + sleep 1 +fi + +# Verify it's stopped +if ps -p "$PID" > /dev/null 2>&1; then + log_error "Failed to stop process $PID" + exit 1 +fi + +# Remove PID file +rm -f "$PID_FILE" + +log_success "VidRip stopped successfully" +exit 0 diff --git a/vidrip.service b/vidrip.service new file mode 100644 index 0000000..f1e5489 --- /dev/null +++ b/vidrip.service @@ -0,0 +1,26 @@ +[Unit] +Description=VidRip - YouTube Video Drip Downloader +After=network.target + +[Service] +Type=simple +User=www-data +Group=www-data +WorkingDirectory=/var/www/vidrip/backend +Environment="NODE_ENV=production" +Environment="PORT=3001" +ExecStart=/usr/bin/node /var/www/vidrip/backend/dist/server.js +Restart=always +RestartSec=10 +StandardOutput=append:/var/log/vidrip/vidrip.log +StandardError=append:/var/log/vidrip/vidrip-error.log + +# Security hardening +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=strict +ProtectHome=true +ReadWritePaths=/var/www/vidrip/backend/downloads /var/www/vidrip/backend/data.db /var/log/vidrip + +[Install] +WantedBy=multi-user.target