diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..f04dbc4 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,54 @@ +# Keila on Cloudron + +This document provides instructions on how to build and install the Keila package for Cloudron. + +## Prerequisites + +- A Cloudron instance +- Cloudron CLI installed and configured +- Docker installed locally + +## Building the Package + +The Keila Cloudron package is built using the `cloudron build` command. This command will use the `Dockerfile` to create a Docker image and push it to a Docker registry. + +To build the package, run the following command in the root of this repository: + +```bash +cloudron build --set-build-service builder.docker.due.ren --build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e --set-repository andreasdueren/keila-cloudron --tag 0.15.0 +``` + +Replace `andreasdueren/keila-cloudron` with your own Docker Hub repository if you wish. The version `0.15.0` corresponds to the Keila version being packaged. + +## Installing the Application + +Once the build is complete, you can install the application on your Cloudron instance using the `cloudron install` command. + +```bash +cloudron install --location keila.due.ren --image andreasdueren/keila-cloudron:0.15.0 +``` + +Replace `keila.due.ren` with the desired location for your Keila instance. + +## First-time Setup + +After installation, Keila will be configured with the following settings: + +- The root user is created with your Cloudron account's email address. +- To set your password for the first time, use the "Forgot Password?" link on the login page. +- Email sending is configured to use Cloudron's mail server. +- User-uploaded content is stored in `/app/data/uploads`. + +## Troubleshooting + +You can view the application logs from the Cloudron dashboard or using the Cloudron CLI: + +```bash +cloudron logs --app keila.due.ren +``` + +To get a shell inside the running application container: + +```bash +cloudron exec --app keila.due.ren +``` diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index 3cc1caf..0000000 --- a/CHANGELOG +++ /dev/null @@ -1,9 +0,0 @@ -[0.1.0] -* Initial Cloudron package for Keila v0.14.7 -* PostgreSQL database integration -* SMTP configuration via Cloudron mail addon -* Persistent data storage in /app/data -* Nginx reverse proxy configuration -* Automatic secret key generation -* Default admin user creation -* Security hardening with disabled registration \ No newline at end of file diff --git a/CloudronManifest.json b/CloudronManifest.json index 5db67af..bea7c93 100644 --- a/CloudronManifest.json +++ b/CloudronManifest.json @@ -1,29 +1,20 @@ { - "id": "io.keila.cloudronapp", + "manifestVersion": 2, + "id": "io.keila.cloudron", + "version": "0.15.13", "title": "Keila", "author": "Pentacent", - "description": "Open-source newsletter tool for creating and sending email campaigns. An alternative to Mailchimp and Sendinblue.", - "tagline": "Self-hosted newsletter and email marketing platform", - "version": "0.14.7", - "healthCheckPath": "/", - "httpPort": 4000, - "manifestVersion": 2, - "website": "https://www.keila.io", - "contactEmail": "hello@keila.io", - "tags": [ - "email", - "newsletter", - "marketing", - "campaigns" - ], + "description": "Open-source email newsletters for creators and businesses. Keila is a reliable, self-hosted, and easy-to-use email marketing platform.", + "website": "https://www.keila.io/", + "contactEmail": "support@keila.io", "memoryLimit": 536870912, + "httpPort": 4000, "addons": { "postgresql": {}, - "sendmail": {}, - "localstorage": {} + "localstorage": {}, + "sendmail": {} }, + "healthCheckPath": "/login", "minBoxVersion": "7.0.0", - "postInstallMessage": "file://POSTINSTALL.md", - "changelog": "file://CHANGELOG", - "optionalSso": false -} \ No newline at end of file + "postInstallMessage": "Your Keila instance is ready.\n\nLogin with the following credentials:\nUsername: admin@cloudron.local\nPassword: changeme123" +} diff --git a/Dockerfile b/Dockerfile index bbd4b9c..f5fb923 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,27 +1,38 @@ -FROM cloudron/base:4.2.0 +# Stage 1: Build +FROM elixir:1.15-slim AS build -# Install runtime dependencies -RUN apt-get update && apt-get install -y \ - postgresql-client \ - supervisor \ - && rm -rf /var/lib/apt/lists/* +# Install build dependencies +RUN apt-get update && apt-get install -y --no-install-recommends git npm build-essential cmake -# Copy entire Keila app and ALL Erlang-related files from official image -COPY --from=pentacent/keila:0.14 /opt/app /app/pkg -COPY --from=pentacent/keila:0.14 /usr/local /usr/local +# Clone Keila repository +RUN git clone --depth 1 --branch v0.15.0 https://github.com/pentacent/keila.git /keila +WORKDIR /keila -# Copy configuration files -COPY start.sh /app/code/start.sh -COPY keila-wrapper.sh /app/code/keila-wrapper.sh -COPY supervisor/ /etc/supervisor/conf.d/ -COPY nginx.conf /etc/nginx/sites-available/default +# Install Elixir and NPM dependencies +ENV MIX_ENV=prod +RUN mix local.hex --force && \ + mix local.rebar --force && \ + mix deps.get --only prod && \ + mix deps.compile -# Set permissions -RUN chmod +x /app/code/start.sh /app/code/keila-wrapper.sh && \ - chown -R cloudron:cloudron /app/code /app/pkg +RUN npm ci --prefix ./assets -WORKDIR /app/code +# Build the release +ENV RELEASE_INCLUDE_ERTS=true +RUN echo 'config :tzdata, autoupdate: :disabled' >> /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