diff --git a/ummah-wordpress/.env.example b/ummah-wordpress/.env.example new file mode 100644 index 0000000..d93c6fb --- /dev/null +++ b/ummah-wordpress/.env.example @@ -0,0 +1,14 @@ +# ummah2.falahos.my WordPress — Contabo Deployment +# Copy to .env and fill values (retrieve from bitwarden.falahos.my) + +DB_ROOT_PASSWORD= +DB_PASSWORD= + +# WP Admin (set during wp-install.sh) +WP_ADMIN_USER=admin +WP_ADMIN_EMAIL=wanjauhari@gmail.com +WP_ADMIN_PASSWORD= + +# Site +WP_SITE_URL=https://ummah2.falahos.my +WP_SITE_TITLE=Ummah FalahOS diff --git a/ummah-wordpress/deploy.sh b/ummah-wordpress/deploy.sh new file mode 100644 index 0000000..1ba117c --- /dev/null +++ b/ummah-wordpress/deploy.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# deploy.sh — spin up ummah2.falahos.my WordPress on Contabo +# Run on Contabo: bash deploy.sh +set -euo pipefail + +DEPLOY_DIR="/opt/ummah-wordpress" +SITE_URL="https://ummah2.falahos.my" +WP_PLUGINS_DIR="$DEPLOY_DIR/wp-data/wp-content/plugins" + +echo "▶ Deploying ummah2.falahos.my WordPress on Contabo" + +# ── Prerequisites ──────────────────────────────────────────────────────────── +command -v docker >/dev/null || { echo "❌ docker not found"; exit 1; } +docker compose version >/dev/null 2>&1 || { echo "❌ docker compose plugin not found"; exit 1; } + +# ── Setup deploy dir ───────────────────────────────────────────────────────── +mkdir -p "$DEPLOY_DIR" +cd "$DEPLOY_DIR" + +if [ ! -f .env ]; then + cp .env.example .env + echo "" + echo "⚠️ Fill in .env before continuing:" + echo " DB_ROOT_PASSWORD, DB_PASSWORD, WP_ADMIN_PASSWORD" + echo " nano $DEPLOY_DIR/.env" + exit 0 +fi + +source .env + +# ── Start stack ─────────────────────────────────────────────────────────────── +echo "── Starting MariaDB + WordPress + Nginx ──" +docker compose up -d db +echo " Waiting for DB to be ready…" +sleep 10 +docker compose up -d + +# ── Wait for WordPress to be ready ─────────────────────────────────────────── +echo "── Waiting for WordPress container ──" +for i in $(seq 1 30); do + if docker compose exec -T wordpress test -f /var/www/html/wp-includes/version.php 2>/dev/null; then + echo " WordPress files ready." + break + fi + sleep 3 +done + +# ── Install WordPress via WP-CLI ────────────────────────────────────────────── +echo "── Installing WordPress ──" +docker compose run --rm wpcli core install \ + --url="$SITE_URL" \ + --title="$WP_SITE_TITLE" \ + --admin_user="$WP_ADMIN_USER" \ + --admin_password="$WP_ADMIN_PASSWORD" \ + --admin_email="$WP_ADMIN_EMAIL" \ + --skip-email 2>/dev/null || echo " (already installed)" + +# ── Install plugins ─────────────────────────────────────────────────────────── +echo "── Installing plugins ──" + +# Hermes AI Bridge (from Gitea raw) +docker compose run --rm wpcli plugin install \ + "https://git.falahos.my/wmj/hermes-cpanel-agent/archive/main.zip" \ + --activate 2>/dev/null || echo " hermes-ai-bridge: manual install needed" + +# Falah Shortcodes (from Gitea raw via falahos org) +docker compose run --rm wpcli plugin install \ + "https://git.falahos.my/falahos/falah-os-ce/raw/branch/community-edition/wordpress-plugins/falah-shortcodes.zip" \ + --activate 2>/dev/null || echo " falah-shortcodes: manual install needed" + +# Essential plugins from WP.org +for plugin in astra-sites tutor learndash-gutenberg-blocks mailpoet woocommerce easy-digital-downloads redis-cache; do + docker compose run --rm wpcli plugin install "$plugin" --activate 2>/dev/null || true +done + +# ── Set permalink structure ─────────────────────────────────────────────────── +docker compose run --rm wpcli rewrite structure '/%postname%/' --hard + +# ── Configure Caddy ─────────────────────────────────────────────────────────── +echo "── Configuring Caddy reverse proxy ──" +CADDY_FILE="/etc/caddy/conf.d/ummah2.conf" +mkdir -p /etc/caddy/conf.d + +cat > "$CADDY_FILE" << 'CADDY' +ummah2.falahos.my { + reverse_proxy localhost:8888 + + encode gzip + + header { + Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" + X-Frame-Options SAMEORIGIN + X-Content-Type-Options nosniff + Referrer-Policy strict-origin-when-cross-origin + -Server + } +} +CADDY + +# Reload Caddy if running +if systemctl is-active --quiet caddy 2>/dev/null; then + systemctl reload caddy + echo " Caddy reloaded ✅" +elif docker ps --format '{{.Names}}' | grep -q caddy; then + docker kill --signal=USR1 "$(docker ps --format '{{.Names}}' | grep caddy)" + echo " Caddy (docker) reloaded ✅" +else + echo " ⚠️ Caddy not found — add ummah2.falahos.my block to your reverse proxy manually" + echo " Nginx port 8888 → ummah2.falahos.my" +fi + +echo "" +echo "✅ ummah2.falahos.my is up on port 8888" +echo " WP Admin: $SITE_URL/wp-admin (user: $WP_ADMIN_USER)" +echo "" +echo "── Next: point DNS ──" +echo " ummah2.falahos.my A → 13.140.161.244" diff --git a/ummah-wordpress/docker-compose.yml b/ummah-wordpress/docker-compose.yml new file mode 100644 index 0000000..530c85e --- /dev/null +++ b/ummah-wordpress/docker-compose.yml @@ -0,0 +1,86 @@ +version: "3.9" + +services: + db: + image: mariadb:11 + container_name: ummah-db + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + MYSQL_DATABASE: ummah_wp + MYSQL_USER: ummah_wp + MYSQL_PASSWORD: ${DB_PASSWORD} + volumes: + - ummah-db-data:/var/lib/mysql + networks: + - ummah-net + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + + wordpress: + image: wordpress:6.7-php8.3-fpm-alpine + container_name: ummah-wp + restart: unless-stopped + depends_on: + db: + condition: service_healthy + environment: + WORDPRESS_DB_HOST: db:3306 + WORDPRESS_DB_NAME: ummah_wp + WORDPRESS_DB_USER: ummah_wp + WORDPRESS_DB_PASSWORD: ${DB_PASSWORD} + WORDPRESS_TABLE_PREFIX: wp_ + WORDPRESS_DEBUG: "false" + # Performance + PHP_MEMORY_LIMIT: 256M + PHP_MAX_EXECUTION_TIME: 300 + PHP_UPLOAD_MAX_FILESIZE: 64M + PHP_POST_MAX_SIZE: 64M + volumes: + - ummah-wp-data:/var/www/html + - ./wordpress-config/php.ini:/usr/local/etc/php/conf.d/ummah.ini:ro + networks: + - ummah-net + + nginx: + image: nginx:alpine + container_name: ummah-nginx + restart: unless-stopped + depends_on: + - wordpress + ports: + - "8888:80" + volumes: + - ummah-wp-data:/var/www/html:ro + - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro + networks: + - ummah-net + + wpcli: + image: wordpress:cli-2-php8.3 + container_name: ummah-wpcli + depends_on: + - wordpress + - db + environment: + WORDPRESS_DB_HOST: db:3306 + WORDPRESS_DB_NAME: ummah_wp + WORDPRESS_DB_USER: ummah_wp + WORDPRESS_DB_PASSWORD: ${DB_PASSWORD} + volumes: + - ummah-wp-data:/var/www/html + networks: + - ummah-net + entrypoint: ["tail", "-f", "/dev/null"] + profiles: ["tools"] + +networks: + ummah-net: + driver: bridge + +volumes: + ummah-db-data: + ummah-wp-data: diff --git a/ummah-wordpress/migrate-from-cpanel.sh b/ummah-wordpress/migrate-from-cpanel.sh new file mode 100644 index 0000000..3a38ad0 --- /dev/null +++ b/ummah-wordpress/migrate-from-cpanel.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# migrate-from-cpanel.sh — import cPanel WordPress backup into Contabo +# Usage: bash migrate-from-cpanel.sh +# Run AFTER deploy.sh has the new WP running. +set -euo pipefail + +DEPLOY_DIR="/opt/ummah-wordpress" +OLD_URL="https://ummah.falahos.my" +NEW_URL="https://ummah2.falahos.my" +BACKUP_FILE="${1:-}" + +if [ -z "$BACKUP_FILE" ]; then + echo "Usage: $0 " + echo "" + echo "Export from cPanel:" + echo " 1. cPanel → Backup Wizard → Full Backup (downloads .tar.gz)" + echo " 2. OR: cPanel → phpMyAdmin → Export DB as SQL" + echo " 3. OR: cPanel → File Manager → compress /public_html" + exit 1 +fi + +cd "$DEPLOY_DIR" +source .env + +EXT="${BACKUP_FILE##*.}" + +if [ "$EXT" = "sql" ]; then + echo "▶ Importing SQL database dump" + docker compose exec -T db mysql \ + -u ummah_wp -p"$DB_PASSWORD" ummah_wp < "$BACKUP_FILE" + + echo "── Updating URLs ──" + docker compose run --rm wpcli search-replace "$OLD_URL" "$NEW_URL" \ + --all-tables --report-changed-only + +elif [ "$EXT" = "gz" ] || [ "$EXT" = "zip" ]; then + echo "▶ Extracting backup archive" + TMP_DIR=$(mktemp -d) + if [ "$EXT" = "gz" ]; then + tar -xzf "$BACKUP_FILE" -C "$TMP_DIR" + else + unzip -q "$BACKUP_FILE" -d "$TMP_DIR" + fi + + # Find SQL dump inside archive + SQL_FILE=$(find "$TMP_DIR" -name "*.sql" | head -1) + if [ -n "$SQL_FILE" ]; then + echo "── Importing database: $SQL_FILE ──" + docker compose exec -T db mysql \ + -u ummah_wp -p"$DB_PASSWORD" ummah_wp < "$SQL_FILE" + docker compose run --rm wpcli search-replace "$OLD_URL" "$NEW_URL" \ + --all-tables --report-changed-only + fi + + # Sync wp-content (themes, plugins, uploads) + WP_CONTENT=$(find "$TMP_DIR" -type d -name "wp-content" | head -1) + if [ -n "$WP_CONTENT" ]; then + echo "── Syncing wp-content ──" + VOLUME_PATH=$(docker volume inspect ummah-wordpress_ummah-wp-data \ + --format '{{.Mountpoint}}') + rsync -av --exclude="cache" "$WP_CONTENT/" "$VOLUME_PATH/wp-content/" + docker compose exec wordpress chown -R www-data:www-data /var/www/html/wp-content + fi + + rm -rf "$TMP_DIR" +else + echo "❌ Unknown file type: $EXT (expected .sql, .tar.gz, or .zip)" + exit 1 +fi + +# ── Post-migration ──────────────────────────────────────────────────────────── +echo "── Post-migration cleanup ──" +docker compose run --rm wpcli cache flush +docker compose run --rm wpcli rewrite flush + +echo "" +echo "✅ Migration complete. Visit: $NEW_URL" +echo " Once verified, update DNS: ummah.falahos.my → 13.140.161.244" +echo " Then run: wp-cli search-replace ummah2.falahos.my ummah.falahos.my --all-tables" diff --git a/ummah-wordpress/nginx/default.conf b/ummah-wordpress/nginx/default.conf new file mode 100644 index 0000000..1c6e45f --- /dev/null +++ b/ummah-wordpress/nginx/default.conf @@ -0,0 +1,47 @@ +server { + listen 80; + server_name _; + root /var/www/html; + index index.php; + + client_max_body_size 64M; + + # Security headers + add_header X-Frame-Options SAMEORIGIN; + add_header X-Content-Type-Options nosniff; + add_header Referrer-Policy strict-origin-when-cross-origin; + + location / { + try_files $uri $uri/ /index.php?$args; + } + + location ~ \.php$ { + fastcgi_pass wordpress:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_read_timeout 300; + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + } + + # WordPress uploads + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + # Block sensitive files + location ~ /\.(ht|git|env) { + deny all; + } + + location = /xmlrpc.php { + deny all; + } + + location /wp-json/ { + try_files $uri $uri/ /index.php?$args; + } +} diff --git a/ummah-wordpress/wordpress-config/php.ini b/ummah-wordpress/wordpress-config/php.ini new file mode 100644 index 0000000..f882dfe --- /dev/null +++ b/ummah-wordpress/wordpress-config/php.ini @@ -0,0 +1,10 @@ +memory_limit = 256M +max_execution_time = 300 +upload_max_filesize = 64M +post_max_size = 64M +max_input_vars = 5000 +opcache.enable = 1 +opcache.memory_consumption = 128 +opcache.interned_strings_buffer = 8 +opcache.max_accelerated_files = 4000 +opcache.revalidate_freq = 60 diff --git a/wordpress-plugins/falah-shortcodes.zip b/wordpress-plugins/falah-shortcodes.zip new file mode 100644 index 0000000..8f43d23 Binary files /dev/null and b/wordpress-plugins/falah-shortcodes.zip differ diff --git a/wordpress-plugins/hermes-ai-bridge.zip b/wordpress-plugins/hermes-ai-bridge.zip new file mode 100644 index 0000000..263ae02 Binary files /dev/null and b/wordpress-plugins/hermes-ai-bridge.zip differ