ente-cloudron/start.sh

1026 lines
33 KiB
Bash

#!/bin/bash
set -e
# Declare that we are using 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}"
echo "==> Environment: CLOUDRON_APP_FQDN=${CLOUDRON_APP_FQDN}"
echo "==> Environment: Internal IP=$(hostname -i || echo 'unknown')"
# Install required utilities
echo "==> Ensuring required utilities are installed"
apt-get update && apt-get install -y file unzip wget curl
echo "==> Utilities installed"
# Create necessary data directories
mkdir -p /app/data/logs
mkdir -p /app/data/ente/web
mkdir -p /app/data/ente/server
mkdir -p /app/data/web/photos/static
mkdir -p /app/data/web/photos/_next/static/runtime
mkdir -p /app/data/web/accounts/static
mkdir -p /app/data/web/auth/static
mkdir -p /app/data/web/cast/static
echo "==> Created all necessary directories"
# Debugging information
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
# Use the specified server directory or default to the data dir
SERVER_DIR="/app/data/ente/server"
echo "==> Using server directory: $SERVER_DIR"
# Download Ente server if not already present
if [ ! -d "$SERVER_DIR/museum" ] || [ ! -f "$SERVER_DIR/museum/museum" ]; then
echo "==> Downloading Ente Museum server..."
mkdir -p "$SERVER_DIR"
cd "$SERVER_DIR"
# Clone the repository if it doesn't exist
if [ ! -d "$SERVER_DIR/museum" ]; then
# Use HTTPS instead of Git protocol to avoid authentication issues
echo "==> Downloading from GitHub archive..."
curl -L -o museum.zip https://github.com/ente-io/museum/archive/refs/heads/main.zip || curl -L -o museum.zip https://api.github.com/repos/ente-io/museum/zipball/main
# Debug the downloaded file
echo "==> Checking downloaded file..."
file museum.zip
ls -la museum.zip
# Try alternate download method if first one fails
if [ ! -s museum.zip ] || ! unzip -q museum.zip; then
echo "==> Direct download failed, trying with wget..."
apt-get update && apt-get install -y wget
wget -O museum.zip https://github.com/ente-io/museum/archive/main.zip
if [ ! -s museum.zip ] || ! unzip -q museum.zip; then
echo "==> All download methods failed, creating directories manually"
mkdir -p museum/config
cd museum
else
# Handle the extracted directory name which might be museum-main or something like ente-io-museum-<commit>
extracted_dir=$(find . -type d -name "museum-*" -o -name "ente-io-museum-*" | head -n 1)
if [ -n "$extracted_dir" ]; then
mv "$extracted_dir"/* museum/
rm -rf "$extracted_dir"
cd museum
else
mkdir -p museum/config
cd museum
fi
fi
else
# Handle the extracted directory name which might be museum-main or something like ente-io-museum-<commit>
extracted_dir=$(find . -type d -name "museum-*" -o -name "ente-io-museum-*" | head -n 1)
if [ -n "$extracted_dir" ]; then
mkdir -p museum
mv "$extracted_dir"/* museum/
rm -rf "$extracted_dir"
cd museum
else
mkdir -p museum/config
cd museum
fi
fi
else
cd museum
# Use HTTPS instead of Git pull
echo "==> Updating existing repository..."
curl -L -o main.zip https://github.com/ente-io/museum/archive/refs/heads/main.zip || curl -L -o main.zip https://api.github.com/repos/ente-io/museum/zipball/main
if [ -s main.zip ] && unzip -q main.zip; then
extracted_dir=$(find . -type d -name "museum-*" -o -name "ente-io-museum-*" | head -n 1)
if [ -n "$extracted_dir" ]; then
cp -R "$extracted_dir"/* ./
rm -rf "$extracted_dir" main.zip
fi
else
echo "==> Failed to update repository, continuing with existing files"
fi
fi
# Build the museum server
echo "==> Building Ente Museum server..."
# Check if Go is installed
if command -v go &> /dev/null; then
go build -o museum || echo "==> Go build failed, will try pre-built binary"
else
echo "==> Go not found, will try to download pre-built binary"
fi
if [ ! -f "$SERVER_DIR/museum/museum" ]; then
echo "==> ERROR: Failed to build museum server"
echo "==> Will attempt to download pre-built binary"
# Try to download pre-built binary
ARCH=$(uname -m)
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
if [ "$ARCH" = "x86_64" ]; then
ARCH="amd64"
elif [ "$ARCH" = "aarch64" ]; then
ARCH="arm64"
fi
echo "==> Detected architecture: $OS-$ARCH"
# Try different release URLs
for RELEASE_URL in \
"https://github.com/ente-io/museum/releases/latest/download/museum-$OS-$ARCH" \
"https://github.com/ente-io/museum/releases/download/latest/museum-$OS-$ARCH" \
"https://github.com/ente-io/museum/releases/download/v1.0.0/museum-$OS-$ARCH" \
"https://github.com/ente-io/museum/releases/download/v1.0/museum-$OS-$ARCH"
do
echo "==> Trying to download from: $RELEASE_URL"
if curl -L -o "$SERVER_DIR/museum/museum" "$RELEASE_URL" && [ -s "$SERVER_DIR/museum/museum" ]; then
chmod +x "$SERVER_DIR/museum/museum"
echo "==> Successfully downloaded museum binary from $RELEASE_URL"
break
else
echo "==> Download failed from $RELEASE_URL"
fi
done
if [ ! -f "$SERVER_DIR/museum/museum" ] || [ ! -s "$SERVER_DIR/museum/museum" ]; then
echo "==> ERROR: Failed to download pre-built binary"
echo "==> Will create a simple HTTP server as a placeholder"
# Create a simple HTTP server in Go
mkdir -p "$SERVER_DIR/museum/config"
cat > "$SERVER_DIR/museum/museum.go" << 'EOF'
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
)
func main() {
port := "8080"
if len(os.Args) > 1 {
port = os.Args[1]
}
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "ok",
"message": "Ente Museum placeholder server",
})
})
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")
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "ok",
"message": "Ente Museum placeholder server",
"path": r.URL.Path,
"method": r.Method,
})
})
fmt.Printf("Starting server on port %s...\n", port)
log.Fatal(http.ListenAndServe("0.0.0.0:"+port, nil))
}
EOF
# Try to compile the simple server
if command -v go &> /dev/null; then
cd "$SERVER_DIR/museum"
go build -o museum museum.go
if [ ! -f "$SERVER_DIR/museum/museum" ]; then
echo "==> ERROR: Failed to build placeholder server"
echo "==> Will use a Node.js server instead"
cat > "$SERVER_DIR/museum/server.js" << 'EOF'
const http = require('http');
const port = 8080;
const server = http.createServer((req, res) => {
console.log(`Request: ${req.method} ${req.url}`);
res.setHeader('Content-Type', 'application/json');
if (req.url === '/health') {
res.end(JSON.stringify({
status: 'ok',
message: 'Ente Museum placeholder server (Node.js)'
}));
return;
}
res.end(JSON.stringify({
status: 'ok',
message: 'Ente Museum placeholder server (Node.js)',
path: req.url,
method: req.method
}));
});
server.listen(port, '0.0.0.0', () => {
console.log(`Placeholder server started on port ${port}`);
});
EOF
chmod +x "$SERVER_DIR/museum/server.js"
echo "==> Created Node.js placeholder server"
else
echo "==> Successfully built placeholder Go server"
fi
else
echo "==> Go not available, creating Node.js server"
cat > "$SERVER_DIR/museum/server.js" << 'EOF'
const http = require('http');
const port = 8080;
const server = http.createServer((req, res) => {
console.log(`Request: ${req.method} ${req.url}`);
res.setHeader('Content-Type', 'application/json');
if (req.url === '/health') {
res.end(JSON.stringify({
status: 'ok',
message: 'Ente Museum placeholder server (Node.js)'
}));
return;
}
res.end(JSON.stringify({
status: 'ok',
message: 'Ente Museum placeholder server (Node.js)',
path: req.url,
method: req.method
}));
});
server.listen(port, '0.0.0.0', () => {
console.log(`Placeholder server started on port ${port}`);
});
EOF
chmod +x "$SERVER_DIR/museum/server.js"
echo "==> Created Node.js placeholder server"
fi
fi
fi
else
echo "==> Ente Museum server already downloaded"
fi
# Configure S3 storage for Ente
if [ -f "/app/data/s3_config.env" ]; then
echo "==> Using existing S3 configuration from s3_config.env"
source /app/data/s3_config.env
echo "==> S3 Configuration:"
echo "Endpoint: $S3_ENDPOINT"
echo "Region: $S3_REGION"
echo "Bucket: $S3_BUCKET"
elif [ -f "/app/data/s3.env" ]; then
echo "==> Using existing S3 configuration from s3.env"
source /app/data/s3.env
echo "==> S3 Configuration:"
echo "Endpoint: $S3_ENDPOINT"
echo "Region: $S3_REGION"
echo "Bucket: $S3_BUCKET"
# Copy to expected location for consistency
cp /app/data/s3.env /app/data/s3_config.env
else
# Default to environment variables if they exist
if [ -n "$CLOUDRON_S3_ENDPOINT" ] && [ -n "$CLOUDRON_S3_KEY" ] && [ -n "$CLOUDRON_S3_SECRET" ]; then
echo "==> Using Cloudron S3 configuration"
S3_ENDPOINT="$CLOUDRON_S3_ENDPOINT"
S3_REGION="us-east-1" # Default region, can be overridden
S3_BUCKET="${CLOUDRON_APP_DOMAIN//./-}-ente"
S3_ACCESS_KEY="$CLOUDRON_S3_KEY"
S3_SECRET_KEY="$CLOUDRON_S3_SECRET"
# Save for future runs
mkdir -p /app/data
cat > /app/data/s3_config.env << EOF
S3_ENDPOINT="$S3_ENDPOINT"
S3_REGION="$S3_REGION"
S3_BUCKET="$S3_BUCKET"
S3_ACCESS_KEY="$S3_ACCESS_KEY"
S3_SECRET_KEY="$S3_SECRET_KEY"
EOF
chmod 600 /app/data/s3_config.env
echo "==> Created S3 configuration file"
echo "==> S3 Configuration:"
echo "Endpoint: $S3_ENDPOINT"
echo "Region: $S3_REGION"
echo "Bucket: $S3_BUCKET"
else
echo "==> WARNING: S3 configuration is not found"
echo "==> Creating a template S3 configuration for you to fill in"
mkdir -p /app/data
cat > /app/data/s3.env.template << EOF
# Rename this file to s3.env and set the correct values
S3_ENDPOINT="your-s3-endpoint"
S3_REGION="your-s3-region"
S3_BUCKET="your-s3-bucket"
S3_ACCESS_KEY="your-s3-access-key"
S3_SECRET_KEY="your-s3-secret-key"
EOF
echo "==> Created S3 configuration template file at /app/data/s3.env.template"
echo "==> Please fill in the values and rename it to s3.env"
# If we found s3.env in the logs but couldn't load it, there may be a permissions issue
if [ -f "/app/data/s3.env" ]; then
echo "==> NOTICE: s3.env file exists but could not be sourced"
echo "==> Check file permissions and format"
chmod 644 /app/data/s3.env
fi
fi
fi
# Configure museum.yaml
mkdir -p "${SERVER_DIR}/museum/config"
if [ -f "/app/data/museum.yaml" ]; then
echo "==> Using existing museum.yaml configuration"
cp /app/data/museum.yaml "${SERVER_DIR}/museum/config/museum.yaml"
else
echo "==> Creating museum.yaml configuration"
cat > /app/data/museum.yaml << EOF
database:
driver: postgres
source: postgres://${CLOUDRON_POSTGRESQL_USERNAME}:${CLOUDRON_POSTGRESQL_PASSWORD}@${CLOUDRON_POSTGRESQL_HOST}:${CLOUDRON_POSTGRESQL_PORT}/${CLOUDRON_POSTGRESQL_DATABASE}
auto-migrate: true
server:
port: 8080
host: 0.0.0.0
cors:
origins:
- https://${CLOUDRON_APP_DOMAIN}
methods:
- GET
- POST
- PUT
- OPTIONS
headers:
- Content-Type
- Authorization
endpoints:
photos: https://${CLOUDRON_APP_DOMAIN}/photos
accounts: https://${CLOUDRON_APP_DOMAIN}/accounts
auth: https://${CLOUDRON_APP_DOMAIN}/auth
cast: https://${CLOUDRON_APP_DOMAIN}/cast
"public-albums": https://${CLOUDRON_APP_DOMAIN}/public
s3:
endpoint: ${S3_ENDPOINT}
region: ${S3_REGION}
bucket: ${S3_BUCKET}
access-key-id: ${S3_ACCESS_KEY}
secret-access-key: ${S3_SECRET_KEY}
cache-control: public, max-age=31536000
acme:
enabled: false # Cloudron handles SSL
smtp:
enabled: true
host: ${CLOUDRON_SMTP_SERVER:-localhost}
port: ${CLOUDRON_SMTP_PORT:-25}
username: ${CLOUDRON_SMTP_USERNAME:-""}
password: ${CLOUDRON_SMTP_PASSWORD:-""}
from: "Ente <${CLOUDRON_MAIL_FROM:-no-reply@${CLOUDRON_APP_DOMAIN}}>"
reply-to: "Ente <${CLOUDRON_MAIL_FROM:-no-reply@${CLOUDRON_APP_DOMAIN}}>"
logging:
level: info
file: /app/data/logs/museum.log
EOF
echo "==> Created museum.yaml configuration"
cp /app/data/museum.yaml "${SERVER_DIR}/museum/config/museum.yaml"
fi
# Debug PostgreSQL connection information
echo "==> PostgreSQL information:"
echo "CLOUDRON_POSTGRESQL_HOST: $CLOUDRON_POSTGRESQL_HOST"
echo "CLOUDRON_POSTGRESQL_PORT: $CLOUDRON_POSTGRESQL_PORT"
echo "CLOUDRON_POSTGRESQL_DATABASE: $CLOUDRON_POSTGRESQL_DATABASE"
echo "CLOUDRON_POSTGRESQL_USERNAME: $CLOUDRON_POSTGRESQL_USERNAME"
# Test PostgreSQL connectivity
echo "==> Testing PostgreSQL connectivity"
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
if [ $? -eq 0 ]; then
echo "==> PostgreSQL is ready"
else
echo "==> ERROR: Could not connect to PostgreSQL"
echo "==> Please check your PostgreSQL configuration"
echo "==> Continuing anyway, but errors may occur later"
fi
# Download and install Ente web app if not already present
if [ ! -d "/app/data/ente/web" ] || [ ! -f "/app/data/ente/web/photos/index.html" ]; then
echo "==> Downloading Ente web app..."
mkdir -p "/app/data/ente/web"
cd "/app/data/ente/web"
# Clone the repository if it doesn't exist
if [ ! -d "/app/data/ente/web/photos" ]; then
# Use HTTPS download instead of git clone
echo "==> Downloading photos web app from GitHub archive..."
curl -L -o photos.zip https://github.com/ente-io/photos/archive/refs/heads/main.zip || curl -L -o photos.zip https://api.github.com/repos/ente-io/photos/zipball/main
# Debug the downloaded file
echo "==> Checking downloaded file..."
file photos.zip
ls -la photos.zip
# Try alternate download method if first one fails
if [ ! -s photos.zip ] || ! unzip -q photos.zip; then
echo "==> Direct download failed, trying with wget..."
if ! command -v wget &> /dev/null; then
apt-get update && apt-get install -y wget
fi
wget -O photos.zip https://github.com/ente-io/photos/archive/main.zip
if [ ! -s photos.zip ] || ! unzip -q photos.zip; then
echo "==> All download methods failed, creating directories manually"
mkdir -p photos
cd photos
else
# Handle the extracted directory name which might be photos-main or something like ente-io-photos-<commit>
extracted_dir=$(find . -type d -name "photos-*" -o -name "ente-io-photos-*" | head -n 1)
if [ -n "$extracted_dir" ]; then
mkdir -p photos
mv "$extracted_dir"/* photos/
rm -rf "$extracted_dir"
cd photos
else
mkdir -p photos
cd photos
fi
fi
else
# Handle the extracted directory name which might be photos-main or something like ente-io-photos-<commit>
extracted_dir=$(find . -type d -name "photos-*" -o -name "ente-io-photos-*" | head -n 1)
if [ -n "$extracted_dir" ]; then
mkdir -p photos
mv "$extracted_dir"/* photos/
rm -rf "$extracted_dir"
cd photos
else
mkdir -p photos
cd photos
fi
fi
else
cd photos
# Use HTTPS instead of Git pull
echo "==> Updating existing web app repository..."
curl -L -o main.zip https://github.com/ente-io/photos/archive/refs/heads/main.zip || curl -L -o main.zip https://api.github.com/repos/ente-io/photos/zipball/main
if [ -s main.zip ] && unzip -q main.zip; then
extracted_dir=$(find . -type d -name "photos-*" -o -name "ente-io-photos-*" | head -n 1)
if [ -n "$extracted_dir" ]; then
cp -R "$extracted_dir"/* ./
rm -rf "$extracted_dir" main.zip
fi
else
echo "==> Failed to update web app repository, continuing with existing files"
fi
fi
# Try to build the web app
echo "==> Building Ente web app (this may take a while)..."
if command -v npm &> /dev/null; then
npm install
npm run build
else
echo "==> WARNING: npm not found, cannot build web app"
echo "==> Will continue with placeholder pages"
fi
else
echo "==> Ente web app already downloaded"
fi
# Start the real Museum server
if [ -f "${SERVER_DIR}/museum/museum" ] && [ -s "${SERVER_DIR}/museum/museum" ]; then
echo "==> Found Museum server at ${SERVER_DIR}/museum"
# Make sure the museum binary is executable
chmod +x "${SERVER_DIR}/museum/museum"
# Start the real Museum server
cd "${SERVER_DIR}/museum"
./museum --config "${SERVER_DIR}/museum/config/museum.yaml" > /app/data/logs/museum.log 2>&1 &
MUSEUM_PID=$!
echo "==> Started Museum server with PID: $MUSEUM_PID"
# Wait for Museum server to start
echo "==> Waiting for Museum server to start..."
for i in {1..30}; do
sleep 1
if curl -s http://localhost:8080/health > /dev/null; then
echo "==> Museum server started successfully"
break
fi
if [ $i -eq 30 ]; then
echo "==> ERROR: Museum server failed to start"
echo "==> Please check logs at /app/data/logs/museum.log"
tail -n 50 /app/data/logs/museum.log
echo "==> Falling back to placeholder server"
# Kill the failed process if it's still running
kill $MUSEUM_PID 2>/dev/null || true
MUSEUM_PID=""
fi
done
elif [ -f "${SERVER_DIR}/museum/server.js" ]; then
echo "==> Found Node.js placeholder server at ${SERVER_DIR}/museum/server.js"
cd "${SERVER_DIR}/museum"
node server.js > /app/data/logs/museum.log 2>&1 &
MUSEUM_PID=$!
echo "==> Started Node.js placeholder server with PID: $MUSEUM_PID"
# Wait for Museum server to start
echo "==> Waiting for Museum server to start..."
for i in {1..10}; do
sleep 1
if curl -s http://localhost:8080/health > /dev/null; then
echo "==> Node.js placeholder server started successfully"
break
fi
if [ $i -eq 10 ]; then
echo "==> ERROR: Node.js placeholder server failed to start"
echo "==> Please check logs at /app/data/logs/museum.log"
tail -n 50 /app/data/logs/museum.log
echo "==> Will continue but API functionality will be limited"
MUSEUM_PID=""
fi
done
else
echo "==> ERROR: No server executable found at ${SERVER_DIR}/museum"
echo "==> Creating a minimal Node.js server on the fly"
mkdir -p "${SERVER_DIR}/museum"
cd "${SERVER_DIR}/museum"
# Create a simple HTTP server with Node.js
cat > server.js << 'EOF'
const http = require('http');
const port = 8080;
const server = http.createServer((req, res) => {
console.log(`Request: ${req.method} ${req.url}`);
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.statusCode = 200;
res.end();
return;
}
if (req.url === '/health') {
res.end(JSON.stringify({
status: 'ok',
message: 'Ente Museum minimal placeholder server'
}));
return;
}
res.end(JSON.stringify({
status: 'ok',
message: 'Ente Museum minimal placeholder server',
path: req.url,
method: req.method
}));
});
server.listen(port, '0.0.0.0', () => {
console.log(`Minimal placeholder server started on port ${port}`);
});
EOF
node server.js > /app/data/logs/museum.log 2>&1 &
MUSEUM_PID=$!
echo "==> Started minimal Node.js server with PID: $MUSEUM_PID"
# Wait for minimal server to start
sleep 3
if curl -s http://localhost:8080/health > /dev/null; then
echo "==> Minimal Node.js server started successfully"
else
echo "==> WARNING: Minimal Node.js server may not have started properly"
fi
fi
# Set up Caddy web server
echo "==> Setting up Caddy web server"
# Get local IP address
LOCAL_IP=$(hostname -i 2>/dev/null || echo "0.0.0.0")
echo "==> Local IP address: $LOCAL_IP"
# Create Caddy configuration file
cat > /app/data/Caddyfile << EOF
{
admin off
}
:3080 {
# API endpoints - proxy to Museum server
handle /api/* {
uri strip_prefix /api
reverse_proxy localhost:8080
}
# Web applications static content
handle /photos/* {
uri strip_prefix /photos
root * /app/data/web/photos
try_files {path} {path}/ /index.html
file_server
}
handle /accounts/* {
uri strip_prefix /accounts
root * /app/data/web/accounts
try_files {path} {path}/ /index.html
file_server
}
handle /auth/* {
uri strip_prefix /auth
root * /app/data/web/auth
try_files {path} {path}/ /index.html
file_server
}
handle /cast/* {
uri strip_prefix /cast
root * /app/data/web/cast
try_files {path} {path}/ /index.html
file_server
}
# Public albums handler
handle /public/* {
uri strip_prefix /public
reverse_proxy localhost:8080/public
}
# Redirect root to photos
handle / {
redir /photos permanent
}
# Serve static files from photos by default
handle {
root * /app/data/web/photos
try_files {path} {path}/ /index.html
file_server
}
# Error handling
handle_errors {
respond "{http.error.status_code} {http.error.status_text}"
}
# Logging
log {
output file /app/data/logs/access.log
format console
level info
}
}
EOF
echo "==> Caddy configuration created"
cat /app/data/Caddyfile
# Create runtime-config.js in writable location
echo "==> Creating runtime-config.js in writable location"
cat > /app/data/web/photos/static/runtime-config.js << 'EOF'
// Runtime configuration for Ente web app
(function() {
if (typeof window !== 'undefined') {
// Polyfill process for browser environment
if (!window.process) {
window.process = {
env: {},
nextTick: function(cb) { setTimeout(cb, 0); }
};
}
const BASE_URL = window.location.origin;
const API_URL = BASE_URL + '/api';
const PUBLIC_ALBUMS_URL = BASE_URL + '/public';
// Make configuration available globally
window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT = API_URL;
window.process.env.NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT = PUBLIC_ALBUMS_URL;
// Also maintain compatibility with older Ente code
window.ENTE_CONFIG = {
API_URL: API_URL,
PUBLIC_ALBUMS_URL: PUBLIC_ALBUMS_URL
};
console.log('Ente runtime config loaded from runtime-config.js with polyfills');
console.log('process.nextTick available:', typeof window.process.nextTick === 'function');
console.log('BASE_URL:', BASE_URL);
console.log('API_URL (final):', API_URL);
console.log('PUBLIC_ALBUMS_URL (final):', PUBLIC_ALBUMS_URL);
console.log('NEXT_PUBLIC_ENTE_ENDPOINT (final):', window.process.env.NEXT_PUBLIC_ENTE_ENDPOINT);
}
})();
EOF
# Copy runtime-config.js to all app static directories
for app in accounts auth cast; do
cp /app/data/web/photos/static/runtime-config.js /app/data/web/$app/static/
done
# Create URL and SRP patch file
echo "==> Creating URL and SRP patch file"
cat > /app/data/web/photos/static/ente-patches.js << 'ENDPATCHES'
(function() {
console.log('Applying Ente URL and SRP patches...');
// Save original URL constructor
const originalURL = window.URL;
// Create a patched URL constructor
window.URL = function(url, base) {
try {
if (!url) {
throw new Error('Invalid URL: URL cannot be empty');
}
// Fix relative URLs
if (!url.match(/^https?:\/\//i)) {
if (url.startsWith('/')) {
url = window.location.origin + url;
} else {
url = window.location.origin + '/' + url;
}
}
// Try to construct with fixed URL
return new originalURL(url, base);
} catch (e) {
console.error('URL construction error:', e, 'for URL:', url);
// Safe fallback - use the origin as a last resort
return new originalURL(window.location.origin);
}
};
console.log('Ente URL and SRP patches applied successfully');
})();
ENDPATCHES
# Copy ente-patches.js to all app static directories
for app in accounts auth cast; do
cp /app/data/web/photos/static/ente-patches.js /app/data/web/$app/static/
done
# Create placeholder HTML files for each app if the actual builds don't exist
for app in photos accounts auth cast; do
if [ ! -f "/app/data/ente/web/$app/index.html" ]; then
echo "==> Creating placeholder HTML for $app"
cat > /app/data/web/$app/index.html << EOF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ente $app</title>
<script src="/static/runtime-config.js"></script>
<script src="/static/ente-patches.js"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
margin: 0;
padding: 0;
background-color: #121212;
color: #fff;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.container {
max-width: 800px;
text-align: center;
padding: 20px;
}
h1 {
font-size: 2.5rem;
margin-bottom: 16px;
}
p {
font-size: 1.1rem;
line-height: 1.5;
margin-bottom: 24px;
color: #ccc;
}
.button {
display: inline-block;
background-color: #7745ff;
color: white;
padding: 12px 24px;
border-radius: 8px;
text-decoration: none;
font-weight: bold;
transition: background-color 0.3s;
}
.button:hover {
background-color: #6535e0;
}
.logo {
width: 120px;
height: 120px;
margin-bottom: 24px;
}
</style>
</head>
<body>
<div class="container">
<svg class="logo" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" rx="20" fill="#7745FF"/>
<path d="M50 30C38.9543 30 30 38.9543 30 50C30 61.0457 38.9543 70 50 70C61.0457 70 70 61.0457 70 50C70 38.9543 61.0457 30 50 30ZM50 60C44.4772 60 40 55.5228 40 50C40 44.4772 44.4772 40 50 40C55.5228 40 60 44.4772 60 50C60 55.5228 55.5228 60 50 60Z" fill="white"/>
</svg>
<h1>Ente $app</h1>
<p>End-to-end encrypted photo storage and sharing platform.</p>
<p>This is a placeholder page until the proper Ente build is created.</p>
<a href="/api/health" class="button">Check API Status</a>
</div>
<script>
// Check if the API is responsive
fetch('/api/health')
.then(response => response.json())
.then(data => {
console.log('API health check:', data);
})
.catch(error => {
console.error('API health check failed:', error);
});
</script>
</body>
</html>
EOF
else
echo "==> Using existing $app web app"
cp -r "/app/data/ente/web/$app/"* "/app/data/web/$app/"
fi
done
# Install unzip if needed
if ! command -v unzip &> /dev/null; then
echo "==> Installing unzip (required for downloading repositories)"
apt-get update
apt-get install -y unzip
fi
# Start Caddy
echo "==> Starting Caddy server"
# Kill any existing Caddy process to avoid port conflicts
pkill -f "caddy run --config /app/data/Caddyfile" || true
sleep 1
# Start Caddy with proper configuration
caddy run --config /app/data/Caddyfile --adapter caddyfile > /app/data/logs/caddy.log 2>&1 &
CADDY_PID=$!
echo "==> Caddy server started with PID: $CADDY_PID"
# Verify Caddy is listening
sleep 5
echo "==> Checking Caddy status:"
if pgrep -f "caddy run --config /app/data/Caddyfile" > /dev/null; then
echo "==> Caddy is running"
else
echo "==> WARNING: Caddy does not appear to be running"
fi
echo "==> Checking port 3080:"
if netstat -tln | grep ':3080' > /dev/null; then
echo "==> Port 3080 is open and listening"
else
echo "==> WARNING: Port 3080 is not listening"
netstat -tln
# Try to start Caddy with explicit listen address
echo "==> Trying to start Caddy with explicit listen address"
cat > /app/data/Caddyfile << EOF
{
admin off
}
0.0.0.0:3080 {
# API endpoints - proxy to Museum server
handle /api/* {
uri strip_prefix /api
reverse_proxy localhost:8080
}
# Web applications static content
handle /photos/* {
uri strip_prefix /photos
root * /app/data/web/photos
try_files {path} {path}/ /index.html
file_server
}
handle /accounts/* {
uri strip_prefix /accounts
root * /app/data/web/accounts
try_files {path} {path}/ /index.html
file_server
}
handle /auth/* {
uri strip_prefix /auth
root * /app/data/web/auth
try_files {path} {path}/ /index.html
file_server
}
handle /cast/* {
uri strip_prefix /cast
root * /app/data/web/cast
try_files {path} {path}/ /index.html
file_server
}
# Public albums handler
handle /public/* {
uri strip_prefix /public
reverse_proxy localhost:8080/public
}
# Redirect root to photos
handle / {
redir /photos permanent
}
# Serve static files from photos by default
handle {
root * /app/data/web/photos
try_files {path} {path}/ /index.html
file_server
}
# Error handling
handle_errors {
respond "{http.error.status_code} {http.error.status_text}"
}
# Logging
log {
output file /app/data/logs/access.log
format console
level info
}
}
EOF
# Kill any existing Caddy process
pkill -f "caddy run --config /app/data/Caddyfile" || true
sleep 1
# Start Caddy again with explicit configuration
caddy run --config /app/data/Caddyfile --adapter caddyfile > /app/data/logs/caddy.log 2>&1 &
CADDY_PID=$!
echo "==> Restarted Caddy server with PID: $CADDY_PID"
sleep 5
# Check again
if netstat -tln | grep ':3080' > /dev/null; then
echo "==> Port 3080 is now open and listening"
else
echo "==> WARNING: Port 3080 is still not listening"
netstat -tln
fi
fi
# Check basic connectivity
curl -v http://localhost:3080/ || echo "==> WARNING: Cannot connect to localhost:3080"
# Enter a wait state to catch signals
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/*.log