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 + + + + + + Ente $app_name + + + +
+ +

Ente $app_name

+ +
+ Status: The Ente server is being configured. +
+ +

+ This is the Ente $app_name application running on your Cloudron. To complete the setup: +

+ +
+
    +
  1. Configure your S3 storage in /app/data/s3.env
  2. +
  3. Ensure the Museum server is properly running
  4. +
  5. You might need to restart the app after configuration changes
  6. +
+
+ +

+ GitHub Repository · + Documentation +

+
+ + +EOF + + # Create runtime config + cat > "$app_dir/runtime-config.js" << EOF +window.RUNTIME_CONFIG = { + API_URL: "/api", + PUBLIC_ALBUMS_URL: "/public", + DEBUG: true +}; +console.log("Loaded Ente runtime config:", window.RUNTIME_CONFIG); +EOF + + log "INFO" "Created placeholder for $app_name app" +} + +# Create placeholder pages for each app if they don't exist +for APP in photos accounts auth cast; do + if [ ! -f "/app/data/web/$APP/index.html" ]; then + create_placeholder_page "$APP" + fi +done + +# =============================================== +# Node.js Placeholder Server +# =============================================== +create_nodejs_placeholder() { + log "INFO" "Creating Node.js placeholder server" + + # Create server script + cat > "/app/data/ente/server/placeholder.js" << 'EOF' const http = require('http'); const fs = require('fs'); -const { promisify } = require('util'); const { execSync } = require('child_process'); const path = require('path'); @@ -316,11 +457,6 @@ const PORT = 3080; const LOG_FILE = '/app/data/logs/museum.log'; const DB_SCHEMA_FILE = '/app/data/ente/server/schema.sql'; -// Ensure log directory exists -if (!fs.existsSync('/app/data/logs')) { - fs.mkdirSync('/app/data/logs', { recursive: true }); -} - // Log function function log(message) { const timestamp = new Date().toISOString(); @@ -333,7 +469,7 @@ function log(message) { } } -log('Starting Node.js placeholder server...'); +log('Starting Ente placeholder server...'); // Try to initialize the database schema function initializeDatabase() { @@ -346,6 +482,7 @@ function initializeDatabase() { CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); @@ -355,6 +492,8 @@ CREATE TABLE IF NOT EXISTS files ( user_id INTEGER REFERENCES users(id), filename VARCHAR(255) NOT NULL, path VARCHAR(255) NOT NULL, + mime_type VARCHAR(100), + size BIGINT, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); @@ -362,7 +501,7 @@ CREATE TABLE IF NOT EXISTS files ( fs.writeFileSync(DB_SCHEMA_FILE, basicSchema); } - // Try to initialize the database + // Initialize database const dbUser = process.env.CLOUDRON_POSTGRESQL_USERNAME; const dbPassword = process.env.CLOUDRON_POSTGRESQL_PASSWORD; const dbHost = process.env.CLOUDRON_POSTGRESQL_HOST; @@ -395,7 +534,7 @@ const apiHandlers = { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'OK', - server: 'Museum Placeholder', + server: 'Ente Placeholder', version: '1.0.0' })); log('Health check request - responded with status OK'); @@ -426,7 +565,7 @@ const apiHandlers = { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true, - token: 'placeholder-jwt-token', + token: 'placeholder-jwt-token-' + Date.now(), user: { id: 1, email: 'placeholder@example.com', @@ -455,7 +594,7 @@ const apiHandlers = { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true, - token: 'placeholder-jwt-token', + token: 'placeholder-jwt-token-' + Date.now(), user: { id: 1, email: 'placeholder@example.com', @@ -520,23 +659,21 @@ const server = http.createServer((req, res) => { return; } - // Check if there's a specific handler for this path - const handler = apiHandlers[req.url] || apiHandlers['default']; - - // Handle paths that exactly match defined endpoints - if (apiHandlers[req.url]) { - handler(req, res); + // Handle health check endpoint + if (req.url === '/health' || req.url === '/api/health') { + apiHandlers['/health'](req, res); return; } - // Handle health check endpoint - if (req.url === '/api/health') { - apiHandlers['/health'](req, res); + // Handle paths that exactly match defined endpoints + if (apiHandlers[req.url]) { + apiHandlers[req.url](req, res); return; } // Route based on URL pattern if (req.url.startsWith('/api/')) { + const handler = apiHandlers['default']; handler(req, res); return; } @@ -554,7 +691,7 @@ const server = http.createServer((req, res) => { // Start server try { server.listen(PORT, '0.0.0.0', () => { - log(`Museum placeholder server running on port ${PORT}`); + log(`Ente placeholder server running on port ${PORT}`); log(`Server is listening at http://0.0.0.0:${PORT}`); }); } catch (err) { @@ -574,181 +711,101 @@ server.on('error', (error) => { } }); -// Log startup -log('Museum placeholder server initialization complete'); +log('Ente placeholder server initialization complete'); EOF - echo "==> Created Node.js placeholder server" - # Start the Node.js placeholder server - echo "==> Starting Node.js placeholder server..." - cd /app/data/ente/server - node server.js > /app/data/logs/museum.log 2>&1 & - NODE_PID=$! - echo "==> Started Node.js server with PID: $NODE_PID" + # Start the Node.js placeholder server + log "INFO" "Starting Node.js placeholder server" + cd /app/data/ente/server + node placeholder.js > "$MUSEUM_LOG" 2>&1 & + PLACEHOLDER_PID=$! + log "INFO" "Started Node.js server with PID: $PLACEHOLDER_PID" - # Wait for server to start - MAX_ATTEMPTS=30 - ATTEMPT=0 - while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do - if curl -s http://localhost:3080/health > /dev/null; then - echo "==> Node.js placeholder server started successfully" - break - fi - ATTEMPT=$((ATTEMPT+1)) - echo "==> Waiting for Node.js server to start (attempt $ATTEMPT/$MAX_ATTEMPTS)..." - sleep 1 - done - - if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then - echo "==> ERROR: Node.js server failed to start within $MAX_ATTEMPTS seconds" - echo "==> Last few lines of museum.log:" - tail -n 20 /app/data/logs/museum.log || echo "==> No log file found" - return 1 + # Wait for the server to start + MAX_ATTEMPTS=30 + ATTEMPT=0 + SUCCESS=false + + while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do + if curl -s http://localhost:3080/health > /dev/null 2>&1; then + log "INFO" "Node.js placeholder server started successfully" + SUCCESS=true + break fi - - return 0 + ATTEMPT=$((ATTEMPT+1)) + log "INFO" "Waiting for server to start (attempt $ATTEMPT/$MAX_ATTEMPTS)" + sleep 1 + done + + if [ "$SUCCESS" = false ]; then + log "ERROR" "Node.js placeholder server failed to start" + log "ERROR" "Last 20 lines of log:" + tail -n 20 "$MUSEUM_LOG" | while read -r line; do + log "ERROR" " $line" + done + return 1 + fi + + return 0 } -# Create placeholder HTML and static files -echo "==> Setting up Ente web app..." -WEB_DIR="/app/data/ente/web" - -if [ ! -d "${WEB_DIR}/photos" ] || [ ! -f "${WEB_DIR}/photos/index.html" ]; then - echo "==> Creating placeholder HTML files..." - mkdir -p ${WEB_DIR}/photos - cat > ${WEB_DIR}/photos/index.html << 'EOF' - - - - Ente Photos - - - - - -
-

