Add robust configuration injection and debugging tools

This commit is contained in:
Andreas Düren 2025-03-16 22:58:06 +01:00
parent 38f08c135e
commit 12b486ace3

465
start.sh
View File

@ -206,7 +206,7 @@ if ! command -v caddy &> /dev/null; then
fi
# Set up the API endpoint for the web apps
API_ENDPOINT=${CLOUDRON_APP_ORIGIN}/api
API_ENDPOINT="${CLOUDRON_APP_ORIGIN}/api"
echo "==> Setting API endpoint to $API_ENDPOINT"
# Set environment variables for the web apps
@ -216,100 +216,249 @@ export REACT_APP_ENTE_ENDPOINT=$API_ENDPOINT
export VUE_APP_ENTE_ENDPOINT=$API_ENDPOINT
echo "==> Set environment variables for web apps"
# Create a very simple Caddy configuration
mkdir -p /app/data/caddy
# Create directory for direct modifications
mkdir -p /app/data/scripts
# Create a script that will directly define the ENTE_CONFIG global in window
cat > /app/data/scripts/ente-config.js <<EOT
// Direct configuration script - should be loaded before any app code
window.ENTE_CONFIG = {
API_URL: "${API_ENDPOINT}"
};
console.log("Ente config loaded, API_URL =", window.ENTE_CONFIG.API_URL);
EOT
# Set up Caddy server
echo "==> Setting up Caddy server for web apps"
mkdir -p /app/data/caddy/public
cat > /app/data/caddy/Caddyfile <<EOT
{
admin off
auto_https off
log {
output file /app/data/caddy/caddy.log
format console
output file /app/data/caddy/caddy.log {
roll_size 10MB
roll_keep 10
}
}
servers {
protocols h1 h2c
}
}
:3080 {
:8000 {
log {
output file /app/data/caddy/access.log
format console
output file /app/data/caddy/access.log {
roll_size 10MB
roll_keep 10
}
}
# Add security headers to enable WebAssembly and IndexedDB
# Allow WebAssembly and IndexedDB
header {
# Security headers
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "strict-origin-when-cross-origin"
# Content Security Policy that allows WebAssembly
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self' ${CLOUDRON_APP_ORIGIN} https://*.${CLOUDRON_APP_DOMAIN}; worker-src 'self' blob:;"
# CORS headers for API access
Access-Control-Allow-Origin "*"
Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Access-Control-Allow-Headers "Content-Type, Authorization"
Access-Control-Allow-Credentials "true"
Cross-Origin-Embedder-Policy "credentialless"
Cross-Origin-Opener-Policy "same-origin"
Cross-Origin-Resource-Policy "cross-origin"
}
# Simple JavaScript file that defines the API endpoint
handle /api-config.js {
header Content-Type "application/javascript"
respond "window.ENTE_CONFIG = { API_URL: '${API_ENDPOINT}' };" 200
# Serve config scripts directly
handle /config.js {
root * /app/data/public
file_server
}
# Photos app - root path
# Serve debug script and page
handle /debug.js {
root * /app/data/public
file_server
}
handle /debug {
root * /app/data/public
rewrite * /debug.html
file_server
}
# Health check response
handle /health {
respond "OK" 200
}
# Serve our custom config-injected landing pages
handle /photos-config {
root * /app/data/public
rewrite * /photos-config.html
file_server
}
handle /accounts-config {
root * /app/data/public
rewrite * /accounts-config.html
file_server
}
handle /auth-config {
root * /app/data/public
rewrite * /auth-config.html
file_server
}
handle /cast-config {
root * /app/data/public
rewrite * /cast-config.html
file_server
}
# Root handler - serve our custom index.html
handle / {
root * /app/data/public
file_server
}
# Serve the photos web app with local config support
handle /photos* {
uri strip_prefix /photos
uri replace /photos /
root * /app/web/photos
try_files {path} /index.html
file_server
}
# Accounts app
handle /accounts/* {
uri strip_prefix /accounts
# Serve the accounts web app
handle /accounts* {
uri replace /accounts /
root * /app/web/accounts
try_files {path} /index.html
file_server
}
# Auth app
handle /auth/* {
uri strip_prefix /auth
# Serve the auth web app
handle /auth* {
uri replace /auth /
root * /app/web/auth
try_files {path} /index.html
file_server
}
# Cast app
handle /cast/* {
uri strip_prefix /cast
# Serve the cast web app
handle /cast* {
uri replace /cast /
root * /app/web/cast
try_files {path} /index.html
file_server
}
# API endpoints - proxy to Museum server
handle /api/* {
# Proxy API calls to the backend server
handle /api* {
reverse_proxy localhost:8080
}
# Health check endpoint for Cloudron
handle /healthcheck {
respond "OK" 200
}
# Default to photos app
handle {
root * /app/web/photos
try_files {path} /index.html
file_server
}
}
EOT
echo "==> Caddy configuration created at /app/data/caddy/Caddyfile"
# Create HTML transformer script to modify all index.html files on load
mkdir -p /app/data/scripts
cat > /app/data/scripts/insert-config.sh <<EOT
#!/bin/bash
# This script injects our configuration into all index.html files
# List of web app directories
WEB_APPS=("/app/web/photos" "/app/web/accounts" "/app/web/auth" "/app/web/cast")
for app_dir in "\${WEB_APPS[@]}"; do
if [ -f "\$app_dir/index.html" ]; then
echo "Processing \$app_dir/index.html"
# Create a writable copy
cp "\$app_dir/index.html" "/app/data/temp.html"
# Insert our config script right before the closing head tag
sed -i 's|</head>|<script>window.ENTE_CONFIG = { API_URL: "${API_ENDPOINT}" };</script></head>|' "/app/data/temp.html"
# Create a symlink from the modified file to the original
# Only if the temp file was created and modified successfully
if [ -f "/app/data/temp.html" ]; then
mkdir -p "/app/data/transformed/\$(basename \$app_dir)"
cp "/app/data/temp.html" "/app/data/transformed/\$(basename \$app_dir)/index.html"
fi
fi
done
echo "All index.html files processed"
EOT
chmod +x /app/data/scripts/insert-config.sh
# Run the transformer script to create modified index.html files
mkdir -p /app/data/transformed
echo "==> Creating modified index.html files with injected configuration"
/app/data/scripts/insert-config.sh
# Create direct configuration files for different frameworks
mkdir -p /app/data/public
echo "==> Creating framework-specific configuration files"
# Create a Next.js public runtime configuration
cat > /app/data/public/env.js <<EOT
// Next.js runtime configuration
window.ENV = {
NEXT_PUBLIC_ENTE_ENDPOINT: "${API_ENDPOINT}"
};
window.process = window.process || {};
window.process.env = window.process.env || {};
window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT = "${API_ENDPOINT}";
EOT
# Create a simple JSON config
cat > /app/data/public/config.json <<EOT
{
"API_URL": "${API_ENDPOINT}"
}
EOT
# Create additional HTML files with the config directly embedded
for app_name in "photos" "accounts" "auth" "cast"; do
cat > "/app/data/public/${app_name}-config.html" <<EOT
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Loading ${app_name}</title>
<script src="/debug.js"></script>
<script>
// Set the global configuration
window.ENTE_CONFIG = {
API_URL: "${API_ENDPOINT}"
};
// Set Next.js environment variables
window.process = window.process || {};
window.process.env = window.process.env || {};
window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT = "${API_ENDPOINT}";
// Store in localStorage as a fallback mechanism
localStorage.setItem('ENTE_CONFIG', JSON.stringify(window.ENTE_CONFIG));
localStorage.setItem('NEXT_PUBLIC_ENTE_ENDPOINT', "${API_ENDPOINT}");
// Log the configuration
console.log("Ente ${app_name} config loaded:", window.ENTE_CONFIG);
// Redirect to the main app after a brief delay
setTimeout(function() {
window.location.href = "/${app_name}/";
}, 100);
</script>
</head>
<body>
<h1>Loading ${app_name}...</h1>
<p>If you are not redirected automatically, <a href="/${app_name}/">click here</a>.</p>
<p><a href="/debug">Debug Information</a></p>
</body>
</html>
EOT
done
echo "==> Created app-specific config loading pages with debug info"
# Looking for Museum (Go server component)
echo "==> Looking for Museum (Go server component)"
@ -813,3 +962,215 @@ echo "==> Caddy: PID $CADDY_PID"
# Wait for child processes to exit
wait $SERVER_PID
wait $CADDY_PID
# Create a special root index.html that loads the config and redirects
cat > /app/data/public/index.html <<EOT
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ente</title>
<script src="/debug.js"></script>
<script>
// Define configuration globally
window.ENTE_CONFIG = {
API_URL: "${API_ENDPOINT}"
};
// Set environment variables for Next.js apps
window.process = window.process || {};
window.process.env = window.process.env || {};
window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT = "${API_ENDPOINT}";
// Store in localStorage as a fallback mechanism
localStorage.setItem('ENTE_CONFIG', JSON.stringify(window.ENTE_CONFIG));
localStorage.setItem('NEXT_PUBLIC_ENTE_ENDPOINT', "${API_ENDPOINT}");
// Redirect to photos app after a small delay to let the configuration load
setTimeout(function() {
window.location.href = "/photos/";
}, 100);
</script>
</head>
<body>
<h1>Loading Ente...</h1>
<p>You will be redirected automatically.</p>
<p><a href="/debug">Debug Information</a></p>
</body>
</html>
EOT
echo "==> Created special root index.html with configuration"
# Create debug script for frontend
mkdir -p /app/data/public
mkdir -p /app/data/debug
echo "==> Creating debug scripts"
cat > /app/data/public/debug.js <<EOT
// Debugging script for Ente
(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';
debugDiv.style.bottom = '10px';
debugDiv.style.right = '10px';
debugDiv.style.backgroundColor = 'rgba(0,0,0,0.7)';
debugDiv.style.color = 'white';
debugDiv.style.padding = '10px';
debugDiv.style.borderRadius = '5px';
debugDiv.style.zIndex = '9999';
debugDiv.style.maxWidth = '400px';
debugDiv.style.maxHeight = '200px';
debugDiv.style.overflow = 'auto';
debugDiv.innerHTML = '<h3>Ente Debug Info</h3>';
// Add configuration info
const configInfo = document.createElement('div');
configInfo.innerHTML = 'ENTE_CONFIG: ' + JSON.stringify(window.ENTE_CONFIG || {}) + '<br>' +
'process.env.NEXT_PUBLIC_ENTE_ENDPOINT: ' + (window.process?.env?.NEXT_PUBLIC_ENTE_ENDPOINT || 'undefined') + '<br>' +
'localStorage ENTE_CONFIG: ' + localStorage.getItem('ENTE_CONFIG') + '<br>' +
'localStorage NEXT_PUBLIC_ENTE_ENDPOINT: ' + localStorage.getItem('NEXT_PUBLIC_ENTE_ENDPOINT');
debugDiv.appendChild(configInfo);
// Add toggle button
const toggleButton = document.createElement('button');
toggleButton.innerText = 'Toggle Debug Info';
toggleButton.style.marginTop = '10px';
toggleButton.onclick = function() {
configInfo.style.display = configInfo.style.display === 'none' ? 'block' : 'none';
};
debugDiv.appendChild(toggleButton);
// Add to document when it's ready
if (document.body) {
document.body.appendChild(debugDiv);
} else {
window.addEventListener('DOMContentLoaded', function() {
document.body.appendChild(debugDiv);
});
}
})();
EOT
# Create debug info script that prints environment info
cat > /app/data/debug/debug_env.js <<EOT
#!/usr/bin/env node
console.log('=== Ente Environment Debug ===');
console.log('CLOUDRON_APP_ORIGIN:', process.env.CLOUDRON_APP_ORIGIN);
console.log('API_ENDPOINT:', process.env.API_ENDPOINT);
console.log('NEXT_PUBLIC_ENTE_ENDPOINT:', process.env.NEXT_PUBLIC_ENTE_ENDPOINT);
console.log('HOSTNAME:', process.env.HOSTNAME);
console.log('===============================');
EOT
chmod +x /app/data/debug/debug_env.js
# Create debug info HTML
cat > /app/data/public/debug.html <<EOT
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ente Debug Info</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.debug-section { margin-bottom: 20px; padding: 10px; border: 1px solid #ccc; }
h1 { color: #333; }
pre { background-color: #f5f5f5; padding: 10px; border-radius: 5px; overflow-x: auto; }
</style>
</head>
<body>
<h1>Ente Debug Information</h1>
<div class="debug-section">
<h2>Frontend Configuration</h2>
<pre id="config-info">Loading...</pre>
</div>
<div class="debug-section">
<h2>URL Test</h2>
<p>Testing URL construction with API endpoint:</p>
<pre id="url-test">Running test...</pre>
</div>
<div class="debug-section">
<h2>API Health Check</h2>
<pre id="api-health">Checking API health...</pre>
</div>
<script>
// Define configuration globally
window.ENTE_CONFIG = {
API_URL: "${CLOUDRON_APP_ORIGIN}/api"
};
// Set environment variables for Next.js apps
window.process = window.process || {};
window.process.env = window.process.env || {};
window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT = "${CLOUDRON_APP_ORIGIN}/api";
// Store in localStorage
localStorage.setItem('ENTE_CONFIG', JSON.stringify(window.ENTE_CONFIG));
localStorage.setItem('NEXT_PUBLIC_ENTE_ENDPOINT', "${CLOUDRON_APP_ORIGIN}/api");
// Display configuration
document.getElementById('config-info').textContent =
'window.ENTE_CONFIG = ' + JSON.stringify(window.ENTE_CONFIG, null, 2) + '\n' +
'window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT = ' + window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT + '\n' +
'localStorage[\'ENTE_CONFIG\'] = ' + localStorage.getItem('ENTE_CONFIG') + '\n' +
'localStorage[\'NEXT_PUBLIC_ENTE_ENDPOINT\'] = ' + localStorage.getItem('NEXT_PUBLIC_ENTE_ENDPOINT');
// Test URL construction
try {
const apiUrl = window.ENTE_CONFIG.API_URL;
const testUrl = new URL('/users/ott', apiUrl);
document.getElementById('url-test').textContent =
'API URL: ' + apiUrl + '\n' +
'Test URL (/users/ott): ' + testUrl.toString() + '\n' +
'Result: SUCCESS';
} catch (e) {
document.getElementById('url-test').textContent =
'Error: ' + e.message + '\n' +
'Stack: ' + e.stack;
}
// Test API health
fetch('/api/health')
.then(response => {
if (response.ok) return response.text();
throw new Error('API returned status: ' + response.status);
})
.then(data => {
document.getElementById('api-health').textContent = 'API health check: OK\nResponse: ' + data;
})
.catch(err => {
document.getElementById('api-health').textContent = 'API health check failed: ' + err.message;
});
</script>
</body>
</html>
EOT