commit 4a1a5129f27ca8cb7973392a283466295483f0e3 Author: Andreas Dueren Date: Fri Jun 20 07:56:11 2025 -0600 Initial Keila Cloudron package diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..3cc1caf --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,9 @@ +[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 new file mode 100644 index 0000000..a22ffce --- /dev/null +++ b/CloudronManifest.json @@ -0,0 +1,37 @@ +{ + "id": "io.keila.cloudronapp", + "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", + "icon": "file://icon.png", + "tags": [ + "email", + "newsletter", + "marketing", + "campaigns" + ], + "memoryLimit": 536870912, + "addons": { + "postgresql": {}, + "sendmail": {}, + "localstorage": {} + }, + "minBoxVersion": "7.0.0", + "postInstallMessage": "file://POSTINSTALL.md", + "changelog": "file://CHANGELOG", + "optionalSso": false, + "tcpPorts": { + "KEILA_PORT": { + "title": "Keila HTTP Port", + "description": "Keila HTTP Port", + "containerPort": 4000 + } + } +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..71451f8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ +FROM cloudron/base:4.2.0 + +# Install dependencies +RUN mkdir -p /app/code /app/data +WORKDIR /app/code + +# Install Elixir and dependencies +RUN curl -fSL https://github.com/elixir-lang/elixir/releases/download/v1.15.7/elixir-otp-26.zip -o elixir.zip \ + && unzip elixir.zip -d /opt/elixir \ + && rm elixir.zip \ + && ln -s /opt/elixir/bin/* /usr/local/bin/ \ + && apt-get update \ + && apt-get install -y git build-essential cmake nodejs npm postgresql-client \ + && rm -rf /var/lib/apt/lists/* + +# Set environment variables +ENV MIX_ENV=prod +ENV PATH="/opt/elixir/bin:$PATH" + +# Clone and build Keila +RUN git clone --depth 1 --branch v0.14.7 https://github.com/pentacent/keila.git . \ + && mix local.hex --force \ + && mix local.rebar --force \ + && mix deps.get --only prod \ + && mix deps.compile \ + && cd assets && npm ci && npm run deploy && cd .. \ + && mix phx.digest \ + && mix release + +# Copy initialization data +RUN cp -r _build/prod/rel/keila /tmp/keila-release + +# Copy start script +COPY start.sh /app/code/start.sh +COPY nginx.conf /etc/nginx/sites-available/default + +# Set permissions +RUN chmod +x /app/code/start.sh \ + && chown -R cloudron:cloudron /app/code /app/data /tmp/keila-release + +EXPOSE 4000 + +CMD ["/app/code/start.sh"] \ No newline at end of file diff --git a/POSTINSTALL.md b/POSTINSTALL.md new file mode 100644 index 0000000..73f0e21 --- /dev/null +++ b/POSTINSTALL.md @@ -0,0 +1,28 @@ +# 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 new file mode 100644 index 0000000..cb80f5c --- /dev/null +++ b/README.md @@ -0,0 +1,143 @@ +# 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/nginx.conf b/nginx.conf new file mode 100644 index 0000000..c87a11a --- /dev/null +++ b/nginx.conf @@ -0,0 +1,79 @@ +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 new file mode 100644 index 0000000..eeccb5e --- /dev/null +++ b/robots.txt @@ -0,0 +1,5 @@ +User-agent: * +Disallow: /admin/ +Disallow: /api/ +Disallow: /auth/ +Allow: / \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..9389957 --- /dev/null +++ b/start.sh @@ -0,0 +1,68 @@ +#!/bin/bash +set -eu + +echo "Starting Keila on Cloudron" + +# Copy Keila release to /app/data on first run +if [[ ! -d "/app/data/keila" ]]; then + echo "==> Initializing Keila installation" + cp -r /tmp/keila-release /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 +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" +export PORT="4000" + +# 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" +mkdir -p /app/data/uploads +chown cloudron:cloudron /app/data/uploads + +# Disable registration for security (admin can create users) +export DISABLE_REGISTRATION="true" + +# Set database pool size +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 +fi + +echo "==> Starting nginx" +nginx -t +nginx + +echo "==> Running database migrations" +cd /app/data/keila +sudo -u cloudron -E /app/data/keila/bin/keila eval "Keila.Release.migrate()" + +echo "==> Starting Keila application" +cd /app/data/keila +exec sudo -u cloudron -E /app/data/keila/bin/keila start \ No newline at end of file