1026 lines
33 KiB
Bash
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 |