From 43cb685842473ab3083992c3668b4f34864e5b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20D=C3=BCren?= Date: Tue, 18 Mar 2025 20:08:15 +0100 Subject: [PATCH] Fixed read-only filesystem issues by using Caddy's filter directives and improved mock servers --- start.sh | 323 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 222 insertions(+), 101 deletions(-) diff --git a/start.sh b/start.sh index ef43717..d651406 100644 --- a/start.sh +++ b/start.sh @@ -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) @@ -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 @@ -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") @@ -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 @@ -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) } @@ -918,7 +946,9 @@ elif [ -d "$SERVER_DIR/cmd/museum" ]; then package main import ( + "encoding/json" "fmt" + "io" "log" "net/http" "os" @@ -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 @@ -974,7 +1042,9 @@ else package main import ( + "encoding/json" "fmt" + "io" "log" "net/http" "os" @@ -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 @@ -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 < /app/data/caddy/Caddyfile < /app/data/caddy/Caddyfile <; rel=preload; as=script" + header * +Link "; 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 + replacement + } } # Next.js static files @@ -1094,6 +1276,12 @@ cat > /app/data/caddy/Caddyfile < + replacement + } } # Auth app @@ -1102,6 +1290,12 @@ cat > /app/data/caddy/Caddyfile < + replacement + } } # Cast app @@ -1110,6 +1304,12 @@ cat > /app/data/caddy/Caddyfile < + replacement + } } # Main API proxy @@ -1142,94 +1342,15 @@ cat > /app/data/caddy/Caddyfile < 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//\n