From 74dd21b95344f34a324af0ef2ea11e154538d7ef Mon Sep 17 00:00:00 2001 From: Andreas Dueren Date: Sun, 6 Jul 2025 17:01:09 -0600 Subject: [PATCH] Initial commit of Keila Cloudron package --- .DS_Store | Bin 0 -> 6148 bytes BUILD.md | 54 ++++++++++++++++ CHANGELOG | 9 --- CloudronManifest.json | 33 ++++------ Dockerfile | 51 +++++++++------ POSTINSTALL.md | 28 --------- README.md | 143 ------------------------------------------ keila-wrapper.sh | 17 ----- nginx.conf | 79 ----------------------- robots.txt | 5 -- start.sh | 109 +++++++++++--------------------- supervisor/keila.conf | 10 --- supervisor/nginx.conf | 8 --- 13 files changed, 135 insertions(+), 411 deletions(-) create mode 100644 .DS_Store create mode 100644 BUILD.md delete mode 100644 CHANGELOG delete mode 100644 POSTINSTALL.md delete mode 100644 README.md delete mode 100644 keila-wrapper.sh delete mode 100644 nginx.conf delete mode 100644 robots.txt delete mode 100644 supervisor/keila.conf delete mode 100644 supervisor/nginx.conf diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0> /keila/config/config.exs +RUN mix assets.deploy && \ + mix release -EXPOSE 4000 +# Stage 2: Runtime +FROM cloudron/base:5.0.0 + +RUN mkdir -p /app/code /app/data + +COPY --from=build /keila/_build/prod/rel/keila /app/code +COPY start.sh /app/code/ +RUN chmod +x /app/code/start.sh + +RUN chown -R cloudron:cloudron /app/code /app/data + +CMD ["/app/code/start.sh"] -CMD ["/app/code/start.sh"] \ No newline at end of file diff --git a/POSTINSTALL.md b/POSTINSTALL.md deleted file mode 100644 index 73f0e21..0000000 --- a/POSTINSTALL.md +++ /dev/null @@ -1,28 +0,0 @@ -# Keila Installation Complete - -Keila has been successfully installed on your Cloudron! - -## Default Administrator Account - -- **Email**: admin@your-domain.com -- **Password**: Check `/app/data/root_credentials` file or the application logs for the generated password - -## Getting Started - -1. Log in using the administrator credentials above -2. Create your first newsletter campaign -3. Set up your email sending configuration (if not using Cloudron's built-in SMTP) -4. Create sign-up forms for your newsletters - -## Important Notes - -- User registration is disabled by default for security -- All data is stored in `/app/data` and will persist across updates -- Email sending is configured to use Cloudron's SMTP service -- File uploads are stored in `/app/data/uploads` - -## Support - -- Documentation: https://www.keila.io/docs -- GitHub: https://github.com/pentacent/keila -- Issues: Please report Cloudron-specific issues to the package maintainer \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index cb80f5c..0000000 --- a/README.md +++ /dev/null @@ -1,143 +0,0 @@ -# Keila Cloudron Package - -This is a Cloudron package for [Keila](https://www.keila.io), an open-source newsletter tool and alternative to Mailchimp and Sendinblue. - -## Features - -- **Newsletter Campaigns**: Create and send email campaigns -- **Sign-up Forms**: Generate customizable subscription forms -- **Multiple Email Providers**: Support for AWS SES, Sendgrid, Mailgun, Postmark, and SMTP -- **Self-hosted**: Complete control over your data and newsletters -- **PostgreSQL Integration**: Reliable database storage via Cloudron addon -- **SMTP Configuration**: Automatic email sending via Cloudron's mail service - -## Installation - -### Prerequisites - -- Cloudron instance with CLI installed -- Access to build service or local Docker environment - -### Build and Install - -1. **Build the package**: - ```bash - cloudron build --set-build-service builder.docker.due.ren \ - --build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e \ - --set-repository andreasdueren/keila-cloudron \ - --tag 0.1.0 - ``` - -2. **Install on Cloudron**: - ```bash - cloudron install --location keila.yourdomain.com \ - --image andreasdueren/keila-cloudron:0.1.0 - ``` - -### Local Development Build - -```bash -# Clone this repository -git clone -cd keila-cloudron - -# Build locally -cloudron build - -# Install for testing -cloudron install --location keila.local.dev -``` - -## Configuration - -### Default Settings - -- **Admin Account**: `admin@yourdomain.com` (password in `/app/data/root_credentials`) -- **Database**: PostgreSQL via Cloudron addon -- **Email**: Configured via Cloudron SMTP service -- **Registration**: Disabled by default for security -- **Port**: 4000 (proxied via nginx) - -### Environment Variables - -The following Cloudron environment variables are automatically configured: - -- `CLOUDRON_POSTGRESQL_URL` - Database connection -- `CLOUDRON_MAIL_SMTP_*` - Email configuration -- `CLOUDRON_APP_DOMAIN` - Application domain -- `CLOUDRON_MAIL_FROM` - From email address - -## Data Persistence - -- **Application Data**: `/app/data/keila` - Keila installation -- **Uploads**: `/app/data/uploads` - User-uploaded content -- **Credentials**: `/app/data/root_credentials` - Admin login info -- **Secrets**: `/app/data/secret_key_base` - Application secret - -## Troubleshooting - -### Common Issues - -1. **Database Connection**: Ensure PostgreSQL addon is properly configured -2. **Email Sending**: Verify Cloudron SMTP settings in mail addon -3. **File Permissions**: All files should be owned by `cloudron:cloudron` - -### Logs - -View application logs: -```bash -cloudron logs -``` - -### Reset Admin Password - -```bash -# Access the container -cloudron exec - -# Check current credentials -cat /app/data/root_credentials - -# Reset if needed (restart required) -rm /app/data/root_credentials -exit -cloudron restart -``` - -## Technical Details - -### Architecture - -- **Base Image**: `cloudron/base:4.2.0` -- **Runtime**: Elixir 1.15 with Phoenix framework -- **Database**: PostgreSQL (via Cloudron addon) -- **Web Server**: Nginx reverse proxy -- **Process Manager**: Direct Elixir application startup - -### Security Features - -- User registration disabled by default -- Security headers configured in nginx -- File upload restrictions -- Database connection over SSL -- Secret key auto-generation - -### File Structure - -``` -/app/code/ # Application code (read-only) -/app/data/keila/ # Keila installation -/app/data/uploads/ # User uploads -/app/data/ # Persistent data -``` - -## Support - -- **Keila Documentation**: https://www.keila.io/docs -- **Keila GitHub**: https://github.com/pentacent/keila -- **Cloudron Docs**: https://docs.cloudron.io - -## License - -- **Keila**: AGPLv3 License -- **This Package**: MIT License \ No newline at end of file diff --git a/keila-wrapper.sh b/keila-wrapper.sh deleted file mode 100644 index 5bc1d99..0000000 --- a/keila-wrapper.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# Keila wrapper script that sets up proper Erlang runtime - -# Set up environment for Keila -export MIX_ENV=prod -export ERL_LIBS=/usr/local/lib/erlang/lib - -# Use system Erlang instead of the embedded one -export PATH="/usr/local/bin:$PATH" - -# Run Keila with system Erlang -exec /usr/local/bin/erl -boot_var ERTS_LIB_DIR /usr/local/lib/erlang \ - -boot /app/data/keila/releases/0.14.11/start \ - -config /app/data/keila/releases/0.14.11/sys.config \ - -args_file /app/data/keila/releases/0.14.11/vm.args \ - -pa /app/data/keila/lib/*/ebin \ - "$@" \ No newline at end of file diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index c87a11a..0000000 --- a/nginx.conf +++ /dev/null @@ -1,79 +0,0 @@ -server { - listen 4000 default_server; - listen [::]:4000 default_server; - - server_name _; - root /tmp; - - client_max_body_size 100m; - - # Security headers - add_header X-Frame-Options SAMEORIGIN; - add_header X-Content-Type-Options nosniff; - add_header X-XSS-Protection "1; mode=block"; - add_header Referrer-Policy strict-origin-when-cross-origin; - - # Logging - access_log /dev/stdout; - error_log /dev/stderr; - - # Gzip compression - gzip on; - gzip_vary on; - gzip_min_length 1024; - gzip_proxied any; - gzip_comp_level 6; - gzip_types - text/plain - text/css - text/xml - text/javascript - application/json - application/javascript - application/xml+rss - application/atom+xml - image/svg+xml; - - # Proxy to Keila application - location / { - proxy_pass http://127.0.0.1:4001; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $host; - proxy_set_header X-Forwarded-Port $server_port; - proxy_cache_bypass $http_upgrade; - proxy_redirect off; - - # Timeout settings - proxy_connect_timeout 60s; - proxy_send_timeout 60s; - proxy_read_timeout 60s; - } - - # Health check endpoint - location /healthz { - proxy_pass http://127.0.0.1:4001/healthz; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - access_log off; - } - - # Static files caching - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - proxy_pass http://127.0.0.1:4001; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - expires 1y; - add_header Cache-Control "public, immutable"; - } -} \ No newline at end of file diff --git a/robots.txt b/robots.txt deleted file mode 100644 index eeccb5e..0000000 --- a/robots.txt +++ /dev/null @@ -1,5 +0,0 @@ -User-agent: * -Disallow: /admin/ -Disallow: /api/ -Disallow: /auth/ -Allow: / \ No newline at end of file diff --git a/start.sh b/start.sh index 7f83544..94c3fc2 100644 --- a/start.sh +++ b/start.sh @@ -1,81 +1,48 @@ #!/bin/bash -set -eu -echo "Starting Keila on Cloudron" +set -euo pipefail -# Initialize Keila data directory by copying from /app/pkg -if [[ ! -d "/app/data/keila" ]]; then - echo "==> Initializing Keila installation" - cp -r /app/pkg /app/data/keila - chown -R cloudron:cloudron /app/data/keila -fi - -# Generate secret key base if not exists -if [[ ! -f "/app/data/secret_key_base" ]]; then - echo "==> Generating secret key base" - openssl rand -hex 64 > /app/data/secret_key_base - chown cloudron:cloudron /app/data/secret_key_base -fi - -# Set environment variables for supervisor -export SECRET_KEY_BASE=$(cat /app/data/secret_key_base) -export DB_URL="${CLOUDRON_POSTGRESQL_URL}" -export URL_HOST="${CLOUDRON_APP_DOMAIN}" -export URL_SCHEMA="https" -export URL_PORT="443" - -# Configure SMTP -export MAILER_SMTP_HOST="${CLOUDRON_MAIL_SMTP_SERVER}" -export MAILER_SMTP_PORT="${CLOUDRON_MAIL_SMTP_PORT}" -export MAILER_SMTP_USERNAME="${CLOUDRON_MAIL_SMTP_USERNAME}" -export MAILER_SMTP_PASSWORD="${CLOUDRON_MAIL_SMTP_PASSWORD}" -export MAILER_SMTP_FROM_EMAIL="${CLOUDRON_MAIL_FROM}" - -# Set user content directory -export USER_CONTENT_DIR="/app/data/uploads" +# Create user content directory mkdir -p /app/data/uploads -chown cloudron:cloudron /app/data/uploads +chown -R cloudron:cloudron /app/data -# Disable registration for security -export DISABLE_REGISTRATION="true" -export DATABASE_POOL_SIZE="10" - -# Create root user credentials file if not exists -if [[ ! -f "/app/data/root_credentials" ]]; then - echo "==> Generating root user credentials" - ROOT_PASSWORD=$(openssl rand -base64 32) - echo "Email: admin@${CLOUDRON_APP_DOMAIN}" > /app/data/root_credentials - echo "Password: ${ROOT_PASSWORD}" >> /app/data/root_credentials - export ROOT_EMAIL="admin@${CLOUDRON_APP_DOMAIN}" - export ROOT_PASSWORD="${ROOT_PASSWORD}" - chown cloudron:cloudron /app/data/root_credentials - chmod 600 /app/data/root_credentials +# Generate secret key on first run +if [ ! -f /app/data/secret_key.txt ]; then + echo "=> Generating new secret key" + head -c 48 /dev/urandom | base64 > /app/data/secret_key.txt fi -# Create complete Erlang runtime structure to fix paths -echo "==> Setting up Erlang runtime" -ERTS_DIR=$(find /usr/local/lib/erlang -name "erts-*" -type d | head -1) -if [[ -n "$ERTS_DIR" ]]; then - echo "Setting up Erlang from: $ERTS_DIR" - - # Create the exact structure Keila expects at both possible locations - rm -rf /app/data/keila/erts-14.2.5 - cp -r "$ERTS_DIR" /app/data/keila/erts-14.2.5 - chown -R cloudron:cloudron /app/data/keila/erts-14.2.5 - - # Create symlink in the releases directory for relative path resolution - mkdir -p /app/data/keila/releases/0.14.11/erts-14.2.5 - rm -rf /app/data/keila/releases/0.14.11/erts-14.2.5 - ln -sf ../../../erts-14.2.5 /app/data/keila/releases/0.14.11/erts-14.2.5 - - # Also ensure the bin directory is accessible - ln -sf /usr/local/bin/erl /app/data/keila/erts-14.2.5/bin/dyn_erl - - echo "Created Erlang symlinks for path resolution" +# Export environment variables for Keila +export PORT=4000 +export DB_URL=$CLOUDRON_POSTGRESQL_URL +export SECRET_KEY_BASE=$(cat /app/data/secret_key.txt) + +# Configure URLs from Cloudron environment variables +export URL_HOST=$CLOUDRON_APP_DOMAIN +export URL_PATH="/" +if [[ "$CLOUDRON_APP_ORIGIN" == https://* ]]; then + export URL_SCHEMA="https" + export URL_PORT="443" +else + export URL_SCHEMA="http" + export URL_PORT="80" fi -echo "==> Running database migrations" -sudo -u cloudron -E /app/data/keila/bin/keila eval "Keila.Release.migrate()" +# Configure email settings +export MAILER_TYPE=smtp +export MAILER_SMTP_FROM_EMAIL=$CLOUDRON_MAIL_FROM +export MAILER_SMTP_HOST=$CLOUDRON_MAIL_SMTP_SERVER +export MAILER_SMTP_PORT=$CLOUDRON_MAIL_SMTP_PORT +export MAILER_SMTP_USER=$CLOUDRON_MAIL_SMTP_USERNAME +export MAILER_SMTP_PASSWORD=$CLOUDRON_MAIL_SMTP_PASSWORD +export MAILER_ENABLE_STARTTLS=true -echo "==> Starting supervisor" -exec /usr/bin/supervisord --configuration /etc/supervisor/supervisord.conf --nodaemon \ No newline at end of file +# Configure user content directory +export USER_CONTENT_DIR=/app/data/uploads + +# Set the initial admin user +export KEILA_USER="admin@cloudron.local" +export KEILA_PASSWORD="changeme123" + +echo "=> Starting Keila" +exec gosu cloudron:cloudron /app/code/bin/keila start diff --git a/supervisor/keila.conf b/supervisor/keila.conf deleted file mode 100644 index d15daf4..0000000 --- a/supervisor/keila.conf +++ /dev/null @@ -1,10 +0,0 @@ -[program:keila] -command=/app/data/keila/bin/keila start -directory=/app/data -user=cloudron -autorestart=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment=PORT=4001,SECRET_KEY_BASE=%(ENV_SECRET_KEY_BASE)s,DB_URL=%(ENV_DB_URL)s,URL_HOST=%(ENV_URL_HOST)s,URL_SCHEMA=%(ENV_URL_SCHEMA)s,URL_PORT=%(ENV_URL_PORT)s,MAILER_SMTP_HOST=%(ENV_MAILER_SMTP_HOST)s,MAILER_SMTP_PORT=%(ENV_MAILER_SMTP_PORT)s,MAILER_SMTP_USERNAME=%(ENV_MAILER_SMTP_USERNAME)s,MAILER_SMTP_PASSWORD=%(ENV_MAILER_SMTP_PASSWORD)s,MAILER_SMTP_FROM_EMAIL=%(ENV_MAILER_SMTP_FROM_EMAIL)s,USER_CONTENT_DIR=%(ENV_USER_CONTENT_DIR)s,DISABLE_REGISTRATION=%(ENV_DISABLE_REGISTRATION)s,DATABASE_POOL_SIZE=%(ENV_DATABASE_POOL_SIZE)s \ No newline at end of file diff --git a/supervisor/nginx.conf b/supervisor/nginx.conf deleted file mode 100644 index abd14ae..0000000 --- a/supervisor/nginx.conf +++ /dev/null @@ -1,8 +0,0 @@ -[program:nginx] -command=nginx -g "daemon off;" -autorestart=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -user=root \ No newline at end of file