diff --git a/BUILD-INSTRUCTIONS.md b/BUILD-INSTRUCTIONS.md new file mode 100644 index 0000000..689a7aa --- /dev/null +++ b/BUILD-INSTRUCTIONS.md @@ -0,0 +1,110 @@ +# Ente Cloudron App Build and Installation Instructions + +This document provides detailed instructions for building and installing the Ente Cloudron app, an open-source, end-to-end encrypted photo storage and authentication solution. + +## Prerequisites + +- **Cloudron CLI**: Ensure the Cloudron CLI is installed and configured on your system. Refer to [Cloudron CLI Documentation](https://docs.cloudron.io/packaging/cli/) for setup instructions. +- **Docker**: Required for local testing or custom builds if needed. +- **Git**: To clone or manage the repository. +- **Repository Access**: Ensure you have access to the Ente Cloudron repository at `andreasdueren/ente-cloudron`. +- **Build Service Token**: A token for the Cloudron build service is required (provided in the command below). + +## Build Commands + +1. **Clone the Repository** (if not already done): + ```bash + git clone https://github.com/andreasdueren/ente-cloudron.git + cd ente-cloudron + ``` + +2. **Build the App Using Cloudron Build Service**: + Use the provided build service and token to build the app. Replace `` with the desired version tag (e.g., `0.1.0` or as per `CloudronManifest.json`). + ```bash + cloudron build --set-build-service builder.docker.due.ren --build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e --set-repository andreasdueren/ente-cloudron --tag 1.0.1 + ``` + **Note**: The build process should complete within a reasonable time. Monitor the output for any errors. + +## Installation Commands + +1. **Install the App on Cloudron**: + After a successful build, install the app on your Cloudron instance at the desired location (e.g., `ente.due.ren`). + ```bash + cloudron install --location ente.due.ren --image andreasdueren/ente-cloudron:1.0.1 + ``` + **Important**: Do not wait more than 30 seconds for feedback after running the install command. If there's an error, the process may hang, and you should terminate it to troubleshoot. + **Note**: Always uninstall and reinstall during development rather than updating an existing app to ensure a clean setup. + +## Testing Procedures + +1. **Verify Installation**: + - Access the app at `https://ente.due.ren` (or your configured domain). + - Ensure the Ente web interfaces (Photos, Accounts, Auth, Cast) load correctly. + +2. **Check S3 Configuration**: + - Confirm that S3 environment variables are set in Cloudron app settings under the 'Environment Variables' section. + - Variables to check: `APP_S3_ENABLED`, `APP_S3_ENDPOINT`, `APP_S3_ACCESS_KEY_ID`, `APP_S3_SECRET_ACCESS_KEY`, `APP_S3_BUCKET`. + +3. **Monitor Logs for Errors**: + - Use the Cloudron CLI to view logs: + ```bash + cloudron logs --app ente.due.ren -f + ``` + - Alternatively, shell into the app for detailed log inspection: + ```bash + cloudron exec --app ente.due.ren + tail -f /app/data/logs/* + ``` + - Look for S3 connection errors or other issues. + +## Deployment Steps + +1. **Post-Installation Configuration**: + - If S3 is not working, update the environment variables in Cloudron app settings and restart the app: + ```bash + cloudron restart --app ente.due.ren + ``` + +2. **User Authentication**: + - Ente uses its own authentication system. Ensure user registration and login work as expected. + - If OIDC integration is desired in the future, it can be configured using Cloudron's OIDC variables (`CLOUDRON_OIDC_IDENTIFIER`, `CLOUDRON_OIDC_CLIENT_ID`, `CLOUDRON_OIDC_CLIENT_SECRET`). + +## Troubleshooting Common Issues + +- **S3 Configuration Errors**: + - **Symptom**: App falls back to local storage or logs show S3 connection failures. + - **Solution**: Verify S3 environment variables in Cloudron settings. Test connectivity manually using AWS CLI (`aws s3 ls s3:// --endpoint-url `). + +- **Build Failures**: + - **Symptom**: Build command errors out or hangs. + - **Solution**: Check network connectivity to the build service, ensure the token is correct, and review build logs for specific errors. + +- **Installation Hangs**: + - **Symptom**: Install command does not complete within 30 seconds. + - **Solution**: Terminate the command and check Cloudron logs for errors (`cloudron logs --app ente.due.ren`). Reinstall if necessary. + +- **App Not Starting**: + - **Symptom**: App shows as 'Stopped' or inaccessible after install. + - **Solution**: Check logs for startup errors (`cloudron logs --app ente.due.ren`). Ensure database connectivity and correct configuration. + +## Configuration Examples + +- **S3 Environment Variables** in Cloudron settings: + ``` + APP_S3_ENABLED=true + APP_S3_ENDPOINT=s3.amazonaws.com + APP_S3_ACCESS_KEY_ID=your_access_key + APP_S3_SECRET_ACCESS_KEY=your_secret_key + APP_S3_BUCKET=your_bucket_name + ``` + +## Additional Resources + +- **Cloudron Documentation**: + - [CLI](https://docs.cloudron.io/packaging/cli/) + - [Packaging Tutorial](https://docs.cloudron.io/packaging/tutorial/) + - [Manifest Reference](https://docs.cloudron.io/packaging/manifest/) + - [Addons Guide](https://docs.cloudron.io/packaging/addons/) + - [Cheat Sheet](https://docs.cloudron.io/packaging/cheat-sheet/) + +For further assistance, contact the Ente team at `contact@ente.io` or refer to the GitHub repository at [https://github.com/ente-io/ente](https://github.com/ente-io/ente). \ No newline at end of file diff --git a/Caddyfile.simple b/Caddyfile.simple new file mode 100644 index 0000000..5516ef8 --- /dev/null +++ b/Caddyfile.simple @@ -0,0 +1,21 @@ +{ + admin off + auto_https off +} + +:3080 { + log { + output stdout + level DEBUG + } + + # Simple health check that always works + handle /health { + respond "{\"status\": \"OK\"}" 200 + } + + # Catch-all for debugging + handle { + respond "Caddy is running on port 3080" 200 + } +} \ No newline at end of file diff --git a/CloudronManifest.json b/CloudronManifest.json index 49c68b0..3fef77d 100644 --- a/CloudronManifest.json +++ b/CloudronManifest.json @@ -7,7 +7,7 @@ "contactEmail": "contact@ente.io", "tagline": "Open Source End-to-End Encrypted Photos & Authentication", "upstreamVersion": "1.0.0", - "version": "0.1.62", + "version": "0.1.64", "healthCheckPath": "/ping", "httpPort": 3080, "memoryLimit": 1073741824, diff --git a/Dockerfile b/Dockerfile index 54d8e3f..6c2ed83 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,9 +29,9 @@ RUN corepack enable # Set environment variables for web app build # Set the API endpoint to use current origin - this will work at runtime -ENV NEXT_PUBLIC_ENTE_ENDPOINT="https://example.com/api" -# Add a note for clarity -RUN echo "Building with placeholder NEXT_PUBLIC_ENTE_ENDPOINT, will be served by Caddy proxy at /api" +ENV NEXT_PUBLIC_ENTE_ENDPOINT="/api" +# Use relative path so it works with any domain +RUN echo "Building with NEXT_PUBLIC_ENTE_ENDPOINT=/api, will work with any domain via Caddy proxy" # Debugging the repository structure RUN find . -type d -maxdepth 3 | sort @@ -160,9 +160,13 @@ RUN chmod +x /app/museum-bin/museum # Copy configuration and startup scripts ADD start.sh /app/pkg/ ADD config.template.yaml /app/pkg/ +ADD otp-email-monitor.js /app/pkg/ +ADD package.json /app/pkg/ +ADD admin-helper.sh /app/pkg/ +ADD admin-helper-direct.sh /app/pkg/ # Set proper permissions -RUN chmod +x /app/pkg/start.sh +RUN chmod +x /app/pkg/start.sh /app/pkg/admin-helper.sh /app/pkg/admin-helper-direct.sh # Expose the web port (Cloudron expects port 3080) EXPOSE 3080 diff --git a/admin-helper-direct.sh b/admin-helper-direct.sh new file mode 100644 index 0000000..97299b2 --- /dev/null +++ b/admin-helper-direct.sh @@ -0,0 +1,133 @@ +#!/bin/bash +# Direct Database Admin Helper for Ente Cloudron +# This script directly updates the database for admin operations + +# Function to update user subscription directly in database +update_subscription() { + local user_email="$1" + local storage_gb="$2" + local valid_days="$3" + + if [ -z "$user_email" ] || [ -z "$storage_gb" ] || [ -z "$valid_days" ]; then + echo "Usage: $0 update-subscription " + echo "Example: $0 update-subscription user@example.com 100 365" + return 1 + fi + + echo "Updating subscription for: $user_email" + echo "Storage: ${storage_gb}GB" + echo "Valid for: ${valid_days} days" + + # Convert GB to bytes (1 GB = 1073741824 bytes) + local storage_bytes=$((storage_gb * 1073741824)) + + # Calculate expiry timestamp (current time + valid_days) + local current_timestamp=$(date +%s) + local expiry_timestamp=$((current_timestamp + (valid_days * 86400))) + # Convert to microseconds for the database + local expiry_microseconds="${expiry_timestamp}000000" + + # Update the database directly + PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql \ + -h "$CLOUDRON_POSTGRESQL_HOST" \ + -p "$CLOUDRON_POSTGRESQL_PORT" \ + -U "$CLOUDRON_POSTGRESQL_USERNAME" \ + -d "$CLOUDRON_POSTGRESQL_DATABASE" << EOF +-- Update user's storage and subscription +UPDATE users +SET + storage_bonus = $storage_bytes, + subscription_expiry = $expiry_microseconds +WHERE email = '$user_email'; + +-- Show the updated values +SELECT + email, + storage_bonus / 1073741824.0 as storage_gb, + to_timestamp(subscription_expiry / 1000000) as subscription_expires +FROM users +WHERE email = '$user_email'; +EOF + + if [ $? -eq 0 ]; then + echo "✓ Subscription updated successfully" + else + echo "✗ Failed to update subscription" + return 1 + fi +} + +# Function to get user details +get_user_details() { + local user_email="$1" + + if [ -z "$user_email" ]; then + echo "Usage: $0 get-user " + return 1 + fi + + PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql \ + -h "$CLOUDRON_POSTGRESQL_HOST" \ + -p "$CLOUDRON_POSTGRESQL_PORT" \ + -U "$CLOUDRON_POSTGRESQL_USERNAME" \ + -d "$CLOUDRON_POSTGRESQL_DATABASE" << EOF +SELECT + email, + storage_bonus / 1073741824.0 as storage_gb, + storage_consumed / 1073741824.0 as used_gb, + to_timestamp(subscription_expiry / 1000000) as subscription_expires, + CASE + WHEN subscription_expiry > (EXTRACT(EPOCH FROM NOW()) * 1000000) THEN 'Active' + ELSE 'Expired' + END as status +FROM users +WHERE email = '$user_email'; +EOF +} + +# Function to list all users +list_users() { + PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql \ + -h "$CLOUDRON_POSTGRESQL_HOST" \ + -p "$CLOUDRON_POSTGRESQL_PORT" \ + -U "$CLOUDRON_POSTGRESQL_USERNAME" \ + -d "$CLOUDRON_POSTGRESQL_DATABASE" << EOF +SELECT + email, + storage_bonus / 1073741824.0 as storage_gb, + storage_consumed / 1073741824.0 as used_gb, + to_timestamp(subscription_expiry / 1000000) as expires, + CASE + WHEN subscription_expiry > (EXTRACT(EPOCH FROM NOW()) * 1000000) THEN 'Active' + ELSE 'Expired' + END as status +FROM users +ORDER BY email; +EOF +} + +# Main command handler +case "$1" in + "update-subscription") + update_subscription "$2" "$3" "$4" + ;; + "get-user") + get_user_details "$2" + ;; + "list-users") + list_users + ;; + *) + echo "Ente Direct Admin Helper" + echo "" + echo "Usage:" + echo " $0 update-subscription " + echo " $0 get-user " + echo " $0 list-users" + echo "" + echo "Examples:" + echo " $0 update-subscription user@example.com 100 365" + echo " $0 get-user user@example.com" + echo " $0 list-users" + ;; +esac \ No newline at end of file diff --git a/admin-helper.sh b/admin-helper.sh new file mode 100644 index 0000000..0f1193f --- /dev/null +++ b/admin-helper.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# Ente Admin Helper Script for Cloudron +# This script simplifies admin operations in the Cloudron terminal + +MUSEUM_BIN="/app/data/ente/server/museum" + +# Check if museum binary exists +if [ ! -f "$MUSEUM_BIN" ]; then + echo "Error: Museum binary not found at $MUSEUM_BIN" + exit 1 +fi + +# Function to update user subscription +update_subscription() { + local user_email="$1" + local storage_gb="$2" + local valid_days="$3" + + if [ -z "$user_email" ] || [ -z "$storage_gb" ] || [ -z "$valid_days" ]; then + echo "Usage: $0 update-subscription " + echo "Example: $0 update-subscription user@example.com 100 365" + return 1 + fi + + echo "Updating subscription for: $user_email" + echo "Storage: ${storage_gb}GB" + echo "Valid for: ${valid_days} days" + + cd /app/data/ente/server + + # Use environment variables for database connection + export DB_HOST="$CLOUDRON_POSTGRESQL_HOST" + export DB_PORT="$CLOUDRON_POSTGRESQL_PORT" + export DB_NAME="$CLOUDRON_POSTGRESQL_DATABASE" + export DB_USERNAME="$CLOUDRON_POSTGRESQL_USERNAME" + export DB_PASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" + + # Museum admin commands need specific syntax + "$MUSEUM_BIN" admin update-subscription "$user_email" "$storage_gb" "$valid_days" +} + +# Function to get user details +get_user_details() { + local user_email="$1" + + if [ -z "$user_email" ]; then + echo "Usage: $0 get-user " + return 1 + fi + + cd /app/data/ente/server + + "$MUSEUM_BIN" admin get-user-details --user "$user_email" +} + +# Function to list all users +list_users() { + cd /app/data/ente/server + + # Connect to PostgreSQL and list users + PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql \ + -h "$CLOUDRON_POSTGRESQL_HOST" \ + -p "$CLOUDRON_POSTGRESQL_PORT" \ + -U "$CLOUDRON_POSTGRESQL_USERNAME" \ + -d "$CLOUDRON_POSTGRESQL_DATABASE" \ + -c "SELECT email, storage_bonus, subscription_expiry FROM users ORDER BY email;" +} + +# Main command handler +case "$1" in + "update-subscription") + update_subscription "$2" "$3" "$4" + ;; + "get-user") + get_user_details "$2" + ;; + "list-users") + list_users + ;; + *) + echo "Ente Admin Helper" + echo "" + echo "Usage:" + echo " $0 update-subscription " + echo " $0 get-user " + echo " $0 list-users" + echo "" + echo "Examples:" + echo " $0 update-subscription user@example.com 100 365" + echo " $0 get-user user@example.com" + echo " $0 list-users" + ;; +esac \ No newline at end of file diff --git a/debug-headers.sh b/debug-headers.sh new file mode 100755 index 0000000..a8ebe2b --- /dev/null +++ b/debug-headers.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +echo "==> Debugging Caddy MIME type headers" +echo "==> Testing various file types..." + +BASE_URL="${1:-https://ente.due.ren}" + +echo +echo "Testing HTML files:" +curl -I "$BASE_URL/" 2>/dev/null | grep -i content-type || echo "No Content-Type header found" +curl -I "$BASE_URL/index.html" 2>/dev/null | grep -i content-type || echo "No Content-Type header found" + +echo +echo "Testing JavaScript files:" +curl -I "$BASE_URL/config.js" 2>/dev/null | grep -i content-type || echo "No Content-Type header found" + +echo +echo "Testing CSS files (if any):" +curl -I "$BASE_URL/styles.css" 2>/dev/null | grep -i content-type || echo "File not found or no Content-Type header" + +echo +echo "Testing JSON files (if any):" +curl -I "$BASE_URL/manifest.json" 2>/dev/null | grep -i content-type || echo "File not found or no Content-Type header" + +echo +echo "==> Full response headers for main page:" +curl -I "$BASE_URL/" 2>/dev/null || echo "Failed to connect to $BASE_URL" + +echo +echo "==> To test from inside a container:" +echo "docker exec -it curl -I http://localhost:3080/" + +echo +echo "==> To view Caddy logs:" +echo "docker exec -it tail -f /app/data/logs/caddy.log" \ No newline at end of file diff --git a/debug-start.sh b/debug-start.sh new file mode 100755 index 0000000..ba91924 --- /dev/null +++ b/debug-start.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Add this debugging section to your start.sh after line 350 + +# Start Caddy with more verbose logging +echo "==> Starting Caddy web server with debug logging" +echo "==> Validating Caddyfile first..." +caddy validate --config /app/data/Caddyfile --adapter caddyfile || { + echo "==> ERROR: Caddyfile validation failed!" + cat /app/data/Caddyfile + exit 1 +} + +echo "==> Starting Caddy..." +# Run Caddy in foreground first to see errors +timeout 10 caddy run --config /app/data/Caddyfile --adapter caddyfile 2>&1 | tee /app/data/logs/caddy-debug.log || { + echo "==> ERROR: Caddy failed to start" + echo "==> Last 50 lines of Caddy debug log:" + tail -50 /app/data/logs/caddy-debug.log +} + +# Check if port is actually listening +echo "==> Checking if port 3080 is listening..." +netstat -tlnp | grep 3080 || lsof -i :3080 || { + echo "==> ERROR: Nothing listening on port 3080" +} + +# Test the health endpoint +echo "==> Testing health endpoint..." +curl -v http://localhost:3080/health || { + echo "==> ERROR: Health check failed" +} \ No newline at end of file diff --git a/ente-cli-config.md b/ente-cli-config.md new file mode 100644 index 0000000..3e5296a --- /dev/null +++ b/ente-cli-config.md @@ -0,0 +1,64 @@ +# Ente CLI Configuration for Custom Server + +The Ente CLI expects configuration in `~/.ente/config.yaml`. Here's how to set it up: + +## Method 1: Direct Configuration + +1. Create the config file: +```bash +mkdir -p ~/.ente +cat > ~/.ente/config.yaml << EOF +api: + url: https://ente.due.ren +EOF +``` + +2. Add your account interactively: +```bash +ente account add +# It will ask for: +# - Export directory: /tmp/ente-export (or any directory) +# - Email: your-admin@email.com +# - Password: your-password +``` + +## Method 2: Using the Admin Commands Directly + +If the interactive setup is problematic, you can use the admin commands with explicit parameters: + +```bash +# Set the API endpoint +export ENTE_API_URL="https://ente.due.ren" + +# Or pass it directly in the command +ente admin update-subscription \ + --api-url https://ente.due.ren \ + --admin-user admin@due.ren \ + --user user@example.com \ + --storage 1000 \ + --valid-for 365 +``` + +## Method 3: Direct Database Update (Fallback) + +Since the CLI setup seems problematic, you can update the database directly in the Cloudron terminal: + +```bash +# In Cloudron terminal +PGPASSWORD="$CLOUDRON_POSTGRESQL_PASSWORD" psql \ + -h "$CLOUDRON_POSTGRESQL_HOST" \ + -U "$CLOUDRON_POSTGRESQL_USERNAME" \ + -d "$CLOUDRON_POSTGRESQL_DATABASE" << EOF +-- Update user to 1TB for 1 year +UPDATE users +SET storage_bonus = 1073741824000, -- 1000 GB in bytes + subscription_expiry = EXTRACT(EPOCH FROM NOW() + INTERVAL '365 days') * 1000000 +WHERE email = 'andreas@due.ren'; + +-- Show the result +SELECT email, + storage_bonus / 1073741824.0 as storage_gb, + to_timestamp(subscription_expiry / 1000000) as expires +FROM users WHERE email = 'andreas@due.ren'; +EOF +``` \ No newline at end of file diff --git a/setup-ente-cli.sh b/setup-ente-cli.sh new file mode 100755 index 0000000..dfd32e6 --- /dev/null +++ b/setup-ente-cli.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Setup Ente CLI for custom server + +echo "Setting up Ente CLI for custom server..." + +# Create config directory +mkdir -p ~/.ente + +# Create the CLI config with custom endpoint +cat > ~/.ente/config.yaml << EOF +host: https://ente.due.ren +EOF + +echo "Configuration created at ~/.ente/config.yaml" +echo "" +echo "Now you can add your account:" +echo " ente account add" +echo "" +echo "Then use admin commands:" +echo " ente admin update-subscription --admin-user admin@due.ren --user user@example.com --storage 1000 --valid-for 365" \ No newline at end of file diff --git a/start-debug.sh b/start-debug.sh new file mode 100755 index 0000000..bb9fab0 --- /dev/null +++ b/start-debug.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +# Better signal handling - forward signals to child processes +trap 'kill -TERM $SERVER_PID; kill -TERM $CADDY_PID; exit' TERM INT + +set -eu + +echo "==> Starting Ente Cloudron app (DEBUG MODE)..." + +# Create necessary directories +mkdir -p /app/data/config /app/data/logs /app/data/caddy + +# Check if web directories exist +echo "==> Checking web app directories:" +for app in photos accounts auth cast; do + if [ -d "/app/web/$app" ]; then + echo "==> Found: /app/web/$app" + ls -la "/app/web/$app" | head -5 + else + echo "==> WARNING: Missing /app/web/$app - creating placeholder" + mkdir -p "/app/web/$app" + echo "

