commit 9a1bb3d3bc7544ee6be765129995a1c4182fd5bc Author: Andreas Düren Date: Mon Dec 29 15:50:37 2025 -0600 Initial Blinko Cloudron package - CloudronManifest.json with PostgreSQL and localstorage addons - Dockerfile based on cloudron/base:5.0.0 - NGINX reverse proxy configuration - Supervisor process management - Initialization script with auto-configuration diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..681cdd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Cloudron build artifacts +.cloudronbuild/ + +# Editor files +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db + +# Logs +*.log diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..172d055 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,149 @@ +# Blinko Cloudron Package - Build Instructions + +## Prerequisites + +- Cloudron CLI installed (`npm install -g cloudron`) +- Docker installed +- Access to build service: `builder.docker.due.ren` +- Build service token configured + +## Package Structure + +``` +blink-cloudron/ +├── CloudronManifest.json # Cloudron app manifest +├── Dockerfile # Container build definition +├── start.sh # Initialization and startup script +├── nginx.conf # NGINX reverse proxy configuration +├── supervisor/ +│ └── supervisord.conf # Process management configuration +├── logo.png # App icon (required) +└── BUILD.md # This file +``` + +## Configuration Details + +### Application + +- **App ID**: `io.blinko.cloudronapp` +- **Internal Port**: 1111 (Blinko Node.js server) +- **External Port**: 8000 (NGINX proxy) +- **Memory Limit**: 512MB + +### Addons + +- **PostgreSQL**: Database storage for notes and user data +- **Local Storage**: Persistent file storage at `/app/data` + +### Environment Variables (Auto-configured) + +| Variable | Source | Description | +|----------|--------|-------------| +| `DATABASE_URL` | `CLOUDRON_POSTGRESQL_URL` | PostgreSQL connection string | +| `NEXTAUTH_URL` | `CLOUDRON_APP_ORIGIN` | Application URL for authentication | +| `NEXT_PUBLIC_BASE_URL` | `CLOUDRON_APP_ORIGIN` | Public-facing URL | +| `NEXTAUTH_SECRET` | Auto-generated | Session encryption key | +| `NODE_ENV` | `production` | Runtime environment | +| `TRUST_PROXY` | `1` | Trust reverse proxy headers | + +## Build Commands + +### 1. Get the logo + +Download or create a `logo.png` file (256x256 recommended): + +```bash +# Download from Blinko repository +curl -o logo.png https://raw.githubusercontent.com/blinkospace/blinko/main/public/logo.svg +# Convert to PNG if needed (requires ImageMagick) +# convert logo.svg -resize 256x256 logo.png +``` + +### 2. Build the package + +```bash +cloudron build \ + --set-build-service builder.docker.due.ren \ + --build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e \ + --set-repository andreasdueren/blinko-cloudron \ + --tag 1.0.0 +``` + +### 3. Install the package + +```bash +cloudron install \ + --location blinko.due.ren \ + --image andreasdueren/blinko-cloudron:1.0.0 +``` + +### 4. View logs during installation + +```bash +cloudron logs --app blinko.due.ren -f +``` + +**Important**: Don't wait more than 30 seconds for installation feedback. If there's an error, the install command may hang indefinitely. + +## Troubleshooting + +### Check application logs + +```bash +cloudron logs --app blinko.due.ren -f +``` + +### Shell into the container + +```bash +cloudron exec --app blinko.due.ren +``` + +### Common issues + +1. **Database connection fails** + - Check PostgreSQL addon is properly configured + - Verify `DATABASE_URL` environment variable + +2. **Authentication issues** + - Ensure `NEXTAUTH_URL` matches the app domain + - Check that `NEXTAUTH_SECRET` was generated + +3. **Static files not loading** + - Verify NGINX is running: `supervisorctl status` + - Check NGINX logs for errors + +4. **Memory issues** + - Increase `memoryLimit` in CloudronManifest.json if needed + - Monitor with: `cloudron status --app blinko.due.ren` + +### Rebuild after changes + +Always uninstall and reinstall fresh during development: + +```bash +cloudron uninstall --app blinko.due.ren +cloudron build --set-build-service builder.docker.due.ren \ + --build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e \ + --set-repository andreasdueren/blinko-cloudron \ + --tag 1.0.1 +cloudron install --location blinko.due.ren --image andreasdueren/blinko-cloudron:1.0.1 +``` + +## Data Persistence + +All persistent data is stored in `/app/data`: + +- `/app/data/.blinko` - Application data directory +- `/app/data/.nextauth_secret` - Authentication secret +- `/app/data/.initialized` - First-run marker + +This directory is automatically backed up by Cloudron. + +## References + +- [Blinko Documentation](https://blinko.mintlify.app/) +- [Blinko GitHub](https://github.com/blinkospace/blinko) +- [Cloudron Packaging Guide](https://docs.cloudron.io/packaging/tutorial/) +- [Cloudron Manifest Reference](https://docs.cloudron.io/packaging/manifest/) +- [Cloudron Addons](https://docs.cloudron.io/packaging/addons/) diff --git a/CloudronManifest.json b/CloudronManifest.json new file mode 100644 index 0000000..e11a383 --- /dev/null +++ b/CloudronManifest.json @@ -0,0 +1,30 @@ +{ + "id": "io.blinko.cloudronapp", + "title": "Blinko", + "author": "Blinko Space", + "description": "AI-powered card note-taking application for capturing fleeting thoughts with RAG-enhanced search, markdown support, and self-hosted privacy.", + "tagline": "AI-powered note-taking", + "version": "1.0.0", + "healthCheckPath": "/", + "httpPort": 8000, + "addons": { + "postgresql": {}, + "localstorage": {} + }, + "manifestVersion": 2, + "website": "https://blinko.mintlify.app/", + "contactEmail": "support@blinko.space", + "icon": "file://logo.png", + "tags": [ + "notes", + "ai", + "productivity", + "markdown" + ], + "memoryLimit": 512000000, + "minBoxVersion": "7.0.0", + "postInstallMessage": "Blinko has been installed successfully!\n\nVisit your Blinko instance to create your first account.\nThe first user to register will become the administrator.\n\nData is stored in /app/data and will be backed up automatically.", + "changelog": { + "1.0.0": "Initial Cloudron package" + } +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ec11cf1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,67 @@ +FROM cloudron/base:5.0.0 + +# Install Bun for building +RUN curl -fsSL https://bun.sh/install | bash +ENV PATH="/root/.bun/bin:${PATH}" + +# Set up working directory +WORKDIR /app/code + +# Clone Blinko repository +ARG BLINKO_VERSION=main +RUN git clone --depth 1 --branch ${BLINKO_VERSION} https://github.com/blinkospace/blinko.git /tmp/blinko + +# Build the application +WORKDIR /tmp/blinko + +# Install dependencies with Bun +RUN bun install --frozen-lockfile + +# Handle Sharp for native image processing +RUN ARCH=$(uname -m) && \ + if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then \ + bun add sharp@0.34.1 --arch=arm64 --platform=linux; \ + fi + +# Generate Prisma client +RUN bunx prisma generate + +# Build the application +RUN bun run build:web +RUN bun run build:seed + +# Copy built application to /app/code +WORKDIR /app/code +RUN cp -r /tmp/blinko/dist /app/code/dist && \ + cp -r /tmp/blinko/server /app/code/server && \ + cp -r /tmp/blinko/prisma /app/code/prisma && \ + cp -r /tmp/blinko/node_modules /app/code/node_modules && \ + cp /tmp/blinko/package.json /app/code/package.json + +# Copy public assets if they exist +RUN if [ -d /tmp/blinko/public ]; then cp -r /tmp/blinko/public /app/code/public; fi + +# Set up initial data directory template +RUN mkdir -p /tmp/data/.blinko + +# Install dumb-init +RUN ARCH=$(uname -m) && \ + if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then \ + DUMB_INIT_ARCH="aarch64"; \ + else \ + DUMB_INIT_ARCH="x86_64"; \ + fi && \ + curl -Lo /usr/local/bin/dumb-init "https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_${DUMB_INIT_ARCH}" && \ + chmod +x /usr/local/bin/dumb-init + +# Clean up +RUN rm -rf /tmp/blinko + +# Copy Cloudron configuration files +COPY start.sh /app/code/start.sh +COPY nginx.conf /app/code/nginx.conf +COPY supervisor/ /app/code/supervisor/ + +RUN chmod +x /app/code/start.sh + +CMD ["/app/code/start.sh"] diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000..e4bad6a --- /dev/null +++ b/NOTES.md @@ -0,0 +1,93 @@ +# Blinko Cloudron Package - Development Notes + +## Application Summary + +**Blinko** is an AI-powered card note-taking application with: +- RAG (Retrieval-Augmented Generation) for natural language search +- Markdown support +- Self-hosted, privacy-focused design +- PostgreSQL database backend + +## Tech Stack + +- **Frontend**: React, Next.js +- **Backend**: Node.js, Express +- **Database**: PostgreSQL 14+ +- **ORM**: Prisma +- **Build Tool**: Bun +- **Runtime Port**: 1111 + +## Key Environment Variables + +| Variable | Purpose | +|----------|---------| +| `DATABASE_URL` | PostgreSQL connection string | +| `NEXTAUTH_URL` | App URL for NextAuth.js | +| `NEXT_PUBLIC_BASE_URL` | Public-facing URL | +| `NEXTAUTH_SECRET` | Session encryption secret | +| `TRUST_PROXY` | Enable when behind reverse proxy | +| `TZ` | Timezone setting | + +## Cloudron Integration + +### Addons Used +- `postgresql` - Database storage +- `localstorage` - Persistent file storage + +### Port Mapping +- NGINX listens on port 8000 (Cloudron httpPort) +- Proxies to Blinko on port 1111 + +### Data Directories +- `/app/data/.blinko` - Application files +- `/app/data/.nextauth_secret` - Auth secret (generated on first run) +- `/app/data/.initialized` - First-run marker + +## Build Commands Quick Reference + +```bash +# Build +cloudron build \ + --set-build-service builder.docker.due.ren \ + --build-service-token e3265de06b1d0e7bb38400539012a8433a74c2c96a17955e \ + --set-repository andreasdueren/blinko-cloudron \ + --tag VERSION + +# Install +cloudron install \ + --location blinko.due.ren \ + --image andreasdueren/blinko-cloudron:VERSION + +# Uninstall (for fresh install during development) +cloudron uninstall --app blinko.due.ren + +# Logs +cloudron logs --app blinko.due.ren -f + +# Shell access +cloudron exec --app blinko.due.ren +``` + +## Files in Package + +| File | Purpose | +|------|---------| +| `CloudronManifest.json` | App metadata, addons, ports | +| `Dockerfile` | Build instructions | +| `start.sh` | Initialization and startup | +| `nginx.conf` | Reverse proxy config | +| `supervisor/supervisord.conf` | Process management | +| `logo.png` | App icon (needs to be added) | + +## Known Considerations + +1. **First user becomes admin** - The first account registered gets admin privileges +2. **Memory usage** - Set to 512MB, may need adjustment for heavy use +3. **AI features** - Require additional configuration (OpenAI API key, etc.) +4. **File uploads** - 50MB limit configured in NGINX + +## Upstream Resources + +- GitHub: https://github.com/blinkospace/blinko +- Docs: https://blinko.mintlify.app/ +- Docker: https://hub.docker.com/r/blinkospace/blinko diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..aec5063 Binary files /dev/null and b/logo.png differ diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..2f1dbd8 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,85 @@ +daemon off; +worker_processes auto; +pid /run/nginx.pid; +error_log stderr; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + access_log /dev/stdout; + error_log stderr; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + client_max_body_size 50M; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript application/xml application/xml+rss text/javascript application/x-javascript image/svg+xml; + + upstream blinko { + server 127.0.0.1:1111; + keepalive 32; + } + + server { + listen 8000; + server_name _; + + # Health check endpoint + location /health { + access_log off; + return 200 'OK'; + add_header Content-Type text/plain; + } + + # Proxy all requests to Blinko + location / { + proxy_pass http://blinko; + proxy_http_version 1.1; + + 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; + + # WebSocket support + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_buffering off; + proxy_cache off; + + # Timeouts for long-running requests + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + } + + # Static files with caching + location /_next/static/ { + proxy_pass http://blinko; + proxy_http_version 1.1; + + 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 7d; + add_header Cache-Control "public, immutable"; + } + } +} diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..59d04a2 --- /dev/null +++ b/start.sh @@ -0,0 +1,61 @@ +#!/bin/bash +set -eu + +echo "=> Starting Blinko for Cloudron" + +# Initialize data directory on first run +if [[ ! -f /app/data/.initialized ]]; then + echo "=> First run: initializing data directory..." + mkdir -p /app/data/.blinko + touch /app/data/.initialized +fi + +# Generate NEXTAUTH_SECRET if not present +if [[ ! -f /app/data/.nextauth_secret ]]; then + echo "=> Generating NEXTAUTH_SECRET..." + openssl rand -base64 32 > /app/data/.nextauth_secret +fi + +NEXTAUTH_SECRET=$(cat /app/data/.nextauth_secret) + +# Set ownership +chown -R cloudron:cloudron /app/data + +# Configure environment from Cloudron +export NODE_ENV=production +export DATABASE_URL="${CLOUDRON_POSTGRESQL_URL}" +export NEXTAUTH_URL="${CLOUDRON_APP_ORIGIN}" +export NEXT_PUBLIC_BASE_URL="${CLOUDRON_APP_ORIGIN}" +export NEXTAUTH_SECRET="${NEXTAUTH_SECRET}" +export TRUST_PROXY=1 +export DISABLE_SECURE_COOKIE=false + +# Configure mail settings if available +if [[ -n "${CLOUDRON_MAIL_SMTP_SERVER:-}" ]]; then + export SMTP_HOST="${CLOUDRON_MAIL_SMTP_SERVER}" + export SMTP_PORT="${CLOUDRON_MAIL_SMTP_PORT}" + export SMTP_USER="${CLOUDRON_MAIL_SMTP_USERNAME}" + export SMTP_PASS="${CLOUDRON_MAIL_SMTP_PASSWORD}" + export SMTP_FROM="${CLOUDRON_MAIL_FROM}" +fi + +# Set timezone if configured +export TZ="${TZ:-UTC}" + +# Link data directory for Blinko +ln -sfn /app/data/.blinko /app/code/.blinko + +# Run database migrations +echo "=> Running database migrations..." +cd /app/code +npx prisma migrate deploy --schema=/app/code/prisma/schema.prisma + +# Seed database if needed +echo "=> Checking database seed..." +node /app/code/server/seed.js 2>/dev/null || true + +# Configure NGINX +cp /app/code/nginx.conf /run/nginx.conf + +echo "=> Starting supervisor..." +exec /usr/bin/supervisord --configuration /app/code/supervisor/supervisord.conf --nodaemon diff --git a/supervisor/supervisord.conf b/supervisor/supervisord.conf new file mode 100644 index 0000000..43dbd0f --- /dev/null +++ b/supervisor/supervisord.conf @@ -0,0 +1,38 @@ +[supervisord] +nodaemon=true +logfile=/dev/stdout +logfile_maxbytes=0 +pidfile=/run/supervisord.pid + +[unix_http_server] +file=/run/supervisor.sock + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///run/supervisor.sock + +[program:nginx] +command=/usr/sbin/nginx -c /run/nginx.conf +directory=/app/code +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +priority=10 + +[program:blinko] +command=/usr/local/bin/dumb-init node /app/code/server/index.js +directory=/app/code +user=cloudron +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +priority=20 +environment=NODE_ENV="production",HOME="/app/data"