281 lines
12 KiB
Bash
281 lines
12 KiB
Bash
#!/bin/bash
|
||
|
||
################################################################################
|
||
# Lux Studio — Production Deployment Script
|
||
#
|
||
# Usage:
|
||
# cd /opt/cinema-studio-pro
|
||
# sudo ./deploy.sh
|
||
#
|
||
# What it does:
|
||
# 1. Builds the React frontend using frontend/.env.production
|
||
# 2. Deploys built files → /var/www/html/lux-studio/
|
||
# 3. Deploys backend PHP → /var/www/html/lux-studio/api/
|
||
# 4. Verifies the deployment
|
||
#
|
||
# Apache serves PHP directly — no systemd service or proxy needed.
|
||
# Apache configuration (AllowOverride, modules) is managed separately by the operator.
|
||
# The backend .env on the server is never overwritten (preserves API keys).
|
||
################################################################################
|
||
|
||
set -e
|
||
|
||
# ─── Colors ───────────────────────────────────────────────────────────────────
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m'
|
||
|
||
# ─── Configuration ─────────────────────────────────────────────────────────────
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
FRONTEND_SRC="$SCRIPT_DIR/frontend"
|
||
BACKEND_SRC="$SCRIPT_DIR/backend"
|
||
WEB_ROOT="/var/www/html/lux-studio"
|
||
API_ROOT="$WEB_ROOT/api"
|
||
|
||
# PHP files that must never be deployed to the server
|
||
SKIP_PHP=("test.php" "config.example.php")
|
||
|
||
# ─── Helper functions ─────────────────────────────────────────────────────────
|
||
step() { echo ""; echo -e "${YELLOW}[$1] $2...${NC}"; }
|
||
ok() { echo -e "${GREEN} ✓ $1${NC}"; }
|
||
warn() { echo -e "${YELLOW} ⚠ $1${NC}"; }
|
||
fail() { echo -e "${RED} Error: $1${NC}"; exit 1; }
|
||
|
||
# ─── Header ───────────────────────────────────────────────────────────────────
|
||
echo ""
|
||
echo -e "${BLUE}========================================"
|
||
echo -e " Lux Studio — Production Deployment"
|
||
echo -e "========================================${NC}"
|
||
echo ""
|
||
echo " Source: $SCRIPT_DIR"
|
||
echo " Frontend: $WEB_ROOT"
|
||
echo " Backend: $API_ROOT"
|
||
echo ""
|
||
|
||
# ─── Root check ───────────────────────────────────────────────────────────────
|
||
[[ $EUID -ne 0 ]] && fail "This script must be run as root. Use: sudo ./deploy.sh"
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
step "1/6" "Preflight checks"
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
[ -d "$FRONTEND_SRC" ] || fail "frontend/ directory not found in $SCRIPT_DIR"
|
||
[ -d "$BACKEND_SRC" ] || fail "backend/ directory not found in $SCRIPT_DIR"
|
||
|
||
[ -f "$FRONTEND_SRC/.env.production" ] \
|
||
|| fail "frontend/.env.production not found — cannot build for production"
|
||
|
||
[ -f "$BACKEND_SRC/.env.production" ] \
|
||
|| warn "backend/.env.production not found — /api/.env will not be created automatically"
|
||
|
||
# Check required system commands
|
||
for cmd in php node npm; do
|
||
command -v "$cmd" &>/dev/null \
|
||
&& ok "$cmd: $(command -v $cmd)" \
|
||
|| fail "$cmd is not installed"
|
||
done
|
||
|
||
ok "Preflight passed"
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
step "2/6" "Building frontend"
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
cd "$FRONTEND_SRC"
|
||
|
||
# Always use .env.production for production builds — never use a cached .env
|
||
cp .env.production .env
|
||
ok "frontend/.env overwritten from .env.production"
|
||
|
||
# Install npm dependencies if node_modules is absent or package.json changed
|
||
echo " Checking npm dependencies..."
|
||
npm install --silent
|
||
ok "npm dependencies ready"
|
||
|
||
echo " Building (this may take 30–60 seconds)..."
|
||
npm run build
|
||
ok "Build complete → frontend/dist/"
|
||
|
||
# Confirm the correct API path was baked into the bundle
|
||
if grep -qo "lux-studio/api" dist/assets/*.js 2>/dev/null; then
|
||
ok "API URL verified in bundle: lux-studio/api"
|
||
else
|
||
fail "Bundle does not contain 'lux-studio/api'. Check VITE_API_URL in frontend/.env.production"
|
||
fi
|
||
|
||
cd "$SCRIPT_DIR"
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
step "3/6" "Deploying frontend to $WEB_ROOT"
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
mkdir -p "$WEB_ROOT"
|
||
|
||
# Delete old assets/ first — Vite produces differently hash-named files each
|
||
# build. Leaving old files causes stale JS to be served.
|
||
if [ -d "$WEB_ROOT/assets" ]; then
|
||
rm -rf "$WEB_ROOT/assets"
|
||
ok "Old assets/ removed (hash names change each build)"
|
||
fi
|
||
|
||
# Copy built output
|
||
cp "$FRONTEND_SRC/dist/index.html" "$WEB_ROOT/index.html"
|
||
cp -r "$FRONTEND_SRC/dist/assets" "$WEB_ROOT/assets"
|
||
ok "index.html and assets/ deployed"
|
||
|
||
# Copy static public assets (logos, favicon — not always present in all builds)
|
||
for item in LUX_STUDIO_LOGO.svg LUX_STUDIO_LOGO.png; do
|
||
src="$FRONTEND_SRC/dist/$item"
|
||
[ -e "$src" ] && cp "$src" "$WEB_ROOT/$item"
|
||
done
|
||
ok "Static assets deployed (LUX_STUDIO_LOGO.svg, LUX_STUDIO_LOGO.png)"
|
||
|
||
# .htaccess is NOT copied into dist/ by Vite — must be taken from source
|
||
if [ -f "$FRONTEND_SRC/.htaccess" ]; then
|
||
cp "$FRONTEND_SRC/.htaccess" "$WEB_ROOT/.htaccess"
|
||
ok ".htaccess deployed from frontend/.htaccess (not from dist/)"
|
||
else
|
||
warn "frontend/.htaccess not found — React Router deep links will break"
|
||
fi
|
||
|
||
chown -R www-data:www-data "$WEB_ROOT"
|
||
chmod -R 755 "$WEB_ROOT"
|
||
ok "Permissions set (www-data:www-data, 755)"
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
step "4/6" "Deploying backend PHP to $API_ROOT"
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
mkdir -p "$API_ROOT"
|
||
|
||
# Copy all .php files from backend/ except those in SKIP_PHP
|
||
PHP_COUNT=0
|
||
for php_file in "$BACKEND_SRC"/*.php; do
|
||
filename=$(basename "$php_file")
|
||
skip=false
|
||
for skip_name in "${SKIP_PHP[@]}"; do
|
||
[ "$filename" = "$skip_name" ] && skip=true && break
|
||
done
|
||
if [ "$skip" = false ]; then
|
||
cp "$php_file" "$API_ROOT/$filename"
|
||
PHP_COUNT=$((PHP_COUNT + 1))
|
||
fi
|
||
done
|
||
ok "$PHP_COUNT PHP files deployed (skipped: ${SKIP_PHP[*]})"
|
||
|
||
# Copy backend .htaccess (security rules, blocks .env/vendor/direct class access)
|
||
if [ -f "$BACKEND_SRC/.htaccess" ]; then
|
||
cp "$BACKEND_SRC/.htaccess" "$API_ROOT/.htaccess"
|
||
ok "Backend .htaccess deployed"
|
||
fi
|
||
|
||
# ── .env handling ──────────────────────────────────────────────────────────────
|
||
# The production .env contains the real GEMINI_API_KEY.
|
||
# NEVER overwrite it — only create it if it does not exist yet.
|
||
if [ -f "$API_ROOT/.env" ]; then
|
||
ok "/api/.env already exists — not overwritten (production secrets preserved)"
|
||
else
|
||
if [ -f "$BACKEND_SRC/.env.production" ]; then
|
||
cp "$BACKEND_SRC/.env.production" "$API_ROOT/.env"
|
||
ok "Created /api/.env from backend/.env.production"
|
||
echo ""
|
||
warn "ACTION REQUIRED: Verify GEMINI_API_KEY and FRONTEND_URL in /api/.env:"
|
||
warn " sudo nano $API_ROOT/.env"
|
||
echo ""
|
||
else
|
||
warn "backend/.env.production not found — /api/.env was NOT created"
|
||
warn "Create it manually: sudo nano $API_ROOT/.env"
|
||
warn "Required keys: GEMINI_API_KEY, FRONTEND_URL, SSO_ENABLED"
|
||
fi
|
||
fi
|
||
|
||
# Create uploads directory (PHP writes generated images/videos here)
|
||
mkdir -p "$API_ROOT/uploads/sessions"
|
||
|
||
# Set ownership before chmod so chmod applies correctly
|
||
chown -R www-data:www-data "$API_ROOT"
|
||
chmod -R 755 "$API_ROOT"
|
||
# www-data must be able to write generated files
|
||
chmod -R 777 "$API_ROOT/uploads"
|
||
ok "Permissions set (uploads/: 777 for www-data writes)"
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
step "5/6" "Verifying deployment"
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
ERRORS=0
|
||
|
||
check() {
|
||
# $1 = path to check, $2 = label, $3 = "warn" (non-fatal) or empty (fatal)
|
||
if [ -e "$1" ]; then
|
||
ok "$2"
|
||
elif [ "$3" = "warn" ]; then
|
||
warn "$2 not found"
|
||
else
|
||
echo -e "${RED} ✗ $2 — MISSING${NC}"
|
||
ERRORS=$((ERRORS+1))
|
||
fi
|
||
}
|
||
|
||
# Frontend
|
||
check "$WEB_ROOT/index.html" "index.html"
|
||
check "$WEB_ROOT/assets" "assets/ directory"
|
||
check "$WEB_ROOT/.htaccess" ".htaccess (frontend)" warn
|
||
|
||
# Backend
|
||
check "$API_ROOT/api.php" "api.php"
|
||
check "$API_ROOT/AuthMiddleware.php" "AuthMiddleware.php ← api.php requires this"
|
||
check "$API_ROOT/video_api.php" "video_api.php"
|
||
check "$API_ROOT/enhance_prompt.php" "enhance_prompt.php"
|
||
check "$API_ROOT/.htaccess" ".htaccess (backend)"
|
||
check "$API_ROOT/uploads/sessions" "uploads/sessions/"
|
||
|
||
if [ -f "$API_ROOT/.env" ]; then
|
||
ok "/api/.env present"
|
||
else
|
||
echo -e "${RED} ✗ /api/.env MISSING — all API calls will fail without it${NC}"
|
||
ERRORS=$((ERRORS+1))
|
||
fi
|
||
|
||
# Confirm bundle has the right API URL
|
||
if grep -qo "lux-studio/api" "$WEB_ROOT"/assets/*.js 2>/dev/null; then
|
||
ok "Bundle contains correct API path (lux-studio/api)"
|
||
else
|
||
warn "Could not verify API path in deployed bundle"
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
step "6/6" "Summary"
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
echo ""
|
||
echo -e "${BLUE} Deployed:${NC}"
|
||
echo " Frontend → $WEB_ROOT"
|
||
echo " Backend → $API_ROOT"
|
||
echo ""
|
||
echo -e "${BLUE} Live URL:${NC}"
|
||
echo " https://ai-sandbox.oliver.solutions/lux-studio/"
|
||
echo ""
|
||
echo -e "${BLUE} Useful commands:${NC}"
|
||
echo " Check .env: sudo cat $API_ROOT/.env"
|
||
echo " Apache errors: sudo tail -f /var/log/apache2/error.log"
|
||
echo " Reload Apache: sudo systemctl reload apache2"
|
||
echo " Re-deploy: cd $SCRIPT_DIR && sudo ./deploy.sh"
|
||
echo ""
|
||
echo -e "${BLUE} Test checklist:${NC}"
|
||
echo " 1. Hard refresh in browser: Ctrl + Shift + R"
|
||
echo " 2. F12 → Network: API calls go to /lux-studio/api/ (not /lux-studio-back/)"
|
||
echo " 3. No 404 errors in the Network tab"
|
||
echo " 4. Create a project and generate an image with a 10+ word prompt"
|
||
echo ""
|
||
|
||
if [ $ERRORS -gt 0 ]; then
|
||
echo -e "${YELLOW} ⚠ $ERRORS check(s) failed — review the output above before testing${NC}"
|
||
echo ""
|
||
exit 1
|
||
else
|
||
echo -e "${GREEN} ✓ All checks passed — deployment successful!${NC}"
|
||
echo ""
|
||
fi
|