Ente Photos

-
- Status: Ente is being set up. Check logs for details. -
-

This is the Ente Photos application running on Cloudron. To complete the setup, you need to:

-
-
    -
  1. Configure your S3 storage in /app/data/s3.env
  2. -
  3. Ensure the Museum server is running
  4. -
  5. Visit the photos app to start using your self-hosted Ente instance
  6. -
-
-

For more information, visit Ente Self-Hosting Documentation

-
- - -EOF - # Create similar placeholders for other apps - mkdir -p ${WEB_DIR}/accounts - cp ${WEB_DIR}/photos/index.html ${WEB_DIR}/accounts/index.html - sed -i 's/Photos/Accounts/g' ${WEB_DIR}/accounts/index.html - - mkdir -p ${WEB_DIR}/auth - cp ${WEB_DIR}/photos/index.html ${WEB_DIR}/auth/index.html - sed -i 's/Photos/Auth/g' ${WEB_DIR}/auth/index.html - - mkdir -p ${WEB_DIR}/cast - cp ${WEB_DIR}/photos/index.html ${WEB_DIR}/cast/index.html - sed -i 's/Photos/Cast/g' ${WEB_DIR}/cast/index.html - - echo "==> Created placeholder HTML files" -else - echo "==> Ente web app already set up" -fi - -# Copy web files to /app/data/web -echo "==> Copying web files to /app/data/web..." -if [ -d "${WEB_DIR}/photos" ]; then - mkdir -p /app/data/web/photos - cp -rf ${WEB_DIR}/photos/* /app/data/web/photos/ || echo "==> Warning: Failed to copy photos files" -fi -if [ -d "${WEB_DIR}/accounts" ]; then - mkdir -p /app/data/web/accounts - cp -rf ${WEB_DIR}/accounts/* /app/data/web/accounts/ || echo "==> Warning: Failed to copy accounts files" -fi -if [ -d "${WEB_DIR}/auth" ]; then - mkdir -p /app/data/web/auth - cp -rf ${WEB_DIR}/auth/* /app/data/web/auth/ || echo "==> Warning: Failed to copy auth files" -fi -if [ -d "${WEB_DIR}/cast" ]; then - mkdir -p /app/data/web/cast - cp -rf ${WEB_DIR}/cast/* /app/data/web/cast/ || echo "==> Warning: Failed to copy cast files" -fi - -# Create runtime config for web app -echo "==> Creating runtime config for web app..." -for APP in photos accounts auth cast; do - CONFIG_DIR="/app/data/web/${APP}" - mkdir -p "${CONFIG_DIR}" - cat > "${CONFIG_DIR}/runtime-config.js" << EOF -window.RUNTIME_CONFIG = { - API_URL: "/api", - PUBLIC_ALBUMS_URL: "/public", - DEBUG: true -}; -console.log("Loaded runtime config:", window.RUNTIME_CONFIG); -EOF - echo "==> Created runtime config for ${APP}" -done - +# =============================================== # Start the appropriate server -if [ "$USE_PLACEHOLDER" = true ] || [ ! -f "$MUSEUM_BIN" ] || ! validate_binary "$MUSEUM_BIN" | grep -q "TRUE"; then - echo "==> Using Node.js placeholder server instead of Museum server" - create_placeholder_server +# =============================================== +log "INFO" "Starting server" + +if [ "$USE_PLACEHOLDER" = true ]; then + log "INFO" "Using Node.js placeholder server" + create_nodejs_placeholder else - echo "==> Starting Museum server..." - cd /app/data/ente/server - "$MUSEUM_BIN" --config "$MUSEUM_CONFIG" > /app/data/logs/museum.log 2>&1 & - MUSEUM_PID=$! - echo "==> Started Museum server with PID: $MUSEUM_PID" - - # Wait for server to start - MAX_ATTEMPTS=30 - ATTEMPT=0 - while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do - if curl -s http://localhost:3080/health > /dev/null; then - echo "==> Museum server started successfully" - break - fi - ATTEMPT=$((ATTEMPT+1)) - echo "==> Waiting for Museum server to start (attempt $ATTEMPT/$MAX_ATTEMPTS)..." - sleep 1 + log "INFO" "Starting actual Museum server" + cd /app/data/ente/server + "$MUSEUM_BIN" --config "$MUSEUM_CONFIG" > "$MUSEUM_LOG" 2>&1 & + MUSEUM_PID=$! + log "INFO" "Started Museum server with PID: $MUSEUM_PID" + + # Wait for the server to start + MAX_ATTEMPTS=30 + ATTEMPT=0 + SUCCESS=false + + while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do + if curl -s http://localhost:3080/health > /dev/null 2>&1; then + log "INFO" "Museum server started successfully" + SUCCESS=true + break + fi + ATTEMPT=$((ATTEMPT+1)) + log "INFO" "Waiting for Museum server to start (attempt $ATTEMPT/$MAX_ATTEMPTS)" + sleep 1 + done + + if [ "$SUCCESS" = false ]; then + log "ERROR" "Museum server failed to start within $MAX_ATTEMPTS seconds" + log "ERROR" "Last 20 lines of museum.log:" + tail -n 20 "$MUSEUM_LOG" | while read -r line; do + log "ERROR" " $line" done - if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then - echo "==> ERROR: Museum server failed to start within $MAX_ATTEMPTS seconds" - echo "==> Last few lines of museum.log:" - tail -n 20 /app/data/logs/museum.log || echo "==> No log file found" - echo "==> Falling back to Node.js placeholder server" - create_placeholder_server - fi + log "WARN" "Falling back to Node.js placeholder server" + create_nodejs_placeholder + fi fi -# Set up Caddy web server -echo "==> Setting up Caddy web server..." -cat > /app/data/Caddyfile << EOF +# =============================================== +# Setup Caddy web server +# =============================================== +log "INFO" "Setting up Caddy web server" + +# Create Caddy configuration +CADDY_CONFIG="/app/data/Caddyfile" +cat > "$CADDY_CONFIG" << EOF :3080 { log { output file /app/data/logs/caddy.log } - # API endpoints go to museum server + # API endpoints handle /api/* { reverse_proxy localhost:3080 } @@ -794,24 +851,64 @@ cat > /app/data/Caddyfile << EOF } } EOF -echo "==> Created Caddy configuration" -# Start Caddy web server -echo "==> Starting Caddy web server..." -caddy run --config /app/data/Caddyfile > /app/data/logs/caddy.log 2>&1 & +log "INFO" "Starting Caddy web server" +caddy run --config "$CADDY_CONFIG" > /app/data/logs/caddy.log 2>&1 & CADDY_PID=$! -echo "==> Caddy web server started with PID: $CADDY_PID" +log "INFO" "Caddy web server started with PID: $CADDY_PID" -# Remove the flag file to indicate that we've started successfully +# =============================================== +# Finalization and monitoring +# =============================================== +log "INFO" "Setup complete" + +# Create startup instructions +cat > /app/data/SETUP-INSTRUCTIONS.md << EOF +# Ente Cloudron App - Setup Instructions + +## Configuration + +1. **S3 Storage**: Edit the configuration file at \`/app/data/s3.env\` with your S3-compatible storage credentials. + +2. **Museum Server**: The server configuration is at \`/app/data/ente/server/museum.yaml\` if you need to customize settings. + +## Troubleshooting + +- **Logs**: Check the logs at \`/app/data/logs/\` for any issues. +- **Restart**: If you change configuration, restart the app to apply changes. + +## Web Applications + +The following web applications are available: + +- Photos: https://${CLOUDRON_APP_FQDN}/photos/ +- Accounts: https://${CLOUDRON_APP_FQDN}/accounts/ +- Auth: https://${CLOUDRON_APP_FQDN}/auth/ +- Cast: https://${CLOUDRON_APP_FQDN}/cast/ + +## Support + +For more information, visit the [Ente GitHub repository](https://github.com/ente-io/ente). +EOF + +# Remove startup flag rm -f /app/data/startup_in_progress -echo "==> Setup complete, everything is running." -# Verify services are running -echo "==> Verifying services..." -ps aux | grep -E "museum|node.*server.js" | grep -v grep && echo "==> Server is running" || echo "==> WARNING: No server running!" -ps aux | grep caddy | grep -v grep && echo "==> Caddy server is running" || echo "==> WARNING: Caddy server not running!" +# Verify running services +log "INFO" "Verifying running services" +if ps aux | grep -E "museum|placeholder" | grep -v grep > /dev/null; then + log "INFO" "Server is running" +else + log "ERROR" "No server is running!" +fi + +if ps aux | grep caddy | grep -v grep > /dev/null; then + log "INFO" "Caddy server is running" +else + log "ERROR" "Caddy server is not running!" +fi + +log "INFO" "Ente Cloudron app startup complete" -# Keep script running -echo "==> Entering wait loop to keep container alive..." # Keep the script running to prevent container exit -tail -f /app/data/logs/museum.log \ No newline at end of file +exec tail -f "$MUSEUM_LOG" \ No newline at end of file