diff --git a/start.sh b/start.sh index bfc48fb..8914ef9 100644 --- a/start.sh +++ b/start.sh @@ -1,113 +1,140 @@ #!/bin/bash -# Disable 'exit on error' to handle errors more gracefully -set +e +# =============================================== +# Ente Cloudron App - Main Startup Script +# =============================================== -# Prevent infinite loops by checking and creating a flag file +# Enable strict error handling +set -e + +# Initialize logging +LOG_FILE="/app/data/logs/ente.log" +mkdir -p /app/data/logs + +# Log function for consistent output +log() { + local level="$1" + local message="$2" + local timestamp=$(date +"%Y-%m-%d %H:%M:%S") + echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE" +} + +log "INFO" "Starting Ente Cloudron app" +log "INFO" "Running in Cloudron environment with domain: ${CLOUDRON_APP_DOMAIN}" + +# Prevent infinite loops through startup flag if [ -f "/app/data/startup_in_progress" ]; then - # Check if flag file is older than 60 seconds - if [ "$(find /app/data/startup_in_progress -mmin +1)" ]; then - echo "==> WARNING: Found old startup flag, removing and continuing" - rm -f /app/data/startup_in_progress + if [ "$(find /app/data/startup_in_progress -mmin +2)" ]; then + log "WARN" "Found old startup flag, removing and continuing" + rm -f "/app/data/startup_in_progress" else - echo "==> ERROR: Startup script was already running (started less than 60 seconds ago). Possible infinite loop detected. Exiting." - echo "==> Check logs for errors." + log "ERROR" "Startup script is already running (started less than 2 minutes ago)" + log "ERROR" "Possible infinite loop detected. Exiting." exit 1 fi fi # Create the flag file to indicate we're starting up echo "$(date): Starting up" > /app/data/startup_in_progress - -# Remove the flag file on exit trap 'rm -f /app/data/startup_in_progress' EXIT -# Use debug output -set -x +# =============================================== +# Initialize directories +# =============================================== +log "INFO" "Creating necessary directories" -# Declare that we're running in a Cloudron environment -echo "==> Starting Ente Cloudron app..." -echo "==> NOTE: Running in Cloudron environment with limited write access" -echo "==> Writable directories: /app/data, /tmp, /run" -echo "==> Current directory: $(pwd)" -echo "==> Environment: CLOUDRON_APP_DOMAIN=${CLOUDRON_APP_DOMAIN:-localhost}" -echo "==> Environment: CLOUDRON_APP_FQDN=${CLOUDRON_APP_FQDN:-$CLOUDRON_APP_DOMAIN}" -echo "==> Environment: Internal IP=$(hostname -I)" - -# Create necessary directories +# App directories mkdir -p /app/data/ente/server mkdir -p /app/data/ente/web -mkdir -p /app/data/logs -mkdir -p /app/data/web/photos -mkdir -p /app/data/web/accounts -mkdir -p /app/data/web/auth -mkdir -p /app/data/web/cast -# Create Go directories -mkdir -p /app/data/go/pkg -mkdir -p /app/data/go/bin -mkdir -p /app/data/go/src -echo "==> Created all necessary directories" +mkdir -p /app/data/tmp +mkdir -p /app/data/web/{photos,accounts,auth,cast} -# Directory listings for debugging -echo "==> Directory listing of /app/data:" -ls -la /app/data -echo "==> Directory listing of /app/data/web:" -ls -la /app/data/web -echo "==> Directory listing of /app/data/ente:" -ls -la /app/data/ente +# =============================================== +# Repository setup +# =============================================== +ENTE_REPO_DIR="/app/data/ente/repository" +log "INFO" "Setting up Ente repository at ${ENTE_REPO_DIR}" -# Clone Ente repository if it doesn't exist -ENTE_DIR="/app/data/ente/repository" -if [ ! -d "$ENTE_DIR" ]; then - echo "==> Cloning Ente repository" - mkdir -p "$ENTE_DIR" - git clone https://github.com/ente-io/ente "$ENTE_DIR" - echo "==> Ente repository cloned successfully" +if [ ! -d "$ENTE_REPO_DIR" ]; then + log "INFO" "Cloning Ente repository" + mkdir -p "$ENTE_REPO_DIR" + if git clone --depth 1 https://github.com/ente-io/ente "$ENTE_REPO_DIR"; then + log "INFO" "Repository cloned successfully" + else + log "ERROR" "Failed to clone repository" + fi else - echo "==> Ente repository already exists, pulling latest changes" - cd "$ENTE_DIR" - git pull + log "INFO" "Repository already exists, pulling latest changes" + cd "$ENTE_REPO_DIR" + if git pull; then + log "INFO" "Repository updated successfully" + else + log "WARN" "Failed to update repository, using existing version" + fi fi -# Set up S3 config -if [ ! -f "/app/data/s3.env" ]; then - echo "==> Creating default S3 environment file" - cat > /app/data/s3.env << 'EOF' +# =============================================== +# Configuration +# =============================================== +log "INFO" "Setting up configuration" + +# S3 configuration +S3_CONFIG="/app/data/s3.env" +if [ ! -f "$S3_CONFIG" ]; then + log "INFO" "Creating default S3 configuration file" + cat > "$S3_CONFIG" << EOF +# Ente S3 Storage Configuration +# Edit these values with your S3-compatible storage credentials + +# Required settings S3_ACCESS_KEY=your-access-key S3_SECRET_KEY=your-secret-key -S3_ENDPOINT=your-s3-endpoint -S3_REGION=your-region -S3_BUCKET=your-bucket +S3_ENDPOINT=your-s3-endpoint # e.g., s3.amazonaws.com +S3_REGION=your-region # e.g., us-east-1 +S3_BUCKET=your-bucket-name + +# Optional settings +# S3_PREFIX=ente/ # Optional prefix for all objects +# S3_PUBLIC_URL= # Optional public URL for the bucket (if different from endpoint) EOF - chmod 600 /app/data/s3.env - echo "==> Created S3 environment file at /app/data/s3.env" - echo "==> IMPORTANT: Please edit this file with your actual S3 credentials" + chmod 600 "$S3_CONFIG" + log "INFO" "Created S3 config template at ${S3_CONFIG}" + log "WARN" "⚠️ YOU MUST EDIT /app/data/s3.env WITH YOUR ACTUAL S3 CREDENTIALS ⚠️" else - echo "==> S3 environment file already exists" + log "INFO" "S3 configuration file already exists" fi -# Load S3 config -if [ -f "/app/data/s3.env" ]; then - source /app/data/s3.env - echo "==> Loaded S3 configuration" +# Load S3 configuration +if [ -f "$S3_CONFIG" ]; then + source "$S3_CONFIG" + log "INFO" "Loaded S3 configuration" fi -# Create museum.yaml config +# Museum server configuration MUSEUM_CONFIG="/app/data/ente/server/museum.yaml" if [ ! -f "$MUSEUM_CONFIG" ]; then - echo "==> Creating museum.yaml configuration" - cat > "$MUSEUM_CONFIG" << EOF + log "INFO" "Creating Museum server configuration" + cat > "$MUSEUM_CONFIG" << EOF +# Museum server configuration + +# Server settings port: 3080 host: 0.0.0.0 +log_level: info + +# Database configuration db: driver: postgres source: "postgres://${CLOUDRON_POSTGRESQL_USERNAME}:${CLOUDRON_POSTGRESQL_PASSWORD}@${CLOUDRON_POSTGRESQL_HOST}:${CLOUDRON_POSTGRESQL_PORT}/${CLOUDRON_POSTGRESQL_DATABASE}?sslmode=disable" max_conns: 10 max_idle: 5 -log_level: info + +# CORS settings cors: allow_origins: - "*" + +# S3 storage configuration s3: endpoint: "${S3_ENDPOINT:-s3.amazonaws.com}" region: "${S3_REGION:-us-east-1}" @@ -115,6 +142,8 @@ s3: secret_key: "${S3_SECRET_KEY}" bucket: "${S3_BUCKET}" public_url: "https://${CLOUDRON_APP_FQDN}/photos" + +# Email settings email: enabled: true host: "${CLOUDRON_SMTP_SERVER:-localhost}" @@ -123,192 +152,304 @@ email: password: "${CLOUDRON_SMTP_PASSWORD:-""}" from: "Ente <${CLOUDRON_MAIL_FROM:-no-reply@${CLOUDRON_APP_DOMAIN}}>" EOF - chmod 600 "$MUSEUM_CONFIG" - echo "==> Created museum.yaml configuration" + chmod 600 "$MUSEUM_CONFIG" + log "INFO" "Created Museum configuration at ${MUSEUM_CONFIG}" else - echo "==> museum.yaml configuration already exists" + log "INFO" "Museum configuration already exists" fi -# Test PostgreSQL connectivity -echo "==> Testing PostgreSQL connectivity..." -if ! PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql -h "$CLOUDRON_POSTGRESQL_HOST" -p "$CLOUDRON_POSTGRESQL_PORT" -U "$CLOUDRON_POSTGRESQL_USERNAME" -d "$CLOUDRON_POSTGRESQL_DATABASE" -c "SELECT 1;" > /dev/null 2>&1; then - echo "==> ERROR: Failed to connect to PostgreSQL" - echo "==> Connection details:" - echo "==> Host: $CLOUDRON_POSTGRESQL_HOST" - echo "==> Port: $CLOUDRON_POSTGRESQL_PORT" - echo "==> User: $CLOUDRON_POSTGRESQL_USERNAME" - echo "==> Database: $CLOUDRON_POSTGRESQL_DATABASE" - exit 1 +# =============================================== +# Database check +# =============================================== +log "INFO" "Testing PostgreSQL connectivity" + +if PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql -h "$CLOUDRON_POSTGRESQL_HOST" \ + -p "$CLOUDRON_POSTGRESQL_PORT" -U "$CLOUDRON_POSTGRESQL_USERNAME" \ + -d "$CLOUDRON_POSTGRESQL_DATABASE" -c "SELECT 1;" > /dev/null 2>&1; then + log "INFO" "PostgreSQL connection successful" else - echo "==> PostgreSQL connection successful" + log "ERROR" "Failed to connect to PostgreSQL" + log "ERROR" "Connection details:" + log "ERROR" " Host: $CLOUDRON_POSTGRESQL_HOST" + log "ERROR" " Port: $CLOUDRON_POSTGRESQL_PORT" + log "ERROR" " User: $CLOUDRON_POSTGRESQL_USERNAME" + log "ERROR" " Database: $CLOUDRON_POSTGRESQL_DATABASE" + exit 1 fi -# Function to validate a binary file -validate_binary() { - local file="$1" - - # Check if file exists - if [ ! -f "$file" ]; then - echo "FALSE: File does not exist" - return 1 - fi - - # Check if file is executable - if [ ! -x "$file" ]; then - echo "FALSE: File is not executable" - return 1 - fi - - # Check if it's a text file (most likely an error message) - if file "$file" | grep -q "text"; then - echo "FALSE: File is a text file, not binary" - return 1 - fi - - # Check if it's actually an ELF or Mach-O binary - if ! file "$file" | grep -q -E "ELF|Mach-O"; then - echo "FALSE: File is not a valid executable binary" - return 1 - fi - - # Looks good - echo "TRUE: Valid binary" - return 0 -} - -# Build or download Museum server -echo "==> Setting up Museum server..." +# =============================================== +# Museum Server Binary Setup +# =============================================== MUSEUM_BIN="/app/data/ente/server/museum" +MUSEUM_LOG="/app/data/logs/museum.log" USE_PLACEHOLDER=false -# Remove existing binary if it's invalid +log "INFO" "Setting up Museum server binary" + +# Function to validate a binary +validate_binary() { + local bin_path="$1" + + # Basic file existence check + if [ ! -f "$bin_path" ]; then + return 1 + fi + + # Check if file is executable + if [ ! -x "$bin_path" ]; then + chmod +x "$bin_path" || return 1 + fi + + # Check if it's a text file (most likely an error message) + if file "$bin_path" | grep -q "text"; then + return 1 + fi + + # Check if it's a valid binary type + if ! file "$bin_path" | grep -q -E "ELF|Mach-O|PE32"; then + return 1 + fi + + return 0 +} + +# Check and remove invalid binary if [ -f "$MUSEUM_BIN" ]; then - if ! validate_binary "$MUSEUM_BIN" | grep -q "TRUE"; then - echo "==> Existing Museum binary is invalid, removing it" - rm -f "$MUSEUM_BIN" - else - echo "==> Existing Museum binary is valid" - fi + if ! validate_binary "$MUSEUM_BIN"; then + log "WARN" "Found invalid Museum binary, removing" + rm -f "$MUSEUM_BIN" + else + log "INFO" "Found valid Museum binary" + fi fi -# Try to build or download if not present or was invalid +# Build or download if needed if [ ! -f "$MUSEUM_BIN" ]; then - # Try to build Museum server from source - echo "==> Attempting to build Museum server from source..." + # Try building first if Go is available + if command -v go >/dev/null 2>&1; then + log "INFO" "Go is available, attempting to build Museum server" - # Check if Go is installed - if command -v go >/dev/null 2>&1; then - echo "==> Go is installed, building Museum server..." - - # Navigate to the server directory and build - cd "$ENTE_DIR/server" - export GOPATH="/app/data/go" - export PATH="$GOPATH/bin:$PATH" - - # Install dependencies if needed - if command -v apt-get >/dev/null 2>&1; then - apt-get update && apt-get install -y gcc libsodium-dev pkg-config - fi - - # Build the server - go build -o "$MUSEUM_BIN" ./cmd/museum - - if [ $? -eq 0 ] && validate_binary "$MUSEUM_BIN" | grep -q "TRUE"; then - echo "==> Successfully built Museum server" - else - echo "==> Failed to build Museum server, will try to download pre-built binary" - # Remove failed build - [ -f "$MUSEUM_BIN" ] && rm -f "$MUSEUM_BIN" - - # Determine architecture for downloading - ARCH=$(uname -m) - OS=$(uname -s | tr '[:upper:]' '[:lower:]') - if [ "$ARCH" == "x86_64" ]; then ARCH="amd64"; fi - if [ "$ARCH" == "aarch64" ] || [ "$ARCH" == "arm64" ]; then ARCH="arm64"; fi - - # Try to download from GitHub releases - echo "==> Downloading Museum server binary for ${OS}-${ARCH}..." - - # Try different download URLs and validate each binary - DOWNLOAD_URLS=( - "https://github.com/ente-io/ente/releases/latest/download/museum-${OS}-${ARCH}" - "https://github.com/ente-io/ente/releases/download/latest/museum-${OS}-${ARCH}" - "https://github.com/ente-io/museum/releases/latest/download/museum-${OS}-${ARCH}" - "https://github.com/ente-io/museum/releases/download/latest/museum-${OS}-${ARCH}" - ) - - for URL in "${DOWNLOAD_URLS[@]}"; do - echo "==> Trying download from: $URL" - if curl -L -f -o "$MUSEUM_BIN.tmp" "$URL"; then - chmod +x "$MUSEUM_BIN.tmp" - if validate_binary "$MUSEUM_BIN.tmp" | grep -q "TRUE"; then - mv "$MUSEUM_BIN.tmp" "$MUSEUM_BIN" - echo "==> Successfully downloaded and validated Museum server binary" - break - else - echo "==> Downloaded file is not a valid binary, trying next URL" - rm -f "$MUSEUM_BIN.tmp" - fi - else - echo "==> Download failed, trying next URL" - fi - done - - # Check if we have a valid binary now - if [ ! -f "$MUSEUM_BIN" ] || ! validate_binary "$MUSEUM_BIN" | grep -q "TRUE"; then - echo "==> All download attempts failed" - USE_PLACEHOLDER=true - fi - fi - else - echo "==> Go is not installed, downloading pre-built Museum server binary..." - - # Determine architecture for downloading - ARCH=$(uname -m) - OS=$(uname -s | tr '[:upper:]' '[:lower:]') - if [ "$ARCH" == "x86_64" ]; then ARCH="amd64"; fi - if [ "$ARCH" == "aarch64" ] || [ "$ARCH" == "arm64" ]; then ARCH="arm64"; fi - - # Try different download URLs and validate each binary - DOWNLOAD_URLS=( - "https://github.com/ente-io/ente/releases/latest/download/museum-${OS}-${ARCH}" - "https://github.com/ente-io/ente/releases/download/latest/museum-${OS}-${ARCH}" - "https://github.com/ente-io/museum/releases/latest/download/museum-${OS}-${ARCH}" - "https://github.com/ente-io/museum/releases/download/latest/museum-${OS}-${ARCH}" - ) - - for URL in "${DOWNLOAD_URLS[@]}"; do - echo "==> Trying download from: $URL" - if curl -L -f -o "$MUSEUM_BIN.tmp" "$URL"; then - chmod +x "$MUSEUM_BIN.tmp" - if validate_binary "$MUSEUM_BIN.tmp" | grep -q "TRUE"; then - mv "$MUSEUM_BIN.tmp" "$MUSEUM_BIN" - echo "==> Successfully downloaded and validated Museum server binary" - break - else - echo "==> Downloaded file is not a valid binary, trying next URL" - rm -f "$MUSEUM_BIN.tmp" - fi - else - echo "==> Download failed, trying next URL" - fi - done - - # Check if we have a valid binary now - if [ ! -f "$MUSEUM_BIN" ] || ! validate_binary "$MUSEUM_BIN" | grep -q "TRUE"; then - echo "==> All download attempts failed" - USE_PLACEHOLDER=true - fi + cd "$ENTE_REPO_DIR/server" + export GOPATH="/app/data/go" + export PATH="$GOPATH/bin:$PATH" + mkdir -p "$GOPATH/src" "$GOPATH/bin" "$GOPATH/pkg" + + # Install dependencies if needed + if command -v apt-get >/dev/null 2>&1; then + log "INFO" "Installing build dependencies" + apt-get update -y && apt-get install -y gcc libsodium-dev pkg-config fi + + log "INFO" "Building Museum server..." + if go build -o "$MUSEUM_BIN" ./cmd/museum; then + if validate_binary "$MUSEUM_BIN"; then + log "INFO" "Successfully built Museum server" + else + log "ERROR" "Build completed but resulted in an invalid binary" + rm -f "$MUSEUM_BIN" + fi + else + log "ERROR" "Failed to build Museum server" + fi + else + log "INFO" "Go is not available, skipping build attempt" + fi + + # If build failed or wasn't attempted, try downloading + if [ ! -f "$MUSEUM_BIN" ] || ! validate_binary "$MUSEUM_BIN"; then + log "INFO" "Attempting to download pre-built Museum server binary" + + # Determine architecture + ARCH=$(uname -m) + OS=$(uname -s | tr '[:upper:]' '[:lower:]') + + # Map architecture to standard names + if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; fi + if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then ARCH="arm64"; fi + + log "INFO" "Detected system: $OS-$ARCH" + + # Define possible download URLs + DOWNLOAD_URLS=( + "https://github.com/ente-io/ente/releases/latest/download/museum-${OS}-${ARCH}" + "https://github.com/ente-io/ente/releases/download/latest/museum-${OS}-${ARCH}" + "https://github.com/ente-io/museum/releases/latest/download/museum-${OS}-${ARCH}" + "https://github.com/ente-io/museum/releases/download/latest/museum-${OS}-${ARCH}" + ) + + # Try each URL + SUCCESS=false + for URL in "${DOWNLOAD_URLS[@]}"; do + log "INFO" "Attempting download from $URL" + if curl -L -f -s -o "$MUSEUM_BIN.tmp" "$URL"; then + chmod +x "$MUSEUM_BIN.tmp" + if validate_binary "$MUSEUM_BIN.tmp"; then + mv "$MUSEUM_BIN.tmp" "$MUSEUM_BIN" + log "INFO" "Successfully downloaded Museum server binary" + SUCCESS=true + break + else + log "WARN" "Downloaded file is not a valid binary" + rm -f "$MUSEUM_BIN.tmp" + fi + else + log "WARN" "Failed to download from $URL" + fi + done + + if [ "$SUCCESS" = false ]; then + log "ERROR" "All download attempts failed" + USE_PLACEHOLDER=true + fi + fi fi -# Function to create and start a placeholder Node.js server -create_placeholder_server() { - echo "==> Creating Node.js placeholder server..." - cat > /app/data/ente/server/server.js << 'EOF' +# Final check for Museum binary +if [ ! -f "$MUSEUM_BIN" ] || ! validate_binary "$MUSEUM_BIN"; then + log "WARN" "No valid Museum binary available" + USE_PLACEHOLDER=true +fi + +# =============================================== +# Web Application Setup +# =============================================== +log "INFO" "Setting up web applications" + +# Function to create a placeholder page +create_placeholder_page() { + local app_name="$1" + local app_dir="/app/data/web/$app_name" + + mkdir -p "$app_dir" + + cat > "$app_dir/index.html" << EOF + + +
+ + ++ This is the Ente $app_name application running on your Cloudron. To complete the setup: +
+ +/app/data/s3.env
+ GitHub Repository · + Documentation +
+This is the Ente Photos application running on Cloudron. To complete the setup, you need to:
-/app/data/s3.env
For more information, visit Ente Self-Hosting Documentation
-