From 23c9581f7bb741fd492fd4426353cc4a9888334c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20D=C3=BCren?= <andreasdueren@Andreass-Mac-mini.local> Date: Sun, 16 Mar 2025 23:29:27 +0100 Subject: [PATCH] Switch from Caddy to NGINX and fix URL construction error --- start.sh | 463 ++++++++++++++++++++++--------------------------------- 1 file changed, 188 insertions(+), 275 deletions(-) diff --git a/start.sh b/start.sh index 3f8856b..84d7f72 100644 --- a/start.sh +++ b/start.sh @@ -1,7 +1,7 @@ #!/bin/bash # Better signal handling - forward signals to child processes -trap 'kill -TERM $SERVER_PID; kill -TERM $CADDY_PID; exit' TERM INT +trap 'kill -TERM $SERVER_PID; kill -TERM $NGINX_PID; exit' TERM INT set -eu @@ -172,7 +172,7 @@ fi # Source S3 configuration if [ -f /app/data/config/s3.env ]; then echo "==> Sourcing S3 configuration from /app/data/config/s3.env" - source /app/data/config/s3.env +source /app/data/config/s3.env fi # Display S3 configuration (masking sensitive values) @@ -213,12 +213,9 @@ sed -i 's|s3.are_local_buckets: true|s3.are_local_buckets: false|g' /app/data/co # Install or verify required packages echo "==> Checking for required packages" -if ! command -v caddy &> /dev/null; then - echo "==> Installing Caddy" - apt-get update && apt-get install -y debian-keyring debian-archive-keyring apt-transport-https - curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg - curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list - apt-get update && apt-get install -y caddy +if ! command -v nginx &> /dev/null; then + echo "==> Installing NGINX" + apt-get update && apt-get install -y nginx fi # Set up the API endpoint for the web apps @@ -235,6 +232,8 @@ echo "==> Set environment variables for web apps" # Create directory for configuration files mkdir -p /app/data/public mkdir -p /app/data/scripts +mkdir -p /app/data/nginx +mkdir -p /app/data/logs/nginx # Create a debugging script cat > /app/data/public/debug.js <<EOT @@ -242,27 +241,6 @@ cat > /app/data/public/debug.js <<EOT (function() { console.log("Debug script loaded"); - // Intercept URL constructor - const originalURL = window.URL; - window.URL = function(url, base) { - console.log("URL constructor called with:", url, base); - try { - return new originalURL(url, base); - } catch (e) { - console.error("URL construction failed:", e.message); - console.error("URL:", url); - console.error("Base:", base); - console.error("Stack:", e.stack); - - // Try to fix common issues - if (url && !url.startsWith("http") && !url.startsWith("/")) { - console.log("Attempting to fix relative URL by adding leading slash"); - return new originalURL("/" + url, base); - } - throw e; - } - }; - // Create debug overlay const debugDiv = document.createElement('div'); debugDiv.style.position = 'fixed'; @@ -306,31 +284,6 @@ cat > /app/data/public/debug.js <<EOT })(); EOT -# Create a configuration script -cat > /app/data/public/config.js <<EOT -// Direct configuration for Ente -window.ENTE_CONFIG = { - API_URL: "${API_ENDPOINT}" -}; - -// Next.js environment variables -window.process = window.process || {}; -window.process.env = window.process.env || {}; -window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT = "${API_ENDPOINT}"; -window.process.env.REACT_APP_ENTE_ENDPOINT = "${API_ENDPOINT}"; -window.process.env.VUE_APP_ENTE_ENDPOINT = "${API_ENDPOINT}"; - -// Store in localStorage for persistence -try { - localStorage.setItem('ENTE_CONFIG', JSON.stringify(window.ENTE_CONFIG)); - localStorage.setItem('NEXT_PUBLIC_ENTE_ENDPOINT', "${API_ENDPOINT}"); -} catch (e) { - console.error("Failed to store config in localStorage:", e); -} - -console.log("Ente config loaded - API_URL:", window.ENTE_CONFIG.API_URL); -EOT - # Create debug info HTML page cat > /app/data/public/debug.html <<EOT <!DOCTYPE html> @@ -417,39 +370,73 @@ cat > /app/data/public/debug.html <<EOT </html> EOT -# Create a simple app that loads the config and redirects to the main app -cat > /app/data/public/index.html <<EOT -<!DOCTYPE html> -<html> -<head> - <meta charset="utf-8"> - <title>Ente</title> - <script src="/config.js"></script> - <script src="/debug.js"></script> -</head> -<body> - <h1>Ente</h1> - <p>Welcome to Ente!</p> - <p><a href="/debug">Debug Information</a></p> -</body> -</html> +# Create a configuration script with properly formatted URL +cat > /app/data/public/config.js <<EOT +// Direct configuration for Ente +window.ENTE_CONFIG = { + API_URL: "${API_ENDPOINT}" +}; + +// Next.js environment variables +window.process = window.process || {}; +window.process.env = window.process.env || {}; +window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT = "${API_ENDPOINT}"; +window.process.env.REACT_APP_ENTE_ENDPOINT = "${API_ENDPOINT}"; +window.process.env.VUE_APP_ENTE_ENDPOINT = "${API_ENDPOINT}"; + +// Create absolute URL helper function to prevent URL construction errors +window.createApiUrl = function(path) { + // Handle paths with or without leading slash + if (path && path.startsWith('/')) { + return "${API_ENDPOINT}" + path; + } else if (path) { + return "${API_ENDPOINT}/" + path; + } + return "${API_ENDPOINT}"; +}; + +// Store in localStorage for persistence +try { + localStorage.setItem('ENTE_CONFIG', JSON.stringify(window.ENTE_CONFIG)); + localStorage.setItem('NEXT_PUBLIC_ENTE_ENDPOINT', "${API_ENDPOINT}"); +} catch (e) { + console.error("Failed to store config in localStorage:", e); +} + +console.log("Ente config loaded - API_URL:", window.ENTE_CONFIG.API_URL); +// Override URL constructor to prevent errors +const originalURL = window.URL; +window.URL = function(url, base) { + try { + // Fix common URL construction issues + if (url && typeof url === 'string') { + // If URL doesn't have a protocol or start with /, add a / + if (!url.match(/^[a-z]+:\/\//) && !url.startsWith('/') && !base) { + url = '/' + url; + } + } + return new originalURL(url, base); + } catch (e) { + console.error("URL construction error:", e, "url:", url, "base:", base); + // Fallback - return a working URL + return new originalURL("${CLOUDRON_APP_ORIGIN}"); + } +}; EOT -# Set up Caddy -echo "==> Setting up Caddy server" -mkdir -p /app/data/caddy -chmod -R 777 /app/data/caddy +# Set up NGINX +echo "==> Setting up NGINX server" # Define ports -CADDY_PORT=3080 +NGINX_PORT=3080 API_PORT=8080 # Check if ports are available echo "==> Checking port availability" -if lsof -i:$CADDY_PORT > /dev/null 2>&1; then - echo "==> WARNING: Port $CADDY_PORT is already in use" +if lsof -i:$NGINX_PORT > /dev/null 2>&1; then + echo "==> WARNING: Port $NGINX_PORT is already in use" else - echo "==> Port $CADDY_PORT is available for Caddy" + echo "==> Port $NGINX_PORT is available for NGINX" fi if lsof -i:$API_PORT > /dev/null 2>&1; then @@ -458,230 +445,156 @@ else echo "==> Port $API_PORT is available for API server" fi -# Create the Caddyfile -cat > /app/data/caddy/Caddyfile <<EOT -{ - admin off - auto_https off - log { - output file /app/data/caddy/caddy.log { - roll_size 10MB - roll_keep 10 - } - } +# Create the NGINX config +cat > /app/data/nginx/nginx.conf <<EOT +worker_processes 1; +error_log /app/data/logs/nginx/error.log warn; +pid /app/data/nginx/nginx.pid; + +events { + worker_connections 1024; } -:$CADDY_PORT { - # Ensure we listen on all interfaces - bind 0.0.0.0 +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; - log { - output file /app/data/caddy/access.log { - roll_size 10MB - roll_keep 10 - } + log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" ' + '\$status \$body_bytes_sent "\$http_referer" ' + '"\$http_user_agent" "\$http_x_forwarded_for"'; + + access_log /app/data/logs/nginx/access.log main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + # Security headers + map \$sent_http_content_type \$cors_header { + default ""; + "~*text/html" "credentialless"; } - # Headers for WebAssembly and IndexedDB - header { - Cross-Origin-Embedder-Policy "credentialless" - Cross-Origin-Opener-Policy "same-origin" - Cross-Origin-Resource-Policy "cross-origin" - } - - # Serve configuration scripts - handle /config.js { - root * /app/data/public - file_server - } - - handle /debug.js { - root * /app/data/public - file_server - } - - # Serve debug page - handle /debug { - root * /app/data/public - rewrite * /debug.html - file_server - } - - # Health check endpoints - handle /health { - respond "OK" 200 - } - - handle /healthcheck { - respond "OK" 200 - } - - # API health check endpoint - direct proxy to API - handle /api/health { - reverse_proxy localhost:$API_PORT - } - - # Serve photos app at the root - handle /* { - root * /app/web/photos + server { + listen $NGINX_PORT default_server; + listen [::]:$NGINX_PORT default_server; - # Inject config script - @html { - path *.html index.html - not path */assets/* */static/* */img/* */css/* */js/* + root /app/web/photos; + index index.html; + + # Security headers + add_header Cross-Origin-Embedder-Policy \$cors_header always; + add_header Cross-Origin-Opener-Policy "same-origin" always; + add_header Cross-Origin-Resource-Policy "cross-origin" always; + + # Configuration scripts + location /config.js { + alias /app/data/public/config.js; } - header @html Content-Type "text/html; charset=utf-8" - - # Use rewrite to modify HTML content - @isIndex path /index.html - rewrite @isIndex /photos-injected.html - - file_server - } - - # Accounts app - handle /accounts* { - uri replace /accounts / - root * /app/web/accounts - - handle_path /* { - @html { - path *.html index.html - not path */assets/* */static/* */img/* */css/* */js/* - } - - header @html Content-Type "text/html; charset=utf-8" - - # Use rewrite to modify HTML content - @isIndex path /index.html - rewrite @isIndex /accounts-injected.html - - file_server + location /debug.js { + alias /app/data/public/debug.js; } - } - - # Auth app - handle /auth* { - uri replace /auth / - root * /app/web/auth - handle_path /* { - @html { - path *.html index.html - not path */assets/* */static/* */img/* */css/* */js/* - } - - header @html Content-Type "text/html; charset=utf-8" - - # Use rewrite to modify HTML content - @isIndex path /index.html - rewrite @isIndex /auth-injected.html - - file_server + # Debug page + location /debug { + alias /app/data/public/debug.html; } - } - - # Cast app - handle /cast* { - uri replace /cast / - root * /app/web/cast - handle_path /* { - @html { - path *.html index.html - not path */assets/* */static/* */img/* */css/* */js/* - } - - header @html Content-Type "text/html; charset=utf-8" - - # Use rewrite to modify HTML content - @isIndex path /index.html - rewrite @isIndex /cast-injected.html - - file_server + # Health check endpoints + location /health { + return 200 "OK"; + } + + location /healthcheck { + return 200 "OK"; + } + + # API health check and API endpoints + location /api/ { + proxy_pass http://localhost:$API_PORT/; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + } + + # Root serves the photos app + location / { + try_files \$uri \$uri/ /index.html; + + # Insert config script for HTML files + sub_filter '</head>' '<script src="/config.js"></script><script src="/debug.js"></script></head>'; + sub_filter_once on; + sub_filter_types text/html; + } + + # Accounts app + location /accounts/ { + alias /app/web/accounts/; + try_files \$uri \$uri/ /accounts/index.html; + + # Insert config script for HTML files + sub_filter '</head>' '<script src="/config.js"></script><script src="/debug.js"></script></head>'; + sub_filter_once on; + sub_filter_types text/html; + } + + # Auth app + location /auth/ { + alias /app/web/auth/; + try_files \$uri \$uri/ /auth/index.html; + + # Insert config script for HTML files + sub_filter '</head>' '<script src="/config.js"></script><script src="/debug.js"></script></head>'; + sub_filter_once on; + sub_filter_types text/html; + } + + # Cast app + location /cast/ { + alias /app/web/cast/; + try_files \$uri \$uri/ /cast/index.html; + + # Insert config script for HTML files + sub_filter '</head>' '<script src="/config.js"></script><script src="/debug.js"></script></head>'; + sub_filter_once on; + sub_filter_types text/html; } - } - - # Proxy API calls to the backend server - handle /api* { - reverse_proxy localhost:$API_PORT } } EOT -echo "==> Created Caddyfile at /app/data/caddy/Caddyfile" +echo "==> Created NGINX config at /app/data/nginx/nginx.conf" -# Create injected HTML files for each app -for app_name in "photos" "accounts" "auth" "cast"; do - # Create an injected HTML file that loads the config - cat > "/app/data/public/${app_name}-injected.html" <<EOT -<!DOCTYPE html> -<html> -<head> - <meta charset="utf-8"> - <title>Ente ${app_name^}</title> - <script src="/config.js"></script> - <script> - // Redirect to the actual app after loading config - window.onload = function() { - const iframe = document.createElement('iframe'); - iframe.style.width = '100%'; - iframe.style.height = '100%'; - iframe.style.border = 'none'; - iframe.style.position = 'absolute'; - iframe.style.top = '0'; - iframe.style.left = '0'; - iframe.src = "/${app_name}/index.html"; - document.body.appendChild(iframe); - }; - </script> - <style> - body { - margin: 0; - padding: 0; - height: 100vh; - width: 100vw; - overflow: hidden; - } - </style> -</head> -<body> - <div id="loading" style="display: flex; justify-content: center; align-items: center; height: 100vh; flex-direction: column;"> - <h1>Loading Ente ${app_name^}...</h1> - <p>Please wait while the application initializes.</p> - </div> -</body> -</html> -EOT -done +# Start NGINX +echo "==> Starting NGINX on port $NGINX_PORT" +nginx -c /app/data/nginx/nginx.conf -p /app/data/nginx & +NGINX_PID=$! +echo "==> NGINX started with PID $NGINX_PID" -echo "==> Created injected HTML files for all apps" - -# Start Caddy -echo "==> Starting Caddy on port $CADDY_PORT" -caddy run --config /app/data/caddy/Caddyfile --adapter caddyfile & -CADDY_PID=$! -echo "==> Caddy started with PID $CADDY_PID" - -# Wait for Caddy to start +# Wait for NGINX to start sleep 2 -# Test Caddy connectivity -echo "==> Testing Caddy connectivity" +# Test NGINX connectivity +echo "==> Testing NGINX connectivity" for i in {1..5}; do - if curl -s --max-time 2 --head --fail http://localhost:$CADDY_PORT/health > /dev/null; then - echo "==> Caddy is running properly on port $CADDY_PORT" + if curl -s --max-time 2 --head --fail http://localhost:$NGINX_PORT/health > /dev/null; then + echo "==> NGINX is running properly on port $NGINX_PORT" break else if [ $i -eq 5 ]; then - echo "==> Failed to connect to Caddy after multiple attempts" - echo "==> Last 20 lines of Caddy log:" - tail -20 /app/data/caddy/caddy.log || echo "==> No Caddy log available" + echo "==> Failed to connect to NGINX after multiple attempts" + echo "==> Last 20 lines of NGINX error log:" + tail -20 /app/data/logs/nginx/error.log || echo "==> No NGINX error log available" echo "==> Network ports in use:" netstat -tuln || echo "==> netstat command not available" else - echo "==> Attempt $i: Waiting for Caddy to start... (1 second)" + echo "==> Attempt $i: Waiting for NGINX to start... (1 second)" sleep 1 fi fi @@ -903,4 +816,4 @@ echo "==> To view debug information, visit: $CLOUDRON_APP_ORIGIN/debug" echo "==> Entering wait state - press Ctrl+C to stop" # Wait for all background processes to complete (or for user to interrupt) wait $SERVER_PID -wait $CADDY_PID \ No newline at end of file +wait $NGINX_PID \ No newline at end of file