$app app placeholder

" > "/app/web/$app/index.html" + fi +done + +# Create a simple test Caddyfile first +echo "==> Creating simple test Caddyfile" +cat > /app/data/Caddyfile <<'EOT' +{ + admin off + auto_https off +} + +:3080 { + log { + output stdout + format console + level DEBUG + } + + # Health check endpoint + handle /health { + header Content-Type "application/json" + respond "{\"status\": \"OK\", \"timestamp\": \"{{now | date \"2006-01-02T15:04:05Z07:00\"}}\"}" 200 + } + + # Test endpoint + handle /test { + respond "Caddy is working on port 3080!" 200 + } + + # API proxy to Museum server + handle /api/* { + uri strip_prefix /api + reverse_proxy localhost:8080 { + transport http { + read_timeout 60s + write_timeout 60s + } + # Add error handling + handle_errors { + respond "{\"error\": \"Museum server not available\"}" 503 + } + } + } + + # Serve web apps with fallback + handle { + root * /app/web/photos + try_files {path} {path}/ /index.html + file_server { + browse + } + } +} +EOT + +# Start a simple Museum mock server for testing +echo "==> Starting mock Museum server on port 8080" +cat > /tmp/museum-mock.js <<'EOF' +const http = require('http'); +const server = http.createServer((req, res) => { + console.log(`Museum mock: ${req.method} ${req.url}`); + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ status: 'ok', path: req.url, timestamp: new Date().toISOString() })); +}); +server.listen(8080, '127.0.0.1', () => { + console.log('Museum mock server running on http://127.0.0.1:8080'); +}); +EOF +node /tmp/museum-mock.js > /app/data/logs/museum-mock.log 2>&1 & +SERVER_PID=$! +echo "==> Mock Museum server started (PID: $SERVER_PID)" + +# Wait for Museum mock to be ready +sleep 2 + +# Test Museum mock +echo "==> Testing Museum mock server..." +curl -s http://localhost:8080/test || echo "WARNING: Museum mock not responding" + +# Validate Caddyfile +echo "==> Validating Caddyfile..." +caddy validate --config /app/data/Caddyfile --adapter caddyfile || { + echo "==> ERROR: Caddyfile validation failed!" + exit 1 +} + +# Start Caddy with explicit environment +echo "==> Starting Caddy web server..." +CADDY_FORMAT=console caddy run --config /app/data/Caddyfile --adapter caddyfile 2>&1 | tee /app/data/logs/caddy-combined.log & +CADDY_PID=$! +echo "==> Caddy started (PID: $CADDY_PID)" + +# Wait for Caddy to start +echo "==> Waiting for Caddy to start..." +for i in {1..30}; do + if curl -s http://localhost:3080/health > /dev/null; then + echo "==> Caddy is responding!" + break + fi + echo -n "." + sleep 1 +done +echo + +# Check process status +echo "==> Process status:" +ps aux | grep -E "(caddy|node)" | grep -v grep || echo "No processes found" + +# Check port status +echo "==> Port status:" +netstat -tlnp 2>/dev/null | grep -E "(3080|8080)" || lsof -i :3080 -i :8080 2>/dev/null || echo "Cannot check port status" + +# Test endpoints +echo "==> Testing endpoints:" +echo "Health check:" +curl -s http://localhost:3080/health | jq . || echo "Failed" +echo -e "\nTest endpoint:" +curl -s http://localhost:3080/test || echo "Failed" +echo -e "\nAPI proxy:" +curl -s http://localhost:3080/api/status | jq . || echo "Failed" + +echo "==> Startup complete. Services:" +echo " - Caddy PID: $CADDY_PID" +echo " - Museum Mock PID: $SERVER_PID" +echo "==> Logs: /app/data/logs/" + +# Keep running +wait $SERVER_PID $CADDY_PID \ No newline at end of file diff --git a/start.sh b/start.sh index f9eacc3..f614eb1 100644 --- a/start.sh +++ b/start.sh @@ -104,6 +104,15 @@ port: 8080 host: 0.0.0.0 log_level: info +# Key used for encrypting customer data (REQUIRED) +key: + encryption: yvmG/RnzKrbCb9L3mgsmoxXr9H7i2Z4qlbT0mL3ln4w= + hash: KXYiG07wC7GIgvCSdg+WmyWdXDAn6XKYJtp/wkEU7x573+byBRAYtpTP0wwvi8i/4l37uicX1dVTUzwH3sLZyw== + +# JWT secrets (REQUIRED) +jwt: + secret: i2DecQmfGreG6q1vBj5tCokhlN41gcfS2cjOs9Po-u8= + # Database configuration db: host: ${CLOUDRON_POSTGRESQL_HOST} @@ -120,14 +129,17 @@ cors: # S3 storage configuration s3: - endpoint: "${S3_ENDPOINT}" - region: "${S3_REGION}" - access_key: "${S3_ACCESS_KEY}" - secret_key: "${S3_SECRET_KEY}" - bucket: "${S3_BUCKET}" # For Wasabi, we need path style URLs - use_path_style_urls: true are_local_buckets: false + use_path_style_urls: true + + # Primary bucket configuration (named bucket structure required by Museum) + b2-eu-cen: + endpoint: "${S3_ENDPOINT}" + region: "${S3_REGION}" + key: "${S3_ACCESS_KEY}" + secret: "${S3_SECRET_KEY}" + bucket: "${S3_BUCKET}" # Email settings email: @@ -212,6 +224,17 @@ else log "INFO" "Web templates already exist or source not available" fi +# Copy mail templates to Museum working directory (required for email functionality) +MUSEUM_MAIL_TEMPLATES_DIR="/app/data/ente/server/mail-templates" +REPO_MAIL_TEMPLATES_DIR="/app/data/ente/repository/server/mail-templates" +if [ ! -d "$MUSEUM_MAIL_TEMPLATES_DIR" ] && [ -d "$REPO_MAIL_TEMPLATES_DIR" ]; then + log "INFO" "Copying mail templates" + cp -r "$REPO_MAIL_TEMPLATES_DIR" "$MUSEUM_MAIL_TEMPLATES_DIR" + log "INFO" "Copied mail templates to $MUSEUM_MAIL_TEMPLATES_DIR" +else + log "INFO" "Mail templates already exist or source not available" +fi + # Check if Museum binary exists and is valid log "INFO" "Checking for Museum binary at: $MUSEUM_BIN" if [ -f "$MUSEUM_BIN" ]; then @@ -243,23 +266,7 @@ fi # =============================================== log "INFO" "Web applications are pre-built and available in /app/web/" -# Fix API endpoint configuration in built JavaScript files -log "INFO" "Updating API endpoint configuration in web apps" -ACTUAL_ENDPOINT="https://${CLOUDRON_APP_DOMAIN}/api" -log "INFO" "Setting API endpoint to: $ACTUAL_ENDPOINT" - -# Replace placeholder endpoint in all JavaScript files -for webapp in photos accounts auth cast; do - WEB_DIR="/app/web/${webapp}" - if [ -d "$WEB_DIR" ]; then - log "INFO" "Processing ${webapp} app" - # Find and replace the placeholder endpoint in all JS files - find "$WEB_DIR" -name "*.js" -type f -exec sed -i "s|https://example.com/api|${ACTUAL_ENDPOINT}|g" {} \; - log "INFO" "Updated endpoint configuration for ${webapp}" - else - log "WARN" "Web directory not found for ${webapp}" - fi -done +# Web apps are pre-built with relative API paths (/api) that work with any domain # =============================================== # Node.js Placeholder Server @@ -631,10 +638,11 @@ cat > "$CADDY_CONFIG" << EOF # Enable compression encode gzip + # Root redirect - must be first + redir / /photos/ 301 + # CORS preflight handling - @options { - method OPTIONS - } + @options method OPTIONS handle @options { header { Access-Control-Allow-Origin "*" @@ -645,14 +653,9 @@ cat > "$CADDY_CONFIG" << EOF respond 204 } - # API endpoints with CORS - handle /api/* { - reverse_proxy localhost:8080 { - header_up Host {http.request.host} - header_up X-Real-IP {http.request.remote} - header_up X-Forwarded-For {http.request.remote} - header_up X-Forwarded-Proto {http.request.scheme} - } + # API endpoints - STRIP /api prefix and proxy to Museum server + handle_path /api/* { + reverse_proxy localhost:8080 header { Access-Control-Allow-Origin "*" Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" @@ -661,29 +664,67 @@ cat > "$CADDY_CONFIG" << EOF } } - # Public albums endpoint - handle /public/* { + # API endpoints for auth app + handle_path /auth/api/* { reverse_proxy localhost:8080 header { Access-Control-Allow-Origin "*" + Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" + Access-Control-Allow-Headers "*" + Access-Control-Allow-Credentials "true" } } - # Health check endpoint - handle /health { + # API endpoints for cast app + handle_path /cast/api/* { + reverse_proxy localhost:8080 + header { + Access-Control-Allow-Origin "*" + Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" + Access-Control-Allow-Headers "*" + Access-Control-Allow-Credentials "true" + } + } + + # API endpoints for accounts app + handle_path /accounts/api/* { + reverse_proxy localhost:8080 + header { + Access-Control-Allow-Origin "*" + Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" + Access-Control-Allow-Headers "*" + Access-Control-Allow-Credentials "true" + } + } + + # Health check endpoint (direct, no /api prefix) + handle /ping { reverse_proxy localhost:8080 } # Static files for Next.js assets from all apps handle /_next/* { - @photosNext path /_next/* - handle @photosNext { - root * /app/web/photos - file_server - } + root * /app/web/photos + file_server header { Cache-Control "public, max-age=31536000" - Access-Control-Allow-Origin "*" + } + } + + # Static images and assets (served from photos app by default) + handle /images/* { + root * /app/web/photos + file_server + header { + Cache-Control "public, max-age=86400" + } + } + + handle /favicon.ico { + root * /app/web/photos + file_server + header { + Cache-Control "public, max-age=86400" } } @@ -715,8 +756,9 @@ cat > "$CADDY_CONFIG" << EOF file_server } - # Root redirect - handle / { + # Root redirect - specifically match root path only + @root path / + handle @root { redir /photos/ permanent } } @@ -764,19 +806,34 @@ cat > /app/data/SETUP-INSTRUCTIONS.md << EOF 2. **Museum Server**: The server configuration is at \`/app/data/ente/server/museum.yaml\` if you need to customize settings. -## Troubleshooting +## API Endpoint -- **Logs**: Check the logs at \`/app/data/logs/\` for any issues. -- **Restart**: If you change configuration, restart the app to apply changes. +The Ente API is available at: **https://${CLOUDRON_APP_FQDN}/api** + +This endpoint can be used to: +- Configure Ente CLI tools +- Integrate with third-party applications +- Access the Museum server API directly + +For admin operations, use the Ente CLI with: +\`\`\`bash +ente admin --api-url https://${CLOUDRON_APP_FQDN}/api +\`\`\` ## 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/ +- **Photos**: https://${CLOUDRON_APP_FQDN}/photos/ - Main photo storage and management +- **Auth**: https://${CLOUDRON_APP_FQDN}/auth/ - 2FA authenticator app +- **Accounts**: https://${CLOUDRON_APP_FQDN}/accounts/ - Account management +- **Cast**: https://${CLOUDRON_APP_FQDN}/cast/ - Photo casting to devices + +## Troubleshooting + +- **Logs**: Check the logs at \`/app/data/logs/\` for any issues. +- **Restart**: If you change configuration, restart the app to apply changes. +- **API Issues**: All apps use the API endpoint at \`/api\`. If apps show loading spinners, check API connectivity. ## Support @@ -800,6 +857,48 @@ else log "ERROR" "Caddy server is not running!" fi +# =============================================== +# OTP Email Monitor Setup +# =============================================== +log "INFO" "Setting up OTP Email Monitor" + +# Install Node.js dependencies if not already installed +if [ ! -d "/app/data/node_modules" ]; then + log "INFO" "Installing Node.js dependencies for OTP Email Monitor" + cd /app/data + cp /app/pkg/package.json . + npm install --production --no-save + log "INFO" "Node.js dependencies installed successfully" +else + log "INFO" "Node.js dependencies already installed" +fi + +# Start OTP Email Monitor +log "INFO" "Starting OTP Email Monitor" +cd /app/data +NODE_PATH="/app/data/node_modules" node /app/pkg/otp-email-monitor.js > /app/data/logs/otp-email.log 2>&1 & +OTP_MONITOR_PID=$! +log "INFO" "OTP Email Monitor started with PID: $OTP_MONITOR_PID" + +# Wait a moment to check if OTP monitor starts successfully +sleep 2 +if ps -p $OTP_MONITOR_PID > /dev/null; then + log "INFO" "OTP Email Monitor is running successfully" +else + log "WARN" "OTP Email Monitor may have failed to start" + log "WARN" "Last 10 lines of OTP email log:" + tail -n 10 /app/data/logs/otp-email.log | while read -r line; do + log "WARN" " $line" + done +fi + +# Copy admin helper script for easy access +if [ -f "/app/pkg/admin-helper.sh" ]; then + cp /app/pkg/admin-helper.sh /app/data/ + chmod +x /app/data/admin-helper.sh + log "INFO" "Admin helper script available at /app/data/admin-helper.sh" +fi + log "INFO" "Ente Cloudron app startup complete" # Keep the script running to prevent container exit diff --git a/update-storage.sh b/update-storage.sh new file mode 100755 index 0000000..658365f --- /dev/null +++ b/update-storage.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Script to update Ente user storage using the Ente CLI +# Run this from your local machine (not inside Cloudron) + +# Check if ente CLI is installed +if ! command -v ente &> /dev/null; then + echo "Ente CLI is not installed. Please install it first:" + echo "" + echo "For macOS:" + echo " brew tap ente-io/ente" + echo " brew install ente-cli" + echo "" + echo "For other systems, download from:" + echo " https://github.com/ente-io/ente/releases" + exit 1 +fi + +# Your Ente instance +ENTE_ENDPOINT="https://ente.due.ren" + +# Function to update subscription +update_subscription() { + local admin_email="$1" + local user_email="$2" + local storage_gb="$3" + local valid_days="$4" + + echo "Updating subscription for: $user_email" + echo "Storage: ${storage_gb}GB" + echo "Valid for: ${valid_days} days" + echo "Using admin account: $admin_email" + echo "" + + # Run the ente CLI command + ente admin update-subscription \ + --host "$ENTE_ENDPOINT" \ + --admin-user "$admin_email" \ + --user "$user_email" \ + --storage "$storage_gb" \ + --valid-for "$valid_days" +} + +# Check arguments +if [ $# -lt 4 ]; then + echo "Usage: $0 " + echo "" + echo "Example:" + echo " $0 admin@due.ren andreas@due.ren 1000 365" + echo "" + echo "Make sure you're logged in to the Ente CLI first:" + echo " ente account add" + echo " API endpoint: $ENTE_ENDPOINT" + exit 1 +fi + +# Run the update +update_subscription "$1" "$2" "$3" "$4" \ No newline at end of file