#!/bin/bash # # Payload CMS Tenant Setup Script # ================================ # Sets up tenants (porwoll.de, blogwoman.de) with all content # # Usage: # ./setup.sh [OPTIONS] # # Options: # -e, --env Environment: staging|production (default: staging) # -u, --user Admin email # -p, --password Admin password (or use PAYLOAD_PASSWORD env var) # -t, --tenant-id Override tenant ID # -d, --dry-run Show what would be created without making changes # -v, --verbose Verbose output # -h, --help Show this help # # Examples: # ./setup.sh porwoll # Setup porwoll.de on staging # ./setup.sh -e production blogwoman # Setup blogwoman.de on production # ./setup.sh -e production -t 5 porwoll # Setup with specific tenant ID # ./setup.sh --dry-run porwoll # Preview without changes set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Defaults ENV="staging" DRY_RUN=false VERBOSE=false ADMIN_EMAIL="" ADMIN_PASSWORD="${PAYLOAD_PASSWORD:-}" TENANT_ID_OVERRIDE="" # API URLs declare -A API_URLS=( ["staging"]="https://pl.porwoll.tech/api" ["production"]="https://cms.c2sgmbh.de/api" ) # Default Tenant IDs (can be overridden) declare -A DEFAULT_TENANT_IDS=( ["porwoll"]="4" ["blogwoman"]="6" ) # State TOKEN="" CREATED_PAGE_IDS=() declare -A PAGE_ID_MAP ####################################### # Logging functions ####################################### log() { echo -e "${BLUE}[INFO]${NC} $*"; } success() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } debug() { [[ "$VERBOSE" == "true" ]] && echo -e "[DEBUG] $*" || true; } ####################################### # Print usage ####################################### usage() { cat << 'EOF' Payload CMS Tenant Setup Script ================================ Sets up tenants (porwoll.de, blogwoman.de) with all content Usage: ./setup.sh [OPTIONS] Options: -e, --env Environment: staging|production (default: staging) -u, --user Admin email -p, --password Admin password (or use PAYLOAD_PASSWORD env var) -t, --tenant-id Override tenant ID -d, --dry-run Show what would be created without making changes -v, --verbose Verbose output -h, --help Show this help Examples: ./setup.sh porwoll # Setup porwoll.de on staging ./setup.sh -e production blogwoman # Setup blogwoman.de on production ./setup.sh -e production -t 5 porwoll # Setup with specific tenant ID ./setup.sh --dry-run porwoll # Preview without changes EOF exit 0 } ####################################### # Parse command line arguments ####################################### parse_args() { while [[ $# -gt 0 ]]; do case $1 in -e|--env) ENV="$2" shift 2 ;; -u|--user) ADMIN_EMAIL="$2" shift 2 ;; -p|--password) ADMIN_PASSWORD="$2" shift 2 ;; -t|--tenant-id) TENANT_ID_OVERRIDE="$2" shift 2 ;; -d|--dry-run) DRY_RUN=true shift ;; -v|--verbose) VERBOSE=true shift ;; -h|--help) usage ;; -*) error "Unknown option: $1" exit 1 ;; *) TENANT="$1" shift ;; esac done # Validate if [[ -z "${TENANT:-}" ]]; then error "Tenant name required. Use: porwoll or blogwoman" exit 1 fi if [[ ! -d "$SCRIPT_DIR/tenants/$TENANT" ]]; then error "Unknown tenant: $TENANT. Available: porwoll, blogwoman" exit 1 fi if [[ ! "${API_URLS[$ENV]+isset}" ]]; then error "Unknown environment: $ENV. Use: staging or production" exit 1 fi API_URL="${API_URLS[$ENV]}" TENANT_ID="${TENANT_ID_OVERRIDE:-${DEFAULT_TENANT_IDS[$TENANT]}}" } ####################################### # Prompt for credentials if needed ####################################### get_credentials() { if [[ -z "$ADMIN_EMAIL" ]]; then read -p "Admin Email: " ADMIN_EMAIL fi if [[ -z "$ADMIN_PASSWORD" ]]; then read -sp "Admin Password: " ADMIN_PASSWORD echo fi } ####################################### # API Functions ####################################### # Login and get JWT token api_login() { log "Logging in to $API_URL..." local response response=$(curl -s -X POST "$API_URL/users/login" \ -H "Content-Type: application/json" \ -d "{\"email\": \"$ADMIN_EMAIL\", \"password\": \"$ADMIN_PASSWORD\"}" \ -w '\n%{http_code}') local http_code http_code=$(echo "$response" | tail -1) local body body=$(echo "$response" | head -n -1) if [[ "$http_code" != "200" ]]; then error "Login failed (HTTP $http_code)" debug "$body" exit 1 fi TOKEN=$(echo "$body" | jq -r '.token') if [[ -z "$TOKEN" || "$TOKEN" == "null" ]]; then error "Failed to extract token from response" exit 1 fi success "Login successful" } # Generic API POST api_post() { local endpoint="$1" local data="$2" local description="${3:-}" if [[ "$DRY_RUN" == "true" ]]; then log "[DRY-RUN] Would POST to $endpoint: $description" echo '{"doc":{"id":0}}' return 0 fi local response response=$(curl -s -X POST "$API_URL/$endpoint" \ -H "Authorization: JWT $TOKEN" \ -H "Content-Type: application/json" \ -d "$data" \ -w '\n%{http_code}') local http_code http_code=$(echo "$response" | tail -1) local body body=$(echo "$response" | head -n -1) if [[ "$http_code" == "201" || "$http_code" == "200" ]]; then debug "POST $endpoint: HTTP $http_code" echo "$body" else warn "POST $endpoint failed (HTTP $http_code)" debug "$body" echo "$body" return 1 fi } # Generic API PATCH api_patch() { local endpoint="$1" local data="$2" if [[ "$DRY_RUN" == "true" ]]; then log "[DRY-RUN] Would PATCH $endpoint" return 0 fi local response response=$(curl -s -X PATCH "$API_URL/$endpoint" \ -H "Authorization: JWT $TOKEN" \ -H "Content-Type: application/json" \ -d "$data" \ -w '\n%{http_code}') local http_code http_code=$(echo "$response" | tail -1) if [[ "$http_code" == "200" ]]; then debug "PATCH $endpoint: OK" return 0 else warn "PATCH $endpoint failed (HTTP $http_code)" return 1 fi } # Generic API GET api_get() { local endpoint="$1" curl -s -G "$API_URL/$endpoint" \ -H "Authorization: JWT $TOKEN" } # Check if documents exist for tenant check_existing() { local collection="$1" local response local count response=$(api_get "$collection?where[tenant][equals]=$TENANT_ID&limit=1") debug "check_existing response: $response" # Handle API errors or invalid JSON if ! count=$(echo "$response" | jq -e '.totalDocs // 0' 2>/dev/null); then warn "Failed to check existing $collection (API error or invalid response)" debug "Response was: $response" echo "0" return 0 fi echo "$count" } ####################################### # Create Site Settings ####################################### create_site_settings() { log "Creating Site Settings..." local config_file="$SCRIPT_DIR/tenants/$TENANT/site-settings.json" if [[ ! -f "$config_file" ]]; then warn "No site-settings.json found for $TENANT" return fi # Check if exists local existing existing=$(check_existing "site-settings") local data data=$(cat "$config_file" | jq --argjson tid "$TENANT_ID" '.tenant = $tid') if [[ "$existing" -gt 0 ]]; then log "Site Settings already exists, updating..." local id id=$(api_get "site-settings?where[tenant][equals]=$TENANT_ID&limit=1" | jq -r '.docs[0].id') api_patch "site-settings/$id" "$data" else api_post "site-settings" "$data" "Site Settings" fi success "Site Settings configured" } ####################################### # Create Social Links ####################################### create_social_links() { log "Creating Social Links..." local config_file="$SCRIPT_DIR/tenants/$TENANT/social-links.json" if [[ ! -f "$config_file" ]]; then warn "No social-links.json found for $TENANT" return 0 fi # Check existing local existing existing=$(check_existing "social-links") || existing=0 if [[ "$existing" -gt 0 ]]; then warn "Social Links already exist ($existing found), skipping..." return 0 fi local count=0 local failed=0 local link local links links=$(jq -c '.[]' "$config_file" 2>/dev/null) || { warn "Failed to parse social-links.json" return 0 } while IFS= read -r link; do [[ -z "$link" ]] && continue local data data=$(echo "$link" | jq --argjson tid "$TENANT_ID" '. + {tenant: $tid}') || continue local platform platform=$(echo "$link" | jq -r '.platform') || platform="unknown" debug "Creating social link: $platform" if api_post "social-links" "$data" "$platform" > /dev/null 2>&1; then count=$((count + 1)) else failed=$((failed + 1)) warn "Failed to create social link: $platform" fi done <<< "$links" if [[ "$failed" -gt 0 ]]; then warn "Created $count Social Links ($failed failed)" else success "Created $count Social Links" fi } ####################################### # Create Pages ####################################### create_pages() { log "Creating Pages..." local config_file="$SCRIPT_DIR/tenants/$TENANT/pages.json" if [[ ! -f "$config_file" ]]; then warn "No pages.json found for $TENANT" return 0 fi local count=0 local skipped=0 local failed=0 local pages pages=$(jq -c '.[]' "$config_file" 2>/dev/null) || { warn "Failed to parse pages.json" return 0 } local page while IFS= read -r page; do [[ -z "$page" ]] && continue local slug title slug=$(echo "$page" | jq -r '.slug' 2>/dev/null) || continue title=$(echo "$page" | jq -r '.title' 2>/dev/null) || title="$slug" # Check if page exists local response existing response=$(api_get "pages?where[tenant][equals]=$TENANT_ID&where[slug][equals]=$slug&limit=1") || response="{}" existing=$(echo "$response" | jq '.totalDocs // 0' 2>/dev/null) || existing=0 if [[ "$existing" -gt 0 ]]; then debug "Page '$slug' already exists, skipping..." local id id=$(echo "$response" | jq -r '.docs[0].id' 2>/dev/null) || id="" [[ -n "$id" && "$id" != "null" ]] && PAGE_ID_MAP["$slug"]="$id" skipped=$((skipped + 1)) continue fi local data data=$(echo "$page" | jq --argjson tid "$TENANT_ID" '. + {tenant: $tid}') || continue local post_response if post_response=$(api_post "pages" "$data" "$title" 2>/dev/null); then local id id=$(echo "$post_response" | jq -r '.doc.id // .id // 0' 2>/dev/null) || id="0" if [[ "$id" != "0" && "$id" != "null" ]]; then PAGE_ID_MAP["$slug"]="$id" count=$((count + 1)) debug "Created page: $title (ID: $id)" else failed=$((failed + 1)) fi else failed=$((failed + 1)) warn "Failed to create page: $title" fi done <<< "$pages" if [[ "$failed" -gt 0 ]]; then warn "Created $count Pages ($skipped skipped, $failed failed)" else success "Created $count Pages ($skipped already existed)" fi # Show page ID mapping if [[ "$VERBOSE" == "true" ]]; then log "Page ID Mapping:" for slug in "${!PAGE_ID_MAP[@]}"; do echo " $slug: ${PAGE_ID_MAP[$slug]}" done fi } ####################################### # Create Navigation ####################################### create_navigation() { log "Creating Navigation..." local config_file="$SCRIPT_DIR/tenants/$TENANT/navigation.json" if [[ ! -f "$config_file" ]]; then warn "No navigation.json found for $TENANT" return 0 fi # Check existing local existing existing=$(check_existing "navigations") || existing=0 if [[ "$existing" -gt 0 ]]; then warn "Navigation already exists, skipping..." return 0 fi # Read navigation template and replace page slugs with IDs local nav_data nav_data=$(cat "$config_file") || { warn "Failed to read navigation.json" return 0 } # Replace placeholders with actual page IDs for slug in "${!PAGE_ID_MAP[@]}"; do local id="${PAGE_ID_MAP[$slug]}" nav_data=$(echo "$nav_data" | sed "s/\"PAGE_ID_${slug^^}\"/\"$id\"/g" | sed "s/\"PAGE_ID_$slug\"/$id/g") || true done # Add tenant ID nav_data=$(echo "$nav_data" | jq --argjson tid "$TENANT_ID" '. + {tenant: $tid}' 2>/dev/null) || { warn "Failed to add tenant ID to navigation data" return 0 } if api_post "navigations" "$nav_data" "Navigation" > /dev/null 2>&1; then success "Navigation created" else warn "Failed to create navigation" fi } ####################################### # Verification ####################################### verify_setup() { log "Verifying setup..." echo "" echo "=== Verification Report for $TENANT (Tenant ID: $TENANT_ID) ===" echo "" # Site Settings local ss_count ss_count=$(check_existing "site-settings") echo "Site Settings: $ss_count" # Social Links local sl_count sl_count=$(check_existing "social-links") echo "Social Links: $sl_count" # Pages local pages_count pages_count=$(check_existing "pages") echo "Pages: $pages_count" # List pages if [[ "$VERBOSE" == "true" ]]; then echo "" echo "Pages:" api_get "pages?where[tenant][equals]=$TENANT_ID&limit=50" | jq -r '.docs[] | " [\(.id)] \(.title) (/\(.slug))"' fi # Navigation local nav_count nav_count=$(check_existing "navigations") echo "Navigation: $nav_count" echo "" echo "=== Setup Complete ===" } ####################################### # Main ####################################### main() { parse_args "$@" echo "" echo "==================================" echo " Payload CMS Tenant Setup" echo "==================================" echo "" echo "Tenant: $TENANT" echo "Tenant ID: $TENANT_ID" echo "Environment: $ENV" echo "API URL: $API_URL" echo "Dry Run: $DRY_RUN" echo "" if [[ "$DRY_RUN" == "true" ]]; then warn "DRY RUN MODE - No changes will be made" echo "" fi get_credentials api_login echo "" log "Starting setup for $TENANT..." echo "" create_site_settings create_social_links create_pages create_navigation echo "" verify_setup } main "$@"