Loading start.sh +222 −101 Original line number Diff line number Diff line Loading @@ -647,6 +647,7 @@ func main() { log.Println("API_ENDPOINT:", os.Getenv("ENTE_API_ENDPOINT")) // Create a logger that logs to both stdout and a file os.MkdirAll("/app/data/logs", 0755) // Ensure the logs directory exists logFile, err := os.OpenFile("/app/data/logs/api_requests.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Printf("Error opening log file: %v", err) Loading @@ -656,6 +657,7 @@ func main() { multiWriter := io.MultiWriter(os.Stdout, logFile) logger := log.New(multiWriter, "", log.LstdFlags) // Initialize random seed rand.Seed(time.Now().UnixNano()) // Map to store verification codes Loading Loading @@ -699,8 +701,13 @@ func main() { // Return a success response w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, \`{"status":"ok","message":"Verification code sent (check logs)"}\`) // Use the encoding/json package to create and send the response jsonResponse := map[string]string{ "status": "ok", "message": "Verification code sent (check logs)", } json.NewEncoder(w).Encode(jsonResponse) } else { // Just handle other methods with a generic response w.Header().Set("Content-Type", "application/json") Loading Loading @@ -757,11 +764,23 @@ func main() { if isValid { // Return a successful verification response w.WriteHeader(http.StatusOK) fmt.Fprintf(w, \`{"status":"ok","token":"mock-token-12345"}\`) // Use the json package to create the response jsonResponse := map[string]string{ "status": "ok", "token": "mock-token-12345", } json.NewEncoder(w).Encode(jsonResponse) } else { // Return an error w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, \`{"status":"error","message":"Invalid verification code"}\`) // Use the json package to create the error response jsonResponse := map[string]string{ "status": "error", "message": "Invalid verification code", } json.NewEncoder(w).Encode(jsonResponse) } } else { // Handle other methods with a generic response Loading @@ -782,11 +801,20 @@ func main() { } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, \`{"status":"mock","endpoint":"%s","method":"%s","time":"%s"}\`, r.URL.Path, r.Method, time.Now().Format(time.RFC3339)) // Use the json package to create a dynamic response response := map[string]string{ "status": "mock", "endpoint": r.URL.Path, "method": r.Method, "time": time.Now().Format(time.RFC3339), } json.NewEncoder(w).Encode(response) }) logger.Printf("Mock Ente API server listening on port %s\n", port) // Make sure we listen on all interfaces, not just localhost if err := http.ListenAndServe("0.0.0.0:" + port, nil); err != nil { logger.Fatalf("Server failed: %v", err) } Loading Loading @@ -918,7 +946,9 @@ elif [ -d "$SERVER_DIR/cmd/museum" ]; then package main import ( "encoding/json" "fmt" "io" "log" "net/http" "os" Loading @@ -937,21 +967,59 @@ func main() { log.Println("PGPORT:", os.Getenv("PGPORT")) log.Println("API_ENDPOINT:", os.Getenv("ENTE_API_ENDPOINT")) // Create a logger that logs to both stdout and a file os.MkdirAll("/app/data/logs", 0755) // Ensure the logs directory exists logFile, err := os.OpenFile("/app/data/logs/public_api_requests.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Printf("Error opening log file: %v", err) } defer logFile.Close() multiWriter := io.MultiWriter(os.Stdout, logFile) logger := log.New(multiWriter, "", log.LstdFlags) http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, \`{"status":"ok","service":"public_albums","version":"mock-1.0.0","time":"%s"}\`, time.Now().Format(time.RFC3339)) // Use json package to create the response response := map[string]string{ "status": "ok", "service": "public_albums", "version": "mock-1.0.0", "time": time.Now().Format(time.RFC3339), } json.NewEncoder(w).Encode(response) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { log.Printf("Public Albums: Received request for %s via %s", r.URL.Path, r.Method) logger.Printf("Public Albums: Received request for %s via %s", r.URL.Path, r.Method) // Log request body if it's a POST or PUT if r.Method == "POST" || r.Method == "PUT" { body, _ := io.ReadAll(r.Body) if len(body) > 0 { logger.Printf("Request body: %s", string(body)) } } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, \`{"status":"mock","service":"public_albums","endpoint":"%s","method":"%s","time":"%s"}\`, r.URL.Path, r.Method, time.Now().Format(time.RFC3339)) // Use json package to create the response response := map[string]string{ "status": "mock", "service": "public_albums", "endpoint": r.URL.Path, "method": r.Method, "time": time.Now().Format(time.RFC3339), } json.NewEncoder(w).Encode(response) }) log.Printf("Mock Public Albums API server listening on port %s\n", port) logger.Printf("Mock Public Albums API server listening on port %s\n", port) // Make sure we listen on all interfaces if err := http.ListenAndServe("0.0.0.0:" + port, nil); err != nil { log.Fatalf("Server failed: %v", err) logger.Fatalf("Server failed: %v", err) } } EOT Loading @@ -974,7 +1042,9 @@ else package main import ( "encoding/json" "fmt" "io" "log" "net/http" "os" Loading @@ -993,23 +1063,59 @@ func main() { log.Println("PGPORT:", os.Getenv("PGPORT")) log.Println("API_ENDPOINT:", os.Getenv("ENTE_API_ENDPOINT")) // Create a logger that logs to both stdout and a file os.MkdirAll("/app/data/logs", 0755) // Ensure the logs directory exists logFile, err := os.OpenFile("/app/data/logs/public_api_requests.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Printf("Error opening log file: %v", err) } defer logFile.Close() multiWriter := io.MultiWriter(os.Stdout, logFile) logger := log.New(multiWriter, "", log.LstdFlags) http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, \`{"status":"ok","service":"public_albums","version":"mock-1.0.0","time":"%s"}\`, time.Now().Format(time.RFC3339)) // Use json package to create the response response := map[string]string{ "status": "ok", "service": "public_albums", "version": "mock-1.0.0", "time": time.Now().Format(time.RFC3339), } json.NewEncoder(w).Encode(response) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { log.Printf("Public Albums: Received request for %s via %s", r.URL.Path, r.Method) logger.Printf("Public Albums: Received request for %s via %s", r.URL.Path, r.Method) // Log request body if it's a POST or PUT if r.Method == "POST" || r.Method == "PUT" { body, _ := io.ReadAll(r.Body) if len(body) > 0 { logger.Printf("Request body: %s", string(body)) } } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, \`{"status":"mock","service":"public_albums","endpoint":"%s","method":"%s","time":"%s"}\`, r.URL.Path, r.Method, time.Now().Format(time.RFC3339)) // Use json package to create the response response := map[string]string{ "status": "mock", "service": "public_albums", "endpoint": r.URL.Path, "method": r.Method, "time": time.Now().Format(time.RFC3339), } json.NewEncoder(w).Encode(response) }) // Start the server log.Printf("Mock Public Albums server listening on port %s\n", port) logger.Printf("Mock Public Albums API server listening on port %s\n", port) // Make sure we listen on all interfaces if err := http.ListenAndServe("0.0.0.0:" + port, nil); err != nil { log.Fatalf("Failed to start Public Albums server: %v", err) logger.Fatalf("Server failed: %v", err) } } EOT Loading Loading @@ -1048,7 +1154,41 @@ done # Set up Caddy web server echo "==> Setting up Caddy web server" # Create a Caddyfile for serving web apps and reverse proxy to API # First inject the config.js script tags into all HTML files echo "==> Injecting config.js into web application HTML files" # Create writable data directories for web assets mkdir -p /app/data/web/photos /app/data/web/accounts /app/data/web/auth /app/data/web/cast # Create runtime-config.js files in writable locations echo "==> Creating runtime-config.js in writable location" cat > /app/data/web/runtime-config.js <<EOT // Runtime configuration for Ente window.ENTE_CONFIG = { API_URL: '${API_ENDPOINT}', PUBLIC_ALBUMS_URL: '${CLOUDRON_APP_ORIGIN}/public' }; // 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.NEXT_PUBLIC_ENTE_PUBLIC_ALBUMS_ENDPOINT = '${CLOUDRON_APP_ORIGIN}/public'; console.log('Ente runtime config loaded from runtime-config.js'); console.log('API_URL:', window.ENTE_CONFIG.API_URL); console.log('PUBLIC_ALBUMS_URL:', window.ENTE_CONFIG.PUBLIC_ALBUMS_URL); EOT # Copy the runtime-config.js to each app directory for app_dir in /app/data/web/photos /app/data/web/accounts /app/data/web/auth /app/data/web/cast; do cp /app/data/web/runtime-config.js "$app_dir/" done # Ensure all files are readable chmod -R 644 /app/data/web/runtime-config.js # Update the Caddy configuration to serve config.js directly cat > /app/data/caddy/Caddyfile <<EOT # Global settings { Loading @@ -1066,11 +1206,53 @@ cat > /app/data/caddy/Caddyfile <<EOT output file /app/data/logs/caddy.log } # Configuration scripts - This must come before the root handler handle /config.js { header Content-Type application/javascript respond " // Direct configuration for Ente window.ENTE_CONFIG = { API_URL: '${API_ENDPOINT}', PUBLIC_ALBUMS_URL: '${CLOUDRON_APP_ORIGIN}/public' }; // 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.NEXT_PUBLIC_ENTE_PUBLIC_ALBUMS_ENDPOINT = '${CLOUDRON_APP_ORIGIN}/public'; console.log('Ente config loaded - API_URL:', window.ENTE_CONFIG.API_URL); console.log('Ente config loaded - PUBLIC_ALBUMS_URL:', window.ENTE_CONFIG.PUBLIC_ALBUMS_URL); // Dynamically inject the config by creating script elements document.addEventListener('DOMContentLoaded', function() { console.log('DOM loaded, injecting runtime config as needed'); }); " } handle /runtime-config.js { root * /app/data/web file_server } # Root path serves the photos app handle / { root * /app/web/photos try_files {path} /index.html file_server # Dynamically inject our configuration script header * +Link "</config.js>; rel=preload; as=script" header * +Link "</runtime-config.js>; rel=preload; as=script" header ?index.html +Content-Security-Policy "script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src *" # Add script injection as HTML filter filter { search_pattern </head> replacement <script src="/config.js" type="text/javascript"></script><script src="/runtime-config.js" type="text/javascript"></script></head> } } # Next.js static files Loading @@ -1094,6 +1276,12 @@ cat > /app/data/caddy/Caddyfile <<EOT uri strip_prefix /accounts try_files {path} /index.html file_server # Dynamically inject our configuration script for accounts app filter ?index.html { search_pattern </head> replacement <script src="/config.js" type="text/javascript"></script><script src="/runtime-config.js" type="text/javascript"></script></head> } } # Auth app Loading @@ -1102,6 +1290,12 @@ cat > /app/data/caddy/Caddyfile <<EOT uri strip_prefix /auth try_files {path} /index.html file_server # Dynamically inject our configuration script for auth app filter ?index.html { search_pattern </head> replacement <script src="/config.js" type="text/javascript"></script><script src="/runtime-config.js" type="text/javascript"></script></head> } } # Cast app Loading @@ -1110,6 +1304,12 @@ cat > /app/data/caddy/Caddyfile <<EOT uri strip_prefix /cast try_files {path} /index.html file_server # Dynamically inject our configuration script for cast app filter ?index.html { search_pattern </head> replacement <script src="/config.js" type="text/javascript"></script><script src="/runtime-config.js" type="text/javascript"></script></head> } } # Main API proxy Loading Loading @@ -1142,94 +1342,15 @@ cat > /app/data/caddy/Caddyfile <<EOT uri strip_prefix /public reverse_proxy 0.0.0.0:$PUBLIC_ALBUMS_PORT } # Configuration scripts handle /config.js { header Content-Type application/javascript respond " // Direct configuration for Ente window.ENTE_CONFIG = { API_URL: '${API_ENDPOINT}', PUBLIC_ALBUMS_URL: '${CLOUDRON_APP_ORIGIN}/public' }; // 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.NEXT_PUBLIC_ENTE_PUBLIC_ALBUMS_ENDPOINT = '${CLOUDRON_APP_ORIGIN}/public'; console.log('Ente config loaded - API_URL:', window.ENTE_CONFIG.API_URL); console.log('Ente config loaded - PUBLIC_ALBUMS_URL:', window.ENTE_CONFIG.PUBLIC_ALBUMS_URL); " } } EOT echo "==> Created Caddy config at /app/data/caddy/Caddyfile" echo "==> Created Caddy config with dynamic script injection at /app/data/caddy/Caddyfile" echo "==> No longer trying to modify read-only HTML files" # Start Caddy server echo "==> Starting Caddy server" # First inject the config.js script tags into all HTML files echo "==> Injecting config.js into web application HTML files" # Function to inject the script tag inject_script_tag() { file="$1" if [ -f "$file" ]; then echo "==> Injecting config.js into $file" # Make a backup just in case cp "$file" "${file}.bak" # Insert the script tag right after the opening head tag sed -i 's/<head>/<head>\n <script src="\/config.js" type="text\/javascript"><\/script>/' "$file" else echo "==> WARNING: Could not find $file to inject config script" fi } # Inject into all the web apps inject_script_tag "/app/web/photos/index.html" inject_script_tag "/app/web/accounts/index.html" inject_script_tag "/app/web/auth/index.html" inject_script_tag "/app/web/cast/index.html" # Also create a runtime-config.js file with properly escaped content cat > /app/web/photos/runtime-config.js <<EOT // Runtime configuration for Ente window.ENTE_CONFIG = { API_URL: '${API_ENDPOINT}', PUBLIC_ALBUMS_URL: '${CLOUDRON_APP_ORIGIN}/public' }; // 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.NEXT_PUBLIC_ENTE_PUBLIC_ALBUMS_ENDPOINT = '${CLOUDRON_APP_ORIGIN}/public'; console.log('Ente runtime config loaded from runtime-config.js'); console.log('API_URL:', window.ENTE_CONFIG.API_URL); console.log('PUBLIC_ALBUMS_URL:', window.ENTE_CONFIG.PUBLIC_ALBUMS_URL); EOT # Copy the runtime-config.js to each app directory for app_dir in /app/web/photos /app/web/accounts /app/web/auth /app/web/cast; do cp /app/web/photos/runtime-config.js "$app_dir/" # Update the HTML to include this file too index_file="$app_dir/index.html" if [ -f "$index_file" ]; then echo "==> Adding runtime-config.js to $index_file" sed -i 's/<\/head>/<script src="runtime-config.js" type="text\/javascript"><\/script>\n <\/head>/' "$index_file" fi done # Ensure all files are readable chmod -R 644 /app/web/*/runtime-config.js echo "==> Config injected into all web applications" caddy start --config /app/data/caddy/Caddyfile --adapter caddyfile & CADDY_PID=$! echo "==> Caddy started with PID $CADDY_PID" Loading Loading
start.sh +222 −101 Original line number Diff line number Diff line Loading @@ -647,6 +647,7 @@ func main() { log.Println("API_ENDPOINT:", os.Getenv("ENTE_API_ENDPOINT")) // Create a logger that logs to both stdout and a file os.MkdirAll("/app/data/logs", 0755) // Ensure the logs directory exists logFile, err := os.OpenFile("/app/data/logs/api_requests.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Printf("Error opening log file: %v", err) Loading @@ -656,6 +657,7 @@ func main() { multiWriter := io.MultiWriter(os.Stdout, logFile) logger := log.New(multiWriter, "", log.LstdFlags) // Initialize random seed rand.Seed(time.Now().UnixNano()) // Map to store verification codes Loading Loading @@ -699,8 +701,13 @@ func main() { // Return a success response w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, \`{"status":"ok","message":"Verification code sent (check logs)"}\`) // Use the encoding/json package to create and send the response jsonResponse := map[string]string{ "status": "ok", "message": "Verification code sent (check logs)", } json.NewEncoder(w).Encode(jsonResponse) } else { // Just handle other methods with a generic response w.Header().Set("Content-Type", "application/json") Loading Loading @@ -757,11 +764,23 @@ func main() { if isValid { // Return a successful verification response w.WriteHeader(http.StatusOK) fmt.Fprintf(w, \`{"status":"ok","token":"mock-token-12345"}\`) // Use the json package to create the response jsonResponse := map[string]string{ "status": "ok", "token": "mock-token-12345", } json.NewEncoder(w).Encode(jsonResponse) } else { // Return an error w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, \`{"status":"error","message":"Invalid verification code"}\`) // Use the json package to create the error response jsonResponse := map[string]string{ "status": "error", "message": "Invalid verification code", } json.NewEncoder(w).Encode(jsonResponse) } } else { // Handle other methods with a generic response Loading @@ -782,11 +801,20 @@ func main() { } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, \`{"status":"mock","endpoint":"%s","method":"%s","time":"%s"}\`, r.URL.Path, r.Method, time.Now().Format(time.RFC3339)) // Use the json package to create a dynamic response response := map[string]string{ "status": "mock", "endpoint": r.URL.Path, "method": r.Method, "time": time.Now().Format(time.RFC3339), } json.NewEncoder(w).Encode(response) }) logger.Printf("Mock Ente API server listening on port %s\n", port) // Make sure we listen on all interfaces, not just localhost if err := http.ListenAndServe("0.0.0.0:" + port, nil); err != nil { logger.Fatalf("Server failed: %v", err) } Loading Loading @@ -918,7 +946,9 @@ elif [ -d "$SERVER_DIR/cmd/museum" ]; then package main import ( "encoding/json" "fmt" "io" "log" "net/http" "os" Loading @@ -937,21 +967,59 @@ func main() { log.Println("PGPORT:", os.Getenv("PGPORT")) log.Println("API_ENDPOINT:", os.Getenv("ENTE_API_ENDPOINT")) // Create a logger that logs to both stdout and a file os.MkdirAll("/app/data/logs", 0755) // Ensure the logs directory exists logFile, err := os.OpenFile("/app/data/logs/public_api_requests.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Printf("Error opening log file: %v", err) } defer logFile.Close() multiWriter := io.MultiWriter(os.Stdout, logFile) logger := log.New(multiWriter, "", log.LstdFlags) http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, \`{"status":"ok","service":"public_albums","version":"mock-1.0.0","time":"%s"}\`, time.Now().Format(time.RFC3339)) // Use json package to create the response response := map[string]string{ "status": "ok", "service": "public_albums", "version": "mock-1.0.0", "time": time.Now().Format(time.RFC3339), } json.NewEncoder(w).Encode(response) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { log.Printf("Public Albums: Received request for %s via %s", r.URL.Path, r.Method) logger.Printf("Public Albums: Received request for %s via %s", r.URL.Path, r.Method) // Log request body if it's a POST or PUT if r.Method == "POST" || r.Method == "PUT" { body, _ := io.ReadAll(r.Body) if len(body) > 0 { logger.Printf("Request body: %s", string(body)) } } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, \`{"status":"mock","service":"public_albums","endpoint":"%s","method":"%s","time":"%s"}\`, r.URL.Path, r.Method, time.Now().Format(time.RFC3339)) // Use json package to create the response response := map[string]string{ "status": "mock", "service": "public_albums", "endpoint": r.URL.Path, "method": r.Method, "time": time.Now().Format(time.RFC3339), } json.NewEncoder(w).Encode(response) }) log.Printf("Mock Public Albums API server listening on port %s\n", port) logger.Printf("Mock Public Albums API server listening on port %s\n", port) // Make sure we listen on all interfaces if err := http.ListenAndServe("0.0.0.0:" + port, nil); err != nil { log.Fatalf("Server failed: %v", err) logger.Fatalf("Server failed: %v", err) } } EOT Loading @@ -974,7 +1042,9 @@ else package main import ( "encoding/json" "fmt" "io" "log" "net/http" "os" Loading @@ -993,23 +1063,59 @@ func main() { log.Println("PGPORT:", os.Getenv("PGPORT")) log.Println("API_ENDPOINT:", os.Getenv("ENTE_API_ENDPOINT")) // Create a logger that logs to both stdout and a file os.MkdirAll("/app/data/logs", 0755) // Ensure the logs directory exists logFile, err := os.OpenFile("/app/data/logs/public_api_requests.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Printf("Error opening log file: %v", err) } defer logFile.Close() multiWriter := io.MultiWriter(os.Stdout, logFile) logger := log.New(multiWriter, "", log.LstdFlags) http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, \`{"status":"ok","service":"public_albums","version":"mock-1.0.0","time":"%s"}\`, time.Now().Format(time.RFC3339)) // Use json package to create the response response := map[string]string{ "status": "ok", "service": "public_albums", "version": "mock-1.0.0", "time": time.Now().Format(time.RFC3339), } json.NewEncoder(w).Encode(response) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { log.Printf("Public Albums: Received request for %s via %s", r.URL.Path, r.Method) logger.Printf("Public Albums: Received request for %s via %s", r.URL.Path, r.Method) // Log request body if it's a POST or PUT if r.Method == "POST" || r.Method == "PUT" { body, _ := io.ReadAll(r.Body) if len(body) > 0 { logger.Printf("Request body: %s", string(body)) } } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, \`{"status":"mock","service":"public_albums","endpoint":"%s","method":"%s","time":"%s"}\`, r.URL.Path, r.Method, time.Now().Format(time.RFC3339)) // Use json package to create the response response := map[string]string{ "status": "mock", "service": "public_albums", "endpoint": r.URL.Path, "method": r.Method, "time": time.Now().Format(time.RFC3339), } json.NewEncoder(w).Encode(response) }) // Start the server log.Printf("Mock Public Albums server listening on port %s\n", port) logger.Printf("Mock Public Albums API server listening on port %s\n", port) // Make sure we listen on all interfaces if err := http.ListenAndServe("0.0.0.0:" + port, nil); err != nil { log.Fatalf("Failed to start Public Albums server: %v", err) logger.Fatalf("Server failed: %v", err) } } EOT Loading Loading @@ -1048,7 +1154,41 @@ done # Set up Caddy web server echo "==> Setting up Caddy web server" # Create a Caddyfile for serving web apps and reverse proxy to API # First inject the config.js script tags into all HTML files echo "==> Injecting config.js into web application HTML files" # Create writable data directories for web assets mkdir -p /app/data/web/photos /app/data/web/accounts /app/data/web/auth /app/data/web/cast # Create runtime-config.js files in writable locations echo "==> Creating runtime-config.js in writable location" cat > /app/data/web/runtime-config.js <<EOT // Runtime configuration for Ente window.ENTE_CONFIG = { API_URL: '${API_ENDPOINT}', PUBLIC_ALBUMS_URL: '${CLOUDRON_APP_ORIGIN}/public' }; // 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.NEXT_PUBLIC_ENTE_PUBLIC_ALBUMS_ENDPOINT = '${CLOUDRON_APP_ORIGIN}/public'; console.log('Ente runtime config loaded from runtime-config.js'); console.log('API_URL:', window.ENTE_CONFIG.API_URL); console.log('PUBLIC_ALBUMS_URL:', window.ENTE_CONFIG.PUBLIC_ALBUMS_URL); EOT # Copy the runtime-config.js to each app directory for app_dir in /app/data/web/photos /app/data/web/accounts /app/data/web/auth /app/data/web/cast; do cp /app/data/web/runtime-config.js "$app_dir/" done # Ensure all files are readable chmod -R 644 /app/data/web/runtime-config.js # Update the Caddy configuration to serve config.js directly cat > /app/data/caddy/Caddyfile <<EOT # Global settings { Loading @@ -1066,11 +1206,53 @@ cat > /app/data/caddy/Caddyfile <<EOT output file /app/data/logs/caddy.log } # Configuration scripts - This must come before the root handler handle /config.js { header Content-Type application/javascript respond " // Direct configuration for Ente window.ENTE_CONFIG = { API_URL: '${API_ENDPOINT}', PUBLIC_ALBUMS_URL: '${CLOUDRON_APP_ORIGIN}/public' }; // 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.NEXT_PUBLIC_ENTE_PUBLIC_ALBUMS_ENDPOINT = '${CLOUDRON_APP_ORIGIN}/public'; console.log('Ente config loaded - API_URL:', window.ENTE_CONFIG.API_URL); console.log('Ente config loaded - PUBLIC_ALBUMS_URL:', window.ENTE_CONFIG.PUBLIC_ALBUMS_URL); // Dynamically inject the config by creating script elements document.addEventListener('DOMContentLoaded', function() { console.log('DOM loaded, injecting runtime config as needed'); }); " } handle /runtime-config.js { root * /app/data/web file_server } # Root path serves the photos app handle / { root * /app/web/photos try_files {path} /index.html file_server # Dynamically inject our configuration script header * +Link "</config.js>; rel=preload; as=script" header * +Link "</runtime-config.js>; rel=preload; as=script" header ?index.html +Content-Security-Policy "script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src *" # Add script injection as HTML filter filter { search_pattern </head> replacement <script src="/config.js" type="text/javascript"></script><script src="/runtime-config.js" type="text/javascript"></script></head> } } # Next.js static files Loading @@ -1094,6 +1276,12 @@ cat > /app/data/caddy/Caddyfile <<EOT uri strip_prefix /accounts try_files {path} /index.html file_server # Dynamically inject our configuration script for accounts app filter ?index.html { search_pattern </head> replacement <script src="/config.js" type="text/javascript"></script><script src="/runtime-config.js" type="text/javascript"></script></head> } } # Auth app Loading @@ -1102,6 +1290,12 @@ cat > /app/data/caddy/Caddyfile <<EOT uri strip_prefix /auth try_files {path} /index.html file_server # Dynamically inject our configuration script for auth app filter ?index.html { search_pattern </head> replacement <script src="/config.js" type="text/javascript"></script><script src="/runtime-config.js" type="text/javascript"></script></head> } } # Cast app Loading @@ -1110,6 +1304,12 @@ cat > /app/data/caddy/Caddyfile <<EOT uri strip_prefix /cast try_files {path} /index.html file_server # Dynamically inject our configuration script for cast app filter ?index.html { search_pattern </head> replacement <script src="/config.js" type="text/javascript"></script><script src="/runtime-config.js" type="text/javascript"></script></head> } } # Main API proxy Loading Loading @@ -1142,94 +1342,15 @@ cat > /app/data/caddy/Caddyfile <<EOT uri strip_prefix /public reverse_proxy 0.0.0.0:$PUBLIC_ALBUMS_PORT } # Configuration scripts handle /config.js { header Content-Type application/javascript respond " // Direct configuration for Ente window.ENTE_CONFIG = { API_URL: '${API_ENDPOINT}', PUBLIC_ALBUMS_URL: '${CLOUDRON_APP_ORIGIN}/public' }; // 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.NEXT_PUBLIC_ENTE_PUBLIC_ALBUMS_ENDPOINT = '${CLOUDRON_APP_ORIGIN}/public'; console.log('Ente config loaded - API_URL:', window.ENTE_CONFIG.API_URL); console.log('Ente config loaded - PUBLIC_ALBUMS_URL:', window.ENTE_CONFIG.PUBLIC_ALBUMS_URL); " } } EOT echo "==> Created Caddy config at /app/data/caddy/Caddyfile" echo "==> Created Caddy config with dynamic script injection at /app/data/caddy/Caddyfile" echo "==> No longer trying to modify read-only HTML files" # Start Caddy server echo "==> Starting Caddy server" # First inject the config.js script tags into all HTML files echo "==> Injecting config.js into web application HTML files" # Function to inject the script tag inject_script_tag() { file="$1" if [ -f "$file" ]; then echo "==> Injecting config.js into $file" # Make a backup just in case cp "$file" "${file}.bak" # Insert the script tag right after the opening head tag sed -i 's/<head>/<head>\n <script src="\/config.js" type="text\/javascript"><\/script>/' "$file" else echo "==> WARNING: Could not find $file to inject config script" fi } # Inject into all the web apps inject_script_tag "/app/web/photos/index.html" inject_script_tag "/app/web/accounts/index.html" inject_script_tag "/app/web/auth/index.html" inject_script_tag "/app/web/cast/index.html" # Also create a runtime-config.js file with properly escaped content cat > /app/web/photos/runtime-config.js <<EOT // Runtime configuration for Ente window.ENTE_CONFIG = { API_URL: '${API_ENDPOINT}', PUBLIC_ALBUMS_URL: '${CLOUDRON_APP_ORIGIN}/public' }; // 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.NEXT_PUBLIC_ENTE_PUBLIC_ALBUMS_ENDPOINT = '${CLOUDRON_APP_ORIGIN}/public'; console.log('Ente runtime config loaded from runtime-config.js'); console.log('API_URL:', window.ENTE_CONFIG.API_URL); console.log('PUBLIC_ALBUMS_URL:', window.ENTE_CONFIG.PUBLIC_ALBUMS_URL); EOT # Copy the runtime-config.js to each app directory for app_dir in /app/web/photos /app/web/accounts /app/web/auth /app/web/cast; do cp /app/web/photos/runtime-config.js "$app_dir/" # Update the HTML to include this file too index_file="$app_dir/index.html" if [ -f "$index_file" ]; then echo "==> Adding runtime-config.js to $index_file" sed -i 's/<\/head>/<script src="runtime-config.js" type="text\/javascript"><\/script>\n <\/head>/' "$index_file" fi done # Ensure all files are readable chmod -R 644 /app/web/*/runtime-config.js echo "==> Config injected into all web applications" caddy start --config /app/data/caddy/Caddyfile --adapter caddyfile & CADDY_PID=$! echo "==> Caddy started with PID $CADDY_PID" Loading