#!/usr/bin/env bash set -euo pipefail # Bunny CDN Deploy Script for tools.slices.network # Syncs .html files to Bunny Storage with clean URLs (no .html extension) # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Counters UPLOADED=0 DELETED=0 # Parse arguments DRY_RUN=false VERBOSE=false while [[ $# -gt 0 ]]; do case $1 in --dry-run) DRY_RUN=true shift ;; --verbose|-v) VERBOSE=true shift ;; *) echo -e "${RED}Unknown option: $1${NC}" exit 1 ;; esac done # Load .env file if it exists if [ -f .env ]; then set -a source .env set +a fi # Required environment variables : "${BUNNY_API_KEY:?BUNNY_API_KEY environment variable is required}" : "${BUNNY_STORAGE_PASSWORD:?BUNNY_STORAGE_PASSWORD environment variable is required}" : "${BUNNY_STORAGE_ZONE:?BUNNY_STORAGE_ZONE environment variable is required}" : "${BUNNY_STORAGE_HOST:?BUNNY_STORAGE_HOST environment variable is required}" : "${BUNNY_PULLZONE_ID:?BUNNY_PULLZONE_ID environment variable is required}" # Configuration STORAGE_URL="https://${BUNNY_STORAGE_HOST}/${BUNNY_STORAGE_ZONE}" echo "Bunny CDN Deploy - tools.slices.network" echo "========================================" echo "Storage Zone: ${BUNNY_STORAGE_ZONE}" if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW}DRY RUN MODE - No changes will be made${NC}" fi echo "" # Upload a single file (strips .html extension for clean URLs, keeps others as-is) upload_file() { local local_path="$1" local filename filename=$(basename "$local_path") # Strip .html extension for clean URLs, keep other extensions local remote_name if [[ "$filename" == *.html ]]; then remote_name="${filename%.html}" else remote_name="$filename" fi if [ "$VERBOSE" = true ]; then echo " Uploading: ${filename} -> ${remote_name}" fi if [ "$DRY_RUN" = true ]; then ((UPLOADED++)) return 0 fi local response local http_code # Determine content type local content_type="text/html" if [[ "$filename" == *.js ]]; then content_type="application/javascript" fi response=$(curl -s -w "\n%{http_code}" -X PUT \ "${STORAGE_URL}/${remote_name}" \ -H "AccessKey: ${BUNNY_STORAGE_PASSWORD}" \ -H "Content-Type: ${content_type}" \ --data-binary "@${local_path}") http_code=$(echo "$response" | tail -n1) if [[ "$http_code" =~ ^2 ]]; then ((UPLOADED++)) return 0 else echo -e "${RED}Failed to upload ${filename}: HTTP ${http_code}${NC}" echo "$response" | head -n -1 return 1 fi } # List all files in remote storage list_remote_files() { local response response=$(curl -s -X GET "${STORAGE_URL}/" \ -H "AccessKey: ${BUNNY_STORAGE_PASSWORD}" \ -H "Accept: application/json") echo "$response" | jq -r '.[] | select(.IsDirectory == false) | .ObjectName' 2>/dev/null } # Delete a single file from remote delete_file() { local remote_name="$1" if [ "$VERBOSE" = true ]; then echo " Deleting: ${remote_name}" fi if [ "$DRY_RUN" = true ]; then ((DELETED++)) return 0 fi local http_code http_code=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \ "${STORAGE_URL}/${remote_name}" \ -H "AccessKey: ${BUNNY_STORAGE_PASSWORD}") if [[ "$http_code" =~ ^2 ]]; then ((DELETED++)) return 0 else echo -e "${RED}Failed to delete ${remote_name}: HTTP ${http_code}${NC}" return 1 fi } # Purge pull zone cache purge_cache() { echo "Purging CDN cache..." if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW} Would purge pull zone ${BUNNY_PULLZONE_ID}${NC}" return 0 fi local http_code http_code=$(curl -s -o /dev/null -w "%{http_code}" -X POST \ "https://api.bunny.net/pullzone/${BUNNY_PULLZONE_ID}/purgeCache" \ -H "AccessKey: ${BUNNY_API_KEY}" \ -H "Content-Type: application/json") if [[ "$http_code" =~ ^2 ]]; then echo -e "${GREEN} Cache purged successfully${NC}" return 0 else echo -e "${RED} Failed to purge cache: HTTP ${http_code}${NC}" return 1 fi } # ============================================ # MAIN EXECUTION # ============================================ # Find all .html and .js files in root directory DEPLOY_FILES=$(find . -maxdepth 1 \( -name "*.html" -o -name "*.js" \) -type f 2>/dev/null) if [ -z "$DEPLOY_FILES" ]; then echo -e "${YELLOW}No .html or .js files found in current directory${NC}" exit 0 fi # Build list of expected remote names (without .html extension) LOCAL_NAMES_LIST=$(mktemp) trap "rm -f $LOCAL_NAMES_LIST" EXIT # Step 1: Upload all local files echo "Uploading files..." echo "$DEPLOY_FILES" | while read -r file; do filename=$(basename "$file") if [[ "$filename" == *.html ]]; then remote_name="${filename%.html}" else remote_name="$filename" fi echo "$remote_name" >> "$LOCAL_NAMES_LIST" upload_file "$file" done echo "" # Step 2: Delete orphaned remote files echo "Checking for orphaned files..." REMOTE_FILES=$(list_remote_files) if [ -n "$REMOTE_FILES" ]; then while IFS= read -r remote_file; do if [ -z "$remote_file" ]; then continue fi if ! grep -qxF "$remote_file" "$LOCAL_NAMES_LIST"; then delete_file "$remote_file" fi done <<< "$REMOTE_FILES" fi echo "" # Step 3: Purge CDN cache purge_cache # Summary echo "" echo "========================================" echo -e "${GREEN}Deploy complete!${NC}" echo " Uploaded: ${UPLOADED} files" echo " Deleted: ${DELETED} files" if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW} (DRY RUN - no actual changes made)${NC}" fi echo "========================================"