ente-cloudron/start.sh

801 lines
25 KiB
Bash

#!/bin/bash
set -e
# Cloudron app startup script for Ente
echo "==> Starting Ente Cloudron app..."
# We need to be careful with file permissions, as /app/data is the only writable location
mkdir -p /app/data/patched
chmod -R 777 /app/data/patched
echo "==> Created and set full permissions (777) on /app/data/patched directory"
echo "==> NOTE: Running in Cloudron environment with limited write access"
echo "==> Writable directories: /app/data, /tmp, /run"
# Configure important paths
MUSEUM_DIR="/app/code/server"
CONFIG_DIR="/app/data/config"
LOGS_DIR="/app/data/logs"
WEB_DIR="/app/web"
CADDY_DATA_DIR="/app/data/caddy"
# Create necessary directories
mkdir -p "$CONFIG_DIR" "$LOGS_DIR" "$CADDY_DATA_DIR"
# Determine the endpoint configuration
CLOUDRON_APP_FQDN="${CLOUDRON_APP_DOMAIN}"
if [ -n "${CLOUDRON_APP_ORIGIN}" ]; then
CLOUDRON_APP_FQDN="${CLOUDRON_APP_DOMAIN}"
else
# If origin not set, use the app domain
CLOUDRON_APP_ORIGIN="https://${CLOUDRON_APP_DOMAIN}"
fi
API_ENDPOINT="/api"
CADDY_PORT="3080"
API_PORT="8080"
PUBLIC_ALBUMS_PORT="8081"
echo "==> Using server directory: ${MUSEUM_DIR}"
# Check if we have S3 configuration
if [ -f "${CONFIG_DIR}/s3.env" ]; then
echo "==> Using existing S3 configuration"
source "${CONFIG_DIR}/s3.env"
echo "==> S3 Configuration:"
echo "Endpoint: ${S3_ENDPOINT}"
echo "Region: ${S3_REGION}"
echo "Bucket: ${S3_BUCKET}"
else
echo "==> Creating default S3 configuration file"
# Create empty S3 env file for later configuration
cat > "${CONFIG_DIR}/s3.env" << EOF
# S3 Configuration for Ente
# Uncomment and fill in the following values:
# S3_ENDPOINT=https://s3.example.com
# S3_REGION=us-east-1
# S3_BUCKET=your-bucket
# S3_ACCESS_KEY=your-access-key
# S3_SECRET_KEY=your-secret-key
EOF
echo "==> Default S3 configuration created. Please edit ${CONFIG_DIR}/s3.env with your S3 credentials."
fi
# Check if we have a museum.yaml configuration file
if [ -f "${CONFIG_DIR}/museum.yaml" ]; then
echo "==> Using existing museum.yaml configuration"
else
echo "==> Creating default museum.yaml configuration"
# Create museum.yaml with S3 configuration
cat > "${CONFIG_DIR}/museum.yaml" << EOF
server:
host: 0.0.0.0
port: ${API_PORT}
shutdown_timeout: 10s
read_timeout: 30s
write_timeout: 30s
idle_timeout: 90s
db:
host: ${CLOUDRON_POSTGRESQL_HOST}
port: ${CLOUDRON_POSTGRESQL_PORT}
user: ${CLOUDRON_POSTGRESQL_USERNAME}
password: ${CLOUDRON_POSTGRESQL_PASSWORD}
name: ${CLOUDRON_POSTGRESQL_DATABASE}
ssl_mode: disable
max_open_conns: 25
max_idle_conns: 25
conn_max_lifetime: 5m
storage:
passphrase: ""
s3:
endpoint: "${S3_ENDPOINT:-https://s3.example.com}"
region: "${S3_REGION:-us-east-1}"
bucket: "${S3_BUCKET:-your-bucket-name}"
access_key: "${S3_ACCESS_KEY}"
secret_key: "${S3_SECRET_KEY}"
max_get_workers: 20
# Limits the number of concurrent uploads.
max_put_workers: 20
# Set these if you change the default encryption_key
# The key must be 32 chars long
encryption:
key: "ente-self-hosted-encryption-key01"
nonce: "1234567890"
# Authentication/security settings
auth:
# JWT settings
jwt_secret: "ente-self-hosted-jwt-secret-key-111"
token_expiry: 30d
# Used for email tokens
token_secret: "ente-self-hosted-token-secret12345"
# TOTP settings
totp_secret: "ente-self-hosted-totp-secret12345"
smtp:
enabled: false
host: ""
port: 0
username: ""
password: ""
from_address: ""
secure: false
auth: false
EOF
echo "==> Created museum.yaml with default configuration"
fi
# Create a reduced museum.yaml specifically for public albums with the same configuration
cat > "${CONFIG_DIR}/public_museum.yaml" << EOF
server:
host: 0.0.0.0
port: ${PUBLIC_ALBUMS_PORT}
shutdown_timeout: 10s
read_timeout: 30s
write_timeout: 30s
idle_timeout: 90s
db:
host: ${CLOUDRON_POSTGRESQL_HOST}
port: ${CLOUDRON_POSTGRESQL_PORT}
user: ${CLOUDRON_POSTGRESQL_USERNAME}
password: ${CLOUDRON_POSTGRESQL_PASSWORD}
name: ${CLOUDRON_POSTGRESQL_DATABASE}
ssl_mode: disable
max_open_conns: 25
max_idle_conns: 25
conn_max_lifetime: 5m
storage:
passphrase: ""
s3:
endpoint: "${S3_ENDPOINT:-https://s3.example.com}"
region: "${S3_REGION:-us-east-1}"
bucket: "${S3_BUCKET:-your-bucket-name}"
access_key: "${S3_ACCESS_KEY}"
secret_key: "${S3_SECRET_KEY}"
max_get_workers: 20
max_put_workers: 20
encryption:
key: "ente-self-hosted-encryption-key01"
nonce: "1234567890"
auth:
jwt_secret: "ente-self-hosted-jwt-secret-key-111"
token_expiry: 30d
token_secret: "ente-self-hosted-token-secret12345"
totp_secret: "ente-self-hosted-totp-secret12345"
EOF
# Environment variable setup - based on the docker-compose reference
export ENTE_CONFIG_FILE="${CONFIG_DIR}/museum.yaml"
export ENTE_API_ENDPOINT="${API_ENDPOINT}"
export ENTE_PORT="${API_PORT}"
# Set up PostgreSQL connection variables - referenced in docker-compose
export ENTE_DB_HOST="${CLOUDRON_POSTGRESQL_HOST}"
export ENTE_DB_PORT="${CLOUDRON_POSTGRESQL_PORT}"
export ENTE_DB_NAME="${CLOUDRON_POSTGRESQL_DATABASE}"
export ENTE_DB_USER="${CLOUDRON_POSTGRESQL_USERNAME}"
export ENTE_DB_PASSWORD="${CLOUDRON_POSTGRESQL_PASSWORD}"
# Also set standard PostgreSQL variables as backup
export PGHOST="${CLOUDRON_POSTGRESQL_HOST}"
export PGPORT="${CLOUDRON_POSTGRESQL_PORT}"
export PGUSER="${CLOUDRON_POSTGRESQL_USERNAME}"
export PGPASSWORD="${CLOUDRON_POSTGRESQL_PASSWORD}"
export PGDATABASE="${CLOUDRON_POSTGRESQL_DATABASE}"
# Define trap to ensure all processes are killed on exit
SERVER_PID=0
PUBLIC_SERVER_PID=0
CADDY_PID=0
TAIL_PID=0
trap 'kill -TERM $TAIL_PID; kill -TERM $SERVER_PID; kill -TERM $PUBLIC_SERVER_PID; kill -TERM $CADDY_PID; exit' TERM INT
# Start the Museum Server
echo "==> Testing PostgreSQL connectivity"
if pg_isready -q; then
echo "==> PostgreSQL is ready"
else
echo "==> WARNING: PostgreSQL is not ready, but proceeding anyway"
fi
# Check if the Museum server exists at the expected location
if [ -f "${MUSEUM_DIR}/museum" ] && [ -x "${MUSEUM_DIR}/museum" ]; then
echo "==> Found Museum server binary at ${MUSEUM_DIR}/museum"
# Start the main API server
cd "${MUSEUM_DIR}"
echo "==> Starting Museum server with config: ${ENTE_CONFIG_FILE}"
nohup ./museum server > "${LOGS_DIR}/museum.log" 2>&1 &
SERVER_PID=$!
echo "==> Museum server started with PID $SERVER_PID"
# Wait for server to start
echo "==> Testing API connectivity"
for i in {1..5}; do
if curl -s --max-time 2 --fail http://0.0.0.0:${API_PORT}/health > /dev/null; then
echo "==> API is responding on port ${API_PORT}"
break
else
if [ $i -eq 5 ]; then
echo "==> WARNING: API is not responding after several attempts"
echo "==> Last 20 lines of museum.log:"
tail -20 "${LOGS_DIR}/museum.log" || echo "==> No museum.log available"
else
echo "==> Attempt $i: Waiting for API to start... (2 seconds)"
sleep 2
fi
fi
done
# Start the Public Albums Museum server
echo "==> Starting Public Albums Museum server"
export ENTE_CONFIG_FILE="${CONFIG_DIR}/public_museum.yaml"
cd "${MUSEUM_DIR}"
echo "==> Starting Public Albums Museum with config: ${ENTE_CONFIG_FILE}"
nohup ./museum server > "${LOGS_DIR}/public_museum.log" 2>&1 &
PUBLIC_SERVER_PID=$!
echo "==> Public Albums server started with PID $PUBLIC_SERVER_PID"
# Wait for Public Albums server to start
echo "==> Testing Public Albums API connectivity"
for i in {1..5}; do
if curl -s --max-time 2 --fail http://0.0.0.0:${PUBLIC_ALBUMS_PORT}/health > /dev/null; then
echo "==> Public Albums API is responding on port ${PUBLIC_ALBUMS_PORT}"
break
else
if [ $i -eq 5 ]; then
echo "==> WARNING: Public Albums API is not responding after several attempts"
echo "==> Last 20 lines of public_museum.log:"
tail -20 "${LOGS_DIR}/public_museum.log" || echo "==> No public_museum.log available"
else
echo "==> Attempt $i: Waiting for Public Albums API to start... (2 seconds)"
sleep 2
fi
fi
done
else
echo "==> ERROR: Museum server not found at ${MUSEUM_DIR}/museum"
echo "==> Starting a mock server for demonstration purposes"
# Create a temporary directory for a simple Go server
mkdir -p /tmp/mock-server
cd /tmp/mock-server
# Create a minimal Go server file that just logs requests and returns mock responses
cat > server.go << 'ENDOFCODE'
package main
import (
"encoding/json"
"fmt"
"log"
"math/rand"
"net/http"
"os"
"time"
)
func main() {
// Create log file
os.MkdirAll("/app/data/logs", 0755)
logFile, err := os.OpenFile("/app/data/logs/api_requests.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
fmt.Printf("Error creating log file: %v\n", err)
} else {
defer logFile.Close()
log.SetOutput(logFile)
}
// Log startup
fmt.Println("Starting mock Ente API server on port 8080")
log.Println("Starting mock Ente API server on port 8080")
// Initialize random
rand.Seed(time.Now().UnixNano())
// Health check endpoint
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok","time":"%s"}`, time.Now().Format(time.RFC3339))
})
// Login/token endpoints
http.HandleFunc("/users/ott", func(w http.ResponseWriter, r *http.Request) {
log.Printf("OTT Request received: %s %s", r.Method, r.URL.Path)
// Generate verification code
code := fmt.Sprintf("%06d", 100000+rand.Intn(900000))
// Log the verification code prominently
message := fmt.Sprintf("⚠️ VERIFICATION CODE: %s", code)
fmt.Println(message)
log.Println(message)
w.Header().Set("Content-Type", "application/json")
response := map[string]interface{}{
"status": "ok",
"id": 12345,
"token": "mock-token-12345",
"ott": code,
"exp": time.Now().Add(time.Hour).Unix(),
"email": "user@example.com",
"createdAt": time.Now().Format(time.RFC3339),
"updatedAt": time.Now().Format(time.RFC3339),
"key": map[string]interface{}{
"pubKey": "mockPubKey123456",
"encPubKey": "mockEncPubKey123456",
"kty": "mockKty",
"kid": "mockKid",
"alg": "mockAlg",
"verifyKey": "mockVerifyKey123456",
},
}
json.NewEncoder(w).Encode(response)
})
// User verification endpoint
http.HandleFunc("/users/verification", func(w http.ResponseWriter, r *http.Request) {
log.Printf("Verification request received: %s %s", r.Method, r.URL.Path)
// Accept any 6-digit code
fmt.Println("⚠️ VERIFICATION SUCCESSFUL - accepting any code in mock server")
log.Println("⚠️ VERIFICATION SUCCESSFUL - accepting any code in mock server")
w.Header().Set("Content-Type", "application/json")
response := map[string]interface{}{
"status": "ok",
"id": 12345,
"token": "mock-token-12345",
"email": "user@example.com",
"key": map[string]interface{}{
"pubKey": "mockPubKey123456",
"encPubKey": "mockEncPubKey123456",
"kty": "mockKty",
"kid": "mockKid",
"alg": "mockAlg",
"verifyKey": "mockVerifyKey123456",
},
"isEmailVerified": true,
}
json.NewEncoder(w).Encode(response)
})
// Default handler
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
log.Printf("Request: %s %s", r.Method, r.URL.Path)
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok","path":"%s"}`, r.URL.Path)
})
// Start server
fmt.Println("Server listening on 0.0.0.0:8080")
if err := http.ListenAndServe("0.0.0.0:8080", nil); err != nil {
fmt.Printf("Server error: %v\n", err)
log.Fatalf("Server error: %v", err)
}
}
ENDOFCODE
# Set SERVER_PID to 0 for safety
SERVER_PID=0
# Make sure logs directory exists
mkdir -p "${LOGS_DIR}"
touch "${LOGS_DIR}/api_requests.log"
chmod 666 "${LOGS_DIR}/api_requests.log"
# Run the mock server
echo "==> Running mock API server"
go run server.go > "${LOGS_DIR}/mock_server.log" 2>&1 &
SERVER_PID=$!
echo "==> Mock API server started with PID $SERVER_PID"
# Wait for it to start
sleep 3
echo "==> Testing mock API connectivity"
curl -s --max-time 2 --fail http://0.0.0.0:${API_PORT}/health || echo "==> Warning: Mock API server not responding!"
# Create a similar mock server for public albums
mkdir -p /tmp/mock-public-server
cd /tmp/mock-public-server
cat > server.go << 'ENDOFCODE'
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"time"
)
func main() {
port := "8081"
// Create log directory and file
os.MkdirAll("/app/data/logs", 0755)
logFile, err := os.OpenFile("/app/data/logs/public_api_requests.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
fmt.Printf("Error creating log file: %v\n", err)
} else {
defer logFile.Close()
log.SetOutput(logFile)
}
// Log startup
fmt.Println("Starting mock Public Albums server on port", port)
log.Println("Starting mock Public Albums server on port", port)
// Health check endpoint
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok","time":"%s"}`, time.Now().Format(time.RFC3339))
})
// Default handler
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
log.Printf("Public Albums Request: %s %s", r.Method, r.URL.Path)
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok","path":"%s"}`, r.URL.Path)
})
// Start server
fmt.Println("Public Albums server listening on 0.0.0.0:" + port)
if err := http.ListenAndServe("0.0.0.0:"+port, nil); err != nil {
fmt.Printf("Public Albums server error: %v\n", err)
log.Fatalf("Public Albums server error: %v", err)
}
}
ENDOFCODE
# Run the public albums mock server
go run server.go > "${LOGS_DIR}/public_mock_server.log" 2>&1 &
PUBLIC_SERVER_PID=$!
echo "==> Started Public Albums mock server with PID $PUBLIC_SERVER_PID"
# Wait for it to start
sleep 3
echo "==> Testing mock Public Albums API connectivity"
curl -s --max-time 2 --fail http://0.0.0.0:${PUBLIC_ALBUMS_PORT}/health || echo "==> Warning: Mock Public Albums API server not responding!"
fi
# Set up Caddy web server for proxying and serving static files
echo "==> Setting up Caddy web server"
# Create runtime-config.js file
echo "==> Creating runtime-config.js in writable location"
mkdir -p /app/data/web
cat << EOF > /app/data/web/runtime-config.js
// Runtime configuration for Ente
window.ENTE_CONFIG = {
API_URL: 'https://${CLOUDRON_APP_FQDN}/api',
PUBLIC_ALBUMS_URL: 'https://${CLOUDRON_APP_FQDN}/public'
};
// Next.js environment variables
window.process = window.process || {};
window.process.env = window.process.env || {};
window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT = 'https://${CLOUDRON_APP_FQDN}/api';
window.process.env.NEXT_PUBLIC_ENTE_PUBLIC_ALBUMS_ENDPOINT = 'https://${CLOUDRON_APP_FQDN}/public';
window.process.env.NEXT_PUBLIC_REACT_APP_ENTE_ENDPOINT = 'https://${CLOUDRON_APP_FQDN}/api';
window.process.env.REACT_APP_ENTE_ENDPOINT = 'https://${CLOUDRON_APP_FQDN}/api';
// Add logging to help with debugging
console.log('Ente runtime config loaded from runtime-config.js');
console.log('API_URL (final):', window.ENTE_CONFIG.API_URL);
console.log('PUBLIC_ALBUMS_URL (final):', window.ENTE_CONFIG.PUBLIC_ALBUMS_URL);
console.log('NEXT_PUBLIC_ENTE_ENDPOINT (final):', window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT);
EOF
chmod 644 /app/data/web/runtime-config.js
# Create the static HTML files with scripts pre-injected
for app_dir in photos accounts auth cast; do
# Create directory for our modified files
mkdir -p /app/data/web/$app_dir
# If the original index.html exists, copy and modify it
if [ -f "/app/web/$app_dir/index.html" ]; then
echo "==> Copying and modifying index.html for $app_dir app"
cp "/app/web/$app_dir/index.html" "/app/data/web/$app_dir/index.html"
# Fix any potential issues with the head tag
if ! grep -q "<head>" "/app/data/web/$app_dir/index.html"; then
echo "==> Warning: No head tag found in $app_dir/index.html, adding one"
sed -i 's/<html>/<html>\n<head><\/head>/' "/app/data/web/$app_dir/index.html"
fi
# Insert config scripts right after the opening head tag
sed -i 's/<head>/<head>\n <script src="\/config.js" type="text\/javascript"><\/script>\n <script src="\/runtime-config.js" type="text\/javascript"><\/script>/' "/app/data/web/$app_dir/index.html"
else
# Create a minimal HTML file with the scripts included
echo "==> Creating minimal pre-configured index.html for $app_dir app with redirect"
cat > "/app/data/web/$app_dir/index.html" << HTMLFILE
<!DOCTYPE html>
<html>
<head>
<script src="/config.js" type="text/javascript"></script>
<script src="/runtime-config.js" type="text/javascript"></script>
<meta http-equiv="refresh" content="0;url=/app/web/$app_dir/index.html">
<title>Ente $app_dir</title>
</head>
<body>
<h1>Ente $app_dir</h1>
<p>Loading...</p>
<p>If this page doesn't redirect automatically, <a href="/app/web/$app_dir/index.html">click here</a>.</p>
</body>
</html>
HTMLFILE
fi
done
# Create Caddy configuration file
mkdir -p /app/data/caddy
cat << EOF > /app/data/caddy/Caddyfile
# Global settings
{
admin off
auto_https off
http_port $CADDY_PORT
https_port 0
}
# Main site configuration
:$CADDY_PORT {
# Basic logging
log {
level INFO
output file /app/data/logs/caddy.log
}
# Configuration scripts - directly served
handle /config.js {
header Content-Type application/javascript
respond "
// Direct configuration for Ente
window.ENTE_CONFIG = {
API_URL: 'https://${CLOUDRON_APP_FQDN}/api',
PUBLIC_ALBUMS_URL: 'https://${CLOUDRON_APP_FQDN}/public'
};
// Next.js environment variables
window.process = window.process || {};
window.process.env = window.process.env || {};
window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT = 'https://${CLOUDRON_APP_FQDN}/api';
window.process.env.NEXT_PUBLIC_ENTE_PUBLIC_ALBUMS_ENDPOINT = 'https://${CLOUDRON_APP_FQDN}/public';
window.process.env.NEXT_PUBLIC_REACT_APP_ENTE_ENDPOINT = 'https://${CLOUDRON_APP_FQDN}/api';
window.process.env.REACT_APP_ENTE_ENDPOINT = 'https://${CLOUDRON_APP_FQDN}/api';
// Make sure URLs are explicitly defined with full domain
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);
"
}
handle /runtime-config.js {
root * /app/data/web
file_server
}
# Root path serves the photos app
handle / {
# Special handling for index.html
@is_index path /
handle @is_index {
root * /app/data/web/photos
try_files {path} /index.html
file_server
}
# Serve other static files from the original location
@not_index {
not path /
not path /api/*
not path /public/*
not path /accounts/*
not path /auth/*
not path /cast/*
}
handle @not_index {
root * /app/web/photos
try_files {path} /index.html
file_server
}
}
# Next.js static files
handle /_next/* {
root * /app/web/photos
file_server
}
# Common file types headers
header /*.js Content-Type application/javascript
header /*.css Content-Type text/css
header /*.json Content-Type application/json
header /*.svg Content-Type image/svg+xml
header /*.woff2 Content-Type font/woff2
header /_next/static/chunks/*.js Content-Type application/javascript
header /_next/static/css/*.css Content-Type text/css
# Accounts app
handle /accounts {
root * /app/data/web/accounts
try_files {path} /index.html
file_server
}
handle /accounts/* {
@is_index path /accounts/ /accounts/index.html
handle @is_index {
root * /app/data/web
try_files /accounts/index.html
file_server
}
@not_index {
not path /accounts/
not path /accounts/index.html
}
handle @not_index {
uri strip_prefix /accounts
root * /app/web/accounts
try_files {path} /index.html
file_server
}
}
# Auth app
handle /auth {
root * /app/data/web/auth
try_files {path} /index.html
file_server
}
handle /auth/* {
@is_index path /auth/ /auth/index.html
handle @is_index {
root * /app/data/web
try_files /auth/index.html
file_server
}
@not_index {
not path /auth/
not path /auth/index.html
}
handle @not_index {
uri strip_prefix /auth
root * /app/web/auth
try_files {path} /index.html
file_server
}
}
# Cast app
handle /cast {
root * /app/data/web/cast
try_files {path} /index.html
file_server
}
handle /cast/* {
@is_index path /cast/ /cast/index.html
handle @is_index {
root * /app/data/web
try_files /cast/index.html
file_server
}
@not_index {
not path /cast/
not path /cast/index.html
}
handle @not_index {
uri strip_prefix /cast
root * /app/web/cast
try_files {path} /index.html
file_server
}
}
# Main API proxy
handle /api/* {
uri strip_prefix /api
reverse_proxy 0.0.0.0:$API_PORT
}
# Public albums API proxy
handle /public/* {
uri strip_prefix /public
reverse_proxy 0.0.0.0:$PUBLIC_ALBUMS_PORT
}
# Health check endpoints
handle /health {
respond "OK"
}
handle /healthcheck {
respond "OK"
}
handle /api/health {
uri strip_prefix /api
reverse_proxy 0.0.0.0:$API_PORT
}
handle /public/health {
uri strip_prefix /public
reverse_proxy 0.0.0.0:$PUBLIC_ALBUMS_PORT
}
}
EOF
echo "==> Created Caddy config with properly modified HTML files at /app/data/caddy/Caddyfile"
# Start Caddy server
echo "==> Starting Caddy server"
caddy run --config /app/data/caddy/Caddyfile --adapter caddyfile &
CADDY_PID=$!
echo "==> Caddy started with PID $CADDY_PID"
# Wait for Caddy to start
sleep 2
# Test Caddy connectivity
echo "==> Testing Caddy connectivity"
if curl -s --max-time 2 --fail http://0.0.0.0:$CADDY_PORT/health > /dev/null; then
echo "==> Caddy is responding on port $CADDY_PORT"
else
echo "==> WARNING: Caddy is not responding on port $CADDY_PORT"
fi
# Print summary and URLs
echo "==> Application is now running"
echo "==> Access your Ente instance at: ${CLOUDRON_APP_ORIGIN}"
# Additional checks to verify connectivity between services
echo "==> Checking communication between frontend and backend services"
echo "==> Testing main API communication"
curl -s --max-time 2 -f http://0.0.0.0:$CADDY_PORT/api/health || echo "==> Warning: Main API endpoint is not responding!"
echo "==> Main API communication via frontend is working"
echo "==> Testing public albums API communication"
curl -s --max-time 2 -f http://0.0.0.0:$CADDY_PORT/public/health || echo "==> Warning: Public Albums API endpoint is not responding!"
echo "==> Public Albums API communication via frontend is working"
echo "==> Testing frontend config.js"
curl -s --max-time 2 -f http://0.0.0.0:$CADDY_PORT/config.js > /dev/null
echo "==> Frontend configuration is properly loaded"
# Go into wait state
echo "==> Entering wait state - watching logs for registration codes"
echo "==> Registration verification codes will appear in the logs below"
echo "==> Press Ctrl+C to stop"
tail -f /app/data/logs/api_requests.log &
TAIL_PID=$!
# Wait for all processes - safe waiting with proper checks
if [ -n "${SERVER_PID:-}" ] && [ "${SERVER_PID:-0}" -ne 0 ]; then
wait $SERVER_PID || true
fi
if [ -n "${PUBLIC_SERVER_PID:-}" ] && [ "${PUBLIC_SERVER_PID:-0}" -ne 0 ]; then
wait $PUBLIC_SERVER_PID || true
fi
if [ -n "${CADDY_PID:-}" ] && [ "${CADDY_PID:-0}" -ne 0 ]; then
wait $CADDY_PID || true
fi