diff --git a/common.nix b/common.nix index 77a7e3d..310f778 100644 --- a/common.nix +++ b/common.nix @@ -561,679 +561,679 @@ isoConfig serverList = builtins.concatStringsSep " " cloudServerNames; in '' - # Workshop Environment Welcome - echo "๐Ÿš€ CODE CRISPIES Workshop Environment" - echo "Mode: Local Development (Offline Co-op Cloud)" - echo "" - - # DNS Health Check - if command -v nslookup >/dev/null 2>&1; then - if nslookup test.workshop.local 127.0.0.1 >/dev/null 2>&1; then - echo "โœ… DNS wildcard ready: *.workshop.local โ†’ 127.0.0.1" - else - echo "โš ๏ธ DNS not working! Run: sudo systemctl restart dnsmasq" - fi - fi - - # Ensure /root/.local/bin is in PATH (safety net) - if [[ ":$PATH:" != *":/root/.local/bin:"* ]]; then - echo "โœ… adding abra to PATH" - export PATH="$PATH:/root/.local/bin" - fi - - # Check abra installation - if sudo abra >/dev/null 2>&1; then - echo "โœ… abra ready: $(sudo which abra)" - source <(sudo abra autocomplete bash) 2>/dev/null || true - echo "โœ… abra autocomplete enabled" - else - echo "โš ๏ธ abra not found! Check: systemctl status workshop-abra-install" - fi - - # Bash Completion Configuration - _workshop_completion() { - local cur prev - COMPREPLY=() - cur="''${COMP_WORDS[COMP_CWORD]}" - prev="''${COMP_WORDS[COMP_CWORD-1]}" - - case "$prev" in - deploy|browser) - COMPREPLY=($(compgen -W "${recipeList}" -- "$cur")) - return 0 - ;; - connect) - COMPREPLY=($(compgen -W "${serverList}" -- "$cur")) - return 0 - ;; - esac - } - complete -F _workshop_completion deploy browser connect abra - - # Core Workshop Functions - setup() { - echo "๐Ÿ”ง Setting up LOCAL Co-op Cloud environment..." - - # Run permission checks first - setup_permissions || return 1 - - # Run setup steps individually - setup_dns || return 1 - setup_docker || return 1 - setup_abra_server || return 1 - setup_certificates || return 1 - setup_traefik || return 1 - - echo "๐ŸŽ‰ Setup complete!" - } - - setup_permissions() { - echo "๐Ÿ” Checking system permissions and prerequisites..." - - # Check if running as workshop user - if [[ "$(whoami)" != "workshop" ]]; then - echo "โš ๏ธ Not running as workshop user (current: $(whoami))" - echo " This may cause permission issues. Consider running as workshop user." - else - echo "โœ… Running as workshop user" - fi - - # Check sudo access - if sudo -n true 2>/dev/null; then - echo "โœ… Sudo access available (no password required)" - else - echo "โš ๏ธ Sudo may require password - this could interrupt automated setup" - fi - - # Check Docker group membership - if id -nG | grep -q "docker"; then - echo "โœ… User is in docker group" - else - echo "โš ๏ธ User not in docker group - Docker commands may fail" - echo " Current groups: $(id -nG)" - fi - - # Check if abra is available via sudo - if sudo abra --version >/dev/null 2>&1; then - echo "โœ… abra available via sudo: $(sudo which abra)" - else - echo "โŒ abra not available via sudo" - echo " Check: systemctl status workshop-abra-install" - return 1 - fi - - # Check abra server configuration - if sudo abra server ls 2>/dev/null | grep -q "default"; then - echo "โœ… Abra default server configured" - else - echo "โš ๏ธ Abra default server not configured - will be set up" - fi - - # Check /tmp permissions - if [[ -w "/tmp" ]]; then - echo "โœ… /tmp directory is writable" - else - echo "โŒ /tmp directory is not writable" - ls -ld /tmp - return 1 - fi - - # Check openssl availability - if command -v openssl >/dev/null 2>&1; then - echo "โœ… OpenSSL available: $(openssl version | head -1)" - else - echo "โŒ OpenSSL not found - certificate generation will fail" - return 1 - fi - - # Check curl availability - if command -v curl >/dev/null 2>&1; then - echo "โœ… curl available for health checks" - else - echo "โš ๏ธ curl not found - health checks may not work properly" - fi - - echo "๐ŸŽฏ Permission checks complete!" - } - - setup_dns() { - echo "๐ŸŒ Step 1: Verifying DNS configuration..." - - if ! nslookup traefik.workshop.local 127.0.0.1 >/dev/null 2>&1; then - echo "๐Ÿ”„ DNS not working, restarting dnsmasq..." - sudo systemctl restart dnsmasq - sleep 3 - - # Test again - if nslookup traefik.workshop.local 127.0.0.1 >/dev/null 2>&1; then - echo "โœ… DNS restarted successfully" - else - echo "โŒ DNS restart failed" - return 1 - fi - else - echo "โœ… DNS working correctly" - fi - } - - setup_docker() { - echo "๐Ÿณ Step 2: Setting up Docker Swarm and networks..." - - # Check Docker status - if ! docker info 2>/dev/null | grep -q "Swarm: active"; then - echo "๐Ÿ”ฅ Initializing Docker Swarm..." - if docker swarm init --advertise-addr 127.0.0.1; then - echo "โœ… Docker Swarm initialized" - else - echo "โŒ Docker Swarm initialization failed" - return 1 - fi - else - echo "โœ… Docker Swarm already active" - fi - - # Check proxy network - if ! docker network ls | grep -q "proxy"; then - echo "๐ŸŒ Creating proxy network..." - if docker network create -d overlay proxy; then - echo "โœ… Proxy network created" - else - echo "โŒ Proxy network creation failed" - return 1 - fi - else - echo "โœ… Proxy network exists" - fi - } - - setup_abra_server() { - echo "๐Ÿ—„๏ธ Step 3: Setting up Abra server..." - - if ! sudo abra server ls 2>/dev/null | grep -q "default"; then - echo "๐Ÿ—„๏ธ Adding LOCAL server to abra..." - if sudo abra server add --local; then - echo "โœ… Local server registered" - else - echo "โŒ Failed to add local server" - return 1 - fi - else - echo "โœ… Abra server already configured" - fi - } - - setup_certificates() { - echo "๐Ÿ” Step 4: Generating self-signed certificates..." - - setup_certificates_dir || return 1 - setup_certificates_generate || return 1 - setup_certificates_verify || return 1 - - # Export CERT_DIR for use in setup_traefik - export CERT_DIR - } - - setup_certificates_dir() { - echo "๐Ÿ“ Creating certificate directory..." - - CERT_DIR="/tmp/workshop-certs" - echo " Target directory: $CERT_DIR" - - # Check if directory already exists and clean it up - if [[ -d "$CERT_DIR" ]]; then - echo " ๐Ÿงน Cleaning up existing certificate directory..." - rm -rf "$CERT_DIR" || { - echo "โŒ Failed to remove existing directory" - return 1 - } - fi - - # Create fresh directory - if mkdir -p "$CERT_DIR"; then - echo "โœ… Certificate directory created" - else - echo "โŒ Failed to create certificate directory" - echo " Current user: $(whoami)" - echo " User ID: $(id)" - echo " /tmp permissions: $(ls -ld /tmp)" - return 1 - fi - - # Verify directory permissions - echo "๐Ÿ” Verifying directory permissions..." - ls -la /tmp/ | grep workshop-certs || { - echo "โŒ Directory not found in /tmp listing" - return 1 - } - - local dir_perms=$(stat -c "%a" "$CERT_DIR" 2>/dev/null || echo "unknown") - echo " Directory permissions: $dir_perms" - echo " Directory owner: $(stat -c "%U:%G" "$CERT_DIR" 2>/dev/null || echo "unknown")" - } - - setup_certificates_generate() { - echo "๐Ÿ”‘ Generating self-signed certificate..." - - CERT_FILE="$CERT_DIR/workshop.crt" - KEY_FILE="$CERT_DIR/workshop.key" - - echo " Certificate file: $CERT_FILE" - echo " Key file: $KEY_FILE" - - # Check if openssl is available - if ! command -v openssl >/dev/null 2>&1; then - echo "โŒ OpenSSL not found in PATH" - which openssl || echo " openssl command not found" - return 1 - fi - - echo " OpenSSL version: $(openssl version)" - - # Check if certificate already exists - if [[ -f "$CERT_FILE" ]]; then - echo " โš ๏ธ Certificate file already exists, removing..." - rm -f "$CERT_FILE" "$KEY_FILE" || { - echo "โŒ Failed to remove existing certificate files" - return 1 - } - fi - - # Generate certificate following abra guidelines - echo " Generating RSA key and certificate (abra-compatible)..." - if openssl req -x509 -out "$CERT_FILE" -keyout "$KEY_FILE" \ - -newkey rsa:2048 -nodes -sha256 -days 365 \ - -subj "/CN=*.workshop.local" -extensions EXT -config <( \ - printf "[dn]\nCN=*.workshop.local\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:*.workshop.local,DNS:workshop.local,DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth"); then - - echo "โœ… Certificate generation completed successfully (abra-compatible)" - else - echo "โŒ Certificate generation failed" - echo " OpenSSL exit code: $?" - return 1 - fi - } - - setup_certificates_verify() { - echo "๐Ÿ” Verifying certificate files..." - - CERT_FILE="$CERT_DIR/workshop.crt" - KEY_FILE="$CERT_DIR/workshop.key" - - # Check if files exist - if [[ ! -f "$CERT_FILE" ]]; then - echo "โŒ Certificate file not found: $CERT_FILE" - ls -la "$CERT_DIR" || echo " Directory listing failed" - return 1 - fi - - if [[ ! -f "$KEY_FILE" ]]; then - echo "โŒ Key file not found: $KEY_FILE" - ls -la "$CERT_DIR" || echo " Directory listing failed" - return 1 - fi - - echo "โœ… Certificate files created successfully" - - # Show file details - echo " Certificate file details:" - ls -la "$CERT_FILE" - echo " Key file details:" - ls -la "$KEY_FILE" - - # Verify certificate content - echo " Verifying certificate content..." - if openssl x509 -in "$CERT_FILE" -text -noout >/dev/null 2>&1; then - echo "โœ… Certificate is valid X.509 format" - - # Show certificate details - echo " Certificate subject:" - openssl x509 -in "$CERT_FILE" -subject -noout 2>/dev/null || echo " Could not read certificate subject" - - echo " Certificate issuer:" - openssl x509 -in "$CERT_FILE" -issuer -noout 2>/dev/null || echo " Could not read certificate issuer" - - echo " Certificate validity:" - openssl x509 -in "$CERT_FILE" -dates -noout 2>/dev/null || echo " Could not read certificate dates" - - echo " Certificate extensions:" - openssl x509 -in "$CERT_FILE" -text -noout 2>/dev/null | grep -A 5 "Subject Alternative Name" || echo " No SAN extension found" - - else - echo "โŒ Certificate file is not valid" - return 1 - fi - - # Verify key content - if openssl rsa -in "$KEY_FILE" -check -noout >/dev/null 2>&1; then - echo "โœ… Private key is valid" - else - echo "โŒ Private key is invalid" - return 1 - fi - - # Verify certificate matches key - if openssl x509 -in "$CERT_FILE" -noout -modulus 2>/dev/null | openssl md5 >/dev/null 2>&1 && \ - openssl rsa -in "$KEY_FILE" -noout -modulus 2>/dev/null | openssl md5 >/dev/null 2>&1; then - echo "โœ… Certificate and key match" - else - echo "โš ๏ธ Could not verify certificate/key match (non-critical)" - fi - - echo "๐ŸŽ‰ Certificate generation and verification complete!" - } - - setup_traefik() { - echo "๐Ÿš€ Step 5: Setting up Traefik..." - - setup_traefik_app || return 1 - setup_traefik_config || return 1 - setup_traefik_secrets || return 1 - setup_traefik_deploy || return 1 - setup_traefik_wait || return 1 - } - - setup_traefik_app() { - echo "๐Ÿ“ฆ Checking Traefik app..." - - if ! sudo abra app ls 2>/dev/null | grep -q "traefik"; then - echo "๐Ÿš€ Creating Traefik app for OFFLINE use..." - echo " Command: sudo abra app new traefik --domain=traefik.workshop.local --server=default" - - if sudo abra app new traefik --domain=traefik.workshop.local --server=default; then - echo "โœ… Traefik app created successfully" - else - echo "โŒ Failed to create Traefik app" - echo " abra exit code: $?" - sudo abra app ls 2>&1 || echo " Could not list apps" - return 1 - fi - else - echo "โœ… Traefik app already exists" - fi - } - - setup_traefik_config() { - echo "โš™๏ธ Configuring Traefik for offline mode..." - - TRAEFIK_ENV="/root/.abra/servers/default/traefik.workshop.local.env" - echo " Config file: $TRAEFIK_ENV" - - # Check if config file exists - if [[ -f "$TRAEFIK_ENV" ]]; then - echo " โš ๏ธ Config file already exists, backing up..." - cp "$TRAEFIK_ENV" "$TRAEFIK_ENV.backup" || echo " Backup failed, continuing..." - fi - - # Create offline-friendly traefik configuration - echo " Writing offline configuration..." - if sudo tee -a "$TRAEFIK_ENV" >/dev/null </dev/null || echo " Parent directory not accessible" - return 1 - fi - } - - setup_traefik_secrets() { - echo "๐Ÿ“‹ Installing self-signed certificates as Docker secrets..." - - # Verify certificate files exist - if [[ ! -f "$CERT_DIR/workshop.crt" ]]; then - echo "โŒ Certificate file not found: $CERT_DIR/workshop.crt" - ls -la "$CERT_DIR" 2>/dev/null || echo " Certificate directory not accessible" - return 1 - fi - - if [[ ! -f "$CERT_DIR/workshop.key" ]]; then - echo "โŒ Key file not found: $CERT_DIR/workshop.key" - ls -la "$CERT_DIR" 2>/dev/null || echo " Certificate directory not accessible" - return 1 - fi - - echo " Certificate files verified:" - ls -la "$CERT_DIR/workshop.crt" "$CERT_DIR/workshop.key" - - # Insert SSL certificate secret - echo " ๐Ÿ” Inserting SSL certificate secret..." - echo " Command: sudo abra app secret insert traefik.workshop.local ssl_cert v1" - - if sudo abra app secret insert traefik.workshop.local ssl_cert v1 -f "$CERT_DIR/workshop.crt"; then - echo "โœ… SSL certificate secret inserted successfully" - else - echo "โŒ Failed to insert SSL certificate secret" - echo " abra exit code: $?" - echo " Checking abra app status..." - sudo abra app ls 2>&1 || echo " Could not list apps" - echo " Checking certificate file..." - file "$CERT_DIR/workshop.crt" 2>/dev/null || echo " Could not check certificate file type" - return 1 - fi - - # Insert SSL key secret - echo " ๐Ÿ”‘ Inserting SSL key secret..." - echo " Command: sudo abra app secret insert traefik.workshop.local ssl_key v1" - - if sudo abra app secret insert traefik.workshop.local ssl_key v1 -f "$CERT_DIR/workshop.key"; then - echo "โœ… SSL key secret inserted successfully" - else - echo "โŒ Failed to insert SSL key secret" - echo " abra exit code: $?" - echo " Checking abra app status..." - sudo abra app ls 2>&1 || echo " Could not list apps" - echo " Checking key file..." - file "$CERT_DIR/workshop.key" 2>/dev/null || echo " Could not check key file type" - return 1 - fi - - echo "๐ŸŽ‰ All secrets inserted successfully!" - } - - setup_traefik_deploy() { - echo "๐Ÿš€ Deploying Traefik..." - - echo " Command: sudo abra app deploy traefik.workshop.local" - - if sudo abra app deploy traefik.workshop.local; then - echo "โœ… Traefik deployment initiated successfully" - else - echo "โŒ Traefik deployment failed" - echo " abra exit code: $?" - echo " Checking deployment status..." - sudo abra app ps traefik.workshop.local 2>&1 || echo " Could not check app status" - return 1 - fi - } - - setup_traefik_wait() { - echo "โณ Waiting for Traefik to be ready..." - - for i in {1..30}; do - echo " Checking Traefik status (attempt $i/30)..." - - # Try HTTPS first - if curl -s -k --max-time 5 https://traefik.workshop.local/ping >/dev/null 2>&1; then - echo "โœ… Traefik ready via HTTPS!" - echo " Dashboard: https://traefik.workshop.local (accept self-signed cert)" - echo " ๐Ÿ’ก For HTTP: http://traefik.workshop.local" - break - fi - - # Try HTTP as fallback - if curl -s --max-time 5 http://traefik.workshop.local/ping >/dev/null 2>&1; then - echo "โœ… Traefik ready via HTTP!" - echo " Dashboard: http://traefik.workshop.local" - echo " ๐Ÿ’ก For HTTPS: https://traefik.workshop.local (may require accepting cert)" - break - fi - - if [[ $i -eq 30 ]]; then - echo "โŒ Traefik failed to respond after 30 attempts" - echo " ๐Ÿ” Debug commands:" - echo " sudo abra app logs traefik.workshop.local" - echo " sudo abra app ps traefik.workshop.local" - echo " docker service ls | grep traefik" - return 1 - fi - - sleep 2 - done - - # Cleanup temporary certs - echo "๐Ÿงน Cleaning up temporary certificate files..." - if rm -rf "$CERT_DIR" 2>/dev/null; then - echo "โœ… Certificate cleanup completed" - else - echo "โš ๏ธ Certificate cleanup failed (non-critical)" - fi - - echo "๐ŸŽ‰ Traefik setup complete!" - } - - deploy() { - if [[ -z "$1" ]]; then - echo "Usage: deploy " - echo "Available: ${recipeList}" - return 1 - fi - - local recipe="$1" - local domain="$recipe.workshop.local" - - echo "๐Ÿš€ Deploying $recipe locally..." - echo "Domain: $domain" - - # Ensure Traefik is running - if ! curl -s -k --max-time 3 https://traefik.workshop.local/ping >/dev/null 2>&1 && \ - ! curl -s --max-time 3 http://traefik.workshop.local/ping >/dev/null 2>&1; then - echo "โš ๏ธ Traefik not responding. Setting up..." - setup || return 1 - fi - - # Create and deploy app - echo "๐Ÿ“ฆ Creating app: $recipe" - sudo abra app new "$recipe" --domain="$domain" --server=default - - echo "๐Ÿš€ Deploying: $domain" - sudo abra app deploy "$domain" - - echo "โณ Waiting for deployment..." - for i in {1..60}; do - if curl -s -k --max-time 3 https://$domain >/dev/null 2>&1 || \ - curl -s --max-time 3 http://$domain >/dev/null 2>&1; then - echo "โœ… Deployed! Access at: https://$domain (accept self-signed cert)" - echo "๐Ÿ’ก Or HTTP: http://$domain" - return 0 - fi - sleep 3 - done - - echo "โš ๏ธ Deployment may still be starting..." - echo "๐Ÿ” Debug: sudo abra app ps $domain" - } - - connect() { - if [[ -z "$1" ]]; then - echo "Usage: connect " - echo "Available: ${serverList}" - return 1 - fi - echo "๐Ÿ”Œ Connecting to $1.codecrispi.es..." - ssh -o StrictHostKeyChecking=no workshop@$1.codecrispi.es - } - - browser() { - local target_url="about:blank" - - if [[ -n "$1" ]]; then - target_url="http://$1.workshop.local" - echo "๐ŸŒ Opening $1 at $target_url in Firefox" - else - echo "๐ŸŒ Opening Firefox browser" - fi - - if [[ -n "$DISPLAY" ]]; then - firefox "$target_url" & - else - echo "โŒ No GUI session. Run 'desktop' first" - echo "๐ŸŒ Target was: $target_url" - fi - } - - recipes() { - echo "๐Ÿ“š Complete Co-op Cloud Recipe Catalog:" - echo "" - echo "โญ Tier 1 - Production Ready: gitea mealie nextcloud" - echo "๐Ÿ”ง Tier 2 - Stable: gotosocial wordpress" - echo "๐Ÿงช Tier 3 - Community: collabora croc dokuwiki ghost loomio..." - echo "๐ŸŒ Extended: matrix-synapse rocketchat gitlab n8n mastodon..." - echo "" - echo "๐Ÿš€ Usage:" - echo " deploy - Deploy locally" - echo " browser [recipe] - Open in browser" - echo " ๐Ÿ“– Full catalog: https://recipes.coopcloud.tech" - echo "" - echo "๐Ÿ’ก Tab completion: deploy or browser " - } - - desktop() { - echo "๐Ÿ–ฅ๏ธ Starting GUI session..." - if command -v startx >/dev/null 2>&1; then - if [[ -z "$DISPLAY" ]]; then - startx & - export DISPLAY=:0 - sleep 3 - echo "โœ… GUI started" - else - echo "โ„น๏ธ GUI already running" - fi - else - echo "๐Ÿ’ก GUI available in QEMU window" - fi - } - - help() { - echo "๐Ÿš€ CODE CRISPIES Workshop Commands:" - echo "" - echo "๐Ÿ  Local Development:" - echo " setup - Setup local proxy (REQUIRED FIRST!)" - echo " recipes - Show all available apps" - echo " deploy - Deploy app locally" - echo " browser [recipe] - Launch browser [to app]" - echo " desktop - Start GUI session" - echo " sudo abra - Run abra CLI directly as root" - echo "" - echo "โ˜๏ธ Cloud Access:" - echo " connect - SSH to cloud server" - echo " Available: ${serverList}" - echo "" - echo "๐Ÿ” Debug:" - echo " docker service ls - List running services" - echo " systemctl status dnsmasq - Check DNS" - echo " systemctl status workshop-abra-install - Check abra installation" - echo "" - echo "๐Ÿ“š Learning Flow:" - echo " 1. setup" - echo " 2. deploy wordpress" - echo " 3. browser wordpress" - echo " 4. connect hopper" - } + # Workshop Environment Welcome + echo "๐Ÿš€ CODE CRISPIES Workshop Environment" + echo "Mode: Local Development (Offline Co-op Cloud)" + echo "" + + # DNS Health Check + if command -v nslookup >/dev/null 2>&1; then + if nslookup test.workshop.local 127.0.0.1 >/dev/null 2>&1; then + echo "โœ… DNS wildcard ready: *.workshop.local โ†’ 127.0.0.1" + else + echo "โš ๏ธ DNS not working! Run: sudo systemctl restart dnsmasq" + fi + fi + + # Ensure /root/.local/bin is in PATH (safety net) + if [[ ":$PATH:" != *":/root/.local/bin:"* ]]; then + echo "โœ… adding abra to PATH" + export PATH="$PATH:/root/.local/bin" + fi + + # Check abra installation + if sudo abra >/dev/null 2>&1; then + echo "โœ… abra ready: $(sudo which abra)" + source <(sudo abra autocomplete bash) 2>/dev/null || true + echo "โœ… abra autocomplete enabled" + else + echo "โš ๏ธ abra not found! Check: systemctl status workshop-abra-install" + fi + + # Bash Completion Configuration + _workshop_completion() { + local cur prev + COMPREPLY=() + cur="''${COMP_WORDS[COMP_CWORD]}" + prev="''${COMP_WORDS[COMP_CWORD-1]}" + + case "$prev" in + deploy|browser) + COMPREPLY=($(compgen -W "${recipeList}" -- "$cur")) + return 0 + ;; + connect) + COMPREPLY=($(compgen -W "${serverList}" -- "$cur")) + return 0 + ;; + esac + } + complete -F _workshop_completion deploy browser connect abra + + # Core Workshop Functions + setup() { + echo "๐Ÿ”ง Setting up LOCAL Co-op Cloud environment..." + + # Run permission checks first + setup_permissions || return 1 + + # Run setup steps individually + setup_dns || return 1 + setup_docker || return 1 + setup_abra_server || return 1 + setup_certificates || return 1 + setup_traefik || return 1 + + echo "๐ŸŽ‰ Setup complete!" + } + + setup_permissions() { + echo "๐Ÿ” Checking system permissions and prerequisites..." + + # Check if running as workshop user + if [[ "$(whoami)" != "workshop" ]]; then + echo "โš ๏ธ Not running as workshop user (current: $(whoami))" + echo " This may cause permission issues. Consider running as workshop user." + else + echo "โœ… Running as workshop user" + fi + + # Check sudo access + if sudo -n true 2>/dev/null; then + echo "โœ… Sudo access available (no password required)" + else + echo "โš ๏ธ Sudo may require password - this could interrupt automated setup" + fi + + # Check Docker group membership + if id -nG | grep -q "docker"; then + echo "โœ… User is in docker group" + else + echo "โš ๏ธ User not in docker group - Docker commands may fail" + echo " Current groups: $(id -nG)" + fi + + # Check if abra is available via sudo + if sudo abra --version >/dev/null 2>&1; then + echo "โœ… abra available via sudo: $(sudo which abra)" + else + echo "โŒ abra not available via sudo" + echo " Check: systemctl status workshop-abra-install" + return 1 + fi + + # Check abra server configuration + if sudo abra server ls 2>/dev/null | grep -q "default"; then + echo "โœ… Abra default server configured" + else + echo "โš ๏ธ Abra default server not configured - will be set up" + fi + + # Check /tmp permissions + if [[ -w "/tmp" ]]; then + echo "โœ… /tmp directory is writable" + else + echo "โŒ /tmp directory is not writable" + ls -ld /tmp + return 1 + fi + + # Check openssl availability + if command -v openssl >/dev/null 2>&1; then + echo "โœ… OpenSSL available: $(openssl version | head -1)" + else + echo "โŒ OpenSSL not found - certificate generation will fail" + return 1 + fi + + # Check curl availability + if command -v curl >/dev/null 2>&1; then + echo "โœ… curl available for health checks" + else + echo "โš ๏ธ curl not found - health checks may not work properly" + fi + + echo "๐ŸŽฏ Permission checks complete!" + } + + setup_dns() { + echo "๐ŸŒ Step 1: Verifying DNS configuration..." + + if ! nslookup traefik.workshop.local 127.0.0.1 >/dev/null 2>&1; then + echo "๐Ÿ”„ DNS not working, restarting dnsmasq..." + sudo systemctl restart dnsmasq + sleep 3 + + # Test again + if nslookup traefik.workshop.local 127.0.0.1 >/dev/null 2>&1; then + echo "โœ… DNS restarted successfully" + else + echo "โŒ DNS restart failed" + return 1 + fi + else + echo "โœ… DNS working correctly" + fi + } + + setup_docker() { + echo "๐Ÿณ Step 2: Setting up Docker Swarm and networks..." + + # Check Docker status + if ! docker info 2>/dev/null | grep -q "Swarm: active"; then + echo "๐Ÿ”ฅ Initializing Docker Swarm..." + if docker swarm init --advertise-addr 127.0.0.1; then + echo "โœ… Docker Swarm initialized" + else + echo "โŒ Docker Swarm initialization failed" + return 1 + fi + else + echo "โœ… Docker Swarm already active" + fi + + # Check proxy network + if ! docker network ls | grep -q "proxy"; then + echo "๐ŸŒ Creating proxy network..." + if docker network create -d overlay proxy; then + echo "โœ… Proxy network created" + else + echo "โŒ Proxy network creation failed" + return 1 + fi + else + echo "โœ… Proxy network exists" + fi + } + + setup_abra_server() { + echo "๐Ÿ—„๏ธ Step 3: Setting up Abra server..." + + if ! sudo abra server ls 2>/dev/null | grep -q "default"; then + echo "๐Ÿ—„๏ธ Adding LOCAL server to abra..." + if sudo abra server add --local; then + echo "โœ… Local server registered" + else + echo "โŒ Failed to add local server" + return 1 + fi + else + echo "โœ… Abra server already configured" + fi + } + + setup_certificates() { + echo "๐Ÿ” Step 4: Generating self-signed certificates..." + + setup_certificates_dir || return 1 + setup_certificates_generate || return 1 + setup_certificates_verify || return 1 + + # Export CERT_DIR for use in setup_traefik + export CERT_DIR + } + + setup_certificates_dir() { + echo "๐Ÿ“ Creating certificate directory..." + + CERT_DIR="/tmp/workshop-certs" + echo " Target directory: $CERT_DIR" + + # Check if directory already exists and clean it up + if [[ -d "$CERT_DIR" ]]; then + echo " ๐Ÿงน Cleaning up existing certificate directory..." + rm -rf "$CERT_DIR" || { + echo "โŒ Failed to remove existing directory" + return 1 + } + fi + + # Create fresh directory + if mkdir -p "$CERT_DIR"; then + echo "โœ… Certificate directory created" + else + echo "โŒ Failed to create certificate directory" + echo " Current user: $(whoami)" + echo " User ID: $(id)" + echo " /tmp permissions: $(ls -ld /tmp)" + return 1 + fi + + # Verify directory permissions + echo "๐Ÿ” Verifying directory permissions..." + ls -la /tmp/ | grep workshop-certs || { + echo "โŒ Directory not found in /tmp listing" + return 1 + } + + local dir_perms=$(stat -c "%a" "$CERT_DIR" 2>/dev/null || echo "unknown") + echo " Directory permissions: $dir_perms" + echo " Directory owner: $(stat -c "%U:%G" "$CERT_DIR" 2>/dev/null || echo "unknown")" + } + + setup_certificates_generate() { + echo "๐Ÿ”‘ Generating self-signed certificate..." + + CERT_FILE="$CERT_DIR/workshop.crt" + KEY_FILE="$CERT_DIR/workshop.key" + + echo " Certificate file: $CERT_FILE" + echo " Key file: $KEY_FILE" + + # Check if openssl is available + if ! command -v openssl >/dev/null 2>&1; then + echo "โŒ OpenSSL not found in PATH" + which openssl || echo " openssl command not found" + return 1 + fi + + echo " OpenSSL version: $(openssl version)" + + # Check if certificate already exists + if [[ -f "$CERT_FILE" ]]; then + echo " โš ๏ธ Certificate file already exists, removing..." + rm -f "$CERT_FILE" "$KEY_FILE" || { + echo "โŒ Failed to remove existing certificate files" + return 1 + } + fi + + # Generate certificate following abra guidelines + echo " Generating RSA key and certificate (abra-compatible)..." + if openssl req -x509 -out "$CERT_FILE" -keyout "$KEY_FILE" \ + -newkey rsa:2048 -nodes -sha256 -days 365 \ + -subj "/CN=*.workshop.local" -extensions EXT -config <( \ + printf "[dn]\nCN=*.workshop.local\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:*.workshop.local,DNS:workshop.local,DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth"); then + + echo "โœ… Certificate generation completed successfully (abra-compatible)" + else + echo "โŒ Certificate generation failed" + echo " OpenSSL exit code: $?" + return 1 + fi + } + + setup_certificates_verify() { + echo "๐Ÿ” Verifying certificate files..." + + CERT_FILE="$CERT_DIR/workshop.crt" + KEY_FILE="$CERT_DIR/workshop.key" + + # Check if files exist + if [[ ! -f "$CERT_FILE" ]]; then + echo "โŒ Certificate file not found: $CERT_FILE" + ls -la "$CERT_DIR" || echo " Directory listing failed" + return 1 + fi + + if [[ ! -f "$KEY_FILE" ]]; then + echo "โŒ Key file not found: $KEY_FILE" + ls -la "$CERT_DIR" || echo " Directory listing failed" + return 1 + fi + + echo "โœ… Certificate files created successfully" + + # Show file details + echo " Certificate file details:" + ls -la "$CERT_FILE" + echo " Key file details:" + ls -la "$KEY_FILE" + + # Verify certificate content + echo " Verifying certificate content..." + if openssl x509 -in "$CERT_FILE" -text -noout >/dev/null 2>&1; then + echo "โœ… Certificate is valid X.509 format" + + # Show certificate details + echo " Certificate subject:" + openssl x509 -in "$CERT_FILE" -subject -noout 2>/dev/null || echo " Could not read certificate subject" + + echo " Certificate issuer:" + openssl x509 -in "$CERT_FILE" -issuer -noout 2>/dev/null || echo " Could not read certificate issuer" + + echo " Certificate validity:" + openssl x509 -in "$CERT_FILE" -dates -noout 2>/dev/null || echo " Could not read certificate dates" + + echo " Certificate extensions:" + openssl x509 -in "$CERT_FILE" -text -noout 2>/dev/null | grep -A 5 "Subject Alternative Name" || echo " No SAN extension found" + + else + echo "โŒ Certificate file is not valid" + return 1 + fi + + # Verify key content + if openssl rsa -in "$KEY_FILE" -check -noout >/dev/null 2>&1; then + echo "โœ… Private key is valid" + else + echo "โŒ Private key is invalid" + return 1 + fi + + # Verify certificate matches key + if openssl x509 -in "$CERT_FILE" -noout -modulus 2>/dev/null | openssl md5 >/dev/null 2>&1 && \ + openssl rsa -in "$KEY_FILE" -noout -modulus 2>/dev/null | openssl md5 >/dev/null 2>&1; then + echo "โœ… Certificate and key match" + else + echo "โš ๏ธ Could not verify certificate/key match (non-critical)" + fi + + echo "๐ŸŽ‰ Certificate generation and verification complete!" + } + + setup_traefik() { + echo "๐Ÿš€ Step 5: Setting up Traefik..." + + setup_traefik_app || return 1 + setup_traefik_config || return 1 + setup_traefik_secrets || return 1 + setup_traefik_deploy || return 1 + setup_traefik_wait || return 1 + } + + setup_traefik_app() { + echo "๐Ÿ“ฆ Checking Traefik app..." + + if ! sudo abra app ls 2>/dev/null | grep -q "traefik"; then + echo "๐Ÿš€ Creating Traefik app for OFFLINE use..." + echo " Command: sudo abra app new traefik --domain=traefik.workshop.local --server=default" + + if sudo abra app new traefik --domain=traefik.workshop.local --server=default; then + echo "โœ… Traefik app created successfully" + else + echo "โŒ Failed to create Traefik app" + echo " abra exit code: $?" + sudo abra app ls 2>&1 || echo " Could not list apps" + return 1 + fi + else + echo "โœ… Traefik app already exists" + fi + } + + setup_traefik_config() { + echo "โš™๏ธ Configuring Traefik for offline mode..." + + TRAEFIK_ENV="/root/.abra/servers/default/traefik.workshop.local.env" + echo " Config file: $TRAEFIK_ENV" + + # Check if config file exists + if [[ -f "$TRAEFIK_ENV" ]]; then + echo " โš ๏ธ Config file already exists, backing up..." + cp "$TRAEFIK_ENV" "$TRAEFIK_ENV.backup" || echo " Backup failed, continuing..." + fi + + # Create offline-friendly traefik configuration + echo " Writing offline configuration..." + if sudo tee -a "$TRAEFIK_ENV" >/dev/null </dev/null || echo " Parent directory not accessible" + return 1 + fi + } + + setup_traefik_secrets() { + echo "๐Ÿ“‹ Installing self-signed certificates as Docker secrets..." + + # Verify certificate files exist + if [[ ! -f "$CERT_DIR/workshop.crt" ]]; then + echo "โŒ Certificate file not found: $CERT_DIR/workshop.crt" + ls -la "$CERT_DIR" 2>/dev/null || echo " Certificate directory not accessible" + return 1 + fi + + if [[ ! -f "$CERT_DIR/workshop.key" ]]; then + echo "โŒ Key file not found: $CERT_DIR/workshop.key" + ls -la "$CERT_DIR" 2>/dev/null || echo " Certificate directory not accessible" + return 1 + fi + + echo " Certificate files verified:" + ls -la "$CERT_DIR/workshop.crt" "$CERT_DIR/workshop.key" + + # Insert SSL certificate secret + echo " ๐Ÿ” Inserting SSL certificate secret..." + echo " Command: sudo abra app secret insert traefik.workshop.local ssl_cert v1" + + if sudo abra app secret insert traefik.workshop.local ssl_cert v1 -f "$CERT_DIR/workshop.crt"; then + echo "โœ… SSL certificate secret inserted successfully" + else + echo "โŒ Failed to insert SSL certificate secret" + echo " abra exit code: $?" + echo " Checking abra app status..." + sudo abra app ls 2>&1 || echo " Could not list apps" + echo " Checking certificate file..." + file "$CERT_DIR/workshop.crt" 2>/dev/null || echo " Could not check certificate file type" + return 1 + fi + + # Insert SSL key secret + echo " ๐Ÿ”‘ Inserting SSL key secret..." + echo " Command: sudo abra app secret insert traefik.workshop.local ssl_key v1" + + if sudo abra app secret insert traefik.workshop.local ssl_key v1 -f "$CERT_DIR/workshop.key"; then + echo "โœ… SSL key secret inserted successfully" + else + echo "โŒ Failed to insert SSL key secret" + echo " abra exit code: $?" + echo " Checking abra app status..." + sudo abra app ls 2>&1 || echo " Could not list apps" + echo " Checking key file..." + file "$CERT_DIR/workshop.key" 2>/dev/null || echo " Could not check key file type" + return 1 + fi + + echo "๐ŸŽ‰ All secrets inserted successfully!" + } + + setup_traefik_deploy() { + echo "๐Ÿš€ Deploying Traefik..." + + echo " Command: sudo abra app deploy traefik.workshop.local" + + if sudo abra app deploy traefik.workshop.local; then + echo "โœ… Traefik deployment initiated successfully" + else + echo "โŒ Traefik deployment failed" + echo " abra exit code: $?" + echo " Checking deployment status..." + sudo abra app ps traefik.workshop.local 2>&1 || echo " Could not check app status" + return 1 + fi + } + + setup_traefik_wait() { + echo "โณ Waiting for Traefik to be ready..." + + for i in {1..30}; do + echo " Checking Traefik status (attempt $i/30)..." + + # Try HTTPS first + if curl -s -k --max-time 5 https://traefik.workshop.local/ping >/dev/null 2>&1; then + echo "โœ… Traefik ready via HTTPS!" + echo " Dashboard: https://traefik.workshop.local (accept self-signed cert)" + echo " ๐Ÿ’ก For HTTP: http://traefik.workshop.local" + break + fi + + # Try HTTP as fallback + if curl -s --max-time 5 http://traefik.workshop.local/ping >/dev/null 2>&1; then + echo "โœ… Traefik ready via HTTP!" + echo " Dashboard: http://traefik.workshop.local" + echo " ๐Ÿ’ก For HTTPS: https://traefik.workshop.local (may require accepting cert)" + break + fi + + if [[ $i -eq 30 ]]; then + echo "โŒ Traefik failed to respond after 30 attempts" + echo " ๐Ÿ” Debug commands:" + echo " sudo abra app logs traefik.workshop.local" + echo " sudo abra app ps traefik.workshop.local" + echo " docker service ls | grep traefik" + return 1 + fi + + sleep 2 + done + + # Cleanup temporary certs + echo "๐Ÿงน Cleaning up temporary certificate files..." + if rm -rf "$CERT_DIR" 2>/dev/null; then + echo "โœ… Certificate cleanup completed" + else + echo "โš ๏ธ Certificate cleanup failed (non-critical)" + fi + + echo "๐ŸŽ‰ Traefik setup complete!" + } + + deploy() { + if [[ -z "$1" ]]; then + echo "Usage: deploy " + echo "Available: ${recipeList}" + return 1 + fi + + local recipe="$1" + local domain="$recipe.workshop.local" + + echo "๐Ÿš€ Deploying $recipe locally..." + echo "Domain: $domain" + + # Ensure Traefik is running + if ! curl -s -k --max-time 3 https://traefik.workshop.local/ping >/dev/null 2>&1 && \ + ! curl -s --max-time 3 http://traefik.workshop.local/ping >/dev/null 2>&1; then + echo "โš ๏ธ Traefik not responding. Setting up..." + setup || return 1 + fi + + # Create and deploy app + echo "๐Ÿ“ฆ Creating app: $recipe" + sudo abra app new "$recipe" --domain="$domain" --server=default + + echo "๐Ÿš€ Deploying: $domain" + sudo abra app deploy "$domain" + + echo "โณ Waiting for deployment..." + for i in {1..60}; do + if curl -s -k --max-time 3 https://$domain >/dev/null 2>&1 || \ + curl -s --max-time 3 http://$domain >/dev/null 2>&1; then + echo "โœ… Deployed! Access at: https://$domain (accept self-signed cert)" + echo "๐Ÿ’ก Or HTTP: http://$domain" + return 0 + fi + sleep 3 + done + + echo "โš ๏ธ Deployment may still be starting..." + echo "๐Ÿ” Debug: sudo abra app ps $domain" + } + + connect() { + if [[ -z "$1" ]]; then + echo "Usage: connect " + echo "Available: ${serverList}" + return 1 + fi + echo "๐Ÿ”Œ Connecting to $1.codecrispi.es..." + ssh -o StrictHostKeyChecking=no workshop@$1.codecrispi.es + } + + browser() { + local target_url="about:blank" + + if [[ -n "$1" ]]; then + target_url="http://$1.workshop.local" + echo "๐ŸŒ Opening $1 at $target_url in Firefox" + else + echo "๐ŸŒ Opening Firefox browser" + fi + + if [[ -n "$DISPLAY" ]]; then + firefox "$target_url" & + else + echo "โŒ No GUI session. Run 'desktop' first" + echo "๐ŸŒ Target was: $target_url" + fi + } + + recipes() { + echo "๐Ÿ“š Complete Co-op Cloud Recipe Catalog:" + echo "" + echo "โญ Tier 1 - Production Ready: gitea mealie nextcloud" + echo "๐Ÿ”ง Tier 2 - Stable: gotosocial wordpress" + echo "๐Ÿงช Tier 3 - Community: collabora croc dokuwiki ghost loomio..." + echo "๐ŸŒ Extended: matrix-synapse rocketchat gitlab n8n mastodon..." + echo "" + echo "๐Ÿš€ Usage:" + echo " deploy - Deploy locally" + echo " browser [recipe] - Open in browser" + echo " ๐Ÿ“– Full catalog: https://recipes.coopcloud.tech" + echo "" + echo "๐Ÿ’ก Tab completion: deploy or browser " + } + + desktop() { + echo "๐Ÿ–ฅ๏ธ Starting GUI session..." + if command -v startx >/dev/null 2>&1; then + if [[ -z "$DISPLAY" ]]; then + startx & + export DISPLAY=:0 + sleep 3 + echo "โœ… GUI started" + else + echo "โ„น๏ธ GUI already running" + fi + else + echo "๐Ÿ’ก GUI available in QEMU window" + fi + } + + help() { + echo "๐Ÿš€ CODE CRISPIES Workshop Commands:" + echo "" + echo "๐Ÿ  Local Development:" + echo " setup - Setup local proxy (REQUIRED FIRST!)" + echo " recipes - Show all available apps" + echo " deploy - Deploy app locally" + echo " browser [recipe] - Launch browser [to app]" + echo " desktop - Start GUI session" + echo " sudo abra - Run abra CLI directly as root" + echo "" + echo "โ˜๏ธ Cloud Access:" + echo " connect - SSH to cloud server" + echo " Available: ${serverList}" + echo "" + echo "๐Ÿ” Debug:" + echo " docker service ls - List running services" + echo " systemctl status dnsmasq - Check DNS" + echo " systemctl status workshop-abra-install - Check abra installation" + echo "" + echo "๐Ÿ“š Learning Flow:" + echo " 1. setup" + echo " 2. deploy wordpress" + echo " 3. browser wordpress" + echo " 4. connect hopper" + } ''; programs.firefox = {