mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 16:14:12 +00:00
- Add robust error handling to check_existing function - Fix create_social_links to handle API errors gracefully - Fix create_pages to handle API errors and track skipped/failed counts - Fix create_navigation with proper error handling - Replace ((count++)) with $((count + 1)) for POSIX compatibility - Add 2>/dev/null to jq calls to suppress error output - Return 0 from functions on early exit to prevent set -e issues Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
593 lines
16 KiB
Bash
Executable file
593 lines
16 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
# Payload CMS Tenant Setup Script
|
|
# ================================
|
|
# Sets up tenants (porwoll.de, blogwoman.de) with all content
|
|
#
|
|
# Usage:
|
|
# ./setup.sh [OPTIONS] <tenant>
|
|
#
|
|
# 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] <tenant>
|
|
|
|
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 "$@"
|