From ff465cdab69340419317b043c168c5c4d418fd9a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 8 May 2026 19:27:02 -0600 Subject: [PATCH 01/78] fix(cloudflared): opt-out of isolated deployment (#856) Cloudflared requires network_mode: host which is mutually exclusive with Docker networks. Setting isolated = false prevents Dokploy from injecting networks into this compose. Fixes https://github.com/Dokploy/dokploy/issues/4366 --- blueprints/cloudflared/template.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/blueprints/cloudflared/template.toml b/blueprints/cloudflared/template.toml index 7d2557cc..2b4d5239 100644 --- a/blueprints/cloudflared/template.toml +++ b/blueprints/cloudflared/template.toml @@ -1,6 +1,7 @@ variables = {} [config] +isolated = false domains = [] mounts = [] From 5ca048afcf29dd0af74eba5aecf2da7e9cd5f329 Mon Sep 17 00:00:00 2001 From: Dries Augustyns Date: Tue, 12 May 2026 22:17:35 +0200 Subject: [PATCH 02/78] Update Plunk template to use next version (#622) * Update Plunk template to use next version * Add domain configuration to config.env * Remove NTFY from Plunk --- blueprints/plunk/docker-compose.yml | 186 ++++++++++++++++++++++------ blueprints/plunk/template.toml | 120 +++++++++++++++--- 2 files changed, 252 insertions(+), 54 deletions(-) diff --git a/blueprints/plunk/docker-compose.yml b/blueprints/plunk/docker-compose.yml index 914001cf..9991d855 100644 --- a/blueprints/plunk/docker-compose.yml +++ b/blueprints/plunk/docker-compose.yml @@ -1,58 +1,166 @@ -# IMPORTANT: Plunk requires HTTPS to work properly -# go to the "Domains" tab and enable HTTPS for your domain +# Plunk Self-Hosting Docker Compose for Dokploy +# This setup runs all Plunk services with nginx reverse proxy +# +# IMPORTANT: This template requires multiple subdomains: +# - api.yourdomain.com -> API Server +# - app.yourdomain.com -> Web Dashboard +# - www.yourdomain.com -> Landing Page +# - docs.yourdomain.com -> Documentation +# - minio.yourdomain.com -> Minio Console (optional) +# +# All domains will be automatically configured in Dokploy services: - plunk: - image: driaug/plunk - expose: - - "3000" - depends_on: - db: - condition: service_healthy - redis: - condition: service_started - environment: - REDIS_URL: ${REDIS_URL} - DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB} - JWT_SECRET: ${JWT_SECRET} - AWS_REGION: ${AWS_REGION} - AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} - AWS_SES_CONFIGURATION_SET: ${AWS_SES_CONFIGURATION_SET} - APP_URI: ${APP_URI} - NEXT_PUBLIC_API_URI: ${APP_URI}/api - API_URI: ${APP_URI}/api - DISABLE_SIGNUPS: ${DISABLE_SIGNUPS} - entrypoint: ["/app/entry.sh"] - restart: unless-stopped + # ============================================ + # Infrastructure Services + # ============================================ - db: - image: postgres:alpine + postgres: + image: postgres:16-alpine + restart: unless-stopped environment: - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_DB: plunk + POSTGRES_USER: plunk + POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + test: ["CMD-SHELL", "pg_isready -U plunk"] interval: 10s + timeout: 5s retries: 5 - timeout: 10s - restart: unless-stopped - expose: - - 5432 redis: - image: redis:alpine + image: redis:7-alpine restart: unless-stopped - expose: - - 6379 + command: redis-server --appendonly yes volumes: - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + minio: + image: minio/minio:latest + restart: unless-stopped + command: server /data --console-address ":9001" + environment: + MINIO_ROOT_USER: ${MINIO_ROOT_USER} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} + volumes: + - minio_data:/data + ports: + - 9000 + - 9001 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 30s + timeout: 20s + retries: 3 + + # ============================================ + # Plunk Application + # ============================================ + + plunk: + image: ghcr.io/useplunk/plunk:latest + restart: unless-stopped + environment: + # Service mode - runs all services in one container + SERVICE: all + NODE_ENV: production + + # Database + DATABASE_URL: postgresql://plunk:${DB_PASSWORD}@postgres:5432/plunk + DIRECT_DATABASE_URL: postgresql://plunk:${DB_PASSWORD}@postgres:5432/plunk + + # Redis + REDIS_URL: ${REDIS_URL} + + # Security + JWT_SECRET: ${JWT_SECRET} + + # Nginx configuration + NGINX_PORT: ${NGINX_PORT} + + # Domain configuration (subdomain-based routing) + API_DOMAIN: ${API_DOMAIN} + DASHBOARD_DOMAIN: ${DASHBOARD_DOMAIN} + LANDING_DOMAIN: ${LANDING_DOMAIN} + WIKI_DOMAIN: ${WIKI_DOMAIN} + USE_HTTPS: ${USE_HTTPS} + + # AWS SES (for sending emails) - REQUIRED + AWS_SES_REGION: ${AWS_SES_REGION} + AWS_SES_ACCESS_KEY_ID: ${AWS_SES_ACCESS_KEY_ID} + AWS_SES_SECRET_ACCESS_KEY: ${AWS_SES_SECRET_ACCESS_KEY} + SES_CONFIGURATION_SET: ${SES_CONFIGURATION_SET} + SES_CONFIGURATION_SET_NO_TRACKING: ${SES_CONFIGURATION_SET_NO_TRACKING} + + # Optional: OAuth + GITHUB_OAUTH_CLIENT: ${GITHUB_OAUTH_CLIENT} + GITHUB_OAUTH_SECRET: ${GITHUB_OAUTH_SECRET} + GOOGLE_OAUTH_CLIENT: ${GOOGLE_OAUTH_CLIENT} + GOOGLE_OAUTH_SECRET: ${GOOGLE_OAUTH_SECRET} + + # Optional: Stripe + STRIPE_SK: ${STRIPE_SK} + STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET} + STRIPE_PRICE_ONBOARDING: ${STRIPE_PRICE_ONBOARDING} + STRIPE_PRICE_EMAIL_USAGE: ${STRIPE_PRICE_EMAIL_USAGE} + STRIPE_METER_EVENT_NAME: ${STRIPE_METER_EVENT_NAME} + + # S3-compatible storage (Minio) + S3_ENDPOINT: ${S3_ENDPOINT} + S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID} + S3_ACCESS_KEY_SECRET: ${S3_ACCESS_KEY_SECRET} + S3_BUCKET: ${S3_BUCKET} + S3_PUBLIC_URL: ${S3_PUBLIC_URL} + S3_FORCE_PATH_STYLE: ${S3_FORCE_PATH_STYLE} + + # SMTP Server (for sending emails via SMTP) + SMTP_DOMAIN: ${SMTP_DOMAIN} + PORT_SECURE: ${PORT_SECURE} + PORT_SUBMISSION: ${PORT_SUBMISSION} + + # Internal + PLUNK_API_KEY: ${PLUNK_API_KEY} + PLUNK_FROM_ADDRESS: ${PLUNK_FROM_ADDRESS} + + volumes: + # Mount Traefik certificates for SSL + - /etc/dokploy/traefik/dynamic/acme.json:/certs/acme.json:ro + + ports: + # Main nginx port (handles all subdomain routing) + - 80 + # SMTP ports (for email relay) + - 465 + - 587 + + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + minio: + condition: service_healthy + + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:80/"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s volumes: postgres_data: driver: local redis_data: - driver: local \ No newline at end of file + driver: local + minio_data: + driver: local + plunk_data: + driver: local diff --git a/blueprints/plunk/template.toml b/blueprints/plunk/template.toml index 831f2c47..ce92c1fe 100644 --- a/blueprints/plunk/template.toml +++ b/blueprints/plunk/template.toml @@ -1,30 +1,120 @@ [variables] -main_domain = "${domain}" -postgres_user = "plunk" -postgres_db = "plunk" +# Generate unique domains for each service +api_domain = "${domain}" +dashboard_domain = "${domain}" +landing_domain = "${domain}" +wiki_domain = "${domain}" +minio_domain = "${domain}" + +# Database credentials +postgres_password = "${password:32}" + +# Security +jwt_secret = "${password:64}" + +# Minio credentials +minio_root_user = "plunk" +minio_root_password = "${password:32}" [config] isolated = true +# API Domain [[config.domains]] serviceName = "plunk" -port = 3000 -host = "${main_domain}" +port = 80 +host = "${api_domain}" +path = "/" + +# Dashboard Domain +[[config.domains]] +serviceName = "plunk" +port = 80 +host = "${dashboard_domain}" +path = "/" + +# Landing Page Domain +[[config.domains]] +serviceName = "plunk" +port = 80 +host = "${landing_domain}" +path = "/" + +# Documentation Domain +[[config.domains]] +serviceName = "plunk" +port = 80 +host = "${wiki_domain}" +path = "/" + +# Minio Console Domain (optional) +[[config.domains]] +serviceName = "minio" +port = 9001 +host = "${minio_domain}" path = "/" [config.env] -POSTGRES_USER = "${postgres_user}" -POSTGRES_DB = "${postgres_db}" -POSTGRES_PASSWORD = "${password:32}" +# Database +DB_PASSWORD = "${postgres_password}" +# Redis REDIS_URL = "redis://redis:6379" -JWT_SECRET = "${password:64}" -APP_URI = "https://${main_domain}" +# Security +JWT_SECRET = "${jwt_secret}" -AWS_REGION = "" -AWS_ACCESS_KEY_ID = "" -AWS_SECRET_ACCESS_KEY = "" -AWS_SES_CONFIGURATION_SET = "" +# Nginx configuration +NGINX_PORT = "80" -DISABLE_SIGNUPS = "False" \ No newline at end of file +# Domain configuration (subdomain-based routing) +API_DOMAIN = "${api_domain}" +DASHBOARD_DOMAIN = "${dashboard_domain}" +LANDING_DOMAIN = "${landing_domain}" +WIKI_DOMAIN = "${wiki_domain}" +USE_HTTPS = "true" + +# AWS SES (Required - users must configure these for email to work) +AWS_SES_REGION = "us-east-1" +AWS_SES_ACCESS_KEY_ID = "" +AWS_SES_SECRET_ACCESS_KEY = "" +SES_CONFIGURATION_SET = "" + +# S3-compatible storage (Minio) +MINIO_ROOT_USER = "${minio_root_user}" +MINIO_ROOT_PASSWORD = "${minio_root_password}" +S3_ENDPOINT = "http://minio:9000" +S3_ACCESS_KEY_ID = "${minio_root_user}" +S3_ACCESS_KEY_SECRET = "${minio_root_password}" +S3_BUCKET = "uploads" +S3_PUBLIC_URL = "https://${minio_domain}/uploads" +S3_FORCE_PATH_STYLE = "true" + +# Minio ports +MINIO_API_PORT = "9000" +MINIO_CONSOLE_PORT = "9001" + +# SMTP Server ports +PORT_SECURE = "465" +PORT_SUBMISSION = "587" + +# Notifications (ntfy.sh) +NTFY_URL = "http://ntfy/plunk-notifications" + +# Optional: OAuth (leave empty if not using) +GITHUB_OAUTH_CLIENT = "" +GITHUB_OAUTH_SECRET = "" +GOOGLE_OAUTH_CLIENT = "" +GOOGLE_OAUTH_SECRET = "" + +# Optional: Stripe (leave empty if not using) +STRIPE_SK = "" +STRIPE_WEBHOOK_SECRET = "" +STRIPE_PRICE_ONBOARDING = "" +STRIPE_PRICE_EMAIL_USAGE = "" +STRIPE_METER_EVENT_NAME = "emails" + +# Internal +PLUNK_API_KEY = "" +PLUNK_FROM_ADDRESS = "" +SMTP_DOMAIN = "" \ No newline at end of file From 94b9aa70de65e46cbdae0ccddbb6e6bf7f1b868a Mon Sep 17 00:00:00 2001 From: Harikrishnan Dhanasekaran Date: Wed, 13 May 2026 02:02:59 +0530 Subject: [PATCH 03/78] Added the frappe lending template (#665) * added the frappe lending template * removed the external network --- blueprints/frappe-lending/docker-compose.yml | 550 +++++++++++++++++++ blueprints/frappe-lending/frappe-lending.png | Bin 0 -> 2985 bytes blueprints/frappe-lending/template.toml | 29 + meta.json | 20 + 4 files changed, 599 insertions(+) create mode 100644 blueprints/frappe-lending/docker-compose.yml create mode 100644 blueprints/frappe-lending/frappe-lending.png create mode 100644 blueprints/frappe-lending/template.toml diff --git a/blueprints/frappe-lending/docker-compose.yml b/blueprints/frappe-lending/docker-compose.yml new file mode 100644 index 00000000..60972a3b --- /dev/null +++ b/blueprints/frappe-lending/docker-compose.yml @@ -0,0 +1,550 @@ +x-custom-image: &custom_image + image: ${IMAGE_NAME:-frappe/erpnext}:${VERSION:-v15} + pull_policy: ${PULL_POLICY:-always} + deploy: + restart_policy: + condition: always + +services: + backend: + <<: *custom_image + entrypoint: ["bash", "-c"] + command: + - > + cd /home/frappe/frappe-bench; + if [ -d "apps/lending" ] && ! ./env/bin/pip show lending > /dev/null 2>&1; then + echo "Installing lending app in Python environment..."; + ./env/bin/pip install -e apps/lending || echo "Failed to install lending"; + fi; + echo "Waiting for site to be ready..."; + export start=`date +%s`; + while [ ! -f "sites/sites.txt" ] || [ ! -d "sites/${SITE_NAME}" ] || [ ! -f "sites/${SITE_NAME}/site_config.json" ]; do + echo "Waiting for site ${SITE_NAME} to be created and ready..."; + sleep 2; + if (( `date +%s`-start > 300 )); then + echo "Timeout waiting for site to be ready"; + echo "Checking sites directory:"; + ls -la sites/ || echo "sites directory not found"; + echo "Checking sites.txt:"; + cat sites/sites.txt 2>/dev/null || echo "sites.txt not found"; + break; + fi + done; + if [ -f "sites/sites.txt" ] && [ -d "sites/${SITE_NAME}" ] && [ -f "sites/${SITE_NAME}/site_config.json" ]; then + echo "Site ${SITE_NAME} is ready!"; + echo "sites.txt contents:"; + cat sites/sites.txt; + echo "Verifying site is accessible via bench..."; + bench --site ${SITE_NAME} list-apps > /dev/null 2>&1 && echo "Site is accessible via bench" || echo "Warning: Site accessibility check via bench failed"; + echo "Site directory structure:"; + ls -la sites/${SITE_NAME}/ | head -10; + if [ -n "${MAIN_DOMAIN}" ]; then + echo "Updating host_name and SSL settings to https://${MAIN_DOMAIN} (ensuring correct domain for emails)..."; + bench --site ${SITE_NAME} set-config host_name "https://${MAIN_DOMAIN}" || echo "Warning: Could not update host_name"; + bench --site ${SITE_NAME} set-config host_url "https://${MAIN_DOMAIN}" || echo "Warning: Could not update host_url"; + bench --site ${SITE_NAME} set-config use_ssl 1 || echo "Warning: Could not set use_ssl"; + echo "Verifying configuration..."; + bench --site ${SITE_NAME} get-config host_name 2>/dev/null || echo "Could not read host_name (this is OK if site is not fully initialized)"; + echo "Clearing cache to apply new domain settings..."; + bench --site ${SITE_NAME} clear-cache || echo "Warning: Could not clear cache"; + bench --site ${SITE_NAME} clear-website-cache || echo "Warning: Could not clear website cache"; + fi; + else + echo "Warning: Site may not be fully ready, but starting backend anyway"; + fi; + echo "Starting bench serve (production-ready)"; + export PYTHONUNBUFFERED=1; + bench serve --port 8000 2>&1 | awk '!/127\.0\.0\.1.*400/ {print}' || bench serve --port 8000; + volumes: + - sites:/home/frappe/frappe-bench/sites + - apps:/home/frappe/frappe-bench/apps + environment: + SITE_NAME: ${SITE_NAME} + FRAPPE_SITE_NAME_HEADER: "" + MAIN_DOMAIN: ${MAIN_DOMAIN} + depends_on: + configurator: + condition: service_completed_successfully + required: true + create-site: + condition: service_completed_successfully + required: true + healthcheck: + test: + - CMD + - wait-for-it + - '0.0.0.0:8000' + interval: 2s + timeout: 10s + retries: 30 + + frontend: + <<: *custom_image + entrypoint: ["bash", "-c"] + command: + - > + echo "Starting Nginx with port_in_redirect fix..."; + nginx-entrypoint.sh & + NGINX_PID=$!; + sleep 3; + if [ -f /etc/nginx/conf.d/frappe.conf ]; then + if ! grep -q "port_in_redirect off" /etc/nginx/conf.d/frappe.conf; then + echo "Applying port_in_redirect fix to prevent :8080 in URLs..."; + sed -i 's/listen 8080;/listen 8080;\n port_in_redirect off;\n absolute_redirect off;/' /etc/nginx/conf.d/frappe.conf; + echo "Nginx config updated - reloading..."; + sleep 1; + nginx -s reload 2>/dev/null || echo "Nginx will use updated config on next reload"; + else + echo "Nginx config already has port_in_redirect settings"; + fi + else + echo "Warning: frappe.conf not found yet, will check again"; + sleep 2; + if [ -f /etc/nginx/conf.d/frappe.conf ]; then + if ! grep -q "port_in_redirect off" /etc/nginx/conf.d/frappe.conf; then + sed -i 's/listen 8080;/listen 8080;\n port_in_redirect off;\n absolute_redirect off;/' /etc/nginx/conf.d/frappe.conf; + nginx -s reload 2>/dev/null || true; + fi + fi + fi; + wait $NGINX_PID + depends_on: + backend: + condition: service_started + required: true + websocket: + condition: service_started + required: true + environment: + BACKEND: backend:8000 + FRAPPE_SITE_NAME_HEADER: "" + SOCKETIO: websocket:9000 + UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1 + UPSTREAM_REAL_IP_HEADER: X-Forwarded-For + UPSTREAM_REAL_IP_RECURSIVE: "off" + USE_X_FORWARDED_HOST: "1" + PROXY_FIX: "1" + X_FORWARDED_PROTO: "https" + volumes: + - sites:/home/frappe/frappe-bench/sites + - apps:/home/frappe/frappe-bench/apps + + healthcheck: + test: + - CMD-SHELL + - curl -fsS http://127.0.0.1:8080/login || exit 1 + interval: 30s + timeout: 5s + retries: 3 + + queue-default: + <<: *custom_image + entrypoint: ["bash", "-c"] + command: + - > + cd /home/frappe/frappe-bench; + export start=`date +%s`; + while [ ! -d "apps/lending" ]; do + echo "Waiting for apps to be available..."; + sleep 2; + if (( `date +%s`-start > 300 )); then + echo "Timeout waiting for apps"; + break; + fi; + done; + if [ -d "apps/lending" ] && ! ./env/bin/pip show lending > /dev/null 2>&1; then + echo "Installing lending app in Python environment..."; + ./env/bin/pip install -e apps/lending || echo "Failed to install lending"; + fi; + exec bench worker --queue default + volumes: + - sites:/home/frappe/frappe-bench/sites + - apps:/home/frappe/frappe-bench/apps + healthcheck: + test: + - CMD + - wait-for-it + - 'redis-queue:6379' + interval: 2s + timeout: 10s + retries: 30 + depends_on: + configurator: + condition: service_completed_successfully + required: true + + queue-long: + <<: *custom_image + entrypoint: ["bash", "-c"] + command: + - > + cd /home/frappe/frappe-bench; + export start=`date +%s`; + while [ ! -d "apps/lending" ]; do + echo "Waiting for apps to be available..."; + sleep 2; + if (( `date +%s`-start > 300 )); then + echo "Timeout waiting for apps"; + break; + fi; + done; + if [ -d "apps/lending" ] && ! ./env/bin/pip show lending > /dev/null 2>&1; then + echo "Installing lending app in Python environment..."; + ./env/bin/pip install -e apps/lending || echo "Failed to install lending"; + fi; + exec bench worker --queue long + volumes: + - sites:/home/frappe/frappe-bench/sites + - apps:/home/frappe/frappe-bench/apps + healthcheck: + test: + - CMD + - wait-for-it + - 'redis-queue:6379' + interval: 2s + timeout: 10s + retries: 30 + depends_on: + configurator: + condition: service_completed_successfully + required: true + + queue-short: + <<: *custom_image + entrypoint: ["bash", "-c"] + command: + - > + cd /home/frappe/frappe-bench; + export start=`date +%s`; + while [ ! -d "apps/lending" ]; do + echo "Waiting for apps to be available..."; + sleep 2; + if (( `date +%s`-start > 300 )); then + echo "Timeout waiting for apps"; + break; + fi; + done; + if [ -d "apps/lending" ] && ! ./env/bin/pip show lending > /dev/null 2>&1; then + echo "Installing lending app in Python environment..."; + ./env/bin/pip install -e apps/lending || echo "Failed to install lending"; + fi; + exec bench worker --queue short + volumes: + - sites:/home/frappe/frappe-bench/sites + - apps:/home/frappe/frappe-bench/apps + healthcheck: + test: + - CMD + - wait-for-it + - 'redis-queue:6379' + interval: 2s + timeout: 10s + retries: 30 + depends_on: + configurator: + condition: service_completed_successfully + required: true + + scheduler: + <<: *custom_image + entrypoint: ["bash", "-c"] + command: + - > + cd /home/frappe/frappe-bench; + export start=`date +%s`; + while [ ! -d "apps/lending" ]; do + echo "Waiting for apps to be available..."; + sleep 2; + if (( `date +%s`-start > 300 )); then + echo "Timeout waiting for apps"; + break; + fi; + done; + if [ -d "apps/lending" ] && ! ./env/bin/pip show lending > /dev/null 2>&1; then + echo "Installing lending app in Python environment..."; + ./env/bin/pip install -e apps/lending || echo "Failed to install lending"; + fi; + exec bench schedule + healthcheck: + test: + - CMD + - wait-for-it + - 'redis-queue:6379' + interval: 2s + timeout: 10s + retries: 30 + depends_on: + configurator: + condition: service_completed_successfully + required: true + volumes: + - sites:/home/frappe/frappe-bench/sites + - apps:/home/frappe/frappe-bench/apps + + websocket: + <<: *custom_image + healthcheck: + test: + - CMD + - wait-for-it + - '0.0.0.0:9000' + interval: 2s + timeout: 10s + retries: 30 + command: + - node + - /home/frappe/frappe-bench/apps/frappe/socketio.js + depends_on: + configurator: + condition: service_completed_successfully + required: true + volumes: + - sites:/home/frappe/frappe-bench/sites + - apps:/home/frappe/frappe-bench/apps + + configurator: + <<: *custom_image + deploy: + mode: replicated + replicas: ${CONFIGURE:-0} + restart_policy: + condition: none + entrypoint: ["bash", "-c"] + command: + - > + [[ $${REGENERATE_APPS_TXT} == "1" ]] && ls -1 apps > sites/apps.txt; + [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && exit 0; + bench set-config -g db_host $$DB_HOST; + bench set-config -gp db_port $$DB_PORT; + bench set-config -g redis_cache "redis://$$REDIS_CACHE"; + bench set-config -g redis_queue "redis://$$REDIS_QUEUE"; + bench set-config -g redis_socketio "redis://$$REDIS_QUEUE"; + bench set-config -gp socketio_port $$SOCKETIO_PORT; + environment: + DB_HOST: "${DB_HOST:-db}" + DB_PORT: "3306" + REDIS_CACHE: redis-cache:6379 + REDIS_QUEUE: redis-queue:6379 + SOCKETIO_PORT: "9000" + REGENERATE_APPS_TXT: "${REGENERATE_APPS_TXT:-0}" + volumes: + - sites:/home/frappe/frappe-bench/sites + - apps:/home/frappe/frappe-bench/apps + + create-site: + <<: *custom_image + deploy: + mode: replicated + replicas: ${CREATE_SITE:-0} + restart_policy: + condition: none + entrypoint: ["bash", "-c"] + command: + - > + wait-for-it -t 120 $$DB_HOST:$$DB_PORT; + wait-for-it -t 120 redis-cache:6379; + wait-for-it -t 120 redis-queue:6379; + export start=`date +%s`; + until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \ + [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \ + [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]]; + do + echo "Waiting for sites/common_site_config.json to be created"; + sleep 5; + if (( `date +%s`-start > 120 )); then + echo "could not find sites/common_site_config.json with required keys"; + exit 1 + fi + done; + echo "sites/common_site_config.json found"; + cd /home/frappe/frappe-bench; + if [ -d "sites/${SITE_NAME}" ] && [ -f "sites/${SITE_NAME}/site_config.json" ]; then + echo "Site ${SITE_NAME} already exists, skipping creation"; + bench use $${SITE_NAME} || echo "Warning: Could not set current site"; + echo "$${SITE_NAME}" > sites/sites.txt || echo "Warning: Could not update sites.txt"; + bench set-config -g default_site $${SITE_NAME} || echo "Warning: Could not set default site"; + if [ -n "$${ADMIN_PASSWORD}" ]; then + echo "Updating admin password for existing site..."; + bench --site $${SITE_NAME} set-admin-password $${ADMIN_PASSWORD} || echo "Warning: Could not update admin password (may need to be done manually)"; + fi; + if [ -n "$${MAIN_DOMAIN}" ]; then + echo "Setting host_name and SSL settings to https://${MAIN_DOMAIN} (with HTTPS)..."; + bench --site $${SITE_NAME} set-config host_name "https://$${MAIN_DOMAIN}" || echo "Warning: Could not set host_name"; + bench --site $${SITE_NAME} set-config host_url "https://$${MAIN_DOMAIN}" || echo "Warning: Could not set host_url"; + bench --site $${SITE_NAME} set-config use_ssl 1 || echo "Warning: Could not set use_ssl"; + echo "Clearing cache to apply new domain settings..."; + bench --site $${SITE_NAME} clear-cache || echo "Warning: Could not clear cache"; + bench --site $${SITE_NAME} clear-website-cache || echo "Warning: Could not clear website cache"; + fi; + echo "Verifying existing site is accessible..."; + bench --site $${SITE_NAME} list-apps > /dev/null 2>&1 && echo "Existing site is accessible and working" || echo "Warning: Site accessibility check failed"; + exit 0; + fi; + if [ ! -d "apps/lending" ]; then + echo "Cloning lending app..."; + git clone https://github.com/frappe/lending.git apps/lending || echo "Failed to clone lending app"; + fi; + if [ -d "apps/lending" ]; then + echo "Installing lending app in Python environment..."; + ./env/bin/pip install -e apps/lending || echo "Failed to install lending app"; + fi; + echo "Regenerating apps.txt to include all apps..."; + ls -1 apps > sites/apps.txt || (echo "frappe" > sites/apps.txt && echo "lending" >> sites/apps.txt); + echo "apps.txt contents:"; + cat sites/apps.txt; + bench set-mariadb-host $${DB_HOST:-db}; + bench new-site --mariadb-user-host-login-scope='%' --admin-password=$${ADMIN_PASSWORD} --db-root-username=root --db-root-password=$${DB_ROOT_PASSWORD} $${INSTALL_APP_ARGS} $${SITE_NAME}; + if [ ! -d "sites/${SITE_NAME}" ]; then + echo "ERROR: Site ${SITE_NAME} was not created successfully"; + exit 1; + fi; + echo "Site ${SITE_NAME} created successfully"; + bench use $${SITE_NAME}; + echo "Current site set to: $${SITE_NAME}"; + if [ -d "apps/lending" ]; then + echo "Installing lending app on site..."; + bench --site $${SITE_NAME} install-app lending || echo "Lending app may already be installed or failed to install"; + fi; + echo "Building assets for all apps (this ensures lending JS is available)..."; + bench build --apps lending || echo "Asset build completed"; + echo "Clearing all caches..."; + bench --site $${SITE_NAME} clear-cache || echo "Cache cleared"; + bench --site $${SITE_NAME} clear-website-cache || echo "Website cache cleared"; + echo "Creating sites.txt for site resolution..."; + echo "$${SITE_NAME}" > sites/sites.txt || echo "Warning: Could not create sites.txt"; + echo "Site registered in sites.txt:"; + cat sites/sites.txt || echo "sites.txt not found"; + echo "Verifying site directory and config exist..."; + if [ -d "sites/${SITE_NAME}" ] && [ -f "sites/${SITE_NAME}/site_config.json" ]; then + echo "Site directory and config verified successfully"; + else + echo "Warning: Site directory or config file missing"; + ls -la sites/${SITE_NAME}/ 2>/dev/null || echo "Site directory listing failed"; + fi; + echo "Setting default site in common_site_config.json..."; + bench set-config -g default_site $${SITE_NAME} || echo "Warning: Could not set default site"; + if [ -n "$${MAIN_DOMAIN}" ]; then + echo "Setting host_name and SSL settings to https://${MAIN_DOMAIN} (with HTTPS)..."; + bench --site $${SITE_NAME} set-config host_name "https://$${MAIN_DOMAIN}" || echo "Warning: Could not set host_name"; + bench --site $${SITE_NAME} set-config host_url "https://$${MAIN_DOMAIN}" || echo "Warning: Could not set host_url"; + bench --site $${SITE_NAME} set-config use_ssl 1 || echo "Warning: Could not set use_ssl"; + echo "Clearing cache to apply new domain settings..."; + bench --site $${SITE_NAME} clear-cache || echo "Warning: Could not clear cache"; + bench --site $${SITE_NAME} clear-website-cache || echo "Warning: Could not clear website cache"; + fi; + echo "Verifying site is accessible..."; + bench --site $${SITE_NAME} list-apps > /dev/null 2>&1 && echo "Site is accessible and working" || echo "Warning: Site accessibility check failed"; + volumes: + - sites:/home/frappe/frappe-bench/sites + - apps:/home/frappe/frappe-bench/apps + environment: + SITE_NAME: ${SITE_NAME} + MAIN_DOMAIN: ${main_domain} + ADMIN_PASSWORD: ${ADMIN_PASSWORD} + DB_HOST: ${DB_HOST:-db} + DB_PORT: "${DB_PORT:-3306}" + DB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + INSTALL_APP_ARGS: ${INSTALL_APP_ARGS} + + migration: + <<: *custom_image + deploy: + mode: replicated + replicas: ${MIGRATE:-0} + restart_policy: + condition: none + entrypoint: ["bash", "-c"] + command: + - > + curl -f http://${SITE_NAME}:8080/api/method/ping || echo "Site busy" && exit 0; + bench --site all set-config -p maintenance_mode 1; + bench --site all set-config -p pause_scheduler 1; + bench --site all migrate; + bench --site all set-config -p maintenance_mode 0; + bench --site all set-config -p pause_scheduler 0; + volumes: + - sites:/home/frappe/frappe-bench/sites + - apps:/home/frappe/frappe-bench/apps + + db: + image: mariadb:10.6 + deploy: + mode: replicated + replicas: ${ENABLE_DB:-0} + restart_policy: + condition: always + healthcheck: + test: mysqladmin ping -h localhost --password=${DB_ROOT_PASSWORD} + interval: 1s + retries: 20 + command: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + - --skip-character-set-client-handshake + - --skip-innodb-read-only-compressed + environment: + - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD} + - MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} + volumes: + - db-data:/var/lib/mysql + + redis-cache: + deploy: + restart_policy: + condition: always + image: redis:6.2-alpine + volumes: + - redis-cache-data:/data + healthcheck: + test: + - CMD + - redis-cli + - ping + interval: 5s + timeout: 5s + retries: 3 + + redis-queue: + deploy: + restart_policy: + condition: always + image: redis:6.2-alpine + volumes: + - redis-queue-data:/data + healthcheck: + test: + - CMD + - redis-cli + - ping + interval: 5s + timeout: 5s + retries: 3 + + redis-socketio: + deploy: + restart_policy: + condition: always + image: redis:6.2-alpine + volumes: + - redis-socketio-data:/data + healthcheck: + test: + - CMD + - redis-cli + - ping + interval: 5s + timeout: 5s + retries: 3 + +volumes: + db-data: + redis-cache-data: + redis-queue-data: + redis-socketio-data: + sites: + driver_opts: + type: "${SITE_VOLUME_TYPE}" + o: "${SITE_VOLUME_OPTS}" + device: "${SITE_VOLUME_DEV}" + apps: + diff --git a/blueprints/frappe-lending/frappe-lending.png b/blueprints/frappe-lending/frappe-lending.png new file mode 100644 index 0000000000000000000000000000000000000000..e94e65a6e91233b476d6c394ee356aabb6b133be GIT binary patch literal 2985 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 GHum3@T!&}? literal 0 HcmV?d00001 diff --git a/blueprints/frappe-lending/template.toml b/blueprints/frappe-lending/template.toml new file mode 100644 index 00000000..71612960 --- /dev/null +++ b/blueprints/frappe-lending/template.toml @@ -0,0 +1,29 @@ +[variables] +main_domain = "${domain}" +site_name = "frappe.lending" +db_root_password = "${password:32}" +admin_password = "${password:32}" + +[config] +env = [ + "SITE_NAME=${site_name}", + "ADMIN_PASSWORD=${admin_password}", + "DB_ROOT_PASSWORD=${db_root_password}", + "MIGRATE=1", + "ENABLE_DB=1", + "DB_HOST=db", + "CREATE_SITE=1", + "CONFIGURE=1", + "REGENERATE_APPS_TXT=1", + "INSTALL_APP_ARGS=--install-app erpnext --install-app lending", + "IMAGE_NAME=frappe/erpnext", + "VERSION=latest", + "FRAPPE_SITE_NAME_HEADER=", + "MAIN_DOMAIN=${main_domain}", +] +mounts = [] + +[[config.domains]] +serviceName = "frontend" +port = 8_080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 3ae0cb5e..5a1dae87 100644 --- a/meta.json +++ b/meta.json @@ -2543,6 +2543,26 @@ "performace" ] }, + { + "id": "frappe-lending", + "name": "Frappe Lending", + "version": "latest", + "description": "A comprehensive loan management system built on Frappe Framework. 100% open source and customizable for managing loans, repayments, and lending operations.", + "logo": "frappe-lending.png", + "links": { + "github": "https://github.com/frappe/lending", + "docs": "https://docs.frappe.io/lending", + "website": "https://frappe.io" + }, + "tags": [ + "lending", + "finance", + "loans", + "payments", + "accounting", + "self-hosted" + ] + }, { "id": "freescout", "name": "FreeScout", From 0df6bc5b2905ea6ef23be422feeb11a931248b5f Mon Sep 17 00:00:00 2001 From: Kien Ngo Date: Wed, 13 May 2026 03:40:05 +0700 Subject: [PATCH 04/78] Add go2rtc (#720) * Add go2rtc * Update blueprints/go2rtc/docker-compose.yml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update blueprints/go2rtc/template.toml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Apply suggestion from @greptile-apps[bot] Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update docker-compose.yml * Update template.toml * Update docker-compose.yml * Update docker-compose.yml --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- blueprints/go2rtc/docker-compose.yml | 24 ++++++++++++++++++ blueprints/go2rtc/go2rtc.png | Bin 0 -> 35192 bytes blueprints/go2rtc/template.toml | 13 ++++++++++ meta.json | 36 +++++++++++++-------------- 4 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 blueprints/go2rtc/docker-compose.yml create mode 100644 blueprints/go2rtc/go2rtc.png create mode 100644 blueprints/go2rtc/template.toml diff --git a/blueprints/go2rtc/docker-compose.yml b/blueprints/go2rtc/docker-compose.yml new file mode 100644 index 00000000..947c0feb --- /dev/null +++ b/blueprints/go2rtc/docker-compose.yml @@ -0,0 +1,24 @@ +version: "3.8" + +services: + go2rtc: + image: alexxit/go2rtc:1.9.14 + restart: unless-stopped + + # To use a local webcam, add the device to the service: + # devices: + # - /dev/video0:/dev/video0 # Adjust path if your device is at /dev/video1, etc. + + expose: + - 1984 # Web UI + - 8554 # RTSP + - 8555 # WebRTC + + environment: + - TZ=${TZ} + + volumes: + - go2rtc-config:/config + +volumes: + go2rtc-config: {} diff --git a/blueprints/go2rtc/go2rtc.png b/blueprints/go2rtc/go2rtc.png new file mode 100644 index 0000000000000000000000000000000000000000..a9a81a3c12463dc49045ced599fbd847edb396c2 GIT binary patch literal 35192 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 ZgkPX7A+5GQ)VKfu002ovPDHLkV1nfq0)GGi literal 0 HcmV?d00001 diff --git a/blueprints/go2rtc/template.toml b/blueprints/go2rtc/template.toml new file mode 100644 index 00000000..f650f2fc --- /dev/null +++ b/blueprints/go2rtc/template.toml @@ -0,0 +1,13 @@ +[variables] +main_domain = "${domain}" + +[config] +[[config.domains]] +serviceName = "go2rtc" +port = 1984 +host = "${main_domain}" + +env = [ + "TZ=${TZ}" +] + diff --git a/meta.json b/meta.json index 5a1dae87..1b81ffab 100644 --- a/meta.json +++ b/meta.json @@ -2830,6 +2830,24 @@ "api" ] }, + { + "id": "go2rtc", + "name": "go2rtc", + "version": "latest", + "description": "Ultimate camera streaming application with support for dozens formats and protocols.", + "logo": "go2rtc.png", + "links": { + "github": "https://github.com/AlexxIT/go2rtc", + "website": "https://go2rtc.org/", + "docs": "https://go2rtc.org/" + }, + "tags": [ + "streaming", + "webrtc", + "video", + "home assistant" + ] + }, { "id": "gotenberg", "name": "Gotenberg", @@ -5972,24 +5990,6 @@ "media" ] }, - { - "id": "strapi", - "name": "Strapi", - "version": "v5.33.0", - "description": "Open-source headless CMS to build powerful APIs with built-in content management.", - "logo": "strapi.svg", - "links": { - "github": "https://github.com/strapi/strapi", - "discord": "https://discord.com/invite/strapi", - "docs": "https://docs.strapi.io", - "website": "https://strapi.io" - }, - "tags": [ - "headless", - "cms", - "content-management" - ] - }, { "id": "supabase", "name": "SupaBase", From 1cde3fac82d4692d636f998b59461761edf8d92e Mon Sep 17 00:00:00 2001 From: Juan Date: Tue, 12 May 2026 15:54:37 -0500 Subject: [PATCH 05/78] Update Meilisearch 1.35.1 and fix Cyberchef logo (#722) * fix: update CyberChef logo from SVG to PNG and remove old SVG file * fix: update Meilisearch version to v1.35.1 --- blueprints/cyberchef/cyberchef.png | Bin 0 -> 14616 bytes blueprints/cyberchef/cyberchef.svg | Bin 43309 -> 0 bytes blueprints/meilisearch/docker-compose.yml | 3 +-- blueprints/meilisearch/template.toml | 3 +-- meta.json | 4 ++-- 5 files changed, 4 insertions(+), 6 deletions(-) create mode 100644 blueprints/cyberchef/cyberchef.png delete mode 100644 blueprints/cyberchef/cyberchef.svg diff --git a/blueprints/cyberchef/cyberchef.png b/blueprints/cyberchef/cyberchef.png new file mode 100644 index 0000000000000000000000000000000000000000..4a3dfc32ce6578c7bb4e890b9f46f681c35b77c7 GIT binary patch literal 14616 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 sOW2o+(%anOm##0hPX2OvWLgtwyb224!<~g=` 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 GtbYLsJmQ@I diff --git a/blueprints/meilisearch/docker-compose.yml b/blueprints/meilisearch/docker-compose.yml index ae5ebcb1..db87310f 100644 --- a/blueprints/meilisearch/docker-compose.yml +++ b/blueprints/meilisearch/docker-compose.yml @@ -2,12 +2,11 @@ version: "3.8" services: meilisearch: - image: getmeili/meilisearch:v1.8.3 + image: getmeili/meilisearch:v1.35.1 volumes: - meili_data:/meili_data environment: MEILI_MASTER_KEY: ${MEILI_MASTER_KEY} - MEILI_ENV: ${MEILI_ENV} volumes: meili_data: diff --git a/blueprints/meilisearch/template.toml b/blueprints/meilisearch/template.toml index b1bdb5f9..5b1b94eb 100644 --- a/blueprints/meilisearch/template.toml +++ b/blueprints/meilisearch/template.toml @@ -11,5 +11,4 @@ port = 7_700 host = "${main_domain}" [config.env] -MEILI_ENV = "development" -MEILI_MASTER_KEY = "${master_key}" +MEILI_MASTER_KEY = "${master_key}" \ No newline at end of file diff --git a/meta.json b/meta.json index 1b81ffab..8be7825d 100644 --- a/meta.json +++ b/meta.json @@ -1685,7 +1685,7 @@ "name": "CyberChef", "version": "latest", "description": "CyberChef is a web application for encryption, encoding, compression, and data analysis, developed by GCHQ.", - "logo": "cyberchef.svg", + "logo": "cyberchef.png", "links": { "github": "https://github.com/gchq/CyberChef", "website": "https://gchq.github.io/CyberChef/", @@ -4006,7 +4006,7 @@ { "id": "meilisearch", "name": "Meilisearch", - "version": "v1.8.3", + "version": "v1.35.1", "description": "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", "logo": "meilisearch.png", "links": { From f7dcdc52434eb7fbab7199deb9861bb41c23eb6f Mon Sep 17 00:00:00 2001 From: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> Date: Mon, 22 Jun 2026 18:50:20 +0400 Subject: [PATCH 06/78] feat: add Jitsi Meet template --- blueprints/jitsi/docker-compose.yml | 105 ++++++++++++++++++++++++++++ blueprints/jitsi/jitsi.svg | 1 + blueprints/jitsi/template.toml | 43 ++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 blueprints/jitsi/docker-compose.yml create mode 100644 blueprints/jitsi/jitsi.svg create mode 100644 blueprints/jitsi/template.toml diff --git a/blueprints/jitsi/docker-compose.yml b/blueprints/jitsi/docker-compose.yml new file mode 100644 index 00000000..3e235416 --- /dev/null +++ b/blueprints/jitsi/docker-compose.yml @@ -0,0 +1,105 @@ +services: + web: + image: jitsi/web:${JITSI_IMAGE_VERSION:-unstable} + restart: unless-stopped + depends_on: + - prosody + volumes: + - jitsiWebConfig:/config + - jitsiWebTranscripts:/usr/share/jitsi-meet/transcripts + environment: + - PUBLIC_URL + - TZ + - XMPP_DOMAIN + - XMPP_AUTH_DOMAIN + - XMPP_BOSH_URL_BASE + - XMPP_MUC_DOMAIN + - XMPP_GUEST_DOMAIN + - XMPP_RECORDER_DOMAIN + - ENABLE_AUTH + - ENABLE_GUESTS + - ENABLE_LETSENCRYPT + - ENABLE_HTTP_REDIRECT + - ENABLE_XMPP_WEBSOCKET + - ENABLE_COLIBRI_WEBSOCKET + - DISABLE_HTTPS + - JICOFO_AUTH_USER + - JVB_AUTH_USER + - JICOFO_AUTH_PASSWORD + - JVB_AUTH_PASSWORD + + prosody: + image: jitsi/prosody:${JITSI_IMAGE_VERSION:-unstable} + restart: unless-stopped + volumes: + - jitsiProsodyConfig:/config + - jitsiProsodyPlugins:/prosody-plugins-custom + environment: + - TZ + - XMPP_DOMAIN + - XMPP_AUTH_DOMAIN + - XMPP_MUC_DOMAIN + - XMPP_GUEST_DOMAIN + - XMPP_RECORDER_DOMAIN + - JICOFO_AUTH_USER + - JVB_AUTH_USER + - JICOFO_AUTH_PASSWORD + - JVB_AUTH_PASSWORD + - JICOFO_COMPONENT_SECRET + - JWT_APP_ID + - JWT_APP_SECRET + - ENABLE_AUTH + - ENABLE_GUESTS + + jicofo: + image: jitsi/jicofo:${JITSI_IMAGE_VERSION:-unstable} + restart: unless-stopped + depends_on: + - prosody + volumes: + - jitsiJicofoConfig:/config + environment: + - TZ + - XMPP_DOMAIN + - XMPP_AUTH_DOMAIN + - XMPP_MUC_DOMAIN + - XMPP_INTERNAL_MUC_DOMAIN + - JICOFO_AUTH_USER + - JICOFO_AUTH_PASSWORD + - JICOFO_COMPONENT_SECRET + - JVB_BREWERY_MUC + + jvb: + image: jitsi/jvb:${JITSI_IMAGE_VERSION:-unstable} + restart: unless-stopped + depends_on: + - prosody + ports: + - 10000 + volumes: + - jitsiJvbConfig:/config + environment: + - TZ + - XMPP_AUTH_DOMAIN + - XMPP_INTERNAL_MUC_DOMAIN + - JVB_AUTH_USER + - JVB_AUTH_PASSWORD + - JVB_BREWERY_MUC + - JVB_PORT + - JVB_ADVERTISE_IPS + - PUBLIC_URL + +volumes: + jitsiWebConfig: + driver: local + jitsiWebTranscripts: + driver: local + jitsiProsodyConfig: + driver: local + jitsiProsodyPlugins: + driver: local + jitsiJicofoConfig: + driver: local + jitsiJvbConfig: + driver: local + diff --git a/blueprints/jitsi/jitsi.svg b/blueprints/jitsi/jitsi.svg new file mode 100644 index 00000000..9f6f6700 --- /dev/null +++ b/blueprints/jitsi/jitsi.svg @@ -0,0 +1 @@ + diff --git a/blueprints/jitsi/template.toml b/blueprints/jitsi/template.toml new file mode 100644 index 00000000..83bcfded --- /dev/null +++ b/blueprints/jitsi/template.toml @@ -0,0 +1,43 @@ +[variables] +main_domain = "${domain}" +jitsi_image_version = "unstable" +jicofo_auth_password = "${password:32}" +jvb_auth_password = "${password:32}" +jicofo_component_secret = "${password:32}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "web" +port = 8_000 +host = "${main_domain}" + +[config.env] +PUBLIC_URL = "https://${main_domain}" +TZ = "UTC" +JITSI_IMAGE_VERSION = "${jitsi_image_version}" +XMPP_DOMAIN = "meet.jitsi" +XMPP_AUTH_DOMAIN = "auth.meet.jitsi" +XMPP_BOSH_URL_BASE = "http://prosody:5280" +XMPP_MUC_DOMAIN = "muc.meet.jitsi" +XMPP_INTERNAL_MUC_DOMAIN = "internal-muc.meet.jitsi" +XMPP_GUEST_DOMAIN = "guest.meet.jitsi" +XMPP_RECORDER_DOMAIN = "recorder.meet.jitsi" +ENABLE_AUTH = "0" +ENABLE_GUESTS = "1" +ENABLE_LETSENCRYPT = "0" +ENABLE_HTTP_REDIRECT = "0" +ENABLE_XMPP_WEBSOCKET = "1" +ENABLE_COLIBRI_WEBSOCKET = "1" +DISABLE_HTTPS = "1" +JICOFO_AUTH_USER = "focus" +JVB_AUTH_USER = "jvb" +JICOFO_AUTH_PASSWORD = "${jicofo_auth_password}" +JVB_AUTH_PASSWORD = "${jvb_auth_password}" +JICOFO_COMPONENT_SECRET = "${jicofo_component_secret}" +JVB_BREWERY_MUC = "jvbbrewery" +JVB_PORT = "10000" +JVB_ADVERTISE_IPS = "" +JWT_APP_ID = "" +JWT_APP_SECRET = "" From 98844f7d838f8f17a591286f7f91e4627d7c8c9a Mon Sep 17 00:00:00 2001 From: "Bill Nice G. Havugukuri" <18003647+billnice250@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:47:33 +0200 Subject: [PATCH 07/78] feat: Add Ollama Chat Tone Template (#973) * Add Ollama Chat Tone blueprint with Docker configuration, SVG logo, and metadata * Update .gitignore and replace Ollama Chat Tone SVG with new design * Add Ollama Chat Tone metadata to meta.json * Update GitHub link in meta.json for Ollama Chat Tone * Fix environment variable casing in docker-compose.yml and update template.toml with default values * Remove duplicate .DS_Store entry from .gitignore * Delete .DS_Store Delete .DS_Store --- .gitignore | 2 +- .../ollama-chat-tone/docker-compose.yml | 21 ++++++++++++ .../ollama-chat-tone/ollama-chat-tone.svg | 8 +++++ blueprints/ollama-chat-tone/template.toml | 32 +++++++++++++++++++ meta.json | 18 +++++++++++ 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 blueprints/ollama-chat-tone/docker-compose.yml create mode 100644 blueprints/ollama-chat-tone/ollama-chat-tone.svg create mode 100644 blueprints/ollama-chat-tone/template.toml diff --git a/.gitignore b/.gitignore index 2f8606a3..904891b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ node_modules package-lock.json -meta.json.backup.* +meta.json.backup.* \ No newline at end of file diff --git a/blueprints/ollama-chat-tone/docker-compose.yml b/blueprints/ollama-chat-tone/docker-compose.yml new file mode 100644 index 00000000..8605f824 --- /dev/null +++ b/blueprints/ollama-chat-tone/docker-compose.yml @@ -0,0 +1,21 @@ +services: + ollama-chat-tone: + image: billnice250/chattone:latest + ports: + - 8080 + environment: + APP_NAME: "Ollama Chat Tone" + ADDR: ":8080" + SESSION_SECRET: "${SESSION_SECRET}" + DB_PATH: "/data/app.db" + OLLAMA_URL: "${OLLAMA_URL}" + OLLAMA_TIMEOUT: "${OLLAMA_TIMEOUT}" + DEFAULT_MODEL: "${DEFAULT_MODEL}" + OPEN_BROWSER: "false" + BASIC_AUTH_USER: "${BASIC_AUTH_USER}" + BASIC_AUTH_PASSWORD: "${BASIC_AUTH_PASSWORD}" + volumes: + - ollama-chat-tone-data:/data + +volumes: + ollama-chat-tone-data: {} diff --git a/blueprints/ollama-chat-tone/ollama-chat-tone.svg b/blueprints/ollama-chat-tone/ollama-chat-tone.svg new file mode 100644 index 00000000..b90a86a1 --- /dev/null +++ b/blueprints/ollama-chat-tone/ollama-chat-tone.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/blueprints/ollama-chat-tone/template.toml b/blueprints/ollama-chat-tone/template.toml new file mode 100644 index 00000000..2ab6ae27 --- /dev/null +++ b/blueprints/ollama-chat-tone/template.toml @@ -0,0 +1,32 @@ +[variables] +main_domain = "${domain}" +session_secret = "change-me......32charsOrMore" +ollama_url = "http://your-ollama-host:11434" +ollama_timeout = "5" +default_model = "llama3.2" +basic_auth_user = "admin" +basic_auth_password = "change-me" + +[config] +[[config.domains]] +serviceName = "ollama-chat-tone" +port = 8080 +host = "${main_domain}" +path = "/" + +[config.env] +APP_NAME = "Ollama Chat Tone" +ADDR = ":8080" +SESSION_SECRET = "${session_secret}" +DB_PATH = "/data/app.db" +OLLAMA_URL = "${ollama_url}" +OLLAMA_TIMEOUT = "${ollama_timeout}" +DEFAULT_MODEL = "${default_model}" +OPEN_BROWSER = "false" +BASIC_AUTH_USER = "${basic_auth_user}" +BASIC_AUTH_PASSWORD = "${basic_auth_password}" + +[[config.mounts]] +serviceName = "ollama-chat-tone" +filePath = "/data/app.db" +content = "" diff --git a/meta.json b/meta.json index 8be7825d..361203fd 100644 --- a/meta.json +++ b/meta.json @@ -4532,6 +4532,24 @@ "academic" ] }, + { + "id": "ollama-chat-tone", + "name": "Ollama Chat Tone", + "version": "latest", + "description": "Self-contained Ollama chat client with local auth, streaming responses, and per-user conversations.", + "logo": "ollama-chat-tone.svg", + "links": { + "github": "https://github.com/billnice250/ollama-chat-client", + "website": "https://hub.docker.com/r/billnice250/chattone", + "docs": "https://hub.docker.com/r/billnice250/chattone" + }, + "tags": [ + "chatbot", + "ai", + "ollama", + "docker" + ] + }, { "id": "omni-tools", "name": "Omni-Tools", From 51f0b08b21f593afb528358550959b0033452b7c Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen <86177399+nktnet1@users.noreply.github.com> Date: Wed, 8 Jul 2026 04:47:42 +1000 Subject: [PATCH 08/78] feat: update registry template to v3 (#972) --- blueprints/registry/docker-compose.yml | 44 +++++++++++++++-------- blueprints/registry/template.toml | 48 +++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/blueprints/registry/docker-compose.yml b/blueprints/registry/docker-compose.yml index 08c5c368..a86455d4 100644 --- a/blueprints/registry/docker-compose.yml +++ b/blueprints/registry/docker-compose.yml @@ -1,19 +1,35 @@ services: registry: - restart: always - image: registry:2 - ports: - - 5000 - volumes: - - ../files/auth/registry.password:/auth/registry.password - - registry-data:/var/lib/registry + image: registry:3 + restart: unless-stopped environment: - REGISTRY_STORAGE_DELETE_ENABLED: true - REGISTRY_HEALTH_STORAGEDRIVER_ENABLED: false - REGISTRY_HTTP_SECRET: ${REGISTRY_HTTP_SECRET} - REGISTRY_AUTH: htpasswd - REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm - REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password + - USERNAME=${USERNAME} + - PASSWORD=${PASSWORD} + - REGISTRY_AUTH=htpasswd + - REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm + - REGISTRY_AUTH_HTPASSWD_PATH=/auth/registry.password + - REGISTRY_HTTP_RELATIVEURLS=true + entrypoint: ["sh", "/entrypoint.sh"] + volumes: + - registry-data:/var/lib/registry + - type: bind + source: ../files/entrypoint.sh + target: /entrypoint.sh + - type: bind + source: ../files/config.yml + target: /etc/docker/registry/config.yml + expose: + - 5000 + healthcheck: + test: + - CMD + - wget + - "-q" + - "--spider" + - "http://localhost:5000/" + interval: 5s + timeout: 20s + retries: 10 volumes: - registry-data: \ No newline at end of file + registry-data: diff --git a/blueprints/registry/template.toml b/blueprints/registry/template.toml index a61ca406..a2a16530 100644 --- a/blueprints/registry/template.toml +++ b/blueprints/registry/template.toml @@ -1,6 +1,7 @@ [variables] main_domain = "${domain}" -registry_http_secret = "${password:30}" +username = "${username}" +password = "${password:32}" [[config.domains]] serviceName = "registry" @@ -8,11 +9,48 @@ port = 5_000 host = "${main_domain}" [config.env] -REGISTRY_HTTP_SECRET = "${registry_http_secret}" +USERNAME = "${username}" +PASSWORD = "${password}" [[config.mounts]] -filePath = "/auth/registry.password" +filePath = "./config.yml" content = """ -# from: docker run --rm --entrypoint htpasswd httpd:2 -Bbn docker password -docker:$2y$10$qWZoWev/u5PV7WneFoRAMuoGpRcAQOgUuIIdLnU7pJXogrBSY23/2 +version: 0.1 + +log: + fields: + service: registry +storage: + cache: + blobdescriptor: inmemory + filesystem: + rootdirectory: /var/lib/registry +http: + addr: :5000 + headers: + X-Content-Type-Options: [nosniff] +health: + storagedriver: + enabled: true + interval: 10s + threshold: 3 +""" + +[[config.mounts]] +filePath = "./entrypoint.sh" +content = """ +#!/usr/bin/env sh + +set -e + +if [ -z "$USERNAME" ] || [ -z "$PASSWORD" ]; then + echo "Error: USERNAME and PASSWORD environment variables must be set" + exit 1 +fi + +apk add --no-cache apache2-utils +mkdir -p "$(dirname "$REGISTRY_AUTH_HTPASSWD_PATH")" +chmod 755 "$(dirname "$REGISTRY_AUTH_HTPASSWD_PATH")" +htpasswd -Bbc "$REGISTRY_AUTH_HTPASSWD_PATH" "$USERNAME" "$PASSWORD" +registry serve /etc/docker/registry/config.yml """ From cdd996f73a05401dbcfa438bb0b4f28ba89ae628 Mon Sep 17 00:00:00 2001 From: Julian Gerhards <96697201+juliangerhards@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:53:45 +0200 Subject: [PATCH 09/78] feat: alarik template blueprint (#971) --- blueprints/alarik/alarik.png | Bin 0 -> 56684 bytes blueprints/alarik/docker-compose.yml | 30 +++++++++++++++++++++++++++ blueprints/alarik/template.toml | 26 +++++++++++++++++++++++ meta.json | 17 +++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 blueprints/alarik/alarik.png create mode 100644 blueprints/alarik/docker-compose.yml create mode 100644 blueprints/alarik/template.toml diff --git a/blueprints/alarik/alarik.png b/blueprints/alarik/alarik.png new file mode 100644 index 0000000000000000000000000000000000000000..cc05be94b4135b00ac40179ee56f4841d517198e GIT binary patch literal 56684 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

y3p1U3JS%xDzj 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 Date: Tue, 7 Jul 2026 14:55:31 -0400 Subject: [PATCH 10/78] Add Dokploy configuration template for Spliit with environment variable (#969) * Add Dokploy configuration template for Spliit with environment variables and domain setup * Fix : add double quote * Fix : remove container name * Fix : service name * Fix : use good name of database * Fix : change db name --- blueprints/spliit/docker-compose.yml | 28 +++++++++++++++++++++++++++ blueprints/spliit/spliit.png | Bin 0 -> 9769 bytes blueprints/spliit/template.toml | 24 +++++++++++++++++++++++ meta.json | 17 ++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 blueprints/spliit/docker-compose.yml create mode 100644 blueprints/spliit/spliit.png create mode 100644 blueprints/spliit/template.toml diff --git a/blueprints/spliit/docker-compose.yml b/blueprints/spliit/docker-compose.yml new file mode 100644 index 00000000..69b4acd9 --- /dev/null +++ b/blueprints/spliit/docker-compose.yml @@ -0,0 +1,28 @@ +services: + spliit: + image: ghcr.io/spliit-app/spliit:1.19.0 + restart: unless-stopped + env_file: + - .env + depends_on: + spliit-db: + condition: service_healthy + spliit-db: + image: pgvector/pgvector:pg16 + volumes: + - ${DB_DATA_LOCATION}:/var/lib/DBql/data + environment: + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_DB: ${DB_DATABASE:-spliit} + POSTGRES_INITDB_ARGS: '--data-checksums' + POSTGRES_HOST_AUTH_METHOD: trust + healthcheck: + test: + ['CMD', 'pg_isready', '-U', "${DB_USERNAME}", '-d', "${DB_DATABASE:-spliit}"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + env_file: + - .env \ No newline at end of file diff --git a/blueprints/spliit/spliit.png b/blueprints/spliit/spliit.png new file mode 100644 index 0000000000000000000000000000000000000000..3ac8cdb4ddc3c1e098ca7ffdeff0a7409d7d0cfe GIT binary patch literal 9769 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 DQ8SuX literal 0 HcmV?d00001 diff --git a/blueprints/spliit/template.toml b/blueprints/spliit/template.toml new file mode 100644 index 00000000..528072ab --- /dev/null +++ b/blueprints/spliit/template.toml @@ -0,0 +1,24 @@ + +[variables] +main_domain = "${domain}" +secret_key = "${password}" +project_name = "spliit" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "spliit" +port = 3000 +host = "${main_domain}" + +[config.env] + +DB_USERNAME = "${project_name}-admin" +DB_PASSWORD = "${secret_key}" +DB_DATA_LOCATION = "/data/${project_name}/database" +DB_DATABASE = "${project_name}" + +# --- PostgreSQL --- +POSTGRES_PRISMA_URL = "postgresql://${DB_USERNAME}:${DB_PASSWORD}@${project_name}-db:5432/${DB_DATABASE}" +POSTGRES_URL_NON_POOLING = "postgresql://${DB_USERNAME}:${DB_PASSWORD}@${project_name}-db:5432/${DB_DATABASE}" \ No newline at end of file diff --git a/meta.json b/meta.json index ce662453..204d1c79 100644 --- a/meta.json +++ b/meta.json @@ -5899,6 +5899,23 @@ "storage" ] }, + { + "id": "spliit", + "name": "Spliit", + "version": "latest", + "description": "Spliit is a lightweight, self-hosted alternative to Auth0 and Clerk. Open-source, cost-effective, and developer-friendly, it provides secure authentication (OAuth, email/password, social logins) with full control over your data. No per-user fees, easy to integrate, and perfect for startups or privacy-focused projects.", + "logo": "spliit.png", + "links": { + "github": "https://github.com/spliit-app/spliit", + "website": "https://spliit.app/", + "docs": "https://github.com/spliit-app" + }, + "tags": [ + "budgeting", + "finance", + "money" + ] + }, { "id": "stack-auth", "name": "Stack Auth", From 39d6d0430a798cc646b0c0122fd80c96ad31fc7e Mon Sep 17 00:00:00 2001 From: Marios Antonoudiou Date: Tue, 7 Jul 2026 22:02:09 +0300 Subject: [PATCH 11/78] Introduce chatto app template (#962) * Introduce chatto app template * Clean up template * Update chatto env vars --- blueprints/chatto/docker-compose.yml | 29 +++++++++++++++++++++++++++ blueprints/chatto/logo.png | Bin 0 -> 37271 bytes blueprints/chatto/template.toml | 21 +++++++++++++++++++ meta.json | 18 +++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 blueprints/chatto/docker-compose.yml create mode 100644 blueprints/chatto/logo.png create mode 100644 blueprints/chatto/template.toml diff --git a/blueprints/chatto/docker-compose.yml b/blueprints/chatto/docker-compose.yml new file mode 100644 index 00000000..3edb99d3 --- /dev/null +++ b/blueprints/chatto/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3.8" + +services: + chatto: + image: ghcr.io/chattocorp/chatto:0.3.8 + restart: unless-stopped + expose: + - "4000" + environment: + CHATTO_WEBSERVER_URL: https://${CHATTO_HOST} + CHATTO_WEBSERVER_PORT: "4000" + CHATTO_WEBSERVER_COOKIE_SIGNING_SECRET: ${CHATTO_COOKIE_SECRET} + CHATTO_CORE_SECRET_KEY: ${CHATTO_CORE_SECRET} + CHATTO_CORE_ASSETS_SIGNING_SECRET: ${CHATTO_ASSETS_SECRET} + CHATTO_NATS_EMBEDDED_ENABLED: true + CHATTO_NATS_EMBEDDED_DATA_DIR: /data + CHATTO_OWNERS_EMAILS: ${CHATTO_OWNER_EMAIL} + CHATTO_SMTP_ENABLED: ${CHATTO_SMTP_ENABLED:-false} + CHATTO_SMTP_HOST: ${CHATTO_SMTP_HOST:-} + CHATTO_SMTP_PORT: ${CHATTO_SMTP_PORT:-587} + CHATTO_SMTP_TLS: ${CHATTO_SMTP_TLS:-mandatory} + CHATTO_SMTP_USERNAME: ${CHATTO_SMTP_USERNAME:-} + CHATTO_SMTP_PASSWORD: ${CHATTO_SMTP_PASSWORD:-} + CHATTO_SMTP_FROM: ${CHATTO_SMTP_FROM:-} + volumes: + - chatto-data:/data + +volumes: + chatto-data: diff --git a/blueprints/chatto/logo.png b/blueprints/chatto/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..81928507b36891f7a3d698c8f756812cafc3da37 GIT binary patch literal 37271 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 Date: Wed, 8 Jul 2026 03:02:43 +0800 Subject: [PATCH 12/78] feat: add Hermes agent template (#960) Hermes is an open agent runtime from Nous Research. This template runs the supervised gateway with: - web dashboard (9119) behind Traefik, protected by the bundled basic-auth provider (auth-gated mode triggers automatically once reachable off-loopback); username admin, generated 128-char password - OpenAI-compatible gateway API (8642), enabled and bound to 0.0.0.0 with a generated bearer token (both off by default upstream) Image pinned to v2026.6.19. Provider keys are configured from the dashboard after deploy and persist in the hermes_data volume. Co-authored-by: Claude Opus 4.8 (1M context) --- blueprints/hermes/docker-compose.yml | 27 ++++++++++++++++++++++ blueprints/hermes/logo.png | Bin 0 -> 4100 bytes blueprints/hermes/template.toml | 33 +++++++++++++++++++++++++++ meta.json | 18 +++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 blueprints/hermes/docker-compose.yml create mode 100644 blueprints/hermes/logo.png create mode 100644 blueprints/hermes/template.toml diff --git a/blueprints/hermes/docker-compose.yml b/blueprints/hermes/docker-compose.yml new file mode 100644 index 00000000..6e915e33 --- /dev/null +++ b/blueprints/hermes/docker-compose.yml @@ -0,0 +1,27 @@ +version: "3.8" +services: + hermes: + image: nousresearch/hermes-agent:v2026.6.19 + restart: unless-stopped + command: gateway run + ports: + - 9119 + - 8642 + environment: + - HERMES_DASHBOARD=${HERMES_DASHBOARD} + - HERMES_DASHBOARD_BASIC_AUTH_USERNAME=${HERMES_DASHBOARD_BASIC_AUTH_USERNAME} + - HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=${HERMES_DASHBOARD_BASIC_AUTH_PASSWORD} + - HERMES_DASHBOARD_BASIC_AUTH_SECRET=${HERMES_DASHBOARD_BASIC_AUTH_SECRET} + - API_SERVER_ENABLED=${API_SERVER_ENABLED} + - API_SERVER_HOST=${API_SERVER_HOST} + - API_SERVER_KEY=${API_SERVER_KEY} + volumes: + - hermes_data:/opt/data + deploy: + resources: + limits: + memory: 4G + cpus: "2.0" + +volumes: + hermes_data: diff --git a/blueprints/hermes/logo.png b/blueprints/hermes/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2d629d2f2452f00a7bc5a3252c1c1829c0b476bd GIT binary patch literal 4100 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 128 chars +dashboard_password = "${base64:96}" +dashboard_secret = "${base64:32}" +api_key = "${password:32}" + +[config] +mounts = [] + +# Web dashboard (HERMES_DASHBOARD=1). Exposed behind Traefik, so Hermes' +# auth-gated mode kicks in — protect it with the bundled basic-auth provider. +[[config.domains]] +serviceName = "hermes" +port = 9119 +host = "${dashboard_domain}" + +# OpenAI-compatible gateway API. Disabled and loopback-bound by default, +# so it must be enabled, bound to 0.0.0.0, and given a bearer token. +[[config.domains]] +serviceName = "hermes" +port = 8642 +host = "${gateway_domain}" + +[config.env] +HERMES_DASHBOARD = "1" +HERMES_DASHBOARD_BASIC_AUTH_USERNAME = "admin" +HERMES_DASHBOARD_BASIC_AUTH_PASSWORD = "${dashboard_password}" +HERMES_DASHBOARD_BASIC_AUTH_SECRET = "${dashboard_secret}" +API_SERVER_ENABLED = "true" +API_SERVER_HOST = "0.0.0.0" +API_SERVER_KEY = "${api_key}" diff --git a/meta.json b/meta.json index 06aa27b0..baac068c 100644 --- a/meta.json +++ b/meta.json @@ -2970,6 +2970,24 @@ "self-hosted" ] }, + { + "id": "hermes", + "name": "Hermes", + "version": "v2026.6.19", + "description": "Hermes is an open agent runtime from Nous Research, exposing an OpenAI-compatible gateway API and a supervised web dashboard for building and running AI agents.", + "logo": "logo.png", + "links": { + "github": "https://github.com/NousResearch/hermes-agent", + "website": "https://hermes-agent.nousresearch.com/", + "docs": "https://hermes-agent.nousresearch.com/docs/user-guide/docker" + }, + "tags": [ + "ai", + "agent", + "llm", + "self-hosted" + ] + }, { "id": "heyform", "name": "HeyForm", From 74fd2a960f2057b6095040324102940c198286b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20=C3=87elik?= Date: Tue, 7 Jul 2026 22:03:04 +0300 Subject: [PATCH 13/78] feat: add Inkvoice template (#958) --- blueprints/inkvoice/docker-compose.yml | 22 ++++++++++++++++++++++ blueprints/inkvoice/inkvoice.svg | 7 +++++++ blueprints/inkvoice/template.toml | 17 +++++++++++++++++ meta.json | 18 ++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 blueprints/inkvoice/docker-compose.yml create mode 100644 blueprints/inkvoice/inkvoice.svg create mode 100644 blueprints/inkvoice/template.toml diff --git a/blueprints/inkvoice/docker-compose.yml b/blueprints/inkvoice/docker-compose.yml new file mode 100644 index 00000000..bd275006 --- /dev/null +++ b/blueprints/inkvoice/docker-compose.yml @@ -0,0 +1,22 @@ +version: "3.8" +services: + inkvoice: + image: ghcr.io/pigontech/inkvoice:0.1.0 + restart: unless-stopped + environment: + - ADMIN_USER=${ADMIN_USER} + - ADMIN_PASS=${ADMIN_PASS} + - JWT_SECRET=${JWT_SECRET} + - DATABASE_PATH=/app/data/invoice.db + - PORT=3000 + - HOST=0.0.0.0 + # Dokploy's auto-generated domain is HTTP (sslip.io); keep false so login + # works out of the box. Set true once you attach an HTTPS custom domain. + - COOKIE_SECURE=false + volumes: + - inkvoice-data:/app/data + expose: + - 3000 + +volumes: + inkvoice-data: diff --git a/blueprints/inkvoice/inkvoice.svg b/blueprints/inkvoice/inkvoice.svg new file mode 100644 index 00000000..788f0558 --- /dev/null +++ b/blueprints/inkvoice/inkvoice.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/blueprints/inkvoice/template.toml b/blueprints/inkvoice/template.toml new file mode 100644 index 00000000..0ce2dafc --- /dev/null +++ b/blueprints/inkvoice/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +admin_user = "admin" +admin_pass = "${password:16}" +jwt_secret = "${password:48}" + +[config] +env = [ + "ADMIN_USER=${admin_user}", + "ADMIN_PASS=${admin_pass}", + "JWT_SECRET=${jwt_secret}", +] + +[[config.domains]] +serviceName = "inkvoice" +port = 3000 +host = "${main_domain}" diff --git a/meta.json b/meta.json index baac068c..b7600cb4 100644 --- a/meta.json +++ b/meta.json @@ -3261,6 +3261,24 @@ "database" ] }, + { + "id": "inkvoice", + "name": "Inkvoice", + "version": "0.1.0", + "description": "Open-source invoicing for freelancers and small teams: invoices, online payments (Stripe & PayPal), expenses, multi-currency and reports.", + "logo": "inkvoice.svg", + "links": { + "github": "https://github.com/pigontech/inkvoice", + "website": "https://inkvoice.app", + "docs": "https://docs.inkvoice.app" + }, + "tags": [ + "invoicing", + "billing", + "finance", + "self-hosted" + ] + }, { "id": "inngest", "name": "Inngest", From 942a43ec02eefa75ed06632f440ee05fa5a4fd2f Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Tue, 7 Jul 2026 23:09:44 +0400 Subject: [PATCH 14/78] feat: add ArchiveBox template (#954) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/archivebox/archivebox.svg | 5 +++++ blueprints/archivebox/docker-compose.yml | 16 ++++++++++++++++ blueprints/archivebox/template.toml | 15 +++++++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 blueprints/archivebox/archivebox.svg create mode 100644 blueprints/archivebox/docker-compose.yml create mode 100644 blueprints/archivebox/template.toml diff --git a/blueprints/archivebox/archivebox.svg b/blueprints/archivebox/archivebox.svg new file mode 100644 index 00000000..be38ea52 --- /dev/null +++ b/blueprints/archivebox/archivebox.svg @@ -0,0 +1,5 @@ + + + + A + diff --git a/blueprints/archivebox/docker-compose.yml b/blueprints/archivebox/docker-compose.yml new file mode 100644 index 00000000..2f149345 --- /dev/null +++ b/blueprints/archivebox/docker-compose.yml @@ -0,0 +1,16 @@ +services: + archivebox: + image: archivebox/archivebox:latest + restart: unless-stopped + command: server --quick-init 0.0.0.0:8000 + environment: + ALLOWED_HOSTS: ${ALLOWED_HOSTS} + PUBLIC_INDEX: ${PUBLIC_INDEX} + PUBLIC_SNAPSHOTS: ${PUBLIC_SNAPSHOTS} + volumes: + - archivebox_data:/data + ports: + - "8000" + +volumes: + archivebox_data: diff --git a/blueprints/archivebox/template.toml b/blueprints/archivebox/template.toml new file mode 100644 index 00000000..edcc7fa8 --- /dev/null +++ b/blueprints/archivebox/template.toml @@ -0,0 +1,15 @@ +[variables] +main_domain = "${domain}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "archivebox" +port = 8000 +host = "${main_domain}" + +[config.env] +ALLOWED_HOSTS = "*" +PUBLIC_INDEX = "True" +PUBLIC_SNAPSHOTS = "True" diff --git a/meta.json b/meta.json index b7600cb4..23b8bb51 100644 --- a/meta.json +++ b/meta.json @@ -431,6 +431,23 @@ "nosql" ] }, + { + "id": "archivebox", + "name": "ArchiveBox", + "version": "latest", + "description": "ArchiveBox is a self-hosted internet archiving solution for saving websites, media, and documents.", + "logo": "archivebox.svg", + "links": { + "github": "https://github.com/ArchiveBox/ArchiveBox", + "website": "https://archivebox.io/", + "docs": "https://docs.archivebox.io/" + }, + "tags": [ + "archive", + "bookmarks", + "web" + ] + }, { "id": "argilla", "name": "Argilla", From 47f92da9a20832127d58584205211c2a13e4e277 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Tue, 7 Jul 2026 23:10:07 +0400 Subject: [PATCH 15/78] feat: add FileGator template (#953) * feat: add FileGator template * chore: pin filegator template version --------- Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/filegator/docker-compose.yml | 13 +++++++++++++ blueprints/filegator/filegator.svg | 5 +++++ blueprints/filegator/template.toml | 11 +++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 blueprints/filegator/docker-compose.yml create mode 100644 blueprints/filegator/filegator.svg create mode 100644 blueprints/filegator/template.toml diff --git a/blueprints/filegator/docker-compose.yml b/blueprints/filegator/docker-compose.yml new file mode 100644 index 00000000..f2f73351 --- /dev/null +++ b/blueprints/filegator/docker-compose.yml @@ -0,0 +1,13 @@ +services: + filegator: + image: filegator/filegator:v7.14.3 + restart: unless-stopped + volumes: + - filegator_repository:/var/www/filegator/repository + - filegator_private:/var/www/filegator/private + ports: + - "8080" + +volumes: + filegator_repository: + filegator_private: diff --git a/blueprints/filegator/filegator.svg b/blueprints/filegator/filegator.svg new file mode 100644 index 00000000..7f09ba2d --- /dev/null +++ b/blueprints/filegator/filegator.svg @@ -0,0 +1,5 @@ + + + + F + diff --git a/blueprints/filegator/template.toml b/blueprints/filegator/template.toml new file mode 100644 index 00000000..8646a0c7 --- /dev/null +++ b/blueprints/filegator/template.toml @@ -0,0 +1,11 @@ +[variables] +main_domain = "${domain}" + +[config] +env = {} +mounts = [] + +[[config.domains]] +serviceName = "filegator" +port = 8080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 23b8bb51..c1ebf478 100644 --- a/meta.json +++ b/meta.json @@ -2344,6 +2344,23 @@ "storage" ] }, + { + "id": "filegator", + "name": "FileGator", + "version": "7.14.3", + "description": "FileGator is a simple self-hosted web file manager with multi-user support.", + "logo": "filegator.svg", + "links": { + "github": "https://github.com/filegator/filegator", + "website": "https://filegator.io/", + "docs": "https://docs.filegator.io/" + }, + "tags": [ + "files", + "file-manager", + "self-hosted" + ] + }, { "id": "filestash", "name": "Filestash", From 7faf814ec7e435917f284344b73061d9ffe77242 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Tue, 7 Jul 2026 23:12:35 +0400 Subject: [PATCH 16/78] feat: add SFTPGo template (#951) * feat: add SFTPGo template * fix: use Dokploy port syntax for SFTPGo * chore: pin sftpgo template version --------- Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/sftpgo/docker-compose.yml | 14 ++++++++++++++ blueprints/sftpgo/sftpgo.svg | 5 +++++ blueprints/sftpgo/template.toml | 11 +++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 blueprints/sftpgo/docker-compose.yml create mode 100644 blueprints/sftpgo/sftpgo.svg create mode 100644 blueprints/sftpgo/template.toml diff --git a/blueprints/sftpgo/docker-compose.yml b/blueprints/sftpgo/docker-compose.yml new file mode 100644 index 00000000..2cd48773 --- /dev/null +++ b/blueprints/sftpgo/docker-compose.yml @@ -0,0 +1,14 @@ +services: + sftpgo: + image: drakkan/sftpgo:v2.7.3 + restart: unless-stopped + volumes: + - sftpgo_data:/srv/sftpgo + - sftpgo_home:/var/lib/sftpgo + ports: + - "8080" + - "2022" + +volumes: + sftpgo_data: + sftpgo_home: diff --git a/blueprints/sftpgo/sftpgo.svg b/blueprints/sftpgo/sftpgo.svg new file mode 100644 index 00000000..1928bbb7 --- /dev/null +++ b/blueprints/sftpgo/sftpgo.svg @@ -0,0 +1,5 @@ + + + + S + diff --git a/blueprints/sftpgo/template.toml b/blueprints/sftpgo/template.toml new file mode 100644 index 00000000..12437970 --- /dev/null +++ b/blueprints/sftpgo/template.toml @@ -0,0 +1,11 @@ +[variables] +main_domain = "${domain}" + +[config] +env = {} +mounts = [] + +[[config.domains]] +serviceName = "sftpgo" +port = 8080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index c1ebf478..30727a3b 100644 --- a/meta.json +++ b/meta.json @@ -5864,6 +5864,23 @@ "file-system" ] }, + { + "id": "sftpgo", + "name": "SFTPGo", + "version": "2.7.3", + "description": "SFTPGo is a full-featured and highly configurable SFTP, HTTP/S, FTP/S, and WebDAV server.", + "logo": "sftpgo.svg", + "links": { + "github": "https://github.com/drakkan/sftpgo", + "website": "https://sftpgo.com/", + "docs": "https://docs.sftpgo.com/" + }, + "tags": [ + "sftp", + "files", + "webdav" + ] + }, { "id": "shlink", "name": "Shlink", From 5f037d3e6ba34d7f1a97148d184b10270dc8a578 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Tue, 7 Jul 2026 23:12:56 +0400 Subject: [PATCH 17/78] feat: add Jellyseerr template (#950) * feat: add Jellyseerr template * chore: pin jellyseerr template version --------- Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/jellyseerr/docker-compose.yml | 13 +++++++++++++ blueprints/jellyseerr/jellyseerr.svg | 5 +++++ blueprints/jellyseerr/template.toml | 11 +++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 blueprints/jellyseerr/docker-compose.yml create mode 100644 blueprints/jellyseerr/jellyseerr.svg create mode 100644 blueprints/jellyseerr/template.toml diff --git a/blueprints/jellyseerr/docker-compose.yml b/blueprints/jellyseerr/docker-compose.yml new file mode 100644 index 00000000..e17f3ddb --- /dev/null +++ b/blueprints/jellyseerr/docker-compose.yml @@ -0,0 +1,13 @@ +services: + jellyseerr: + image: fallenbagel/jellyseerr:2.7.3 + restart: unless-stopped + volumes: + - jellyseerr_config:/app/config + environment: + LOG_LEVEL: info + ports: + - "5055" + +volumes: + jellyseerr_config: diff --git a/blueprints/jellyseerr/jellyseerr.svg b/blueprints/jellyseerr/jellyseerr.svg new file mode 100644 index 00000000..93840c4a --- /dev/null +++ b/blueprints/jellyseerr/jellyseerr.svg @@ -0,0 +1,5 @@ + + + + J + diff --git a/blueprints/jellyseerr/template.toml b/blueprints/jellyseerr/template.toml new file mode 100644 index 00000000..b41dae81 --- /dev/null +++ b/blueprints/jellyseerr/template.toml @@ -0,0 +1,11 @@ +[variables] +main_domain = "${domain}" + +[config] +env = {} +mounts = [] + +[[config.domains]] +serviceName = "jellyseerr" +port = 5055 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 30727a3b..de280bb5 100644 --- a/meta.json +++ b/meta.json @@ -3433,6 +3433,23 @@ "media system" ] }, + { + "id": "jellyseerr", + "name": "Jellyseerr", + "version": "2.7.3", + "description": "Jellyseerr is a media request and discovery manager for Jellyfin, Plex, and Emby users.", + "logo": "jellyseerr.svg", + "links": { + "github": "https://github.com/Fallenbagel/jellyseerr", + "website": "https://docs.jellyseerr.dev/", + "docs": "https://docs.jellyseerr.dev/" + }, + "tags": [ + "media", + "requests", + "jellyfin" + ] + }, { "id": "jenkins", "name": "jenkins", From c3ed7d9296e6852eb2f3492ec80af619e9f3e465 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Tue, 7 Jul 2026 23:13:20 +0400 Subject: [PATCH 18/78] feat: add Dockge template (#949) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/dockge/docker-compose.yml | 16 ++++++++++++++++ blueprints/dockge/dockge.svg | 5 +++++ blueprints/dockge/template.toml | 11 +++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 blueprints/dockge/docker-compose.yml create mode 100644 blueprints/dockge/dockge.svg create mode 100644 blueprints/dockge/template.toml diff --git a/blueprints/dockge/docker-compose.yml b/blueprints/dockge/docker-compose.yml new file mode 100644 index 00000000..8736f2fd --- /dev/null +++ b/blueprints/dockge/docker-compose.yml @@ -0,0 +1,16 @@ +services: + dockge: + image: louislam/dockge:1 + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - dockge_data:/app/data + - dockge_stacks:/opt/stacks + environment: + DOCKGE_STACKS_DIR: /opt/stacks + ports: + - "5001" + +volumes: + dockge_data: + dockge_stacks: diff --git a/blueprints/dockge/dockge.svg b/blueprints/dockge/dockge.svg new file mode 100644 index 00000000..69ade613 --- /dev/null +++ b/blueprints/dockge/dockge.svg @@ -0,0 +1,5 @@ + + + + D + diff --git a/blueprints/dockge/template.toml b/blueprints/dockge/template.toml new file mode 100644 index 00000000..79d8a24f --- /dev/null +++ b/blueprints/dockge/template.toml @@ -0,0 +1,11 @@ +[variables] +main_domain = "${domain}" + +[config] +env = {} +mounts = [] + +[[config.domains]] +serviceName = "dockge" +port = 5001 +host = "${main_domain}" diff --git a/meta.json b/meta.json index de280bb5..f1dcccfa 100644 --- a/meta.json +++ b/meta.json @@ -1851,6 +1851,23 @@ "discussion" ] }, + { + "id": "dockge", + "name": "Dockge", + "version": "1", + "description": "Dockge is a reactive, self-hosted Docker Compose stack manager from the creator of Uptime Kuma.", + "logo": "dockge.svg", + "links": { + "github": "https://github.com/louislam/dockge", + "website": "https://dockge.kuma.pet/", + "docs": "https://github.com/louislam/dockge" + }, + "tags": [ + "docker", + "compose", + "management" + ] + }, { "id": "docling-serve", "name": "Docling Serve", From 6d0028bcfcdd2a26549cb3484ee11711ad46158c Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Tue, 7 Jul 2026 23:16:27 +0400 Subject: [PATCH 19/78] feat: add Joplin Server template (#948) * feat: add Joplin Server template * chore: pin joplin-server template version * Update blueprints/joplin-server/template.toml --------- Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> --- blueprints/joplin-server/docker-compose.yml | 30 +++++++++++++++++++++ blueprints/joplin-server/joplin-server.svg | 5 ++++ blueprints/joplin-server/template.toml | 18 +++++++++++++ meta.json | 17 ++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 blueprints/joplin-server/docker-compose.yml create mode 100644 blueprints/joplin-server/joplin-server.svg create mode 100644 blueprints/joplin-server/template.toml diff --git a/blueprints/joplin-server/docker-compose.yml b/blueprints/joplin-server/docker-compose.yml new file mode 100644 index 00000000..fefac15b --- /dev/null +++ b/blueprints/joplin-server/docker-compose.yml @@ -0,0 +1,30 @@ +services: + joplin-db: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_DB: ${POSTGRES_DATABASE} + volumes: + - joplin_db_data:/var/lib/postgresql/data + + joplin-server: + image: joplin/server:3.7.1 + restart: unless-stopped + depends_on: + - joplin-db + environment: + APP_BASE_URL: ${APP_BASE_URL} + APP_PORT: ${APP_PORT} + DB_CLIENT: pg + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DATABASE: ${POSTGRES_DATABASE} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PORT: 5432 + POSTGRES_HOST: joplin-db + ports: + - "22300" + +volumes: + joplin_db_data: diff --git a/blueprints/joplin-server/joplin-server.svg b/blueprints/joplin-server/joplin-server.svg new file mode 100644 index 00000000..93840c4a --- /dev/null +++ b/blueprints/joplin-server/joplin-server.svg @@ -0,0 +1,5 @@ + + + + J + diff --git a/blueprints/joplin-server/template.toml b/blueprints/joplin-server/template.toml new file mode 100644 index 00000000..f8258bc6 --- /dev/null +++ b/blueprints/joplin-server/template.toml @@ -0,0 +1,18 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "joplin-server" +port = 22300 +host = "${main_domain}" + +[config.env] +APP_BASE_URL = "http://${main_domain}" +APP_PORT = "22300" +POSTGRES_PASSWORD = "${postgres_password}" +POSTGRES_USER = "joplin" +POSTGRES_DATABASE = "joplin" diff --git a/meta.json b/meta.json index f1dcccfa..1a253a69 100644 --- a/meta.json +++ b/meta.json @@ -3486,6 +3486,23 @@ "open-source" ] }, + { + "id": "joplin-server", + "name": "Joplin Server", + "version": "3.7.1", + "description": "Joplin Server is the self-hosted sync backend for Joplin notes, notebooks, and attachments.", + "logo": "joplin-server.svg", + "links": { + "github": "https://github.com/laurent22/joplin", + "website": "https://joplinapp.org/", + "docs": "https://github.com/laurent22/joplin/tree/dev/packages/server" + }, + "tags": [ + "notes", + "sync", + "productivity" + ] + }, { "id": "kaneo", "name": "Kaneo", From 5a193c5fb27fe67254be7122f209f60b5413736d Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Tue, 7 Jul 2026 23:16:48 +0400 Subject: [PATCH 20/78] feat: add Gotify template (#946) * feat: add Gotify template * chore: pin gotify template version --------- Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/gotify/docker-compose.yml | 11 +++++++++ blueprints/gotify/gotify.svg | 5 ++++ blueprints/gotify/template.toml | 11 +++++++++ meta.json | 35 +++++++++++++++++++++------- 4 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 blueprints/gotify/docker-compose.yml create mode 100644 blueprints/gotify/gotify.svg create mode 100644 blueprints/gotify/template.toml diff --git a/blueprints/gotify/docker-compose.yml b/blueprints/gotify/docker-compose.yml new file mode 100644 index 00000000..1d3d8265 --- /dev/null +++ b/blueprints/gotify/docker-compose.yml @@ -0,0 +1,11 @@ +services: + gotify: + image: gotify/server:2.9.1 + restart: unless-stopped + volumes: + - gotify_data:/app/data + ports: + - "80" + +volumes: + gotify_data: diff --git a/blueprints/gotify/gotify.svg b/blueprints/gotify/gotify.svg new file mode 100644 index 00000000..29502b8a --- /dev/null +++ b/blueprints/gotify/gotify.svg @@ -0,0 +1,5 @@ + + + + G + diff --git a/blueprints/gotify/template.toml b/blueprints/gotify/template.toml new file mode 100644 index 00000000..2308838a --- /dev/null +++ b/blueprints/gotify/template.toml @@ -0,0 +1,11 @@ +[variables] +main_domain = "${domain}" + +[config] +env = {} +mounts = [] + +[[config.domains]] +serviceName = "gotify" +port = 80 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 1a253a69..83496f3b 100644 --- a/meta.json +++ b/meta.json @@ -182,7 +182,7 @@ "id": "alist", "name": "AList", "version": "v3.55.0", - "description": "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.", + "description": "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.", "logo": "alist.svg", "links": { "github": "https://github.com/AlistGo/alist", @@ -316,7 +316,7 @@ "id": "anytype", "name": "Anytype", "version": "latest", - "description": "Anytype is a personal knowledge base—your digital brain—that lets you gather, connect and remix all kinds of information. Create pages, tasks, wikis, journals—even entire apps—and define your own data model while your data stays offline-first, private and encrypted across devices.\n\nAfter installation, you can view the Anytype client configuration by running `cat /data/client-config.yml` inside the service container.", + "description": "Anytype is a personal knowledge base—your digital brain—that lets you gather, connect and remix all kinds of information. Create pages, tasks, wikis, journals—even entire apps—and define your own data model while your data stays offline-first, private and encrypted across devices.\n\nAfter installation, you can view the Anytype client configuration by running `cat /data/client-config.yml` inside the service container.", "logo": "logo.png", "links": { "github": "https://github.com/grishy/any-sync-bundle", @@ -545,7 +545,7 @@ "id": "autobase", "name": "Autobase", "version": "2.5.2", - "description": "Autobase for PostgreSQL® is an open-source alternative to cloud-managed databases (self-hosted DBaaS).", + "description": "Autobase for PostgreSQL® is an open-source alternative to cloud-managed databases (self-hosted DBaaS).", "links": { "github": "https://github.com/vitabaks/autobase", "website": "https://autobase.tech/", @@ -755,7 +755,7 @@ "id": "blender", "name": "Blender", "version": "latest", - "description": "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", + "description": "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", "logo": "blender.svg", "links": { "github": "https://github.com/linuxserver/docker-blender", @@ -2952,6 +2952,23 @@ "tools" ] }, + { + "id": "gotify", + "name": "Gotify", + "version": "2.9.1", + "description": "Gotify is a simple server for sending and receiving self-hosted push notifications.", + "logo": "gotify.svg", + "links": { + "github": "https://github.com/gotify/server", + "website": "https://gotify.net/", + "docs": "https://gotify.net/docs/" + }, + "tags": [ + "notifications", + "self-hosted", + "push" + ] + }, { "id": "grafana", "name": "Grafana", @@ -3189,7 +3206,7 @@ "id": "huly", "name": "Huly", "version": "0.6.377", - "description": "Huly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)", + "description": "Huly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)", "logo": "huly.svg", "links": { "github": "https://github.com/hcengineering/huly-selfhost", @@ -3747,7 +3764,7 @@ "id": "librechat", "name": "LibreChat", "version": "latest", - "description": "LibreChat is the ultimate open-source app for all your AI conversations, fully customizable and compatible with any AI provider (Openai, Ollama, Google etc.) — all in one sleek interface.", + "description": "LibreChat is the ultimate open-source app for all your AI conversations, fully customizable and compatible with any AI provider (Openai, Ollama, Google etc.) — all in one sleek interface.", "logo": "librechat.png", "links": { "github": "https://github.com/danny-avila/librechat", @@ -5174,7 +5191,7 @@ "id": "photoprism", "name": "Photoprism", "version": "latest", - "description": "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", + "description": "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", "logo": "photoprism.svg", "links": { "github": "https://github.com/photoprism/photoprism", @@ -6239,7 +6256,7 @@ "id": "surrealdb", "name": "SurrealDB", "version": "2.3.10", - "description": "SurrealDB is a native, open-source, multi-model database that lets you store and manage data across relational, document, graph, time-series, vector & search, and geospatial models—all in one place.", + "description": "SurrealDB is a native, open-source, multi-model database that lets you store and manage data across relational, document, graph, time-series, vector & search, and geospatial models—all in one place.", "logo": "surrealdb.svg", "links": { "github": "https://github.com/surrealdb/surrealdb", @@ -6511,7 +6528,7 @@ "id": "typecho", "name": "Typecho", "version": "stable", - "description": "Typecho 是一个轻量级的开源博客程序,基于 PHP 开发,支持多种数据库,简洁而强大。", + "description": "Typecho 是一个轻量级的开源博客程序,基于 PHP 开发,支持多种数据库,简洁而强大。", "logo": "typecho.png", "links": { "github": "https://github.com/typecho/typecho", From 219cd25e22b0e16050ee680fe7c69114af3a49f8 Mon Sep 17 00:00:00 2001 From: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:18:12 +0400 Subject: [PATCH 21/78] Add Jitsi metadata entry --- meta.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/meta.json b/meta.json index 8be7825d..1c95f983 100644 --- a/meta.json +++ b/meta.json @@ -3347,6 +3347,24 @@ "open-source" ] }, + { + "id": "jitsi", + "name": "Jitsi Meet", + "version": "1.0.0", + "description": "Jitsi Meet is an open-source video conferencing platform for secure, self-hosted meetings.", + "links": { + "github": "https://github.com/jitsi/docker-jitsi-meet", + "website": "https://jitsi.org/jitsi-meet/", + "docs": "https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker" + }, + "logo": "jitsi.svg", + "tags": [ + "video-conferencing", + "meetings", + "self-hosted", + "webrtc" + ] + }, { "id": "kaneo", "name": "Kaneo", From 7f722cdeab6c87871e67d54cc59190557635b879 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Tue, 7 Jul 2026 23:19:17 +0400 Subject: [PATCH 22/78] feat: add Firefly III template (#941) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/firefly-iii/README.md | 13 +++++ blueprints/firefly-iii/docker-compose.yml | 65 +++++++++++++++++++++++ blueprints/firefly-iii/logo.svg | 14 +++++ blueprints/firefly-iii/template.toml | 36 +++++++++++++ meta.json | 17 ++++++ 5 files changed, 145 insertions(+) create mode 100644 blueprints/firefly-iii/README.md create mode 100644 blueprints/firefly-iii/docker-compose.yml create mode 100644 blueprints/firefly-iii/logo.svg create mode 100644 blueprints/firefly-iii/template.toml diff --git a/blueprints/firefly-iii/README.md b/blueprints/firefly-iii/README.md new file mode 100644 index 00000000..1687baf8 --- /dev/null +++ b/blueprints/firefly-iii/README.md @@ -0,0 +1,13 @@ +# Firefly III + +Firefly III is a self-hosted personal finance manager for budgets, accounts, transactions, categories, and reports. + +This Dokploy template includes: + +- Firefly III core web application +- MariaDB database with generated credentials +- Persistent upload and database volumes +- Daily cron runner using Firefly III's static cron token +- Dokploy domain routing on the application service + +After deployment, open the generated domain and complete the first-user setup flow. diff --git a/blueprints/firefly-iii/docker-compose.yml b/blueprints/firefly-iii/docker-compose.yml new file mode 100644 index 00000000..b9823407 --- /dev/null +++ b/blueprints/firefly-iii/docker-compose.yml @@ -0,0 +1,65 @@ +version: "3.8" + +services: + firefly-iii: + image: fireflyiii/core:version-6.6.3 + restart: unless-stopped + depends_on: + firefly-iii-db: + condition: service_healthy + environment: + APP_KEY: ${APP_KEY} + APP_URL: ${APP_URL} + TRUSTED_PROXIES: "**" + TZ: ${TZ} + SITE_OWNER: ${SITE_OWNER} + DEFAULT_LANGUAGE: ${DEFAULT_LANGUAGE} + DEFAULT_LOCALE: ${DEFAULT_LOCALE} + DB_CONNECTION: mysql + DB_HOST: firefly-iii-db + DB_PORT: 3306 + DB_DATABASE: ${DB_DATABASE} + DB_USERNAME: ${DB_USERNAME} + DB_PASSWORD: ${DB_PASSWORD} + STATIC_CRON_TOKEN: ${STATIC_CRON_TOKEN} + expose: + - "8080" + volumes: + - firefly-iii-upload:/var/www/html/storage/upload + + firefly-iii-db: + image: mariadb:11.8 + restart: unless-stopped + environment: + MYSQL_DATABASE: ${DB_DATABASE} + MYSQL_USER: ${DB_USERNAME} + MYSQL_PASSWORD: ${DB_PASSWORD} + MYSQL_RANDOM_ROOT_PASSWORD: "yes" + volumes: + - firefly-iii-db:/var/lib/mysql + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + + firefly-iii-cron: + image: alpine:3.22 + restart: unless-stopped + depends_on: + - firefly-iii + environment: + TZ: ${TZ} + STATIC_CRON_TOKEN: ${STATIC_CRON_TOKEN} + command: + - sh + - -c + - | + apk add --no-cache tzdata wget && \ + (ln -snf /usr/share/zoneinfo/$$TZ /etc/localtime || true) && \ + echo "0 3 * * * wget -qO- http://firefly-iii:8080/api/v1/cron/$$STATIC_CRON_TOKEN; echo" | crontab - && \ + crond -f -L /dev/stdout + +volumes: + firefly-iii-upload: {} + firefly-iii-db: {} diff --git a/blueprints/firefly-iii/logo.svg b/blueprints/firefly-iii/logo.svg new file mode 100644 index 00000000..8b4b7d2c --- /dev/null +++ b/blueprints/firefly-iii/logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/blueprints/firefly-iii/template.toml b/blueprints/firefly-iii/template.toml new file mode 100644 index 00000000..3f02a8ae --- /dev/null +++ b/blueprints/firefly-iii/template.toml @@ -0,0 +1,36 @@ +[variables] +main_domain = "${domain}" +app_key = "${base64:32}" +db_name = "firefly" +db_user = "firefly" +db_password = "${password:32}" +cron_token = "${hash:32}" +site_owner = "${email}" +timezone = "UTC" +default_language = "en_US" +default_locale = "equal" + +[config] +env = [ + "APP_KEY=base64:${app_key}", + "APP_URL=https://${main_domain}", + "TRUSTED_PROXIES=**", + "TZ=${timezone}", + "SITE_OWNER=${site_owner}", + "DEFAULT_LANGUAGE=${default_language}", + "DEFAULT_LOCALE=${default_locale}", + "DB_DATABASE=${db_name}", + "DB_USERNAME=${db_user}", + "DB_PASSWORD=${db_password}", + "STATIC_CRON_TOKEN=${cron_token}", + "MYSQL_DATABASE=${db_name}", + "MYSQL_USER=${db_user}", + "MYSQL_PASSWORD=${db_password}", + "MYSQL_RANDOM_ROOT_PASSWORD=yes" +] +mounts = [] + +[[config.domains]] +serviceName = "firefly-iii" +port = 8080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 83496f3b..f1dbb9fe 100644 --- a/meta.json +++ b/meta.json @@ -2414,6 +2414,23 @@ "llm" ] }, + { + "id": "firefly-iii", + "name": "Firefly III", + "version": "6.6.3", + "description": "Firefly III is a self-hosted personal finance manager for tracking expenses, budgets, accounts, and financial reports.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/firefly-iii/firefly-iii", + "website": "https://www.firefly-iii.org/", + "docs": "https://docs.firefly-iii.org/" + }, + "tags": [ + "finance", + "budgeting", + "self-hosted" + ] + }, { "id": "fivem", "name": "FiveM Server", From a2bf4b1393b974abee3dd94a0a81d842ba04c84b Mon Sep 17 00:00:00 2001 From: Akshat Mehta Date: Wed, 8 Jul 2026 01:10:34 +0530 Subject: [PATCH 23/78] fix(vaultwarden): update outdated image version (#921) --- blueprints/vaultwarden/docker-compose.yml | 2 +- meta.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/vaultwarden/docker-compose.yml b/blueprints/vaultwarden/docker-compose.yml index 5b2f14e3..6998809c 100644 --- a/blueprints/vaultwarden/docker-compose.yml +++ b/blueprints/vaultwarden/docker-compose.yml @@ -1,7 +1,7 @@ # the webserver can take a while to start up, so don't be alarmed if it takes a few minutes to get a response services: vaultwarden: - image: vaultwarden/server:1.34.3 + image: vaultwarden/server:1.36.0 restart: always environment: DOMAIN: ${DOMAIN} diff --git a/meta.json b/meta.json index f1dbb9fe..4afcfd83 100644 --- a/meta.json +++ b/meta.json @@ -6712,7 +6712,7 @@ { "id": "vaultwarden", "name": "Vaultwarden", - "version": "1.34.3", + "version": "1.36.0", "description": "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", "logo": "vaultwarden.svg", "links": { From 5bbc380f36ba534b4382c6354c568ed188d7ebd2 Mon Sep 17 00:00:00 2001 From: Mendy Landa <54242706+MendyLanda@users.noreply.github.com> Date: Tue, 7 Jul 2026 22:41:57 +0300 Subject: [PATCH 24/78] Add Cut (self-hosted URL shortener) template (#914) --- blueprints/cut/cut.svg | 10 +++++++++ blueprints/cut/docker-compose.yml | 37 +++++++++++++++++++++++++++++++ blueprints/cut/template.toml | 14 ++++++++++++ meta.json | 16 +++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 blueprints/cut/cut.svg create mode 100644 blueprints/cut/docker-compose.yml create mode 100644 blueprints/cut/template.toml diff --git a/blueprints/cut/cut.svg b/blueprints/cut/cut.svg new file mode 100644 index 00000000..c0604eac --- /dev/null +++ b/blueprints/cut/cut.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/blueprints/cut/docker-compose.yml b/blueprints/cut/docker-compose.yml new file mode 100644 index 00000000..b31578d1 --- /dev/null +++ b/blueprints/cut/docker-compose.yml @@ -0,0 +1,37 @@ +services: + cut: + image: ghcr.io/mendylanda/cut:latest + restart: unless-stopped + depends_on: + redis: + condition: service_healthy + environment: + # Injected by Dokploy from template.toml (generated strong password). + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + # Bundled Redis below, private to the project network. + - REDIS_URL=redis://redis:6379 + # Expose the container port only; Dokploy maps the domain to it. + ports: + - 3000 + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/"] + interval: 10s + timeout: 5s + retries: 10 + + redis: + image: redis:7-alpine + restart: unless-stopped + # appendonly = durable across restarts; noeviction because this is Cut's + # primary datastore, not a cache (don't drop links under memory pressure). + command: redis-server --appendonly yes --maxmemory-policy noeviction + volumes: + - redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 10 + +volumes: + redis-data: diff --git a/blueprints/cut/template.toml b/blueprints/cut/template.toml new file mode 100644 index 00000000..83de686c --- /dev/null +++ b/blueprints/cut/template.toml @@ -0,0 +1,14 @@ +[variables] +main_domain = "${domain}" +admin_password = "${password:24}" + +[config] +env = [ + "ADMIN_PASSWORD=${admin_password}", +] +mounts = [] + +[[config.domains]] +serviceName = "cut" +port = 3000 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 4afcfd83..7c6451dd 100644 --- a/meta.json +++ b/meta.json @@ -1732,6 +1732,22 @@ "self-hosted" ] }, + { + "id": "cut", + "name": "Cut", + "version": "0.1.0", + "description": "A tiny, self-hosted URL shortener — short links that are entirely yours, with per-link passwords, expiries, and click limits.", + "logo": "cut.svg", + "links": { + "github": "https://github.com/MendyLanda/cut", + "website": "https://github.com/MendyLanda/cut", + "docs": "https://github.com/MendyLanda/cut#readme" + }, + "tags": [ + "url-shortener", + "links" + ] + }, { "id": "cyberchef", "name": "CyberChef", From a3d1efad02e0b85488552a801597534f613a1e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Carvajal?= <66692533+ncarvajalc@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:42:42 -0500 Subject: [PATCH 25/78] chore: update hi-events template to version 1.9.0-beta and add smtp mail configuration (#911) --- blueprints/hi-events/docker-compose.yml | 4 +++- blueprints/hi-events/template.toml | 2 ++ meta.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/blueprints/hi-events/docker-compose.yml b/blueprints/hi-events/docker-compose.yml index 3c2291b9..dd3d7269 100644 --- a/blueprints/hi-events/docker-compose.yml +++ b/blueprints/hi-events/docker-compose.yml @@ -1,6 +1,6 @@ services: all-in-one: - image: daveearley/hi.events-all-in-one:v0.8.0-beta.1 + image: daveearley/hi.events-all-in-one:v1.9.0-beta restart: always environment: - VITE_FRONTEND_URL=https://${DOMAIN} @@ -20,6 +20,8 @@ services: - MAIL_MAILER - MAIL_HOST - MAIL_PORT + - MAIL_USERNAME + - MAIL_PASSWORD - MAIL_FROM_ADDRESS - MAIL_FROM_NAME depends_on: diff --git a/blueprints/hi-events/template.toml b/blueprints/hi-events/template.toml index 4fea86ce..a8c2c3d3 100644 --- a/blueprints/hi-events/template.toml +++ b/blueprints/hi-events/template.toml @@ -16,6 +16,8 @@ env = [ "MAIL_MAILER=", "MAIL_HOST=", "MAIL_PORT=", + "MAIL_USERNAME=", + "MAIL_PASSWORD=", "MAIL_FROM_ADDRESS=", "MAIL_FROM_NAME=", ] diff --git a/meta.json b/meta.json index 7c6451dd..7ac97b0b 100644 --- a/meta.json +++ b/meta.json @@ -3111,7 +3111,7 @@ { "id": "hi-events", "name": "Hi.events", - "version": "0.8.0-beta.1", + "version": "1.9.0-beta", "description": "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", "logo": "hi-events.svg", "links": { From bdda9b476be3ab25027e4536f439bbd312fc0504 Mon Sep 17 00:00:00 2001 From: Shabani Magawila <36422571+ShabaniMagawila@users.noreply.github.com> Date: Tue, 7 Jul 2026 22:44:45 +0300 Subject: [PATCH 26/78] feat: Add GeoServer & PostGIS blueprint (#919) * Add GeoServer & PostGIS blueprint * Add GeoServer blueprint metadata and logo * Fix meta.json formatting and sorting * Fix template.toml schema validation errors * Remove invalid config.mounts from template.toml --- blueprints/geoserver/docker-compose.yml | 27 ++++++++++++++++++++++++ blueprints/geoserver/logo.png | Bin 0 -> 16349 bytes blueprints/geoserver/logo.svg | 21 ++++++++++++++++++ blueprints/geoserver/template.toml | 18 ++++++++++++++++ meta.json | 17 +++++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 blueprints/geoserver/docker-compose.yml create mode 100644 blueprints/geoserver/logo.png create mode 100644 blueprints/geoserver/logo.svg create mode 100644 blueprints/geoserver/template.toml diff --git a/blueprints/geoserver/docker-compose.yml b/blueprints/geoserver/docker-compose.yml new file mode 100644 index 00000000..fdd4fc97 --- /dev/null +++ b/blueprints/geoserver/docker-compose.yml @@ -0,0 +1,27 @@ +services: + geoserver: + image: kartoza/geoserver:2.24.1 + depends_on: + - db + environment: + - GEOSERVER_ADMIN_USER=${GEOSERVER_ADMIN_USER} + - GEOSERVER_ADMIN_PASSWORD=${GEOSERVER_ADMIN_PASSWORD} + - INITIAL_MEMORY=${INITIAL_MEMORY} + - MAXIMUM_MEMORY=${MAXIMUM_MEMORY} + volumes: + - geoserver-data:/opt/geoserver/data_dir + restart: unless-stopped + + db: + image: postgis/postgis:17-3.4 + environment: + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + volumes: + - postgis-data:/var/lib/postgresql/data + restart: unless-stopped + +volumes: + geoserver-data: + postgis-data: diff --git a/blueprints/geoserver/logo.png b/blueprints/geoserver/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..14ae15a6a465dd9346acb2f527e48ce6fad517c8 GIT binary patch literal 16349 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 + + + + + + + + + + + \ No newline at end of file diff --git a/blueprints/geoserver/template.toml b/blueprints/geoserver/template.toml new file mode 100644 index 00000000..a975cc8e --- /dev/null +++ b/blueprints/geoserver/template.toml @@ -0,0 +1,18 @@ +[info] +name = "GeoServer & PostGIS" +description = "An open-source server for sharing geospatial data, paired with a PostGIS database. Template created and maintained by OpenGeoCity Tanzania (info@ogctz.org). For further assistance contact +255759968919 or visit https://opengeocity.org or github: https://github.com/ShabaniMagawila or linkedin: https://www.linkedin.com/in/shabani-magawila/" +version = "2.24.1" + +[variables] +GEOSERVER_ADMIN_USER = "admin" +GEOSERVER_ADMIN_PASSWORD = "geoserver_secure" +INITIAL_MEMORY = "2G" +MAXIMUM_MEMORY = "4G" +POSTGRES_USER = "postgres" +POSTGRES_PASSWORD = "postgres_secure" +POSTGRES_DB = "gis" + +[config] +[[config.domains]] +serviceName = "geoserver" +port = 8080 \ No newline at end of file diff --git a/meta.json b/meta.json index 7ac97b0b..ffaa70e2 100644 --- a/meta.json +++ b/meta.json @@ -2753,6 +2753,23 @@ "object-storage" ] }, + { + "id": "geoserver", + "name": "GeoServer & PostGIS", + "version": "2.24.1", + "description": "An open-source server for sharing geospatial data, paired with a PostGIS database. Template created and maintained by OpenGeoCity Tanzania (info@ogctz.org). For further assistance contact +255759968919 or visit https://opengeocity.org or github: https://github.com/ShabaniMagawila or linkedin: https://www.linkedin.com/in/shabani-magawila/", + "logo": "logo.png", + "links": { + "github": "https://github.com/geoserver/geoserver", + "website": "https://geoserver.org/", + "docs": "https://docs.geoserver.org/" + }, + "tags": [ + "geospatial", + "gis", + "postgis" + ] + }, { "id": "ghost", "name": "Ghost", From cf862fccfb40b0e3a911bcf2bb95f30e60f17ab1 Mon Sep 17 00:00:00 2001 From: MANFIT7 <123054818+MANFIT7@users.noreply.github.com> Date: Wed, 8 Jul 2026 03:53:26 +0800 Subject: [PATCH 27/78] feat: add LibreSpeed template (#899) --- blueprints/librespeed/docker-compose.yml | 20 ++++++++++++++++++++ blueprints/librespeed/librespeed.svg | 1 + blueprints/librespeed/template.toml | 14 ++++++++++++++ meta.json | 18 ++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 blueprints/librespeed/docker-compose.yml create mode 100644 blueprints/librespeed/librespeed.svg create mode 100644 blueprints/librespeed/template.toml diff --git a/blueprints/librespeed/docker-compose.yml b/blueprints/librespeed/docker-compose.yml new file mode 100644 index 00000000..2f7e8bc1 --- /dev/null +++ b/blueprints/librespeed/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + librespeed: + image: lscr.io/linuxserver/librespeed:latest + restart: unless-stopped + expose: + - 80 + volumes: + - librespeed-config:/config + environment: + - PUID=1000 + - PGID=1000 + - TZ=${TZ} + - PASSWORD=${PASSWORD} + - CUSTOM_RESULTS=${CUSTOM_RESULTS} + - DB_TYPE=sqlite + +volumes: + librespeed-config: diff --git a/blueprints/librespeed/librespeed.svg b/blueprints/librespeed/librespeed.svg new file mode 100644 index 00000000..c76c99ec --- /dev/null +++ b/blueprints/librespeed/librespeed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/librespeed/template.toml b/blueprints/librespeed/template.toml new file mode 100644 index 00000000..b8ed3e13 --- /dev/null +++ b/blueprints/librespeed/template.toml @@ -0,0 +1,14 @@ +[variables] +main_domain = "${domain}" +password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "librespeed" +port = 80 +host = "${main_domain}" + +[config.env] +PASSWORD = "${password}" +TZ = "UTC" +CUSTOM_RESULTS = "false" diff --git a/meta.json b/meta.json index ffaa70e2..c16a0404 100644 --- a/meta.json +++ b/meta.json @@ -3846,6 +3846,24 @@ "object-storage" ] }, + { + "id": "librespeed", + "name": "LibreSpeed", + "version": "latest", + "description": "LibreSpeed is a lightweight, self-hosted HTML5 speed test for measuring download, upload, ping, and jitter in modern browsers.", + "logo": "librespeed.svg", + "links": { + "github": "https://github.com/librespeed/speedtest", + "website": "https://librespeed.org", + "docs": "https://docs.linuxserver.io/images/docker-librespeed/" + }, + "tags": [ + "speedtest", + "networking", + "monitoring", + "self-hosted" + ] + }, { "id": "libretranslate", "name": "LibreTranslate", From 76ceefdd2b7ae5d7e5149a04318a7ef41c34fa45 Mon Sep 17 00:00:00 2001 From: MANFIT7 <123054818+MANFIT7@users.noreply.github.com> Date: Wed, 8 Jul 2026 03:55:14 +0800 Subject: [PATCH 28/78] feat: add Snipe-IT template (#897) --- blueprints/snipe-it/docker-compose.yml | 68 +++++++++++++++++++++++++ blueprints/snipe-it/snipe-it.png | Bin 0 -> 22482 bytes blueprints/snipe-it/template.toml | 25 +++++++++ meta.json | 18 +++++++ 4 files changed, 111 insertions(+) create mode 100644 blueprints/snipe-it/docker-compose.yml create mode 100644 blueprints/snipe-it/snipe-it.png create mode 100644 blueprints/snipe-it/template.toml diff --git a/blueprints/snipe-it/docker-compose.yml b/blueprints/snipe-it/docker-compose.yml new file mode 100644 index 00000000..be3140b7 --- /dev/null +++ b/blueprints/snipe-it/docker-compose.yml @@ -0,0 +1,68 @@ +version: "3.8" + +services: + snipe-it: + image: snipe/snipe-it:v8.5.0 + restart: unless-stopped + expose: + - 80 + volumes: + - snipe-it-storage:/var/lib/snipeit + environment: + - APP_ENV=production + - APP_DEBUG=false + - APP_KEY=${APP_KEY} + - APP_URL=${APP_URL} + - APP_TIMEZONE=${APP_TIMEZONE} + - APP_LOCALE=${APP_LOCALE} + - APP_TRUSTED_PROXIES=${APP_TRUSTED_PROXIES} + - APP_FORCE_TLS=${APP_FORCE_TLS} + - SECURE_COOKIES=${SECURE_COOKIES} + - PRIVATE_FILESYSTEM_DISK=local + - PUBLIC_FILESYSTEM_DISK=local_public + - DB_CONNECTION=mysql + - DB_HOST=snipe-it-db + - DB_PORT=3306 + - DB_DATABASE=${DB_DATABASE} + - DB_USERNAME=${DB_USERNAME} + - DB_PASSWORD=${DB_PASSWORD} + - DB_PREFIX=null + - DB_DUMP_PATH=/usr/bin + - DB_DUMP_SKIP_SSL=true + - DB_CHARSET=utf8mb4 + - DB_COLLATION=utf8mb4_unicode_ci + - MAIL_MAILER=log + - MAIL_FROM_ADDR=${MAIL_FROM_ADDR} + - MAIL_FROM_NAME=Snipe-IT + - MAIL_REPLYTO_ADDR=${MAIL_FROM_ADDR} + - MAIL_REPLYTO_NAME=Snipe-IT + - ALLOW_BACKUP_DELETE=false + - ALLOW_DATA_PURGE=false + - IMAGE_LIB=gd + - LOG_CHANNEL=stderr + - CACHE_DRIVER=file + - SESSION_DRIVER=file + - QUEUE_DRIVER=sync + depends_on: + snipe-it-db: + condition: service_healthy + + snipe-it-db: + image: mariadb:11.4.7 + restart: unless-stopped + volumes: + - snipe-it-db-data:/var/lib/mysql + environment: + - MYSQL_DATABASE=${DB_DATABASE} + - MYSQL_USER=${DB_USERNAME} + - MYSQL_PASSWORD=${DB_PASSWORD} + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 5s + timeout: 2s + retries: 10 + +volumes: + snipe-it-storage: + snipe-it-db-data: diff --git a/blueprints/snipe-it/snipe-it.png b/blueprints/snipe-it/snipe-it.png new file mode 100644 index 0000000000000000000000000000000000000000..423a5b74906c4a1f67dab665a59a6731f0dbaa98 GIT binary patch literal 22482 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 literal 0 HcmV?d00001 diff --git a/blueprints/snipe-it/template.toml b/blueprints/snipe-it/template.toml new file mode 100644 index 00000000..aca258a5 --- /dev/null +++ b/blueprints/snipe-it/template.toml @@ -0,0 +1,25 @@ +[variables] +main_domain = "${domain}" +app_key = "${base64:32}" +db_password = "${password:24}" +mysql_root_password = "${password:24}" + +[config] +[[config.domains]] +serviceName = "snipe-it" +port = 80 +host = "${main_domain}" + +[config.env] +APP_KEY = "base64:${app_key}" +APP_URL = "https://${main_domain}" +APP_TIMEZONE = "UTC" +APP_LOCALE = "en-US" +APP_TRUSTED_PROXIES = "172.0.0.0/8" +APP_FORCE_TLS = "true" +SECURE_COOKIES = "true" +DB_DATABASE = "snipeit" +DB_USERNAME = "snipeit" +DB_PASSWORD = "${db_password}" +MYSQL_ROOT_PASSWORD = "${mysql_root_password}" +MAIL_FROM_ADDR = "snipe-it@${main_domain}" diff --git a/meta.json b/meta.json index c16a0404..5dae84df 100644 --- a/meta.json +++ b/meta.json @@ -6108,6 +6108,24 @@ "authentication" ] }, + { + "id": "snipe-it", + "name": "Snipe-IT", + "version": "v8.5.0", + "description": "Snipe-IT is an open-source asset management system for tracking hardware, software licenses, accessories, and consumables.", + "logo": "snipe-it.png", + "links": { + "github": "https://github.com/grokability/snipe-it", + "website": "https://snipeitapp.com/", + "docs": "https://snipe-it.readme.io/docs" + }, + "tags": [ + "asset-management", + "inventory", + "business", + "self-hosted" + ] + }, { "id": "soketi", "name": "Soketi", From e83e869bd3b6b030a7a22924f62c6a4acf7df735 Mon Sep 17 00:00:00 2001 From: Kokoro <100moreplus1@proton.me> Date: Tue, 7 Jul 2026 21:55:48 +0200 Subject: [PATCH 29/78] feat: add LubeLogger template (#896) Co-authored-by: 100more <184098138+100more@users.noreply.github.com> --- blueprints/lubelogger/docker-compose.yml | 20 ++++++++++++++++++++ blueprints/lubelogger/lubelogger.png | Bin 0 -> 8262 bytes blueprints/lubelogger/template.toml | 18 ++++++++++++++++++ meta.json | 18 ++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 blueprints/lubelogger/docker-compose.yml create mode 100644 blueprints/lubelogger/lubelogger.png create mode 100644 blueprints/lubelogger/template.toml diff --git a/blueprints/lubelogger/docker-compose.yml b/blueprints/lubelogger/docker-compose.yml new file mode 100644 index 00000000..535b8e55 --- /dev/null +++ b/blueprints/lubelogger/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + lubelogger: + image: ghcr.io/hargata/lubelogger:v1.6.5 + restart: unless-stopped + environment: + - LC_ALL=${LUBELOGGER_LOCALE} + - LANG=${LUBELOGGER_LOCALE} + - LUBELOGGER_DOMAIN=${LUBELOGGER_DOMAIN} + - EnableAuth=${LUBELOGGER_ENABLE_AUTH} + volumes: + - lubelogger-data:/App/data + - lubelogger-keys:/root/.aspnet/DataProtection-Keys + expose: + - 8080 + +volumes: + lubelogger-data: {} + lubelogger-keys: {} diff --git a/blueprints/lubelogger/lubelogger.png b/blueprints/lubelogger/lubelogger.png new file mode 100644 index 0000000000000000000000000000000000000000..b3713e8067a37d3e7a04f8d777add1d4c4c27d1b GIT binary patch literal 8262 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 Eg0dmvRR910 literal 0 HcmV?d00001 diff --git a/blueprints/lubelogger/template.toml b/blueprints/lubelogger/template.toml new file mode 100644 index 00000000..4ff28963 --- /dev/null +++ b/blueprints/lubelogger/template.toml @@ -0,0 +1,18 @@ +[variables] +main_domain = "${domain}" +lubelogger_locale = "en_US.UTF-8" +lubelogger_enable_auth = "false" +lubelogger_domain = "https://${main_domain}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "lubelogger" +port = 8080 +host = "${main_domain}" + +[config.env] +LUBELOGGER_LOCALE = "${lubelogger_locale}" +LUBELOGGER_ENABLE_AUTH = "${lubelogger_enable_auth}" +LUBELOGGER_DOMAIN = "${lubelogger_domain}" diff --git a/meta.json b/meta.json index 5dae84df..86705cb4 100644 --- a/meta.json +++ b/meta.json @@ -4053,6 +4053,24 @@ "development" ] }, + { + "id": "lubelogger", + "name": "LubeLogger", + "version": "v1.6.5", + "description": "LubeLogger is a self-hosted vehicle maintenance and fuel mileage tracker for managing service records, reminders, fuel logs, and vehicle expenses.", + "logo": "lubelogger.png", + "links": { + "github": "https://github.com/hargata/lubelog", + "website": "https://lubelogger.com/", + "docs": "https://docs.lubelogger.com/" + }, + "tags": [ + "vehicle", + "maintenance", + "fleet", + "self-hosted" + ] + }, { "id": "macos", "name": "MacOS (dockerized)", From c70cd23b7edee4edebefe5ef68389dcbd3a71de6 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 00:01:01 +0400 Subject: [PATCH 30/78] feat: add Paperclip template (#943) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/paperclip/README.md | 15 +++++++++ blueprints/paperclip/docker-compose.yml | 44 +++++++++++++++++++++++++ blueprints/paperclip/logo.svg | 5 +++ blueprints/paperclip/template.toml | 20 +++++++++++ meta.json | 18 ++++++++++ 5 files changed, 102 insertions(+) create mode 100644 blueprints/paperclip/README.md create mode 100644 blueprints/paperclip/docker-compose.yml create mode 100644 blueprints/paperclip/logo.svg create mode 100644 blueprints/paperclip/template.toml diff --git a/blueprints/paperclip/README.md b/blueprints/paperclip/README.md new file mode 100644 index 00000000..2ccae853 --- /dev/null +++ b/blueprints/paperclip/README.md @@ -0,0 +1,15 @@ +# Paperclip + +Paperclip is an open-source control plane for managing teams of AI agents, goals, budgets, tickets, and recurring work. + +This Dokploy template includes: + +- Paperclip web/API service using the official GHCR image +- PostgreSQL 17 database with health check +- Generated `BETTER_AUTH_SECRET` +- Generated database password +- Persistent `/paperclip` storage for instance data +- Public URL configured from the Dokploy domain +- Dokploy domain routing to Paperclip on port 3100 + +After deployment, check the Paperclip container logs for first-run setup or invite information. \ No newline at end of file diff --git a/blueprints/paperclip/docker-compose.yml b/blueprints/paperclip/docker-compose.yml new file mode 100644 index 00000000..5c1ce7bf --- /dev/null +++ b/blueprints/paperclip/docker-compose.yml @@ -0,0 +1,44 @@ +version: "3.8" + +services: + postgres: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_USER: paperclip + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: paperclip + healthcheck: + test: ["CMD-SHELL", "pg_isready -U paperclip -d paperclip"] + interval: 10s + timeout: 5s + retries: 10 + volumes: + - postgres-data:/var/lib/postgresql/data + + paperclip: + image: ghcr.io/paperclipai/paperclip:latest + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + HOST: 0.0.0.0 + PORT: "3100" + SERVE_UI: "true" + DATABASE_URL: ${DATABASE_URL} + BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET} + PAPERCLIP_HOME: /paperclip + PAPERCLIP_INSTANCE_ID: default + PAPERCLIP_CONFIG: /paperclip/instances/default/config.json + PAPERCLIP_DEPLOYMENT_MODE: ${PAPERCLIP_DEPLOYMENT_MODE} + PAPERCLIP_DEPLOYMENT_EXPOSURE: ${PAPERCLIP_DEPLOYMENT_EXPOSURE} + PAPERCLIP_PUBLIC_URL: ${PAPERCLIP_PUBLIC_URL} + expose: + - "3100" + volumes: + - paperclip-data:/paperclip + +volumes: + postgres-data: {} + paperclip-data: {} \ No newline at end of file diff --git a/blueprints/paperclip/logo.svg b/blueprints/paperclip/logo.svg new file mode 100644 index 00000000..44a8f649 --- /dev/null +++ b/blueprints/paperclip/logo.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/blueprints/paperclip/template.toml b/blueprints/paperclip/template.toml new file mode 100644 index 00000000..6a79e07c --- /dev/null +++ b/blueprints/paperclip/template.toml @@ -0,0 +1,20 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +better_auth_secret = "${password:64}" + +[config] +env = [ + "POSTGRES_PASSWORD=${postgres_password}", + "DATABASE_URL=postgres://paperclip:${postgres_password}@postgres:5432/paperclip", + "BETTER_AUTH_SECRET=${better_auth_secret}", + "PAPERCLIP_DEPLOYMENT_MODE=authenticated", + "PAPERCLIP_DEPLOYMENT_EXPOSURE=private", + "PAPERCLIP_PUBLIC_URL=https://${main_domain}" +] +mounts = [] + +[[config.domains]] +serviceName = "paperclip" +port = 3100 +host = "${main_domain}" \ No newline at end of file diff --git a/meta.json b/meta.json index 86705cb4..a3fdfc39 100644 --- a/meta.json +++ b/meta.json @@ -5129,6 +5129,24 @@ "open-source" ] }, + { + "id": "paperclip", + "name": "Paperclip", + "version": "latest", + "description": "Paperclip is an open-source control plane for managing teams of AI agents, goals, budgets, tickets, and recurring work.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/paperclipai/paperclip", + "website": "https://paperclip.ing/", + "docs": "https://docs.paperclip.ing/" + }, + "tags": [ + "ai", + "agents", + "automation", + "self-hosted" + ] + }, { "id": "parseable", "name": "Parseable", From f14cc64043f84d7bc3e440267ad311336d17d692 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 00:03:54 +0400 Subject: [PATCH 31/78] feat: add PrivateBin template (#947) * feat: add PrivateBin template * chore: pin privatebin template version --------- Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/privatebin/docker-compose.yml | 11 +++++++++++ blueprints/privatebin/privatebin.svg | 5 +++++ blueprints/privatebin/template.toml | 11 +++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 blueprints/privatebin/docker-compose.yml create mode 100644 blueprints/privatebin/privatebin.svg create mode 100644 blueprints/privatebin/template.toml diff --git a/blueprints/privatebin/docker-compose.yml b/blueprints/privatebin/docker-compose.yml new file mode 100644 index 00000000..dc621ca0 --- /dev/null +++ b/blueprints/privatebin/docker-compose.yml @@ -0,0 +1,11 @@ +services: + privatebin: + image: privatebin/nginx-fpm-alpine:2.0.4 + restart: unless-stopped + volumes: + - privatebin_data:/srv/data + ports: + - "8080" + +volumes: + privatebin_data: diff --git a/blueprints/privatebin/privatebin.svg b/blueprints/privatebin/privatebin.svg new file mode 100644 index 00000000..0391799d --- /dev/null +++ b/blueprints/privatebin/privatebin.svg @@ -0,0 +1,5 @@ + + + + P + diff --git a/blueprints/privatebin/template.toml b/blueprints/privatebin/template.toml new file mode 100644 index 00000000..747c3bc7 --- /dev/null +++ b/blueprints/privatebin/template.toml @@ -0,0 +1,11 @@ +[variables] +main_domain = "${domain}" + +[config] +env = {} +mounts = [] + +[[config.domains]] +serviceName = "privatebin" +port = 8080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index a3fdfc39..6dd27215 100644 --- a/meta.json +++ b/meta.json @@ -5585,6 +5585,23 @@ ], "dokploy_version": "<0.22.5" }, + { + "id": "privatebin", + "name": "PrivateBin", + "version": "2.0.4", + "description": "PrivateBin is a minimalist, open-source online pastebin where the server has zero knowledge of pasted data.", + "logo": "privatebin.svg", + "links": { + "github": "https://github.com/PrivateBin/PrivateBin", + "website": "https://privatebin.info/", + "docs": "https://github.com/PrivateBin/docker-nginx-fpm-alpine" + }, + "tags": [ + "pastebin", + "privacy", + "self-hosted" + ] + }, { "id": "prometheus", "name": "Prometheus", From b0429d94b9d2df2812388a5e3196a6698e8b784a Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 00:04:12 +0400 Subject: [PATCH 32/78] fix: keep Valkey internal (#937) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/valkey/docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/blueprints/valkey/docker-compose.yml b/blueprints/valkey/docker-compose.yml index bcabc622..f52cfafd 100644 --- a/blueprints/valkey/docker-compose.yml +++ b/blueprints/valkey/docker-compose.yml @@ -4,8 +4,6 @@ services: valkey: image: valkey/valkey:8.1.4 restart: unless-stopped - ports: - - 6379 volumes: - ../files/valkey.conf:/etc/valkey/valkey.conf - valkey-data:/data From ac716a337f3ce1162e048b165fa27d4c97f0c354 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 00:04:26 +0400 Subject: [PATCH 33/78] feat: add Rallly template (#933) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/rallly/docker-compose.yml | 66 ++++++++++++++++++++++++++++ blueprints/rallly/rallly.svg | 8 ++++ blueprints/rallly/template.toml | 40 +++++++++++++++++ meta.json | 17 +++++++ 4 files changed, 131 insertions(+) create mode 100644 blueprints/rallly/docker-compose.yml create mode 100644 blueprints/rallly/rallly.svg create mode 100644 blueprints/rallly/template.toml diff --git a/blueprints/rallly/docker-compose.yml b/blueprints/rallly/docker-compose.yml new file mode 100644 index 00000000..ac6badae --- /dev/null +++ b/blueprints/rallly/docker-compose.yml @@ -0,0 +1,66 @@ +version: "3.8" + +services: + rallly: + image: lukevella/rallly:4.11.1 + restart: unless-stopped + depends_on: + - db + - minio + - minio-init + environment: + NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL} + DATABASE_URL: ${DATABASE_URL} + SECRET_PASSWORD: ${SECRET_PASSWORD} + SUPPORT_EMAIL: ${SUPPORT_EMAIL} + NOREPLY_EMAIL: ${NOREPLY_EMAIL} + SMTP_HOST: ${SMTP_HOST} + SMTP_PORT: ${SMTP_PORT} + SMTP_USER: ${SMTP_USER} + SMTP_PWD: ${SMTP_PWD} + SMTP_SECURE: ${SMTP_SECURE} + S3_ENDPOINT: ${S3_ENDPOINT} + S3_BUCKET_NAME: ${S3_BUCKET_NAME} + S3_REGION: ${S3_REGION} + S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID} + S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY} + INITIAL_ADMIN_EMAIL: ${INITIAL_ADMIN_EMAIL} + + db: + image: postgres:14-alpine + restart: unless-stopped + environment: + POSTGRES_DB: rallly + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - rallly-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d rallly"] + interval: 10s + timeout: 5s + retries: 5 + + minio: + image: minio/minio:latest + restart: unless-stopped + command: server /data + environment: + MINIO_ROOT_USER: ${S3_ACCESS_KEY_ID} + MINIO_ROOT_PASSWORD: ${S3_SECRET_ACCESS_KEY} + volumes: + - rallly-minio:/data + + minio-init: + image: minio/mc:latest + depends_on: + - minio + restart: "no" + entrypoint: > + /bin/sh -c " + until mc alias set local http://minio:9000 ${S3_ACCESS_KEY_ID} ${S3_SECRET_ACCESS_KEY}; do sleep 2; done; + mc mb --ignore-existing local/rallly; + " + +volumes: + rallly-db: + rallly-minio: \ No newline at end of file diff --git a/blueprints/rallly/rallly.svg b/blueprints/rallly/rallly.svg new file mode 100644 index 00000000..b4e44b30 --- /dev/null +++ b/blueprints/rallly/rallly.svg @@ -0,0 +1,8 @@ + + Rallly + + + + + + \ No newline at end of file diff --git a/blueprints/rallly/template.toml b/blueprints/rallly/template.toml new file mode 100644 index 00000000..902a72f8 --- /dev/null +++ b/blueprints/rallly/template.toml @@ -0,0 +1,40 @@ +[variables] +main_domain = "${domain}" +support_email = "${email}" +noreply_email = "${email}" +initial_admin_email = "${email}" +postgres_password = "${password:32}" +secret_password = "${password:32}" +s3_access_key_id = "rallly" +s3_secret_access_key = "${password:32}" +smtp_host = "smtp.example.com" +smtp_port = "587" +smtp_user = "change-me" +smtp_password = "${password:32}" +smtp_secure = "false" + +[config] +env = [ + "NEXT_PUBLIC_BASE_URL=https://${main_domain}", + "DATABASE_URL=postgres://postgres:${postgres_password}@db:5432/rallly", + "SECRET_PASSWORD=${secret_password}", + "SUPPORT_EMAIL=${support_email}", + "NOREPLY_EMAIL=${noreply_email}", + "SMTP_HOST=${smtp_host}", + "SMTP_PORT=${smtp_port}", + "SMTP_USER=${smtp_user}", + "SMTP_PWD=${smtp_password}", + "SMTP_SECURE=${smtp_secure}", + "S3_ENDPOINT=http://minio:9000", + "S3_BUCKET_NAME=rallly", + "S3_REGION=us-east-1", + "S3_ACCESS_KEY_ID=${s3_access_key_id}", + "S3_SECRET_ACCESS_KEY=${s3_secret_access_key}", + "INITIAL_ADMIN_EMAIL=${initial_admin_email}", + "POSTGRES_PASSWORD=${postgres_password}", +] + +[[config.domains]] +serviceName = "rallly" +port = 3000 +host = "${main_domain}" \ No newline at end of file diff --git a/meta.json b/meta.json index 6dd27215..7b91c0d2 100644 --- a/meta.json +++ b/meta.json @@ -5759,6 +5759,23 @@ "rabbitmq" ] }, + { + "id": "rallly", + "name": "Rallly", + "version": "4.11.1", + "description": "Rallly is an open-source scheduling and collaboration tool for organizing events and meetings.", + "logo": "rallly.svg", + "links": { + "website": "https://rallly.co/", + "docs": "https://support.rallly.co/self-hosting/introduction", + "github": "https://github.com/lukevella/rallly" + }, + "tags": [ + "scheduling", + "polls", + "collaboration" + ] + }, { "id": "reactive-resume", "name": "Reactive Resume", From c9dc13aab86f8a772239619adf297bc83465eb29 Mon Sep 17 00:00:00 2001 From: Luke Dunsmore Date: Tue, 7 Jul 2026 21:09:30 +0100 Subject: [PATCH 34/78] feat: update MediaFetch template version to 1.3.5 (#879) * feat: add MediaFetch template * Corrected .toml * .toml * Updated template to version 1.3.5 * Codex issues raised now fixed * Codex issues addressed * TOML Update --- blueprints/mediafetch/docker-compose.yml | 9 ++++++--- blueprints/mediafetch/template.toml | 14 +++++++++++--- meta.json | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/blueprints/mediafetch/docker-compose.yml b/blueprints/mediafetch/docker-compose.yml index f0c690cd..17ba6176 100644 --- a/blueprints/mediafetch/docker-compose.yml +++ b/blueprints/mediafetch/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.8" services: mediafetch: - image: lukedunsmoto/mediafetch:latest + image: lukedunsmoto/mediafetch:1.3.5 restart: unless-stopped expose: - "3002" @@ -11,8 +11,11 @@ services: - PORT=3002 - BASIC_AUTH_USER=${BASIC_AUTH_USER} - BASIC_AUTH_PASS=${BASIC_AUTH_PASS} - - PUBLIC_BASE_URL=https://${DOMAIN} - - OUTPUT_DIR=/data/downloads + - PUBLIC_BASE_URL=${PUBLIC_BASE_URL} + - OUTPUT_DIR=${OUTPUT_DIR} + - MAX_CONCURRENT_JOBS=${MAX_CONCURRENT_JOBS} + - JOB_TIMEOUT_MS=${JOB_TIMEOUT_MS} + - VERSION_CHECK_TTL_MS=${VERSION_CHECK_TTL_MS} volumes: mediafetch_data: \ No newline at end of file diff --git a/blueprints/mediafetch/template.toml b/blueprints/mediafetch/template.toml index 07672d4e..791bd07f 100644 --- a/blueprints/mediafetch/template.toml +++ b/blueprints/mediafetch/template.toml @@ -1,13 +1,21 @@ [variables] +main_domain = "${domain}" +PORT="3002" +PUBLIC_BASE_URL="https://${main_domain}" +OUTPUT_DIR="/data/downloads" BASIC_AUTH_USER = "admin" -BASIC_AUTH_PASS = "${password:12}" +BASIC_AUTH_PASS = "changeme" +MAX_CONCURRENT_JOBS="2" +JOB_TIMEOUT_MS="600000" +VERSION_CHECK_TTL_MS="21600000" [config] + [[config.domains]] name = "Domain" -variable = "DOMAIN" serviceName = "mediafetch" -port = 3002 +port = "3002" +host = "${main_domain}" [[config.mounts]] name = "Downloads" diff --git a/meta.json b/meta.json index 7b91c0d2..55a16f71 100644 --- a/meta.json +++ b/meta.json @@ -4248,7 +4248,7 @@ { "id": "mediafetch", "name": "MediaFetch", - "version": "1.1.1", + "version": "1.3.5", "description": "A tiny, self-hosted web wrapper for yt-dlp to download video and audio. Optional basic auth.", "logo": "mediafetch.svg", "links": { From 0074651a45c20ee7f60fac203e12417cafa8285d Mon Sep 17 00:00:00 2001 From: Artemis-ignis Date: Wed, 8 Jul 2026 05:10:00 +0900 Subject: [PATCH 35/78] feat: add Miniflux template (#881) * feat: add Miniflux template * fix: guard GitHub star count fallback --------- Co-authored-by: Artemis-ignis --- app/src/components/Navigation.tsx | 4 ++- blueprints/miniflux/docker-compose.yml | 45 ++++++++++++++++++++++++++ blueprints/miniflux/miniflux.svg | 1 + blueprints/miniflux/template.toml | 17 ++++++++++ meta.json | 17 ++++++++++ 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 blueprints/miniflux/docker-compose.yml create mode 100644 blueprints/miniflux/miniflux.svg create mode 100644 blueprints/miniflux/template.toml diff --git a/app/src/components/Navigation.tsx b/app/src/components/Navigation.tsx index 47639224..adba7dd7 100644 --- a/app/src/components/Navigation.tsx +++ b/app/src/components/Navigation.tsx @@ -13,7 +13,9 @@ const Navigation = () => { "https://api.github.com/repos/dokploy/dokploy" ); const data = await response.json(); - setGithubStars(data.stargazers_count); + setGithubStars( + Number.isFinite(data.stargazers_count) ? data.stargazers_count : 0 + ); } catch (error) { console.error("Error fetching GitHub stars:", error); } diff --git a/blueprints/miniflux/docker-compose.yml b/blueprints/miniflux/docker-compose.yml new file mode 100644 index 00000000..874021ac --- /dev/null +++ b/blueprints/miniflux/docker-compose.yml @@ -0,0 +1,45 @@ +version: "3.8" + +services: + miniflux: + image: miniflux/miniflux:2.2.19 + restart: unless-stopped + depends_on: + db: + condition: service_healthy + environment: + DATABASE_URL: user=miniflux password=${POSTGRES_PASSWORD} dbname=miniflux host=db sslmode=disable + RUN_MIGRATIONS: "1" + CREATE_ADMIN: "1" + ADMIN_USERNAME: ${ADMIN_USERNAME} + ADMIN_PASSWORD: ${ADMIN_PASSWORD} + BASE_URL: https://${MINIFLUX_DOMAIN} + expose: + - "8080" + healthcheck: + test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + + db: + image: postgres:18-alpine + restart: unless-stopped + environment: + POSTGRES_USER: miniflux + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: miniflux + expose: + - "5432" + volumes: + - miniflux-db:/var/lib/postgresql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U miniflux -d miniflux"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + +volumes: + miniflux-db: diff --git a/blueprints/miniflux/miniflux.svg b/blueprints/miniflux/miniflux.svg new file mode 100644 index 00000000..3537ce7e --- /dev/null +++ b/blueprints/miniflux/miniflux.svg @@ -0,0 +1 @@ + diff --git a/blueprints/miniflux/template.toml b/blueprints/miniflux/template.toml new file mode 100644 index 00000000..d6f553f7 --- /dev/null +++ b/blueprints/miniflux/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +admin_username = "admin" +admin_password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "miniflux" +port = 8080 +host = "${main_domain}" + +[config.env] +MINIFLUX_DOMAIN = "${main_domain}" +POSTGRES_PASSWORD = "${postgres_password}" +ADMIN_USERNAME = "${admin_username}" +ADMIN_PASSWORD = "${admin_password}" diff --git a/meta.json b/meta.json index 55a16f71..6584865f 100644 --- a/meta.json +++ b/meta.json @@ -4345,6 +4345,23 @@ "docker" ] }, + { + "id": "miniflux", + "name": "Miniflux", + "version": "2.2.19", + "description": "Miniflux is a minimalist and opinionated feed reader for self-hosted RSS and Atom subscriptions.", + "logo": "miniflux.svg", + "links": { + "github": "https://github.com/miniflux/v2", + "website": "https://miniflux.app/", + "docs": "https://miniflux.app/docs/" + }, + "tags": [ + "rss", + "feed-reader", + "news" + ] + }, { "id": "minio", "name": "Minio", From 201fcd1ed8acd99b68374e7b620bfcca3630a26f Mon Sep 17 00:00:00 2001 From: Robert Da Silva Date: Tue, 7 Jul 2026 22:11:16 +0200 Subject: [PATCH 36/78] feat(blueprint): add LimeSurvey template (#878) --- blueprints/limesurvey/docker-compose.yml | 40 ++++++++++++++++++++++++ blueprints/limesurvey/limesurvey.svg | 38 ++++++++++++++++++++++ blueprints/limesurvey/template.toml | 19 +++++++++++ meta.json | 17 ++++++++++ 4 files changed, 114 insertions(+) create mode 100644 blueprints/limesurvey/docker-compose.yml create mode 100644 blueprints/limesurvey/limesurvey.svg create mode 100644 blueprints/limesurvey/template.toml diff --git a/blueprints/limesurvey/docker-compose.yml b/blueprints/limesurvey/docker-compose.yml new file mode 100644 index 00000000..936e5199 --- /dev/null +++ b/blueprints/limesurvey/docker-compose.yml @@ -0,0 +1,40 @@ +version: "3.8" + +services: + db: + image: mariadb:10.11 + restart: always + volumes: + - db_data:/var/lib/mysql + environment: + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_DATABASE=limesurvey + - MYSQL_USER=limesurvey + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + + limesurvey: + image: martialblog/limesurvey:latest + restart: always + volumes: + - limesurvey_upload:/var/www/html/upload + environment: + - ADMIN_EMAIL=${ADMIN_EMAIL} + - ADMIN_NAME=Admin + - ADMIN_USER=admin + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + - DB_HOST=db + - DB_MYSQL_ENGINE=InnoDB + - DB_NAME=limesurvey + - DB_PASSWORD=${MYSQL_PASSWORD} + - DB_USERNAME=limesurvey + - DEBUG=0 + - DEBUG_SQL=0 + - PUBLIC_URL=${PUBLIC_URL} + - SHOW_SCRIPT_NAME=false + - URL_FORMAT=path + depends_on: + - db + +volumes: + db_data: + limesurvey_upload: diff --git a/blueprints/limesurvey/limesurvey.svg b/blueprints/limesurvey/limesurvey.svg new file mode 100644 index 00000000..9ee4c0c6 --- /dev/null +++ b/blueprints/limesurvey/limesurvey.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + diff --git a/blueprints/limesurvey/template.toml b/blueprints/limesurvey/template.toml new file mode 100644 index 00000000..07623f50 --- /dev/null +++ b/blueprints/limesurvey/template.toml @@ -0,0 +1,19 @@ +[variables] +main_domain = "${domain}" +mysql_root_password = "${password:32}" +mysql_user_password = "${password:32}" +admin_email = "${email}" +admin_password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "limesurvey" +port = 8080 +host = "${main_domain}" + +[config.env] +PUBLIC_URL = "http://${main_domain}" +ADMIN_EMAIL = "${admin_email}" +ADMIN_PASSWORD = "${admin_password}" +MYSQL_PASSWORD = "${mysql_user_password}" +MYSQL_ROOT_PASSWORD = "${mysql_root_password}" diff --git a/meta.json b/meta.json index 6584865f..65dde0c7 100644 --- a/meta.json +++ b/meta.json @@ -3882,6 +3882,23 @@ "language" ] }, + { + "id": "limesurvey", + "name": "LimeSurvey", + "version": "6.17.2+260507", + "description": "LimeSurvey is a powerful, open-source survey platform, making it simple to create online surveys and forms with unmatched flexibility.", + "logo": "limesurvey.svg", + "links": { + "github": "https://github.com/LimeSurvey/LimeSurvey", + "website": "https://www.limesurvey.org", + "docs": "https://www.limesurvey.org/manual/" + }, + "tags": [ + "forms", + "questionnaire", + "survey" + ] + }, { "id": "linkding", "name": "Linkding", From cc99a025d3bb998720e44aa5b12b57d7327f5670 Mon Sep 17 00:00:00 2001 From: atlasclaw369 Date: Wed, 8 Jul 2026 00:12:07 +0400 Subject: [PATCH 37/78] feat: add Homepage template (#871) Co-authored-by: atlasclaw369 <268303653+atlasclaw369@users.noreply.github.com> --- blueprints/homepage/docker-compose.yml | 9 +++++++++ blueprints/homepage/homepage.png | Bin 0 -> 1407 bytes blueprints/homepage/template.toml | 13 +++++++++++++ meta.json | 18 ++++++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 blueprints/homepage/docker-compose.yml create mode 100644 blueprints/homepage/homepage.png create mode 100644 blueprints/homepage/template.toml diff --git a/blueprints/homepage/docker-compose.yml b/blueprints/homepage/docker-compose.yml new file mode 100644 index 00000000..2a6d48b9 --- /dev/null +++ b/blueprints/homepage/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3.8" +services: + homepage: + image: ghcr.io/gethomepage/homepage:v1.13.1 + restart: unless-stopped + expose: + - 3000 + volumes: + - ../files/homepage-config:/app/config diff --git a/blueprints/homepage/homepage.png b/blueprints/homepage/homepage.png new file mode 100644 index 0000000000000000000000000000000000000000..0a11f3dc8a690fe9fb31f6cd695f27e7bf53e5e6 GIT binary patch literal 1407 zcmZ8hX;4!K5RS$c5rk2&j6fM$w1PlT6VW`5gd4&kgcKnhiXt%r29scjlzS?oawr7k zcrk^fwNB+Iks%yG3j!8=!V^aWm{ceU!3G(@4PVHVeAdp#JAOE)VtwYl*+pK%^-aiiuXZ`QX_ts7Csh%_~9sz!uyZU5_Ij|Vj zuDA!U+=Vm+k5yq06p7-ub>r`b-{{b}gObHG8j!1G7 z(K^Q|Xm!&vo;dYp&%=2ih^^%7PlYHa8v4dbEq(&$>n;BkT>3v+hEet*DK+^6PWbcT zqSa;Hv;8t`JGYH1(^9pf`H*Mrw+#lv-TjJiX`Kb~SZSU1oS%**e_!ICOc02o8RkHti? zm7-!ujDz>qNmY_18MLr@r}ipP6{gPP>rO3Cgb$8!3T}$CX;fRaaXVcLb>Loby}uGQ zKvopnWp93jXG4_`Nmx5lP&3#9ynu*}AVOe9Mk%(<|DY4+twc22y9unxUtL=Fo zdm2)lK-Y{o+odFYEBr{hYf4m}kM`L4m)*l7uI^F>t_Ysj8LxeUJMikp=N|b1s^2o( z*uKuAG3F%2B;ImsY?3d-zT7E(h6z=aYWnjk2|c;;^W@Q8$uld>ULP|mp#>$!lCBdD zo>Lr$f~~t?D6={ASm7+o*b@Rsx*35zVz1{?yqm-Z*|uTBCjx) z2y8jN2A2CgsrOYSxrgNO0|v2fnmH36b-|@gQTi0{yaIbrBk%K38|QDB$ceMa?%wy` zQs|f6ys^*w#reU#kwEA`pJ376BaE&};M`*}gR}%DbY9_v$Q)wd*a|)yhz@gIQ z^mbhd?=ocMmSv=woXvQ5TJ*-?k}Q0;xMVR}!dsIk=G?~Aw-W5cVqHN;6I)_`qB(|c z@W~8OKXS{bk!SK0AV*48dUe5Tx{4L}<>^Eyo4&C=qU5)`hT2qOU9vcsmA-h1y*n)$ znW`f1XqUJd8v*bdR0~Y+<6R;HxN)RV+UU^y6y6;^DVb-g%t`GA&`iR3d^ns63>f%u zI_N{BzLrocd&JYb*(OcAm8|mj@!>J_r~8*IJe`xHPkJ_s|99&x&L3S1w{W+bQqfdA zfPA!*S19ppaw^9$o5@J0XP$FpbFOuMYy3fv{M(was2i4*zm-#{jkY+QK*1~K`q Date: Wed, 8 Jul 2026 04:23:01 +0800 Subject: [PATCH 38/78] Add qui A fast, modern web interface for qBittorrent. Manage multiple qBittorrent instances from a single, lightweight application built by autobrr (#825) * feat: add qui template qui is a fast, modern web interface for qBittorrent by autobrr, allowing management of multiple qBittorrent instances from a single, lightweight self-hosted application. - docker-compose.yml: pinned ghcr.io/autobrr/qui:1.17.0, exposes 7476 internally (no host port; Dokploy's Traefik handles routing), persistent named volume for /config. - template.toml: maps the qui service port 7476 to the user-supplied domain so Dokploy's Traefik can proxy it. - meta.json: register the template with logo and tags. * fix(qui): use latest image tag ghcr.io/autobrr/qui has no v1.17.0 tag published; only 'latest' and PR/dev tags are available. Switch the image and meta.json version to 'latest' so the template actually deploys. * revert: restore duplicate strapi entry removed by dedupe script The previous commit accidentally removed a pre-existing duplicate strapi entry as a side effect of running process-meta. That entry exists upstream and is unrelated to the qui template; restore it verbatim. * fix: deduplicate meta.json to pass CI validation The upstream meta.json had a pre-existing duplicate strapi entry. Remove it so dedupe-and-sort produces identical output to the committed file, satisfying the validate-meta CI check. * fix(qui): replace logo with autobrr org avatar (128x128) --- blueprints/qui/docker-compose.yml | 13 +++++++++++++ blueprints/qui/qui.png | Bin 0 -> 12411 bytes blueprints/qui/template.toml | 9 +++++++++ meta.json | 19 +++++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 blueprints/qui/docker-compose.yml create mode 100644 blueprints/qui/qui.png create mode 100644 blueprints/qui/template.toml diff --git a/blueprints/qui/docker-compose.yml b/blueprints/qui/docker-compose.yml new file mode 100644 index 00000000..0158d9cd --- /dev/null +++ b/blueprints/qui/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.8" + +services: + qui: + image: ghcr.io/autobrr/qui:latest + restart: unless-stopped + expose: + - "7476" + volumes: + - qui-config:/config + +volumes: + qui-config: diff --git a/blueprints/qui/qui.png b/blueprints/qui/qui.png new file mode 100644 index 0000000000000000000000000000000000000000..64897dc4516cca619f265498c6305030b00806bf GIT binary patch literal 12411 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 literal 0 HcmV?d00001 diff --git a/blueprints/qui/template.toml b/blueprints/qui/template.toml new file mode 100644 index 00000000..3858b997 --- /dev/null +++ b/blueprints/qui/template.toml @@ -0,0 +1,9 @@ +[variables] +main_domain = "${domain}" + +[config] +[[config.domains]] +serviceName = "qui" +port = 7476 +host = "${main_domain}" +path = "/" diff --git a/meta.json b/meta.json index b63a3177..af267b5d 100644 --- a/meta.json +++ b/meta.json @@ -5794,6 +5794,25 @@ "analytics" ] }, + { + "id": "qui", + "name": "qui", + "version": "latest", + "description": "A fast, modern web interface for qBittorrent. Manage multiple qBittorrent instances from a single, lightweight application built by autobrr.", + "logo": "qui.png", + "links": { + "github": "https://github.com/autobrr/qui", + "website": "https://getqui.com", + "docs": "https://getqui.com" + }, + "tags": [ + "torrent", + "qbittorrent", + "download", + "media", + "self-hosted" + ] + }, { "id": "rabbitmq", "name": "RabbitMQ", From f1210e6ac4feda626eab3cf45097e00c35c44741 Mon Sep 17 00:00:00 2001 From: Ennio Marke Date: Tue, 7 Jul 2026 22:34:26 +0200 Subject: [PATCH 39/78] feat: Add Seanime and remove strapai duplicate entry (#808) * v1.0.0 * v1.0.1 * Update blueprints/seanime/docker-compose.yml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update blueprints/seanime/docker-compose.yml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update blueprints/seanime/docker-compose.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * v1.0.2 * Update blueprints/seanime/docker-compose.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * v1.0.4 * v1.0.5 * v1.0.6 --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- blueprints/seanime/docker-compose.yml | 26 ++++++++++++++++++++++++++ blueprints/seanime/seanime.png | Bin 0 -> 20568 bytes blueprints/seanime/template.toml | 7 +++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 blueprints/seanime/docker-compose.yml create mode 100644 blueprints/seanime/seanime.png create mode 100644 blueprints/seanime/template.toml diff --git a/blueprints/seanime/docker-compose.yml b/blueprints/seanime/docker-compose.yml new file mode 100644 index 00000000..9f6992a0 --- /dev/null +++ b/blueprints/seanime/docker-compose.yml @@ -0,0 +1,26 @@ +version: "3.8" +services: + seanime-init: + image: busybox:1.37.0 + user: "0:0" + volumes: + - ./seanime-config:/mnt/config + - ./anime:/mnt/anime + - ./downloads:/mnt/downloads + command: + [ "sh", "-c", "chown -R 1000:1000 /mnt/config && find /mnt/config -type d -exec chmod 755 {} + && find /mnt/config -type f -exec chmod 644 {} +",] + + seanime: + image: umagistr/seanime:v3.6.1-rootless + depends_on: + seanime-init: + condition: service_completed_successfully + environment: + - SEANIME_SERVER_HOST=0.0.0.0 + expose: + - "43211" + volumes: + - ./seanime-config:/home/seanime/.config/Seanime + - ./anime:/anime + - ./downloads:/downloads + restart: unless-stopped \ No newline at end of file diff --git a/blueprints/seanime/seanime.png b/blueprints/seanime/seanime.png new file mode 100644 index 0000000000000000000000000000000000000000..9e2459d918bba4eba1df66bc25e69af30581dc67 GIT binary patch literal 20568 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 literal 0 HcmV?d00001 diff --git a/blueprints/seanime/template.toml b/blueprints/seanime/template.toml new file mode 100644 index 00000000..eabd94ef --- /dev/null +++ b/blueprints/seanime/template.toml @@ -0,0 +1,7 @@ +[variables] +main_domain = "${domain}" + +[[config.domains]] +serviceName = "seanime" +port = 43211 +host = "${main_domain}" \ No newline at end of file diff --git a/meta.json b/meta.json index af267b5d..1cd02407 100644 --- a/meta.json +++ b/meta.json @@ -6103,6 +6103,23 @@ "storage" ] }, + { + "id": "seanime", + "name": "Seanime", + "version": "3.6.1-rootless", + "description": "A self-hosted anime streaming platform.", + "logo": "seanime.png", + "links": { + "github": "https://github.com/5rahim/seanime", + "website": "https://seanime.app/", + "docs": "https://seanime.app/docs" + }, + "tags": [ + "media", + "anime", + "streaming" + ] + }, { "id": "searxng", "name": "SearXNG", From bc31807d6f2a7f433a26985e4e426604ca79fe87 Mon Sep 17 00:00:00 2001 From: Stefan Ernst Date: Tue, 7 Jul 2026 23:02:48 +0200 Subject: [PATCH 40/78] feat: add windshift to dokploy (#787) * Adding Windshift for Dokploy * Added up-to-date version and better tags * Updated logo with less transparency * Update docker-compose.yml with latest version Signed-off-by: Stefan Ernst --------- Signed-off-by: Stefan Ernst --- blueprints/windshift/docker-compose.yml | 44 ++++++++++++++++++++++++ blueprints/windshift/template.toml | 19 ++++++++++ blueprints/windshift/windshift.png | Bin 0 -> 81733 bytes meta.json | 18 ++++++++++ 4 files changed, 81 insertions(+) create mode 100644 blueprints/windshift/docker-compose.yml create mode 100644 blueprints/windshift/template.toml create mode 100644 blueprints/windshift/windshift.png diff --git a/blueprints/windshift/docker-compose.yml b/blueprints/windshift/docker-compose.yml new file mode 100644 index 00000000..81008518 --- /dev/null +++ b/blueprints/windshift/docker-compose.yml @@ -0,0 +1,44 @@ +version: "3.8" + +services: + windshift: + image: ghcr.io/windshiftapp/windshift:v0.5.3 + restart: unless-stopped + environment: + - BASE_URL=https://${DOMAIN} + - PORT=8080 + - USE_PROXY=true + - SSO_SECRET=${SSO_SECRET} + - ATTACHMENT_PATH=/data/attachments + - DB_TYPE=postgres + - POSTGRES_HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + - LOG_LEVEL=info + - LOG_FORMAT=json + volumes: + - windshift-data:/data + depends_on: + db: + condition: service_healthy + + db: + image: postgres:17.4 + restart: unless-stopped + environment: + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + windshift-data: {} + postgres-data: {} diff --git a/blueprints/windshift/template.toml b/blueprints/windshift/template.toml new file mode 100644 index 00000000..e1e4cb02 --- /dev/null +++ b/blueprints/windshift/template.toml @@ -0,0 +1,19 @@ +[variables] +main_domain = "${domain}" +sso_secret = "${hash:64}" +db_password = "${password:32}" + +[config] +env = [ + "DOMAIN=${main_domain}", + "SSO_SECRET=${sso_secret}", + "POSTGRES_USER=windshift", + "POSTGRES_PASSWORD=${db_password}", + "POSTGRES_DB=windshift", +] +mounts = [] + +[[config.domains]] +serviceName = "windshift" +port = 8080 +host = "${main_domain}" diff --git a/blueprints/windshift/windshift.png b/blueprints/windshift/windshift.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef1bee6cf498604a05a4f1b23c58379424f2552 GIT binary patch literal 81733 zcmV(_K-9m9P)(sj)g+?j5k> zxC`)N1NI{U;=I^oi~u1Fk})8I$BSVk4+vlgjEq1YJVyNB5$ptm!MMXlwj)cHC0UZy zRjE|%y|=1v-~Zh?XJvk~TXXOKmt(*N=Sa8y)9k(WYG(7zX3fR)f5lg8On;h~-Y);} z`V8?C^y2)+#&Br9w)Vh4i}TEUZbf{yt?2YHv=eg63F6fWqTz@%aD?0HFfngPOB>W^ zLw;WyX0#^P&*isS8<^!eGijaWxt zX!1Y*jDfNIcV!=09ziR+xjaZ4<m!nfOcPS{+&)tN&=<>)>^c{aWoNzF6s}rL|2f8xkgnR|94Wzu_~} zo?Nmge|O}%UAb>pF4~qRw&ccpa^GE<%iE1-H;ImB3K14k_H{N#+3-?z?`G`+XX*Yxo#ohNS@@A2f- zDw%b3UobdcGKZMomESgcB-?Mzp4){L(L^$|yxyMH>dKdCsq3y|V!M~48GH|ca7F&U zA|GFnA3o1*`Rr^Mo?UD$FRqTQ-9I{P?7g^qZAq_w*^<7GSM;0s^_!2eg^@-3uHV{R zZOk@ihudQIxDd;GWl`QotM*>G;wf2$BeEWYaY8cV!NeC#rynRNf2{FFuPO6)V(d&l zCNBGpUHiIbil;%!R3bHcg4QW*I+Ho!r^ryj!j!5pE_9kSrYi#rfn=AsY|_5@SVjP1 z|Ep@@6aq|rsFg_zkB#Te-WMjK=wb>Jza#s z_K!`Ux{!}Ico;q_^!IW3;UjAUeQJ+audap%@1MMPbN9LHPwxF@WAxt~j4jOkFul6D zdt&zl?X5i|K=(_fYrjP%_p9XBH^{&BnC#G`df!AV?9({5*mXHO^SAn2A$WG0Xr0lf z8fzcXmG^OTD1@;B)|*XQjNvZT`zeOXexvmvdfIs3x*fo<)0Oq{ju^D|Wx~``#L(NF z2)efKSt$?$T5~r#CPLN5NfkY1`_?e`IpRLgMXRSEh^cDboG9jicer)Dl6^A`&Q%an z7f>qLSDc-0Y7X3$PoI~cJ}BRRK#a0a(ca$WwKMnbT=>ja@BC(9@_)08{e5lz@X=RK z?s0o;)#$5bR)4#6=qu#?De2iz@mbo6{kx)9swQatrr;ST0%by;XaVQIk>>3-qO0uQ zE_Y7sf~4D!0cnkZl382f0v``mraB0A7nPWVnj;K36*?^NDm(D+T7C5DS~bB-6_TAv zJ7TDicRc^)w$|^ha68s%YFC%OSE1_+$>ZL3ZCW8hc9q8;^WHRV*gOiOF|tYCZr@Yd zFRdNS6FefUI<`HrJub?J4~W_Slf#NXy18-W(u+r)xI^Cs-@w0_*S{IY{u>_=;r76J zS`KfMx%oEv=C{kgC!K=#Ya@eLTp8ow*9baNY6)lTgYWZu_Cd|mB!S1r+KRgFH`y?T zyVcMhIgz4yD%vJ5wP%DTVk$bH5AJukuS^3K0Ws3N0nLr4TSX*(d!4$trwTkGDgq<4 zTN^P8nWV}-`#`%8=^rU)CDz89amxR7N|S6)klP0v|@84z~kq1X3V+a{TjD0_eAlJiLlIBk!?i?b<&;ABh@jP z&@0oOTuIyoZNE0=GnhI(;4vOD{L!ekZ;;r9VLU9ga0r0nF?8mmqW!UkgTm^B-BX*N zQed}0KO+P(<(B?i7o+yn9eMMteEKJimOn9{FFy0y(dVwycYW8tHVyu-hp}bw$Nuca z)9Y*eUcuMjE>Qd1<-w1n-A8SN0{gr4Tt++ldK}I%)5^|jVfRuilDd+x-Is!b$2M>f z+o%sjBkk3nO;ja*J~!IQzUZ%=7R#un#FZM6I)Pbu3U{t}B#c~xbnQj^W}o8wq8Yhb zHbb>*siKC-WT$XBpzVq97&-YUk;O#7jx;fIU#o5#cWK$$es6y;DN)7zV*%5qp8FVs zJ281W#%QZJDM#VEsy?bupxgE#<#y#ipOg9hhqPSE!-ndkqLH{GUx4dJujc*Yjk zefH-0cN5S4U3uE?mBBt4)7qTAM?_2o8sinjTQwLRpqXqmBNEDAN1DAX0FB-{gK`j6 zOSMqow>{gnv`UA@8oD70KLAfPQtw-gs%4*w>ZRm*nwSnY{@z8(@4c}$m5GHxs5`Ny z_H-Q-RqfILx|K9z&j%3N9L%5N?n=7zoJ~ zHFV`<)E#SMW=O3&ef4k_m9D0yPV5_Ds+#zr-*{BJAPky%;;VPJ%u#Sh{*m|pliFMljf(h)ef?@2SEW(ruBOl;H;j%`fu~cMMy!^@3 zqyAGD`t`S*mnsBls$}|o@BNbN^c(d0 zjbUuq(l5XErMz1G-(^U@Uxt3Z6W~5aRswSBj%W%EGKODul8emmlo9%Y4j5@Cx*Epn zeThXK$%WCPyJUBmRz%F?`YDVp5OyD9Oq@-;kAx~zpNy?fOzY09LQfOOzE897o3C!0 zy2_?8b5jv0Bduj$z=6dBSz=gU!x0MTz3bz3NFAmNQ3-=}2cHY>BJ}HAao}p}bJx1w z9#8D)k+2BC#l;4-RbM{Ej55s%h#x z6$URt#pjyoizN5te2%nNyxLeKaF-LQd4~ys7cX>vRpR|IFBPg2?{|b9D7b6;eoe-y zsl=xz!m$w*fnyF&YMIbd#y)9(q?o3TIx3+vcN+2tk>Jf0c+chAzq5Ad-Ul!Iv9H;p zU&q(4D`TJkE3ZGWTI~G?wBqlTk55adXZ1-3IGU0-$HW&ulyD6BueFJZ)I4a?scRj- zH79or56Ks~KWW@nmBRVF)p-6wD>Uk`cjOl^w?cfM%~!$E2WUU1BC_jS5?!Cbk#?;_ z%WmqX>^T56Ob9*V_jymBrhNj#1Xe`_G*-$J6#blPt>8?#gc;7H9I`-W=}! z*oE)?nqN0){_Dor=l<*~Pqww;4~VPz_ep0T>M}5S#>vD#rnYt3&ecgo?C+B~y}6Ks zZ5k~8Vxt)^P}Ou~tuecvONUz5excpDlXY~Y+v5HE2uFn!8Tk%!029wUQ5OW=8B``{ zzu+2;_8?SKh`D$B0OlXP0`?Yf@Mun_s3H53L>)Hw1QD3?!QBhJW1Rj|nr=d=NWjNV zzHvnzmK_sQup80XMz_8We4tR8%*l9swLPW@65^>%-e;UfVl^dW%;nVQ;8$a#%4Zie z{J`e=+7Dg$?x!!(ufyxtg|Q#_bFaK}ZF`O{Ges(o%@bSVE znP1%WFy=PRLSfDoB8>6sBRfJEwRM1VYX5Vndxoa%mjR6==}3aKa!r5!)N$9d`$i^8 zM{`q0+yUdMDWO4Uk*HFIM09gRHwD3jc6U1HC~tCY&_|%e5ZDvyqLInbdUT=wjq&=r zEad9v<%YjUL;L?-{+@R|PydRqe`Ur#`{yscPloqLrKR5?9Xvku*7{{Z-KvcW$5Tqp zqICm~;@5#)1H?T{75W8+tKR8Qm7K(pt53R{Xh_DT4pI9PoXiSQ(%5{6@#w8wcOu1W zs*P!6YZh;y5n_Rses?F>+75MteKc>(DVN;sl#ap9pkpG>SBX;8X^uyBW;j4o7svg+ z?VN>=l8l+F)!J;Hsv}~eRZH95yIPhwO&h<`?eYA5vWC$nw~25_!B8JO(c*M)-^Q=5 zrQ4VY##GIQLK%*_KADRFJIk1UP`>@!SHI`o&(OcZ>tBhnjj31vrF8P!q-V!lO13ZB zx&f+S&UxXk+r!{3$vy3N#+ zFY>q1y)o-!NgLOWnc5}$2rjjJTEZ-6YNo=}fdqK0xLPc$s|+svFVxf-Crk8?)Y^|_v%jNZM znKeCLnuV5W(mt8U_c6Dy)0?q=J5Vgy#Ck8xbbGc66eg1EK@=f$F z`TCb)Y&gnh!|+GMi}5?;<1t&8x~{FFs#d+n?g;A8cH%^G#_96Ezw;h*tMpNC%jmbfkDXfiApO15_+_%>C2;Uh6-Ed1BHPzx#D3j0T2$VuA8eJi#y$C~GJ zIfY$z*ooR9N!pvKp^Vu40j)40=O7D&Eg`tl_ne=2I2G;IQXBGgHOpF7sN98OOh|Ks z!~|5*hc#M6D~<1`v~&BIQeFmx&cCI~buKF}#*8 zr&^c7hC^&>oFtkhQw$+gIR`~S@Ug4|2aa(PUlp6lCvhKh#09RYxH7(7NXhwu6I<&- z!6!c?bBF1LMv2mT;Qp}^F^#?7t&g=ia}AtQt#z&99^x@5WF!0_GHqoQmqdH96v4pZ zd=5=LWxUKgw;MU(PJd>j&H$rijf!wW{G`zK>n6g`XS`o)>zDlM(lPCoQ0OD3Z1v(P z!`b|BM+`ZpDhbja7`osiS3tElLBqJ9=8Bhf30Z5s>T}Th8td*)j&Za9>tDz1x?g!N z_q{>B_^!R3t>5|R-~5Hs^e_1O7h-HpK3S7EbwfytPI-@rdd!fC+8Bu2YlGBa$|2ixPwsvvT;`T{g>vrbAe9Xcd07qlFM zJ)=yG{umHGWC)Ko+PFjvv0q6v7btvtA&Mt{iXv!Ql}{bl1;tQ!0Pt3ro1UZ@|K2@1 z^39Ka|1TV(e}UJpow0wg&GuG{Zr+=UW_geE7{E~(py@yd(6f35Q#*R@M& zT!aj_(kAb-!-z-XaYT>-h&VsDNPM9Lx6w=+-Pg$LjKxDjt@H5bTLDLbogr1zi5qbqpO;Ap{7mP(k`ruIIChNPI7)}gwMv2piV zcL6p*Y5MF{`GO*(6>M1|K@%O-XySc0J=iFbVH>Tm>yWl7-+zg``+bXJhrW`2@CRq~ zYkd8h85_CZ1M~ODgPvxGnIkP*xQL6wsq^3xxoF4q4ss3V2Sgd!KtUTZIWehKLUYx+ z)EBP~Wa5of`CL>$Cn6g6+rHzJW#dN4o8lmvmW~waY)@pwm;&1&p7ne5cJkvh{KVO&-|3?2Cj)k-dr;sH$f%dH?bqUw?VqyGXVk(3bHlZ^|AvZ2zF5+CY z97FoT>*MYB?s@GffT)`R(GZP?!NbrTaB*nX&{APjr>Mt2x4$#%EHz0R38zAWsWFNe zaG^I*U_>;DyZ5zg2MnWV#s07^FnQEYy1>ZPSNC6X^oa_Ml)eVKXx$^;JJ!d_MT>9| z23$JV>Cu(I#6M9cgo7fKkp};5h$sgBt=z;Y?5fmT!)Dz1zjb1>4i|yPRQp4XiXe&c zL_$JIo;oWg%eO7p+qXRWgP-~}+1vl;jQux0vbJg)-y~kE@0Q1{hYjTM;qU;C97i!# z{R`{-5So^9)WPvIvg3Hl)jq2uurkqZ_GJi7duj-QkyiIUuv>agN<|peF;It4iR3E( znNsTGOoq=+MQk+SbML;darS&fa4l7RG4qSZwJ@D`rwgyr2!-E+#xvD887*wPIu0^R zlk6XrMk_sM!NY4q%!hu_6R`w!*QS?`1{7}rojnT6*+q#pH95 zQS6CAHGLi0hz^5pIGlvz?&KuI0(GrbdqG_r?u)4xc|zeJitk%@HgRby)kF?0=CYMM zg3!v@;c@sZT8T?t5b{`~bj?UrC}~4&G*3>;WdU7y*Bfcnr>ZS7H{901#Z~K02GN?_ zyQTeaCA#5)F0}ngdH0R;wb|D_`FEc?M!(AIi!-)(!RM>t%cR}kD^1SJAV#z~7J2ce z^*=cRN=DPqOl6_E7kb=K5Kqup*ShIdB#Q6zlSNzlZ>3*T8;JJV$X$E62xW&4q`AGO z0NcpH*J6O>Z)cF!8OZ$#t(yg{k1{n7jifPm^Sk$x`^>r)D9IxIw|-wpexk{GIpiNP z7}X1k%!zhl?b2utb_!}}JyqB~u6(YHQJ8!9x@ux)E9N>UoK8-f_TMj?K$;v_7nBq^ zF8-L%>fuc%>Y+F)$S&_F&dT%+wLm~IOF1V&Q59BoPVH^lcvl=vg-~Wk!IiE!*nsFk z`T5)L?%jR*SM!p8QN~6!QMv2ilP5i;i!cP-bg!=4)*M=cCLB)|j>u0T6w;c>0g}6* zTCQ78n3Id6fU&mdKB7K$yKchp{x+FZuk8XOzzncXa zHLET3b&(2VjABgEok{M|)|5mSQEe({LxA--AsM6&zlg`0yI`=9G}jQl(`3(X-fwaZ z5>IM<8B&-qjciZar|w&2&J<>210g4dLP%6DWvW38Q-91A*>r}hLd<73h`7DJrSw)C3qaoftx6#J=VcehoY)-ncKQ@3IZN1h;en{?lH?`R}Jb3rL zU(}IyFk_3uOiKi>+IPtFhM4>o;>{+P3(@(LJMmKtlE+J#9Zb8j{GfAYUCJF(g;83x z9ev>PeO@#s^niw)#>TeELC(c~Z732dl26ClVjpuG&65+NU*FomIV;g#?+0}a!dy`(7iQ|S+<%Sr z&Jd5wn=Tiv)2#^fK>0-zBqreKL#08dk2+sPhQ`blxx)n?bQfI2n?uA+XwX-@%HqTY zv7FqWNL>I>rJPpuQc_GeJq(3Wd4+h>XP=X|-?nOZp8R5tv;)6HF@MzkmX;oFG%g(A zU1|j(r39rK$|?gcl#z>K8brTr6y8T8b|FgXfD-+~*jHd&V*6FZOC?FO<}P z&Lo)knRx2g1dbh=541631y+by?8%J_A;6tgYdecZd&|bF)Vjg!+_%J1=CF(rjl)Qj z5MA4mIV${b_#T1}rij|@OitSHCPvI*&_2u}pPtAen9OA|U%IezlA5ZMm@Ff^3Iz9? zTdg#j2uRIoYrxD7r{ggSG@ndlSTRR_PjRYK9|Of=QZl>Tu_50*&4Xy|7fw9)8tGwz zqt|}M{ywp%mzUlq?|xe#ide8BE;&G3XS9-86E=yhJQp>`W*>)G4mW08N+WxiX}mbx z1%?w%m;C8}D$XkkyEdPkW+8Rv$bPrFP)uE%r9h>wY4c`E)E3fhG@X%oWeehGK$S}x z)g5T%8B_#C_PlDt0|kIyhy41r*+7Tahz`%0H{|^xd4F_mAmL?N8#uMg+|Aw184IHe zlgn>lX5o8f>RquRUc0yCyL*jp@3wY#cgc6;{bFVOokGp~y4(Y&;(C|aj?^G;1Y)%L)gToV#y_M=bk&N3OzZB*`xGo;&@D-cL)8T0PX4c~DY z3Z;+>+Hsa>W7>|!$rVahI2^eFee61{GxGKurG=jqf7tcuaYFkT8)s?lF598lQ0< z@#b6^-rlc?wwEq!%XRX9di~ylpS!h3pS`}zpS!+S=90U0miVV@g+|2_(F&QQZXfB# zj7gmPR*mz2oSlW_W`)-aE-4>#pgE_+Dx{vdC;NPin1)5e_J2~oR8)}fWTt?w(tM2$XQ#iZ)D^@mxFRVmpi+tBonm%Q3wK$}G>w>q-ZCj2Qb^3xoI# z?Y{i?Cznh9<5w@e`?-3`=SX8eaOCwvizR)H-0>8R?8VZ-VA7Z&?@=!O*cs+Xb#ygV zDajRnlzZ}#b$s0a#sqk4DJ6@2?_ATm0o!8If(Yt@@kotOUQfBmqwmVd$LKQRTOGmIBuep@=Q6+JT+m<2n&0BsMsU z4KDC}MLR-Hw|AC&bZy?y&#!rKlaEUKdx8jl;-xMA`HNd*IMJgHW2UzMjzCdTLuZ;s z#TE~$w>eByFOUe33V^J49{yWHe>c+w{m z75`FDpiOy!!r9lB4lN!LF89$CRvK%2j@hPe+{6k|V(qR?UE4P5^1e~7V0bo&T6I0O zu=%MYh?WoQD%GWH^@bMi&#OaX1l4rSK9L2{Le7_q;#MMW+>WgoGap-Hp3iKkU^Mwm zZ#gvw<0-SgdT&X;aA{XHu`{9e^X#F49z8l|Fycwk?qV9#t~kYDFJIkS(ADjwXmDTL zA^^~?GXT%RFohPUINP}CvyN{J?bOD=V#m|Nr`JVb47!sRP#u>EQfPnpJtz44zUnl6 z>go>t(C6>+&s{9#vx^73Iij(p=wLbR-tokD`-r*|j?+W#6hX0+_I9Ysz(|Z*nrYmq zhe?CEgNO=)g>~no#fcQU>xhz$y8K8XUV7ANHocIc(%d1zBU3los)e?|>YSDDzpkB` zy`KtW_t_E{e)jOC_sBOtA$Oj2-CWinMnGe#r^$QjJX5N??bA93FLKt+FOm|r9>@$Z4dgSO_weoUh41Dq4qAByb0@@Vl4x9Ud z&4C|~YhhO(o?F+UVULS8zqf1c1{_D{@tApA z9ac)sx-!XwK5IYB`U*-%LB{;cVH>Ux;M z=HDkU{JIcW;<4tFhXiWRx#X1)J#=KCx1Cw1o8pnaBF-u>PmEzxVDuYLuF(@m1wxD0 z>-zRe99m29WZU4x7N>Kfwa<&=Oth(vbs-E1yZfTF14r5Ja%FWZpt0Gs8a@t*989R` zcjnBY_xMkmH=pAv&+Q@Y^XIQG=;tr)*y2imMf>p+C+7Ty<1@M`UxSf<`udLAmuTX+sCH=f^TJ^F1qPbpkrQ#T%?w;@3aV&QVZ1VejqFZ4^U!3I4s@Ysh_PA|IC@S!$Ja6R5@`w2x z^o~|3*@iAWQf!3nUF@K#kO(IBPkn)=POmDU3NlHt?biV=pB2JK>=^w6?3TCm##3`) zUgB$8E4qAdL5M3uF~T|ap!^fWty=Kad#g5clk{PM)c6Ocz9H)JMF~OO+cWQXIK3V{ zHdB~BGYzb;`>sIfE3mWo8W`W4WR!NgGV<{aPWv0*g^i8(cLTD^HxML%<_{d2sZ$Qm z2Ga}Esz?69-37fty!B6?TGRQsEH?fZq@Qm+wcftwp-mM?ugkdp>c?-;-+kte3XQrp zx#SEFd?%pF@-*?RkC2+TG890b8#Ho_-LI3Xu#n<}xw%V`R95!9Nqj!y<<|Gq$Z=nB z_a_~7#xAa`PTu?IOj_8Mk^Pg|u>GH2{O+&3MtPCg*QbwN{EA^kKP#WsM-aG%mMUX8 zWbvV;J<~>mZUwr>P5zEe!}LY*Fo27oGW|pqvU{H*@uN1%=8!J%-Wiii$RX}^=sjX$ zE(sSCJ;4Wvk9@g)a9Egq8*3A17WsST(43B}8fv&)-}Kh-kSX=-km+ zd&|i+7DjE)Uf*MAG(ce3+)s&SJ}hut5O{lDyxf}Fd?YeoG~5&C6yO&}D1&&2=JobXHm)wuJ-FVnd)T+&HC+)V~Y1XL7V zC^#-Z6eTNPfslVm%?aZBr{(xNkKh#MJ=$a4gFetra!9-nB zg&jV&mtwx#KBn*iuG%Ez(METM{%>81bvvpD^vl)FMIz^7w!DXE>`B?L*~_zNYUfk+ zKTHQWZndS3SM4{5cbXaDD!kT(6NI64qF=hbhm9fuD)^1Ul;T8duZdla5SBf5Xr~39 zKXH7f54$Wu<16=9{u?y+jBp#b4z$inGu>_kT;z>Ja6X@4-6&)BMNeE7w_(B zW*MG7bM71h_Hl`K7p)z(HoH>!G-2U|{C~K+})2^rV zS0GuIbwYj>%PfrmVDWX6x5nA1zQ7NXsDTst;;;OJU=`$@r-o)qWbW5?zU#{bZ3+uG*a_SPj#w<8Y#fqbreR*~M1r(Gm;-{y8&+<_$$i6v!^B|Bb zyDjDa9;1-+&FCv-+bi-lISB;cH^I?~c~VNbLZP zUg(E|ow{jhGjYJm^)==aN%4WM3e(zm$s0y+?SL>>(w^zT4#*=7EuKfc<+h*Z{6XWP zhWX2odOsw@@kd_&58r)He^whhRFYk~5gJm1h%`SfY#LZqWE8*~e;R$Ma+KW4meJu<=?OhIaNaVE16z zTXN+S$Ja=BTmO~61-ty}^t8{#!XNXrnE%%>*pphF82fuW0sam{;x7akk zFSdgWgVby?d1F4bJxfC2WO6bAHMn=J!gGA1v2$VM#}PY6rkZH^=VczCp&t|N{Fw9` zw)cy7mhDX<;NB*}?-@ZBXy9uf+@y6kU(n~j`RQBqmwx#=^3`3vwXvXe6qm#_A=<8> zlA>E`WBkz! z?fo7&(9jb;|NWP~T8QE2<-;(tk#Wl~VMZzvuT5-$n7889rUN9-1a*wAn|LxO_d#V0 zCB|4ZZ>rf=S8s%@-J4Aj?wH!n&F>HVvu^>&wOusn4!GW~!K`DVk)Q>^JRoj_ubw(J zqwDZ=Z;O`3BV=CRbYjK`cRhD=0SA}13%mR2Q|675@0%c5hA?>g%z6`a%+S6FWe+aY z@C-1_!@|_JgkxS2W-(Z8j0Hvq<7~{$5%#Ju&ujP1Gd6QHIQSf#I}E#6yzAH~BJdl~ z8!k0yZ8+5KXr6huxmi8m><&3wN%TkI_#SqGt<*n&`?arZ4mi*a`uvVF8@ldrwtY@Q zVUHc1w@0L}JBua1B+fqA+E0tdMxUP&$bM|Cmk)SFwEVyS6PE<7?A9y>-U1RU7y_4YFopO*G+2&De}&AoiJ100C&q6y83}Ez2B3O#HN7^fx8vX$K zF#-r_zlYq5+npC9NY5jwGgaJW&g|6r+qw;|-D7;2 zvF7WM(7OwS+}wq0PlzpX9{KA>X3EeQFYIfW3^E0}OARnw9EkL+Ff?HI({5uU=8ahQ zlj8M%^NBHYzjAkve)o@E3fsFdV(WCqK@+1eQm(6+V_mbmxn&bk=+-!q>c)LA)d0}41@CW}U@^T2DmQ-sFPpny-4go5G(;UXmqWN<2QUN4gy6W(XG zj7!>tMiNcU5tl|AZxALEyBOgrMJ<5iU_5xE0fmiWNFnJ%A^<)s8X2B$3xTcafk)3> z-{qMf&!E=|!9%;fdKViwR@!A;5ABRRab%cX70pdfkVs&`@9%*27N)wfvru@C$|zw| z$Hoz}jgi^bRH)#3{M~aH4v#*PUSfZCGPnHQfxo~R9`7MI&?Z7?U`}XiIL$EKv%vrs zem}l3Qvn4JIC9QmW?U15fwe^<%hM7Td-mq8CgcE$tjrRu?ojSHrXe2=bkru|7GIsLAXhtTT zn}#W9>L}xpeoke|6JM6M)#7X@f7T>y)k@b4P!)IrN@kKjk4P8` z#C+GYzu@$OC;WNQI=i0u0jC^%#<3i}=nt|kaM`D}cTTzf~}{x7dT zaq|zdZvHsB_M&|KfL+j`LrNxgv^w$pO(e~Tv1z}-pQc9@g{{?}`n3`9rDWdV>EhTJ zr4rc^HJW-KM+47$ie25?%zlmFS4v`+*+~Q^Hf>8K9yn7oRN(HhS%VX6d)E#(KP`lM z_K?`vcf=89ri#XO#S_k-zrLqi96{KyA>k>9(};-C z{uH~N0tO@TxYGLF1=@Fb>=$)UcQVo_4R0a^DPz^HnZpxFCB8^i zWS8lxlUc{t>R40YX-AHWV+fA1U<5D~5=A5grD>301SFt<0bUlz8F|?0yfFDu@g!fo z1C6+%!|Ov6rskLL==REh$nD6QC6-*cy-*E?`yV+n@M97(L%8h9)*>}0o`EnJm<};u zgt~fmm^srPl)gcDK!YQZ<+ZrR(N%c6Z7RTAGtCA&w`rlV!N1oW^F4A+YtYnwGkY3+ zK-?M#0K3b8S|9+x@UW$UWNz%35b?bAc4iZK>@!6_aG1R!a|32Z!~hOHm@5d4A=B<{ zqP-#1ps^n~WWSx2`=^@w(!D+U13&W`ef;WnrOMR4`y6Yr-oqrn!I?10KpP3A?Z1bq zH{rdw7VEy6YV%;^A)_->W*+0)^dl4PQ#!qKxZ-D5hu41xpZ)U}pKinIvm8G=ZxBtH zn8)8u8?N>T_aBNeKAJx&8h)g~$Ac=)FUmWMm#ZU#fqOpqM4~bgO{HJ&f8+g3?PKL| z2zIHcNhd;p$o0<65%Sw(;=LBnv?hX}XdxI7kP!t90s$c16VUt8orU%i8!TX7K7V_O zL+sfVq{49|Y`k=9p>@d)uR{}WXnH9is&;WeA_^%S-w^L8Y|*VfgLI*7LF};gp&7q$ zd!gGtV=%aXrv5Q|=y3_CKXGzSCxn?)^Dc}5Z&+HYf*o~|IU$BWVQ7>gGk3I6I@Gu$ z58IP%b~{o>VT)syMO)MLZ7{N-&ST2V*zht}0fbE?5GGF{4OQ-YK^XkL*yCcx2wEW( zWPtE*KRwahWF7d^AHG6={Zlt43*Y0^&JXT8>sgAeUh9a}KK5(+6+xDvfco=^Xg}YO zYwV%0#eoSn{Rv|(&j+|Hp&prP?H6aeTi?ZJzyForDlwGzXA8;P1-_ckJvCr`;Qj|u z=2lO$fgI|T39@UN5{>^#V_^_-rZQ(sbi_qMcKe7jz8Kf@#3riQKxxyo=-osZR5-#X z1?EDN#NvX$9~9@;?d_#*T|^6_WfRq6Tj`cY`JZ^F8H@+dw9k%tg(bFq z*NYb5_2j9!+T+jP@}w5;Due`VS7An!egK4yY&Bs_gYcBz?YIsvaoUN8dajxk z8WLI@yTkGt_Zi@(<1%L0!Ekt?{1H$`W+e-om;4H)M@DpHvH|G0O0?9F_7&n+78XFM@K=e;u+fms5TOK;BcK!7A zmwx#g{iRP_A46_i-(u&cXSwVa;c&#WRSF6>zA9x~UQEe8_NME{!_xNGw; zL#nM_OV&^kF5IYU{>tgxQMxm_L(1@ew=#ho*>7CtCZu3W8E@1ev}UQ43|0r7 z4i3*Toe$5j@glQLQbHDB6y7uFR zJ7$`^@woXOenrB1Z}8am2VUD!=7s=j#b*64-g^cPNdB`QyV{b#m;)tthiKhrV43CY zqP|CAxYKE)Zs&%%XOWxK{{FQ-C#kML!%(2yrH#{R&@9FK${v2VyVz~zvV7FqY}QU7 z#pztvNgid_AWH9kNw5PR?&{mhg7SiIL4*@u&3WsUFfa1ntlwTk{A+#p}A0* z!21}Et7M)&hh30N`yHdlOT4>$RP5mtSke%(v`}XonhCN`;?9~yg+hP0W-eOo$&+*O zdM_9ygP<{=II-5?Fbg{vT^BDpk~rGOUfjsg%2Ij*i4cO6d7O0FA)K6a7 zC3~g~55J+f0vPH;uWs805s;##o1#8=6Yn>_U7T(nR z?cYMPx{^_ua^HAB?G&6&X6VllIzX>sb*+dtcprTn;om z;p3DrALa)T7T-N69wQ;jwqs#fg_h9NFWuZ@z(ln7h`@2tcmmV=@(I*tWKF#wAul}l z4WiBA`Tpe99i^Nob|6eC#4QfComG07(FpcrA77i(rv;9#d<~<3~&!X{ZvfW@vcWvd`XFC5><*vc@sxh;4%rMI$N{kNRtS2Duex4?eRhS8PJ{ zT6|b6O>;qm-w_)gwzZ%g5nwxRpG){nyoT=)L;=!(RSLnax%to{5wio>Dh8Y!yy(J=|{)r za`lO1YI|V7*y5qfm>xS)p*Be?Esn3k+<9?j9PTYO*R~E-TDOsN&bX@S7SJ`lTM3iQ z`BC~P0(rlP3Hc&SGAx8Q5t1b{4KfB$C-^s8Y1*a$*gRJ@LEP_lKzym2FvGb(I3v) z9yxA9NVHajMNMkyD!*|`;GkV<^W{=Njr@2#8%(Vk_tQj5Q8L7oVFNk0A|QP5JZdYU zesPqwx+gY?Yn-;{LLgbt%*Jj+w04kKr4ASUGXJisY|4D{Xf2Bx!q!9$-dyN4Ssf%> zTy=*P995A1hyw@1qQD^RXV+lrq`I}UP%kbX3&9}JnNb5w_@vDTi)%|rM%B&@dUGxo-=ic>dO| z3L&;Qm*%ds3xhWZBLJCznW4q8dkhRUV_U7krza2toLC}s)Cs6zt~HBolRGEF?jCf0 zn))HYz$mDALkxTP2jM7-R8kzi8)uX4_k2@9zY#k_TPw4xn8x7U??_#r&*zb>qhjaen0T7m6G~l6kgQf* zGR+K7x%A60$2e}-rLpk|qj+6a0E$=L;$8YfKX-|uyjs$etEhwM)YQW`)?Kvu$PPDp+NX{EJ&pM|S*a_{%G7BZs~ES_sP`O_a2OnThahX7xo8li zm40)dm>l55x(`k_r;SP49F*UXV@f-m!(G@qo*-K}#F4cWusfhY=ckZwy|o14Hqa^< zc2Zfo=CPSF=KZDZaRwnjhLq783w@*@U4}ypg%WnX$Uag$!N9+A zZBM^YttUGC>?Ujexxi|7)>-%pb}}>cmsUA36vCNRhUtoAn1OO5iO#C- z0Tw)i5=emiTZ=vY4M?>G1Lex*@7Tgw)urK9_=_-4v#n!qP<_V)l!jiT4X% z2NHoOsAzseTR7oDH(SE@@}OB}m+pc_+}Idvki=@CUE9Gdqa&K z(cVT2zR^SH6ZSAPokRe1ODGa=miW8MFro^;fQ`&nz8I%{;^-hG?TOD%lWfo@XnbsE zy*OLwlEBlCK0SJLUH7viA07;i^UB^HcD2kU(7=1ev4+qXA_(siNd5&eACRmf&k%TS z^9{i;=l#t6AHVgaBC5aev8!(UI$3a%RM2TFwsRV%g8jl zwQZF~Q0@rfDsRT(rgHmT?5%bPjQg%@>Lj;g>%?Guq=I66a7Kxy=4XYeVWMCY1-!hl zDevqo4Rx^Y2xFvyzP^Q6_A-?rU^le&r><-(F~f`gp7R@YOVsH{#3r3NF=B4em#0-b z!&$b&*l{*Efugn<1Q4L{3wmnip2WUaR>j5574>=pb?o8X?3Eq{u%o3*Ov4Ng9U~l* zUUApnD{mv>Dr#9se0>Wa_sH9@F{9rIQ`z}qgD~%sUfr@R3pmZd&@g>Icf;)J$K5lI z1QnPH*j4sviOVCi4cZ#e{f%OazjViHyuJDO+Sr`**B^b?8BKNl>z}?(g;9den#tG^ z=>;cb04nKj3Xxl{zEax`t1whNceT_*-JXQvOjqpo2tZGIW4FWJ*$}TdZPsgIAeHP> z82Lr%ka8xNWua1(6`J?hrXo1(d~ z!%OT`e#@CP4c!1*;(RR}vOZb9Elw@VMHWPil^M zlkgZIItL6EhGy)H0z-fJwQXz(RiOk}e*W;9mLCE{eMVsTq8C+AkM*uR=k8*~*kOh& zIG~8|a1)ng7Eg+BHJ~Y~dhO7cc5sN{gxv;z22g+#qMBtmxBM7YC22w;sLt}UDsMahig9G`Tklwv2VI*Kw(4-b{1oEB7{5P< zr*B-6)D!)l30Nm!3SsjtbzAg6%DWh?7alGQe<5(4F08 zr-pDBph2@%Ma5KCpdoF;2{c1)KGRw3kh!F^_G{D|K3GU`aCjaz9L8c>O>I)}Tx|J- zCVzuKAi#Dw^N?!~qysNIveshGFn@@HW4w6X*KEE)8UG+iju*}!`<8$FJiT;x>GrMB zeG91Hvx-fO^=R%H(Ez>FjGgv%5>x@E?42TlYHSUiFxt z(vvV%dwIbF%}(3Wj4dthPpegGZ6fZQTa2kFonoP^fy zl?PH@FuIgS>K3f9Eq{L)n?PO?Y5RC(63UFPnjAvOkkJNLM&i;B>Q{EY?cpi!PYMcC zevKMeWm~qM7OguSYwJ>kGqb05h(^~HrjD`oPCj^S-VibhhtiRap>6N26gC}MpQ%BG z&*<2br`FqBCH5@NDkMw@Tpu(}i)+EuN9Epos25HOJ2k8|cNGTcj^{LAyXcKWWhKs) zGs+_(2rfUWt0=(LD z6>yB5VSfXY(L5zFZN%lV^>gC@FL}Iv_=Vf_ zhb0V_W+ihcVJZqdMjt+=YKGPq(*c_Kizv|Mj9;|6MsSN$c-mH)oVIq`^2uG*CL#V4)1nbxhkzcMg_%i9YNWqCMlYvBxxQpNJa&E1tVqlLn7 zjS#sqJFeMuJdao}LSg6@4)FWHr7iyHm$%gR4}Oe|8Y_=GH!>ziA6Kd!Gw)B=U^Q6Q zoH|)uYk$5^8*J-5fn?^)uX#T5cTPQgw5JZaWuiMUJWz{xzjbNkXGJ47h0}*U z(gG8xTaMmiPVbALm5`n=>@GbijC*Z+jUE!M3{(S64fq|KM!?hvtGzCs^2d+uPh5%D zcf9eqL?E{5fBNi=u8;~Uk9>-x#THT1n&i?Ip}twwGrm*7F@?$`6NcInb85UWC5^0u z7mP-NMVwLnS!w~z`P^T6`LcXC*Eywg4*MV)^GMtANZa{Xoy~*$s=VoTu513x;(Al9SiWZdz{}ulZn}?p===AD-K4 zC?ok?K2Hdq4a=LFm9*f=ob|QS2N*sGFJsjD4&Y^ zarIB(Ji8`ep7xQM0t;C(8BnPFk@UK>Ix?oy1qb=>HI3Xcq;p4#8(=C|* z2&=XEpp1;HHTW(dJh7&S)^M&k=4qwJ4dI#MVB@z7FfYPWW}i z+(=*j=6~`65_w9}W3pYbHXC({D@|70llfSj9cA*_+whcrFAEYf-z(K7JorB3cgBt@X zrA&A#a;Og_wt5zQqdu8sw z`I#H^Uw`Cv=Yn9=F^7Z+(+)zERIcZa7M&c6hHsw`9!Wtav}fuacJzD z^6>;sbHhh=LK%p|4W7uNHkMdvn~Y#Y9qBQN_yMfwdTk1yncVB+FI;qjUV6L(*g>}lxki=NNr&h zP5_h5Je-5g4WVHbGR_9ooO`Z8He72Wb_}?#JFuX&eHe!oHkuHCW<-A=2+&o0w_L$K zUnvvsHLKEsJ7%uU@jkRQUcunVmc#w;zj$95{BG7TcU!xe(@V{NijzEMm_u_isz5=G zxR!Pry7}a)MeXUin;JkYNJ5z@${3V*!PE=&5vVEB)CVEl4fKQEX+Q-S7X&GU8j445 ziiXC43Mlpf!2sq|hvBxRyq=bYg8+V2!d+0dPaK=`d(IxBw}=g{(F#K8WfPOyl}wc2TGPgI?iS+y8x%=IG8zbf`SXf$F%4ksWTl39sl+ z@VW1Q`Ih{Cy!Fm?z6vG^3_oS2xN@-TdIY}M;T(Agbui8DaUzJ0M{aC1-KCKINnxV% z-Nu5pUpt0K2fU(xYD)`ey!rH6``U-sm01V>70iu_Bn#aJNGjovuWJE)&G|;zp*>5@ zW`thudzDWiK0P-Yxo3cm0qs$w&q7$N9&!-%!j8UCv=Cz!2nr~wP&`sFkzQ%g+XQ83 zZm3g)WkR6fdbL-b>A=Lhwg+18+nAY&fwp#>!SNl?0zB#%v(*~Sqb zdF+iG3nafMkX%Aw$n4{Z_B7(AR2EFcm6;p$S_Ygv)wn2|+TK1Pkbblg``gEW&8fHb zT3$#qKRG-6Er0Y+%6IGJDA#GA@K#|b?#?drG9?I)9%pB*u`WrR>)q+ciJDz%1jKfz zPBjO|I@CNh)gIfIfjb+AmL5Cg>MZ5V>+dKM_)Sk7qA!1NgUzXBTMs2Rim(^Ic5kH# z4+v*rLx^Yy%|L@@vHIW|hS5FO8=3|=)7Un&hR{-zYZYQ5R=m41!39AAIJjn^EIo}g z5{l3V$Oj;Rh1-Le9ceG8Cj zOSjT*^y2tl%^6L+aVEX1wln+~dT@be_Q9Ejh=BB)&uprOW_Y`0Ihr6UO+`gW3)hLZ z7Vr6>wcvK8H*Y$=t`%IKy}1LyK>PbASQCfwBKUxvNyjDJrv;Y@JMiuV4eFW#dceDQJDj$Y*+YAqF8$`w)7Q6g;Nc2YSoIMXM0!d1i*f2GpgFYk?|jp7h31&sGaJ2HK=>z!$Bg~q%lGL! ze*C4NI~oxqC)v`zuvArx`5nbZkkU%hZ9Bk>WTF5&Ztj<`Zg?lL)WeLy*e`F%yl$4; z!$WPVmWmUsGhFdTcg-9g>D{ztDZiVf#VDA+&{r4?sOIyx3)W1{V6H zHsCrk%gn|GmafVQmWx0`-E4u62`|!*#!M39lc+UUs zvv(S&w|j&PGo(l!qYmM&NiJ+O5<9`t=C*-kZSu%?qr|w~hS%$i=O_*agnbNDbk9mX zixX|8_0=wlRz}W0OdT*WAU9&#uiUdk``#?Z`HQkZoASW#eDb&^wj8!Y#k}eZ>l{RJ z*P+nXfBC^z>F+&vb3$aTqDPwWKwjm`)|Fm$l=(bP9l&YpcO}Y+rg?0?H)|W)0Dqd# z{pFXp<;IO{tAvrLyXUD(ovJM3mmX4z5LI@XLh!ZJ%u}YT(A=KZQk?aeA9u{%Od~XV zZX4HwI>2i$@7{ZULu~1F?GzXdIpJ4?fdyu_Q?kM!1RMabi5!lCprMgt3|I`}*MsI6 zVU?LCNnpXxYu#&c#E5nfg`o_O=OJ!=Sa%{>TsrJ#Av}fqxJI0F7MI-^G`>CfWB_t+ zySExIuvolDjCx6Z8haY3{oQm289qnI2$!z8H+U!vj(d-wK6q>CfI~bOcIaYdN9`K( z8<9S9eV6{?$F9?r?F9|Q`CLyYjgG|TkJg#f%Ln;AnxW=<3eP>&dHP_udZ@WWnXdNx9aL=OmgvnGu{S>n zuhmzE)2e5Y=pb_-1jUAM%-27zhQR|Ua#I;se=p~@Np|nz=XLCTNX@oP6!1V zO3==6RBRmD-0&fYw!+YKk`Vqy?um_>xLG{|s zvT3LbupRwbxfYnQ*{GWN%oZ*S&oQoqz_D0$okN>NFfGDi;y{W$)Ry2fv$WA|_lyU_ zoj?6928>eWlJ9J0efaZVl7p^FrRqaX1gwA{x2~Z|2RTjLEVfJt6Zl+N&hR(2o z;6pe#3AkqG{N2+#iIlk?5T<_hzEx^@TwpkAqQTY%GoZi(>}m`f*SzEO20eFcSI71p zZ#+id{MfOK(JQQ7wPf(r9P;P;KY5kD{}WfpIWQDz*}_R(<2knlf}!G%8k06Z+pS+4dLSepddc$E2T_J}0#PJ!UKmwd$U`(WlqRQu9#-9a_ ziZcvVN3j*~mN;qV62jTkB$bugcGzqnjNESRqO7hJAwW9h1BVA>qG>^d+k2#Nf8l1( z=|eaG9-iBoZZcV{3=u&i!I`FB=hia!Ta!KtAv+q@3MIQ9_tsirWY>Y&H$C$Vzac>b zT0NL2V8{E{HUhPev9AP(^K87OCiv@#y0_A$0i{mNStUA@>w_X&)nK;J7TZ@nXh<&&Tq~-l;#oTCvQM&f6b3P z-)?U2@reKG+s=~3KQz(o<+hP9aA#RG5p3d2Pw87@?pmD;6*>F-% z#Y3u);vyH={r4?3B-cdsDoIK_+xuRbp4}7b{(pZ~G>}M+@&^Os9xkiGBjk59poQpJ zz23q}$B-K=jP=qjJD&F3VGB9ocpYpBgu@H=?;B3eRbZjU_U_6UXblcCX!awvu>dD? zpbT%+JcC_)a($+rWE2e4@YtG#U(U)iu`e5^V9bW08U0#<20FQC>DsCt_m-9=CJIrD z1fpoaz;{MshV&W8fWy>+1MH|51H?GMs({Pe?tt4ARe9JAUHrCmjSXVP*<6v0hA|?1 z24|i^{AMYvq0Ki2J54i$2*wLD0nh!qhY#y={L0lGHwzLeS=JOGv_+lr(tM))r#$L+ zwr~QObmAp(r$FxakPG$1eaRIR;}xO1WaJ*8Ld-qH!sRzvBSxM$oMq36=0=1I+c%%N zx!c|;-(8o1VaiaSJWeYDiou-BhR$`&CMZEOA9&5mOSK+yYMlb@Bll#!;K(p5Ilqd} z&IW1z-%fNit3w-YTU&L02UdA!PP&^epz1TrdY;S$xxI8?ERS`g! zg3U4XRO8m`TNM`eWrnN*0y)Lx-rl$mn)vG;S!Za{hei9M9@p!47y2wfa>SIcirobX zhs8wkeAE!TcxRz72w8?o)}fI7>yrUsz_NPlk^WptMWO$>p7kQEMc-<+AV z3Jx*d#IR=PQlU*$@rX0BJ=~$1yKBA>TwTW6%GtUPC6*s^1?Sy{t3xQ{?Un8sSI3$r zeGHb`ifMy}w}a_PHJLe#Sc!Mf`vnGufWSr<2s~(LK>avfLo@2wuKE8zbDRF|=Wc07 z(mPHSYWGt=h^PC!O@e2ri~DEyn5WvB1;*pafq&EwPZ*r)tyHt=vqz4utKR@#@-I2H z!PwFXjSdGJ;?qa$9I*Bk=MM?eTGD_1&NDj3Syz`7JK0nKx&(p~^{fBk=QZ@!>9^|_ zav7CBWd28INHd>vF|$}G_nQK9x7uQ7ZVG#rdUgyn75&WIZQW#w;H(OhX(V(wY-;Y0 zA8F#0V0By^(1!LQ04i-$b*{xAv@@Rc(2eYHy@{OKpO*QlSBAD-)@RzoE?YaLJYb~4NME| z4u(c(2+soXqgX$#)jpd!p>6yJPaoG6_<_&fqV5{6W*3rHntHyeIAv|!P(z)0ne=D% z*jB;A&75qG>omwSke6TbXR)TUnjlztXp&bg@}1f%cXk>5f6v)N{43YDMbNJ?>}xpK zUe_a876afN@Q*Lwz-O5ZAI~vHI3!(p?y!{OQ`Cq==X|)@ooe}fP zF;lnBlg@rEfoH4Ua7wb#8B8|?cSYztHDWx@2f$rmM`` z)qATYTKgt&|D7!Tu-O(r7ZjZGD^(GV2RZtI_6q>ydVl3Z8@jlU9-Hwcv7fQC3bAJ} z@P#`og}GoPoEMD~HZI;-rW+leX~dt!L#{c~q0vAXs9Odmleo1ymC(~So}6pa#D-{W zINk&vqb#w)z^hxfd&4xMrLH2kTdq?Ef!2oOt@&vbL8vwX9I!pdHgtHy;m}(5T}vQ7 zts1(DqW-2Kp&!_!%{)Ds{lS=eq}v$Wt)Td7m- zt`?Q(-q?Ti{2lrqKY25O)L5f-&vJ&2Tzd~AP0se-op0=#jGZ1sqenYKhkeWwG{BRv z8r}rVMo1IB1{{)bZF|ous)!IkX{i6~ooD!Q(deO}nanu}tO%ie;yVE^9QX$@6K!T1Zy z?$Sa8kroQZ$4(~f>pnD#eaK!tM328>HoMP~OJGB3#Su1d{gE|GeH?sk)XC=Yc;uf) zc_rP1F|(|A-45xSEx^QRdt+vHHm+CiySFu;nfDyqht|~g?!pMRE8c(f@=mI);KG#? z^U7($T&zG`g9A|`jON;H*-C}leQMA=t1YOaM>g&IR7A>voY+vlzbuRJz#&aoLC%2+ zNTjD;6wQ5N!-_=$&7m5qMkA0^)T+QWYFjx2%zc;Khc4d_dJUHAb~N*pk?k7Ju5tu> zoCVT(MiW^O(3_@vH)tQvzA*2%Dlc+rYU~`NvD^sG%rwxNVizq^m=~dK(=O~rdPtHQ zCZJJwz1BIq;PL$9)@=|l?w3Ebp)Ol!=9g|S8?p>>Ob+UU3vAYq6S9`D2Nws_p%?<} zYiJ{!yz!7QpoDN3+Eg57YUc^;*7!3{y@WQ$=8n^wYnl`xjKVlp7wmEAp5{8M#=w!a zXLVx%NvI5`YbyW^_qnypAp~1cWZ8oFPA0^LUZSPuiX}iAof;=Zv&R* z?|u4&ZmIkMvAI`n*?GL0mz}D*y)BW36~~4WwcB0dH;3h#q-H0Wu)}il^2w1yt*l^b zL~ZMpr(`-Bv$--B0OBBor;cyXr*3TXyH0OveR8kCmIW)=->(fE zyV*h|(|`Q*Df(NVyQYUC7*|rr9;S-B{<}gx9C8m47HPONh?Tey^4;``mZqyUMxnr9# zzK9u|kGIy#XW{3WM1`6E}i+EXXi*^UjGDKpPKX&izHsA$o=He2(20<5>t$VbIS z??}b_!O2I4h&g#6Sb}Nf`;Xb+kQ*l8C{+xHO!dm`J=M5~ zSwDiauWsxq$Uk*RbFbSo*LT@yvcY4nMf-8u<^4tT#E+ppeR@vUgrO0`*8RF-pubg^ z8l4hN*-oyLZW6h-P>rG`lu(&;M{xTqcXU^hYCbs9kl=za7&6js>_miS#j#PsN*S?+ z+VD3v9;nCJ^LsQo#W#0kx}y_27ubGJgcLtN8>peVKrn?HvlqQs26a*XofBkLN@ z#8JNDJYyuRK=5i_fjb1EdX35bnE&KG`qsye)BoqQ*SV9dF*Mo z=)!~6JdBksjUq?m{T&X1O$W}48tuWW=Dg@`rAFn|TKADhV-7pKt&|*vNr}^X_UX+| zF;`IH&!CckGJ!I7DMCbF_s9k$ZaXg_t-BI>+IFUf*n|I0|Rwvk2$O zo5XvraqOj4=|I}3ILP?LTae}}C4L+RZw2Uaf+M`=OMdaT7dn_BwmnnAf>TbNEy7f1 z1+D{z3nL2*UpcMY5vs@|Ke{o~jU{>_yxYpK-S1i9vOq&RX@l*>Zu5pyIGUwcHaoCh z%P+xJ_PWJjVrczcR8bvlvjnI@opj2~!cH^ueyfsohHXBfv8Kr=t=QPy+%wFn#r8by z6pqEk?lRj6YWkC3evUqO<06jnP2yds=YDCX*}|%(p!ax&7IpM`X)fc{0@T!3CoIi} zC*$l4xjHido-aA6OM5f;E<5~x>Es&w#36!ZJ=^Jx6|c+MKZg3`y9>ge{9hLB`?!S3 zP!+WWnqeC={1J#q7@D!9qW7LTL|=LSF#Y1?dlY;?4q{uIu4}MnqtwcU_>*UPVwN*3 zO@iNT#5Vn}&Gno@wlwM)e-nulD%E~#Q>R(JG9xVQ<>DE4v!eSLIun;JxmVF-Sesnm zxNq=(*SR%5zBx3jSlO};G-BAWv60<%Ml>l11~5C|A8b!-7Qn%HD_@xN3axR>jh-9D zu({v&pdPf2EtiaV=gW6i&FwUL0FZmIYAVpI=7h0si(TEW+I4ED!}+Cqp9d5`TroCv zM8O0lNDSCKtxDi)gB4d0VWa1T4ctW{W&lp~mBDD8?LF6Ch+~Vhj)4yF+Ev@wLQ@*{ za)#Vhk~SyU$`^d$nrP`e#i9gqF1aaU(^_OgA!;+h?#B{rEmx_@aXrTUKYaaz^xZ#o zN$Zf61y98gWtHJ+2UptGQ?+HSVoR8kM5zo6;~4@m2G`%(s>9nB?h8Yn99vxdutxh} z4Bz;fo7?oxGlyiYR(wW;Eo|L>l+<_;+Bw%4+!7BXO@n@7w$(y7cznD#sm zv4xgRUCh*t<%mppZ!*ix9HM#s=<(sRYi4VEOcMvbzva}rmfZp4Ub<_r>fGUZgN==h zuCs?{>ZH1G*GLo#aPF|}272MPYI8k;79MgCHVO-Xm%?BGCTIeLZC(POEI_8@%hM4#BH# z#RDK*pe5$Ei&DMkI6zZ;a7_y$kSCbv{$xE7RQ&_&j4X)7PGe(RW2-IZbMId7fjOBy zUNhGl2K$|7+5U#J$XLdfQ+I>;Qh6yOnpN@R|KOAOWA8mp9@30ebs3w2qXqOse*uG$ z#B)o}&o*HShaB?hksK7ExrIgwDZTe*n&<`|J)f+_cdezs0=sI>cIpQ(;NK^%ZZY=m zAP*kq-I>Gl)Hfmg(>`(J zgtRPJLS^YF{1^$d{Q7|=JnNx|bh=s0IZ%M(BO0@x-N(H;spF_3C|Z$DHkimDYXxq{ zciA+%g+)1T5dW8)Th}HY(VaUQG-_uK&Gl^NIQ2o(Kks_Fl}tbKwV~BsyT8ym!0$Mg z7F9T)d0}UL%|n~e_?iImuxkE=5*P>@NB~6(al#{V&BYvJakA-|T-UbD>kKW0@Ytyh zV{|a{{Y6jKK(!R4v+ALLgXLUb-?DR`Z+JnzjUlV65Nh6Uh*KP3U}LbnUrYo-XZNLF z+sNyd*5LpHlVeVHG;Ys^f5NI{E5xuU;i>SQ*k%%A;+b=6*zopyY)I+%YFQQ6Vo?%# z!#9?2wrz{{t#3F+zwHf2-H#J~o0h63z-X!!7Xn})?1H=Lc>{pI#w%>Jqq zZLpG6nEB%}QNQbrC%9)zPLyPL#0yVnvY2zQ(>#qKv8i{LhGX`iK6mk-P>!A#!BkBB zjv3sgjRVtC@48`c-Q#Y7JN%AsJVX^}i1PXtgBEEjr?ko$fZ+%>@BFxw4AOzmO^XM^$&@l+=nv|zh z-a=i690)C+bHm*E(&jcJQLRM;zt83933gCbw_*bPhTz~LI82k*2Nb73!h?Y$1EJf< zBV|p6N?Kpd-mwz@%Q5ObI1l=mmjuRFUht1`(D}1BcGWHwP0L@nxu+Bcl!}6c!RUAf z9A|GnJ{KpLo=Z(%B5gi(YK`Fqhn7Z(T3J&?6c1S81wASU8W0=%75d*>SvHou7kiuU z?2>9{^iq%I!I3|l=*58-gK>5GHs(4Q!`wBg=x)x@r0(dN1ffG}C+o?W%)8FuRAVw-Pc`d-dh z*WLSvsx7;1)z{3`&{28rAA8SP`qLl2;&3h<7kBK`uaD4jhto^}0xOfYvCF?(_FR}g5)U#=uQwok^se*kn8b#oXkQdN8IybN z$gI6A9&fZDacYLWjW)25n`@?(UcI~ENZf!o9EZs)ni4V!>Z6Uv1@6XED9brCm>{+L6AVx-`Fw2LAIhma~&SCn(piv zYCvDGt#s2jZ@@{max9CwU5lmCrKXY&OJf1Gf!X+IHZvy{Hm5+N0d?HiUge3IuSmaN zzOyS{{yh>9(Qa%nv>Y-Xf$TPHWI-Gn2|LJ+`;xPp^tRLMR(K(_HW+=@OE76aoz+&6 zzFG(iaC~>K6KYT=e*4xp9Hk$5;WmBj>JGV`KyePbHQBiBgPHYZ^8_TcwOaCGW|?~4 z2eao^C1s85fCme+H}i_SeNCKOO!b+=5S-L>|Mt7Y%l(BrJM^U+hxN%?NOHz$Ux%Pv ztjJN3ADk!*{vB^TN&oXR*IfBIGbg`L@Pa$STEbX9V8C6Ox4a~{Ciyk^sz;8Dv4hiz z!r_>4<5-}Oay*9euHRK^d-!4H;fk8`+Lx$VWGfwLDY++DETnd;*M04yhjcMcA7TdF z#V^R~-outg`S_q{Z7{V6UA4Plqk_pU+}YE~of}ira}owak-|61dlWvv7C)3cLN;1d z4H05z5f&Og6D_OfTfZu>9p*Ah2g`HY)%#1$^)`kkt$Yc{3`R%az~m@N1Pys}*QjPq zrWln>H|CjO!YE~=2L-UDa$>W}X3WblWbOxMMZC+5+SwRAnDO%>pukLsv;WGq9r~FI z_vvGox8WP$D2MFAIlQI;arp~A=|6YTuW{Se-~8BN{-(!|XyJmrRWOP<-Eep0 zorgAGwP1Q9^f*{gVJx{LN3Qwax1XXv__MDv2dNSjdZ^^wl0d5imO4&n%b;7r^RM;B zhe*_L|Bi1aQH>niFf-@8vBL1&H{Q%PA3SpV;gB-*bA|(;2qn z6#vd6$LR;2y-vjiR$)YEa&#$_sDu^#yz8_=0Y;%H=EU~y-%uCdd_ zPOVYJ-I-m$G5O);%MB*<7p9I#Ko0}B(GiHjKgqi{pImF=bW&XoN8ZYlJfQW2 z;o33rIPEs8{D}rpQ~;@^U$|vO$)MSADyG11jd`P30zA@tB4QD@M4>(qAIcFwB)>g( zbJwb5CNT$Id?|8`?^|K^vj&{sTgh<@jj$LTxY zd@Mc5U8{P+jB9Tq@`DUG4?)N$J5>L&bDR3_qgS>%(s>xoS=1Ixy}&`dom zbG5#v6={Qy%r_w)-f?PE_qd~uywACF@7Sj103YP+qBWxw|wrwUr@_iCqnuQ_oT;t4mK zgU~?Q#nGm@_L|_M@p^NHnFrkB+lnCIHAg^Gz2KzYarKJ(?;0Xg`w_N=t%$97aEPJ!pm?mYJwR6LBs7@g@xS`uW%`aEyP&6R zM0%yBntGKLyFIs4X5Dk6itgHd`s9Y@)kk-NrmV-U=qMxdu8o57s1Ue)Z0#rFaT~Y< zu39oo9r7rgZ*$e7PMtMuZJa3*D7W!7Ox#b{zo7|9?f*RI<(0C>Y0-7C}W{6#%mTR`;aiWbaFPyFLKb&|ts`r1tw5>wn zJ?A#)1FvqQ*de_|7~r$w=?86m!&1zF%8I1T^Q2e0slU*8^0VM}?N4B~n((UUu= zA}SeoH7@RA(~h19K|~{;$mgza({KF;FVbKAUnc-#J2#<<&L%(sP^s zbPfP$K$pL>F&?>f(lSz&GZ)Q%AxONBR^>qW6z+{coVq)oEVasVxoPzFiX3#92UoXDRO;`-v0!r5 z+H2U=W&5z@vsEpXVWCjN3u3ds|CKHERDbZb?S^Z<^nnffCDGJ!U3Fos+FJuw7rep13c@4vXE#UL(; z5ZRL0*87C1KO#&G&;9$(Z!j1cq0lEp7=2P~?Ja@oKlRc*`d^>D#UWIw@NO6TjY>qq z13H~L^wTgxOe2x(L=zf?i`OcjVT8c#fBV8M`u2bNGR%?O_USq7eGjmU^kwTFU;OVu zgb^H5c#AE`}Lr)0KM0)xKW&>7;VmqTARi=(XGw zHHYgCy~%gm`8;R_+F+-;%!WWLrrmcPj^p01ICsRU*_B-Iz+irmA~(ir(bu7GO>dFJ zrwB6y)Z_;L8$K2lKEsIdQ}ng&G4)2N*l7;&T$+Yq&)w>YHUctm?etXLP(dti0DP%v zYbadxFz;GMEjB`QygULVJbXU0PCjcfk-QI7jRCd&=+5AQ8vq@TLJ*S_@phUSe!ZhY;-8{&oDQ7HJf(`)qE>w8+s<4q!X zK6!Oly}loMZHs=&?QhptBU)%B8NwGh=bk7aICZ9P5!v)LbIn6N?YO|>1oSPu*dgF? zY|el5&n~Ew&$P8^ZLH6xr^YTEs?9t<9bN?#=kMea9NWfbT1%)`xBIpfJl)mHF$X-6 zQyp4&=8Y6n7;DD#w$lsb4(#WNCu~B&G;9mu#O_zXZ>v5*wsFRu5nFHHar%%s-;>&_ zV(#WH@tDEKs?tZ<_Ct23g5uD2Y~wSlDgVszlq3u4-Z_rh&B+Zt!T#v-#2_4B88#`B zjBoFI225Em3XfjXBU9gc#xyD%Wk|R{K|DC6a4zz3J-2C;1B*s;&wQm~~vw;)wTJ1YcxgLu0&W-T8Oi-u_>PAY}*qSYB219p~XZ$mEM@YF4M z(8Y$PtM{?d0=eoY5OA~)oM{*$3K?FKwm*K`Y*QnL|7k zzkN;2?_o~nfHy8Y#|yz_V-)AKaDI^p*I6i``%oPi;rODGEf&vmX_?x$^)ZJyX>Ii6 zpoJDb+9bM0Z5(UiF~WFVi76K#-Q@wWA_F5wZ^YF2G zL;YyY7CSy@O)+R@GEIUbb#xmi8bw8wSA_8pay)}rxIpV`!nhC^4@e9fFdc~y$mK@C z0vsQMTx^7(ZtbWRR%aMWBq8ey(|24eeUhF_{mWN&+AqAi)v$vMwZO261Uca!5y#h; zoL!@jUEWbA&D+ncE5V?Bt&rO4~W4w^V)WuBLcJ>3dPO2u}RAy5R3mA+ojMxBi+mB5R zNFD{}kRBN}G)}WT?jCK70ZLS0HNz%^9S-J1X<{%C;543xSTQ!#BV>k97}gqDYPYua zoNNto9hDe3c9ek-p#4!T^!nCHv&=xu=oy^%Kl92xfzsRbZ#}%p9~A}{w9jAl&>{MW zynnZNzi((2me$}UhO~!64<31h&t4al@P^~-^v^He*JEGDYtvWRLx;KMJ+eX5SIhWC zci_Z2G3DnPLJ4S_RFtr?RpIXX=EB`w`h7ojK^=RzLl5v|R;|731qz}M_jZ>_BShVq z6gDzdjYI(%m`dob(L~sI=2p$nZb6&zk0T`HifrjyVV9xRR|dN^E+FUs_Sang6 zhxNrK5!)ETxrH=%@3EunImhn9@=&AmoX@YDxlQY|byGFW+-^pdwNC}=8G};}99ksp z_i0LxrP!(*M3IH0~J!Y&k+IlsI@t7`CmB1+81 z!ePeX4~lK9%hi5v(DSy3L5G0KDt#)Q4?-vfn-CW>xBIIy7bHyGw(7#WILQLMSV^ds z#@djQH%Cl8<+OSwsXWZ4EpSnv*XDLaNA1lYxL@M9@(9^P-j8>jUDt!_4*5;!s_3G{oVeE0pN698nG)P9gm(tF zsH*KX@HRY7-2xS4A_`u!Awp;^wRpIrA3d_(g4}D+mWCg`_f};M~mx zF8g?bzf9N<_O|Y^E~`Izjlx}>hQ^MJ0>{VlEG;SO%Q%**(2&@Dg`eRxlh8uTqWFXE zjn)JN2t+u^Zthy~|96Q^{+z^pG0EZp)5?>ukLT)Go2hLD#}@W+W0x4BvJ&C>j^RL`jVFXwXVf67U zJDQLIg8BTdJ&8}RvDn;7C@5_V)QYgyTTjmwisL#UhQIgBZ4o?+bSB1x(}*hCST|+X zR#1BymRk0IIEe(z zj)U+%er=mV%M@7II;F%GgaSbvsuLGL;`b?I431uUTO|U}DE&q_sG>7c7lXXL;?te&=~emzoG>d2E9kFWsP?;P6wIOm2J)Z*WwDuFGrha z)j780U1W;M!$r%ec2`fh^zNG2yfcC9V18tjk;3$u#;qZIu!tYLv?YSbPRDq&XyVUZ z*94RHTOT=0zx4VRN-E*Nz6MV>YM{aCcj3++qgEP<9s<^343TVi#Ju{5N``~wB3hpV%p1JlnK7E~j$3Jq zc8#}MuXd6+Iwf=K7W%;HSsSe(LIO zWHjkB1xcvJmv}Y*^o9FMGf0T}z@;rltoz+(H&DDmcbZ{Jfehdr{EP^LpSy5BC}5Q2 z*d4sDG>ZmGkL9>9R1IZB7<`*@lbcz4! z_M-i{k6mH<*wxZSqnj4Q8YVj@G$rq<_tBn)c2`sawH9wlA3;KTV}Nu(C9USrTXdmo zE}Oa50#(Y-j~rgtAHDvZe(5`5m4PGq#kEydEM2ZT_IcpYn$|({on9>+purT**ON8r z37H7;y00pn)s1?*-lb|JdNl(<#-u|mLytzc9K0K0Au~BCXW-PZs-v(H{}Kh61BH??5rzAe|MsjSxrC?4kO>j0kr&ht;sVEy`-N znk4{>`dqZKEZM!L$M^Q$yI+IaD6)`q9L{os!@-5cBnhQxbICczcyM>3TY_Ox4<2c;}`QpAk+pnHGdz!fU=Meu9herioGSsN_50~4w>tHr?4&Fo%l z>hRnO$l;)Q94`O5r+GejWI&#_j@jmt)n10({jf0k<$E~(hMUHmcTHHJIK%B-v*U$% z^{{(5t?@i;IKkMk%@XOO$Qy^I7G@`G{{tic^6NX9frS6OMLgfIr7@^C3-tb|K=2Jc ze`{!WwP=HtIezKRqQRke@!nGX0dOq6CSkf?ymY@)3U!7eoN<*tGImkg1#yVjpjh^d zr*U3*>dUaW`$E?>;cP3ksI6ewvaVs``E3Lkj5h51&XCc5IEKpeGK9worHiAmM^ku} z3aU?$hRBU^`#tr!At5NjtXw+OuMM3Y%=HwGZSfak?|Vz*k3}jcri`)sWH#!ywT8)j zhTeR9vspyR`sks%#-sU8NKw9H|8DHSwkdM~!DC@vM7wG1F3lpy+&JvGZpbwEg!Wd# zqnOgXHXhW@Hd#SQZQ^SSvGk6!Ysx$VZ5haA<_?vVz@q%kcvVGW)o=70UDVonrdTT1#{_p@p$hTO~R9oc(uo~8#ZLT`P8~ra*4zR?3ex&>Uqf+P&@4NH+PlU zvG6$m3&LsHY&)RD+!4pt@UbaCtEh^LZ4boUr?yT5kH1q=~-{lao_VlL>paUq)um7CSi^Q zg>z4vq%RVsp$N&j7r%`J1C6Ab{yv0yF*Eed$2SVawuI!tyJ2G+kZS2EkNWHN+J_FU zl{HE4cN+<+zH~)w<@iRvmdGAwPe;{tPLraU7-J2&xy_v_b~OHyB2F1w^TCa!D;E*q zN#(8#xTt`ZP98O(M)3i^K<%Cdc<1bn>E3hiA)=`K{ zGNz)!P|Pq!9fuJ_l8OmGYhaCelxgN0NGOWRpetDC5IC`T-H@pr&wllJ8F@JJl5hfY zsNSejmK~&Uo)KkCaU|KsljPQT{>H@_(v#o(x^S!d!V&ekvzZL3Dc8N@tnK!Jmc|wE zM@Ll-#6Wc`AhVV;f=3*`+}^SClTa234lhK&5VO9shvRV>49|LO6oKIfrE~ZuPYFXp zyW8fG8Ejv5*r7Mb6+ap$R(iAmHu|pG$1_c3{h62U@w?A&R9|x*+C&kV7Hm{)=?ux`18G8x<@O3( zYW&;VA|CU7qKXJ4Fm+haS^-3)r))lXVngQ$>B~DyW1pE*ar5?~AHncwW7S*D7s_4f zY*cw!MDWNV%F({h(RA8~xJWr?-WDG9xt7MlE*n5+u}m zJheP0yqnso$B)mHfwbh&%3{^v3rwaS)<&8Mq+97wVhFY?(m7$P;&+L2TkM;3yPb|5 zjED>_J=#Y!HymR#uP=pbpp|jn7bfKNCT!wm)bD1*#o^&ThBkKXv`X~)?>~D-PosS3 zkVP-y(Am_ps+($SankXNcXqW}>swE4&c`R))%9DJ7iBiFQ$QTH@H6 zWrp}T>wU9^{m^Bu zjl`Ps;`W9%*XpW7THff87o&&Q|H{@vZEx7&21ARSZEdusY!Il`bzm;?-&V-Ui&C?u z1=d>rGn?47wi9~+$ZfoC9&=8VRq2el_d7kk{#{KJnbM*&DpPV=AD8kpz7tH9S{YxQ8@?(S z!kuMR&-yKp<++@zGF-iWARm?^a!%hK- zf>t4YLt|toT_STI*i)zG>L`n9tyJ$#w(5(~b7A1@sHIc66nX?FOdBZnIY8KRLWmTC zu(CnE2{6`@@u)Ie%h^qV8=Ac{Ue%^lA7KU@=NBC7TFy$|1wcMvs)j;2+x6GqYm?v)E29dlVQIv0yMfyxJ%%i1*m(1sh1TvO30a zD-OK7k3;Dhht?16)~-bsQGyDR7Hwe@8nh&!JeU$5b`Y%}FsJvzVJFBz>#Kc0v^AXN zkBVbVAtx3{H-I1BVqez(*ldj z6(!e>_@aN{!m^>0({}ch!$xD|W^2?v(jj1}EFn37YEC$BsqI@U{<_H5YYBg}Ir&~}Ykt7J*w_TJ z3w6ls}eD8f6N9Bsn(06enwn-S7dnE}=S%|1W5`$ky-2GIe z>+o8+#x_WnJ!(@shEpZCGHU^aXbk1MR-l7kZiBH+E1%o6V_Fo3H}h-{uKCTWg%c=A z6G+67jcs*$Fy6uwR?vyTk)HruK4jV&@$9y)PIS_^^L^jxh0HKOb|i|RCYgG>MQCg< zb^9mkgR^C*8KuAx_VKH`dfhwEY;@vK+g-y}Eo@LMtl0Jqx#gG`m>74&{>Krymqpr~ z-k9mJJFv^~d=zT{EgV*9_)MvAajK(W*_$@h{BQ6^|h!MI6f7r>Lz)Iq<@-9Bhzu!)DGT0oS(M0W_FO zr;p0|W~@A!Gy29yj;9m?s=L3mD_3Q#0kfgGx9vchWHtQNlR{Q~9p_xE5%-EDwCzje z+}!Q$fb2P6@tvclbgflETQK)PAq2uv(+=>Aww#_GS4`(upP12WVqb-9@EtBGd7h9k zkjAQgpplzvWskf#{oI^r*aEq`LI}$A3nmZ;-O1NFx?>v()z$Ha#DFdLieu09Vu_Sd z-PxnrWw-|UW6Jak%WG1HyFV8xgeW=tBVV|qvFa1j_8X7S3*odpHWmk_U$cV*^eixF zargj$CQjmzHi6C_P?{(K8@V{xy}INGEM!S=ApL{ zOC)_wlu6NuT=i#BDiltra_!f9*8-qj3;(MHesnse5yADtYzgg-iLql8Ysl1Q6 zRA5{<$)MG7dIxgPQ5qO)wdPK+)@;Lx!YLfeH`s=G6gz+u4qk4Ar&`gLfZ_MFo=#(l zL-XaWWx`A_G>!^_8F2Q9)??H5wB9*2g?WJCdVH5c;mu#%F}1q9WU+jpJb)Uv^N&AkdKe#N?MyG@PA%a$WQ z&`V^Z#WBVD9^ZR?Cfs&rW8T*+ozBJ)Ata5ziiWHX!bCUmbDF&F1)3rj+_$1$f^9No zDh0YU%V<}6z3VMgN!;4#l18cFO*{CVe$JIL6ub9%ZlcBu;8~q!Fef%i2*}oKGAQB= zV|;AoL}3Qe#&(JWYp%ED_u>-qU1$hRkpL?*^V~3?X1t*cj;5-vnV?vo+1!Kae)^UB zdE^U@+cj^vG51+5pt#8Oz#?zDQx0F@z`t!n54>cp$EMpt(SNkJClU(8gTf8i%S}#b zwoomkx-VXBZ zP3R|CxTV9btJzkG*Eo*TCAexa_#!NflMa$Fy|eSLlFV<#wt zBaZzI2FAaryQhvbFVI*WjAoD9x1gmsN>~eV!n@199I-RQut!V(M?q7mjV)x*Lcp0* zGW;pjXWSKTnHv3ms0&dW&YQ>=p)B6YCul6${Q;pFS}s7$ED`l1gYZHLpn%*_PA86u zwPcm1w)cj6o-ti(U=$a`faH4)4-uCJW5Ui}vmKXOxNxyD(iSFGyBy98t--~1Zn9|! zPC|zuf#ASaP#l@-H{hvfk%gT;A@m5f z@rW+CKg(?7jM0oYoVfT7fa63-8J~x;B88(Z4g zcLolq!lEaunI}~ zNv*^6fU7OcnUO0luFG7CHpUlkKe?I5_f&5<5TzdV16rLUf?}0-V0VXOO%;QS5DR~G z)diZltu^s!pM%p^q0z(_l~;t=j3Lm(Nj5S$Ql#WHLpIk63iCw_36+>WIzz1hk?1Bx zEZl`-?O@2~ephHGh2Jhp@#~Dkan|zmLd_tfq|s*n2+dN{zQZO)Sy@Owf$lDvo(~p>9N=KkrTNV`Y%^3;joIXil=^x&a%z-yoH))vAR9Y8whj;{|g&VtE}_Usn(p4<1E5$`4o)k2}% z++9*Ur;Qe;Xl+VNoNF{5UNu8nOe%AUT#!4Zs<^zvlIkYJzD<&FB6B7z&g>DXMW}^x;Qm5LQo}F!3Qfo6y z15}tD)qU)E6;|(fgCJ0+oGv!@mmo-mXaDl$9YYMdH=G+z>Ws}Pu_s&^oBE+`n;MHY zs=@o?GMMXLoIxEeD-eOD#lo&f_*+kcBukLk9Qv%+r2Y_sWtBr8g{?)c2<9MKL+d<} z^p!~3=qvqZiV6GkVigO??OSALP!6YakvdK_jrPT(i{OgBb<}4+MVrZ|MR*lWoqh0U zaXdI|R3bMG*i8^??aR*{rsK#2WGk7x=Bj(mi*BGr(C6G^urSqjma80H&?hd$xjk@8hlwgV;O(KY$9I8LQUbLGsC9 zF?uAOA~XjL0J?ya?yjHdj0szx$2^4;?Zn=UE?m=UPBzCnr>EZU4mchcw1BEbnwKI7 zJR&Eiu-_+Z7wzyqG*LH1wCKKT_BL|(QAZsWT|~S-+iaB~s|G7ZT+mTbDn|9xg`K*g z6HQcKK+rRO`<6#fXn2ZTLtjO*1T_s|-d{xS1Dv-@rR-x^=Vf!?)al0cf4CDh9ER7$ ztvwpOx|ur?NUfVCjjAx_q8J0Mj7*q`d0OP!TVg+glZ$X2x(VS+EEVQf-qA; zPvm>=nAxj#f#;(b6AktkjC4twhrV{pGYlqi7 zb7&w(2b7phwfl>A7DiK6KtR(zkIn7cHAfV%&vDS3?hhBC4)_h{9Rv`L*2Q_;8o!k1 zLb{`^J@=a5TUa$xfpzLNU$#yV_`S@3+K)Q7L#9U-W++nIiCPT{cWre|9!%|w(%UX8 zP2E;*>AEg3&yyUhlx`+e!qyvU{i4$AK7lsX5Z1qBK3_@Q6Q9D#rYMYpkv*U80sk+D z#;3;V?ax>^xr5DK?>l>#PTA2-`qB`pTk~+R?rEhQ^NVb|O}&;ILYms$Ue8P&WxJl( z5C{&h+a?e=AxF&x;j!52W9`Duoa0mkarhR}kI?7DTGg+Iu{YBvi>e0Sr|o;G;&?PW zDlCgP^7d7!?5*G#SGHDeQ@gWl;AC=KJ=g3Wih5HzJKcZjNH8vl17txb>%~nwDBRiH-tArfq0`9QX3`SgRLx-%BYcJ+jBI zoOz>yY3*xj^^PLgPTPPo8R(lIJDx;&QXrGs!_AzaQOpKf{I;KNa%8Tkj3VTl*=h3G z?&**`UblC%0%D>M{2eMZD?X39_T=mCOY6|deTXZI2BpKh!*%_8a2(t0RR5Zb_Mij_ zp_U`p*Ucc#1*#4o2MI|K)I(k zEv;4^31CSE?*VLU+Z}6=e7GQO(xN6T!U+EG~rA;1GI4^M@5x!fk zB9!WN^*HYg<_~)27{C1bZ$EsLPOQ(862)cXx!+c>(i~#kVK_Bq*xP$c6;y!Q_Z9(` ztl&Z0klWACd%WwJ#JtvfR(ho+3q zJv6*#s+VUj;qbh<$L(wby@tKohhDwU7IKTwiY;0s&cMTs_M7)j`LL1ax7&4_UQX2n zG3$9>XmDfZ*+~ws#nxtZp5Y+v6`X>09B>k!X&IeG*R{q1F^4wqeUk%Gx0YQiHHRoY z(a2~$h`c3)q2{%KhpY)_n9~Va7^W7bQ)VDlD1&t03jYl9g6!vr|h3FCf9@dbo zo#B2+;QG352Q~zEZFkXg3VJ^&ts^4h>PW2X7~;3APXhWb+)9&%!89i%Dc@v3#2bPn zQql+8j^LWZh>@sq_?DB~y??>KeK7JXGO}je!(73%V8S&vTh+YTuI0_7*GXGT?|DZl z!Q}IqfxWpM{Q_ofCD_sLulQA-e73_TVu;@H$kJ$G@VR% z;MZ;QxaOfF%#Q1jRtkIEa@vQ6f7_Z;YFkB-_W(E!FZ!uNb7$23nv!a$XtJ&x0K;du zD7V&8QY<|c*-B&En0Z7>MAf7!4^6MNdugjI$j054u5?p0Jo)qONvnj+H1f}M%^Up9 z)caFO&n6%kwk0iwhC@q_N=V~9XAY(Iedp;-Le(4G3`SN@xNB(q4gqZMTb3V2cuX0d zECqSdx-doZ3LKe%*DRs)Iv!ZHA?BE z<)}#4E;MT!5l*&4ZlgCht(g0omFLB^^*(A7Z5aZln{2^c>Hgaja+G z!{Ok>7u}cBAy=bdO&*~krYdZT@*OR(E|v$}x%MHTL{~uEUc0|^_|2)gV^8+V9a7NV zC~br`ou7;;&4X^?FE$u#2*MfItO~ZH!0b!rfcv%sj9o}Pdt*;)G3zsE#Q2OYnRD0D zv%#2ieXxDEh+pITqgv5LY(Jb724{8x9}9E-}m@Gy+KM z7>v6%acG$!>>7HgsKPnGHE3jswG_JXxzkRgV()hGpS!3k;n}2?B@X7T%O1FYC{~yE zyI&ab+LTaTcC@h&>w22HIN`W3Z)tgF42gN{CCU&i)7L+AluvkuA1->+u?>3m){bgj zWd2>t>H;w1__Kd8-h-7VDz})|Mf=&8Vn{sxrkEO8+*lDOxP$)rUynTB9_HGM7YBsq4CZCnD z2eaR`u$>s1vG%glnLdRY^7jtqp-u8N4Dk)*-=1orJ9YW7v{Ycu46>mNF@-oE9r6D^Lr)dH5H zY4I=2t9yPp7nnEFM^DQ7UfVH^4hX&%M|%@#;f&Dam@I_9cDxuTXzpj!h~!mI%AY;>_;D>H)VJZD4rJ=7UH}m9P@%;OLqi zat~(4zHWHOk=%j|C|H`%AV=1Q$*PA5l;5Nv=Hg@E5ewKjV`+475uEIyEaD#1=8+yr zn|I9dt+-J7|0z*|FLc+oTnFtz=miecOdVZo6zfF=eYxdsUF(Kg@`;v(e3G6 z%7kcCH{(%@_Ruy`IFo5Pl1rRn*^=8si@T&rOdh6`Y2PTTWDac$tlesHiQuvTK8OAeP@PKRzqR1zwDU| z=x6=6f{D2Aru+w5-nVhuk~`rz{0fheqe&z5ND=K?!PQ@NQpLPeRiIOMb61^lTPucN zBpr3nOL5>hYUo4}8oLrn3a99wJ!KS5?Hx`9QV5d0r_TuEE+|Tp zed(FQ^!`hC^#Kox17y8y(?lD=$PbAyhMkS?;1z%M{vy!=@)dCCMD+EzpM#D*t+@dG z%#H04g0e1^x$*f;2Nw1ISCmCGo|%>cuY?+@gqf-DIO`8B6IS3+IyR~R>7f_$=t@fN ze$|99!c3r|WnOhVdsbk0NI!$k#-t9_mC@aCrq@RSLTWakna8D>4K2(Pq$C87>@!tj zFW+9oguA3O2c*Ms6;211Hs~gPakO0C(%NWxF8JDvC(AKfWS8vJXzXdu8=PEP3Iz{Y zI~y8gs(utIu-gUoU@kR|9P2jH-AOo=C`L#Q|5gbw!q;CMdK^|gcc&evkm@3A0vMf# zz28+9ap_#+FFSig_gc$3u{q2%J4mw~^WJDvxj z%#Y7ujzz8UKx%vXooRFY{miZHv4fd43Nqq|(Hy!E;|VM0cocGn@1_1w7Q?mjP$5$r z5l}cCs7TYkuC8(^V4~gMN{wdD1x|@O2~jlv|Hoe6cFjxaK=XX@HuUv!=uF16-p9dK zm_uN*hhfytb^)NVWoy3;?(sGUnKAt9|3`K29c^iL)d%kV-IpteuBz_poYZQmB_U8i zNEnGA%wWKPL@*eSjqSC-%rIicV|y)+v1ewP`NP2SEW!XXU@!t977SS=Nd!qKhYspi zhwARC?yel(tM}@Kd(WKTPG_I{UdtjG=98-5d-s0d`A*p3x5L?|GE?@=`s>n% z_dK)e_OKiQgsd?y?yxAkdM0w5i=u@h;VpHlF{8jC=H&>Wxm18D1!<~)c5vmuPQxMT z82f59+@T3j>GZ(-jD-Y@Ff1Nx8q)-QtW~H(;doMJFtC-@s&J?SoAc5v)OMK`Eu7L0 zT)8y6w`2yR=5go5z*AO#+4c@HFEs~Qx&$#BlPG!HdqlvE93zh8H{#r^fAz9s^r>p= zj-|K;3k3hrxi#4Zcwc4e_ny8gi0g3G$Y<6z#bGPX&5_o^E7s0A^N{0%BMUPvRg_d) zZ%veb<}5V!$dzl=PTz|C?E#scr@}`RjAv6^oKHIEM3!poFf=)%BY(v198%n{$!!o2 zI}A$zkDZO#H8d$)cfxX-X*sMaaGMX#_y3wAk%2!9kMz(qb%W!?97eGxzR2_J_qY>m zg$>j*7v~5V8Vw@j2q7>u zh>Zf4OU__i96o^L6dW9QKyuAdFN@?f5ZSl8g)_uv#4`?}h4jB+&z$gBC7N=&-h9Wt zIo@O6r*%zh;uAvQL5-DlQ>i$oC#O~lAvG=SD7ttkicH!&+$p6)clw=q=B5nWKFaCp zwQUBOav&+RGOs<63lx#6PB$qW7KnqX2?T9OUueSp-@o#>Fy{)Q4z)usJg`9TJ+mrI zedpeJ2}dCwJwMX$h;Q^bT-WQIo%sfaNA{Te?+%#_jz1veJ^ZC7I$6oC`rrc>*P3F@ zp}{ODZM@u|c%GZy-;u@{PD*wN-l)PK9#R@$TlmSuG=hDsOyydUY09Q+3q;g~vtr$x z2&tE}rYWAiIxf#$-HNO;xox08RiXuvCfQ(k8N5RjiibzCR}+lPAs947rW)H5Pey}g zvf=GG-NOU}aR`aF)6B<%B$MAdv0@5T7jR{3T+@Z>;^l3ZBlOAJ1YVK8XtuoZmy0Tbh^er3yMA5d0@Ux?~Vl05`>zTOsk{# zxx;rHYe9`EaK0Hk+KL-vi3_EBDHWtN^x*fqhqm>A6#NfvE=`TWf=ju^yrm!DMNMdfq7o8>vVZzivh=RYU(5PJM7T9ea{@7H3$yp z*=4C}Q>+L_bK=g}yEZJl1%JUajtk+iH9130hxh(-E0z~h!tY1EKBe>Pm$r$gG?x?w z7PjHJJ#Nxqm`1~Kr~H;}><9}byhXiDO#zv0!i#sWx%CTeGZI|vw%3q*PuYOlM^0~$ zd#PoYmv_@rJ6V+iyKU_mX3`(i>cAPs$iaXnOE{cqMn(P|Y+8Zb1G}(@@i~eZ8LIis zCs!oj8`~{$bT2MQYQV3tR7@Sj5wldiYM1KjU%z`++PYd{+jW(@!Qd!4d~Ej&G(3av zQK1yfjdm_oxCi#SW6vA|)K@51h2Dl2ZlumVQhaw$)}jHc-IYu|$Jh@JUQORlxb6Ci z(nWD{OV^ZY-DtKnN14M+qAs_A1HC1C!5F&j!0KOPqb;_84Ol1^%uF3a)>r(47aXQ< zyX$a`BVQ}aRX{@M&Q{8R7aZ&Jh1c(<51w0<_FsBvkuF#nn^`k;5cYx@b+oSWnQ9jK zaNuZ@q!pXYy=IEpvr;QQ%HxuF$7AIu?_hn^l8nZ>Gwdl5B?N@Nik>t7siCr;PmkaCJ`Mwh^gF38pL- z>KaIo+4ao|+?-z{Q>(06+2FUqd8BZLL&a@zKXpWp+$EW;1lPI60-ed>ki!Q1-+uC{ zYH&TBL{bn@3=h&8#TBqC=$IC1#PPhatHoQ6!~L#bQte!A>T>7)IUG|Y%(GI({apv< zMX+3w@ZXs3Ikts1Hadk}tXi(4>AXz+XC)XaEu><$G94G9MjajMQUb9*oZ++9 zRbndK(Tq_UWyRyRIVB1U1)bxej_^FIuryDwV?U4EQ*s8@|9|22d+5Kv`!GGZvQCdL zuOmr_Z{Iyv?dnw_yq6tbD(`-FMLa~Gb8rb|pd~h~=d2HW&KUW?T!q}%##R(iA^5Xv zo1MJcH>cE!3-!>Dg$4#cS%vY*wN2VKzuf=eq?6=bDoqV3S^9VOa3{@~=AC5yOsT(>!Tt+SyxO$QpI zAttJwtF6sXn!U`r%W4!3G!8@oG;?`<9Nu_$o{5vqY?ybpBHxz+}wf`R4yuj(4x zC#i*%xo0&=0cnU3-Z-PnZE`IBjk{(%6AvAfly?wU`SH7%kFThALH&O*Ei0cSjivrN;lokA#Y3xkDdZp-J7y5pBzW5>7b z_Bl<_QFzU5Da;Y)ee-)fzD|Ww-%ZYhU}`e%jy>}=UVXi69Qx>mH94~QHP`K;kDObR ziFx7vg;KRP!z2EpYHy?IOBqen#A1Ez>#6* z+Ern;(6cyXjOjTv@yH;z$GfeHWXm=qaXbi|pRwoEM9@V-cX=ecJ-ZXIPRJF0XG%nIzOLhl32BS%g){Y@z!O&he5Yz;k)Fngw_7pQC*Xqk;{A zPc3iYcq4xG(M7&_&#XIULUV^Ibm3~~!T_F%@DR{V@>5VqlK5t$me3F-U``^Y?%ojW z-X2Gz6lQ~^jdr(|&My$o*sgm{Q`wKgP1l&iDTE5<^gq7lWgX;Ke>uMY4NF z%V&eZK{xXjzkcVbD>ek{oGCnZ5>4}zeke=3?BXc!+~$meTgfLh=Cz3m`DO6ME87+> z{h56f)Jxx}pT*n;Qz#%iQRE&|5M)yL{tW*Aq0{S80}UP9nz-aqA{m*~GR>-5o5;Q2 zDimz%BxYg8${Tsd9g(v;-|GLd0fX5y+;kX&V0@!F+fFE1al+(8?|AeIJ-xgc8$WOk z*YXw|XIjh-!~m^+sM=`w4vY^drzd|-_&;{JE3`^Wj{SbJMkA4nkY!BoU=e zH*9UZ{o=+%=mz03RAg~6SsiD7yZ5optlh+U4x5vbDWffNf!u3rb%?sIGdXAfaVEE7 z)`fZ6Gnsz(NsRgSV0!9wgtRzT*OxNv^}<LdET}4H zka2vbel)zvc7u6yJcPioq!XJ@oQmnO^KecWlk2|Lk|J)V)$1QWa{onv;Wz}~rs@ns z8%QZWxG=~uLFhZeW6&f>G=c4WW3}~m*Dx4TXt9O!j{WoEcsp@voo}gJeoNKLsC$O3 zeiy3xxTku=@7OmlrJ<@eFZZ8cm-x1GbFhwU1=qU8=iQSYjQdM|q=k>z7gwH|v{-V| z+$E7(?-)y(+O&H|teoJtiJ)egH6n2}?nG-l%>t@>mK{^C*{eB2KlZv?_}JncJye^cD`D)On<^h9+XC zy`Y7i{>2l7OTphr^la)+PD;S!yhC1T{)$ zA6uRXrL{_JdyHQN?=USDDB^?m4t5k>$TfiVyg@4W;Q3eN(x#1o8s7(@b)whofnRxS z#ZCm#$`m6_Efe>5gt)|0t{T7#6RN_IG|b`}U3u2D@Zj3mDooVp*SAX3R8%Afd@w@u z)Kg_})A*L`GBN`F&8Joh4(`JPV8FwRnt_CSao{;N4B#{n2#^D_gV@|F%IJ(tGYlA- z_@=6g15f2kvprjLb9KgWZVlJG&wy z9AY4aClu&tyP^yf%h~QJlx}W6xe36CFg69MAz_`Vfhp|_NqAhQyLcF^By=D5Cuw5Q zrXfM}wpextISk=8szg#Iv;~fnr*@)@uu_L^rih14OKZw0K7;)|F%1L44SRfGlV>Un z+yl;3nr-fxY}C?CjRM1&=MYLYydDDtxc;LRlEWs09gQLHw+$Ut8HhB8_Z*rVp|a~1 zXT@0q=(kZP_l8}wblrlU<%JBWS5?i7`1Vs34P2a%G&r;Zi1bsJ)(dR+ zue^0Hy|UiV9hFcsg_?#9joE+EB&`4Igl*dXXc$W;#IG>PCr5L=F8IKcwYSA}%gY-C;&9kQ-x|W-}|rI+fY4dE9hymx1=X{MC8W6!UvhkWzUY^B~F8}U>=L(n` z>5Hh!p;=~n!Y6iap-`UGuP#Lh=%!sWVq+r{>XyB;bmzV~nTSWKW5L0_hPShZ*p(8p5d>qa2j2Wi-WAWlC-)tzELR?XL8MH>pCw@{Yru z+v^?jCEoUT(T*5YpM(#<=_m-;q9(qlyZC4RB7^6M=fg?%Z7(8bVwb*0p{>vAM)XARqfXPF!@D60t{XrAIlYn2a%9JG&1gL0uwhEplHHymT|( z_H3(y8^|VboyXBTSfnIW8k^l4G zx|LsfWVdWMUbce`AG*9=o?YF*Jn->4e;+%)Dv0P_i&KB%!kWDI?h3!5Wj|K`u0oa} z0Iu30_IMwJCOqhN7B*Y`Nm(ZVZkUD7UE3;1GJ+6?2>@F0G*i6eozGnMv|k%YH_Q-X z5nn>(tpaauQXDtyWr4|W71+=?M}m4p>+zUW)d|7k@zb3 zTnY_&`oAY>5IgCRBUeRL6pBlB$$k4h*U`Uz`7xTG8A*|Zr)$*UgH`LEsK^C5`M2(# zlSUquGDibdrx~575FOEl+4}b@YUt~!`ZqRn)cK?vYl!P?o#Yy}5|nqe3du`|R7_m3 z>AO*;cr>UJ6%1dsGoJ@as<0dy9HFq#)=yrbUw!%_rRi|}H_Z_|#YN21aEHPq61^4W zq?`+N?qV$+o!jK%&l58|<*i5z9U7S|Dpi7tvxYaNOl_;RiTUWR83!E1VhibyYlU$S zDWS;vR{7Isukov{Ul51XGgmiwVMfz0VJDA`PK~L-tn7su&^MGAo>$69eP%m~MdJU`qs!&*+`1cBL9{dMZFp`0!Q2O)>t$(5?3C%yiv}sZgGX<<*w=l2LV5MWm7izL)*Xnx&;Z3 zJ$iYA9;-x*jR(;7fcdXEwnPY@{nnGq*lc1fOTm%GZe?0qHm^u=2$e*7+P!50KbvA? zav^e--La-flp9sJElBy*iUV8(CE?*39@{Ga^~Y_CM0uCMWH z>}v-k$M;}bgcmI^GB){uL8EbaQ^}?)cFGx|H7kt80DZ3S0+7InS687 z=okN4)Ee?iM;s-&M36wZ*r_dK+(BeJ2=%L7F$;Lj3?8_&t?|{YjSC^wn9nLMjXvL7!cl$VGNDXj7bhq4CuY*nm1ZRz|48+>xm-QWBvAkg zpcVpw*jKPHH9S^mO0yJ)-l42ZBeVM5k;U2aPhN1CzxvjF5~+X#@_`!8`gjdvRjpd8 zCWY->o?F}CkDlL<`MIMa7kIHhbYWG-3;P)dF}~-y6%?`H7w=z`0{;(70sq1`*7-xS z4^Ym;ea*%L;8~;yV<*^dDK$JP`uCWR%xm2HAWl(a@vOmNM(;nn()6<@Z8`I0MV749 z%x}dRzJ&QoPH7RZ)@;f^#?dbLOy^W-w=RyK@mM@vi9E*K#3?sH!UhXWB-uByXpvf) z?CcC7lFU{Hwtcsji&j#+ABZ1%C* z&Qj5${=HTIwi;GNwe&=!vZs-XWGscVG0|Y7OPykZK^VMD%sqh5k=KAzJQdetN2E>I%OLs;+<# z5kI|9Uj&aYh}6n3VV^NWV1Pi+)bCz+pvG&@tT8k@G?$V{sPDj7l#rsQ24`6t(ApJy zN^%V1a*R{b-HlYe@iK`BOGA#TJgj-Z?ke5B>Xrla_wPEyC`)vqGW7bmItnX814?6) z1=bh^As@WBDztNRh1GRl__391lJ1M#{CoDzGwwfMZ5fz4faVWeTw^)Ax<2!&!%MuU z!uksl;_`0V5y)10_|W`pxmf4-+LvkmxjE?EEY^eHhvXc|z$r`!-Cr@|$hkN(dt=l~5ratVMsjKK@2+#s#Kj*k z?o7=h%j}Bq@==59$rO0&1DEM7FFzm~ATDkcIW9+&OZ3(OCu?j?*yTQ8`ovstBb%5R z+1*7rhg!VjdiVf(h=2g#8uKMP$9iJgM}5eHc<~4GFiceX$$K=!p*HHorbgNwg1n)5 z%YS+J5`XzEyXj?z7bWvd>S7TNBUFaQt}tx(L`6(6`%4cmh^;Mq)9c?as0@B}v+&uf zRq*dQD_#$~8ls>o{>udlJ`yHHrLO5^IU5axQQ zq7U>3&O&UQxni};ph0Vhi|akr*1jg~-nwUA=CMNYvRTcMyY?-}sVg5ozgA7gaQZecb~`*5&|{?#xTLlEG-`)6nBoNsasiOH@mwqPX-29`5IXUKrL@n)fv+e>a=+TwK# zJoR_8#f*soh{0y;JHN=JjfX!9hoom00FBtt-=Cp=Cp+`=xRj;RzN^wFU2o95dcY+u5ycr` zBSM3q63em5;ION4LIt!kHviwUrwWHkS+XUtKD?!e5=e#=m;p|nvuj%dEm5H1>8i1+ zE3MpCUr-%we5DfWdrz;nrDLM7iNiE9aO~c1{jAt&L@vQ7G)!X(S2MB(6U*ftC~BLV zd1enOB{EVvUwd>nPQ7F}w6KZdLS;CF3Xw^6W^F7r*H=(th{VH7D9yZ37xhYo$)CP* zO=timq$(m}+<$(3Q*!KId}ygcPuT&E4Y&)b2|;frtBL!WA?C8esDx-w;{ z4#aCxqil2H&OByhi2-hdVNVNsS(oxtaE?`vHluX#1DCFe@I|x$^YqD!aG>cqY1rk3 zc=sQkzFLk~=bCPTrSQbV%ME(KzIgnOc|Eo$T3tv1Ip&CqL%_~(V^~~2_~5gFaOKm| z5G0i;eBrIlqWJ^V0;O=UVJOxflCan4Xt)*y~IKG+E=aRE&059 z!lb}%2l!Ib+XxExZB*DkI~qwWdJDe~@Klcw_hv^4#T*fX!$}jlE)^#V`EfRr+%!yz z)s5nUy!IMfW!ceH?YU~J6;#IrA)Q!W=l49bDtoiP;OG*UO|`Xe*rn%1A!QV8i!D8? z#|k~Mz9G;LUSah0guwJoI#qN#A&3uYl(5IKpBqeGzpvp!-FY@H3f-?;)UXVG|I(Xw z(?7lUP>qYP(}&Kk(eFL8Qa*ZaO-}nXxX5iWbiqQvhjw>|mt%H~LWhzHdtIS;^#PRX zotEN(HNGr!i)0T#V+gj@%GBr&V$4X(1f#=L!Ko@hK&ajdQ)H`Xu}oy=UAHh-=WLT9 z1XOE}svl#vGX7?{w7wyIykqZzc*-BBa2~0-=;UpC=G|u{{iq=;Mtr+E=?V%@o|Y`O z8S!C&tq}@Ch^u|;-Q&(1@_}cW9##KosI9h6YyJdOU92!rt zZ3=NWbqVLB)2FkLi|C0$alJipSqa~lF($O#q;wf__FL&abhbHMNyM15dHTo)lsn;6 zl(dAmiF+?^R_4C{GJj9CwI8YQ3~<~j$de$TW#Jb=Wl6x_aU}#COokZdW?j&kQFGsc z!N(R3LOV$5w4Gr3iNbYZWMN{{(Ehw-9TjN%iG`cst{7Lrf`bHVC|evhE7cyXOf1BJ zIt#Xgj6Ye`5?VOAI9FbBc%E@SIO5lEt|3|Fyx99yGw+#|sw@hvH>Ka$F;<;%qJ?3n zqpZ`j^*a>yL0Iht2j8=zC=x zpRAf3JD{-b@lt)Z3L>%nIo6W6bfGf$wYa^mrG2lfA*gB^@aG??;kSw$_RWh>VLh!> z=3DLq#DM)sN&!;Xt zuWJ8nUDyieH7;bd@Bhw{3&YbCQYhK~b;GL>6w>r@cBQ>mY(=M)`O@!gjt!+pW#`RUx zqbLK$3GI$-wW`q(3@G?)sWJeL5_(zf406jan>~;7=TJ2Sng(Nl-C03Jg~dzsIn~Km zXuly9lEdaMX@-~h4wg4{FF8(=MD<-{)FBh=_^w%5JLJJHiuq9lGJC2f!i^AI$lf}C zZ9>mo+oJL9%> z0t$+-8~-w;9!H}kpqtUWhq{~BpiuIznhoa`bQlUI7)pz|_Rze(@rr5f?|kY0>NVa< z2Me%h>bY4xZr5MqFnT=EeC@5VF*6GHPjuq&`DB8S7uSWZp79Os>PUAXTnhNBEbjV*+7R#YW8Fxi#$F+)p0>;Ca`YdD0DKmXX0*!}CQ zWsqKYU_=V7w+MZMS#fJ+LQFFlAE7ch*ziKY(3^J8%b1Y0hVM}vp#GGN3lCq0JW~xY zmdv+NK@1nR`&HS2HbZyRwqweT8N`uSK^?ro0_#~1CS0;IK==pE2cfkl3=ig;sQ-fX ztScnqY)n)rfY~3vvQD4Ayk5q(wlEKD!%nU`S_M=o$ft*J*(|Ll5wtAU1O3B>7zk>h zg1dyF$MlJdtAb$6G!pa6$An}Rc)2guHmjFhoMZ5SBXoIuake6XO@vRSj(9~%GDjfC z`9U)Tvq$;|A3PnuD_|*^^vPJ=l%x9@NXR$$Tql0EAH-TS6A z2q->gO;q6k3(TG6Tio;#BcdejO+-26g-U2}?`%*@Vq(f3aJ?s_c3^fJxx+x$+N+}V zbYrr3zNHCe#0s&Yg>8GKo|agdnXx@{p}I6Da@O{Q=7wgXV1xY&stx$TGg22zJ?MrW z)Z`ZKZl;9{Of}IkX{e)^)wCt~#}(?0bT4*w{FJrI@CSEmj`3oB_nF$y+o~t~u`8S6 z@OrfVyt270o?*nWkrV;D8`=e08GRBb+Nu_`heHUq_>9FgalimP=r9FfDr3JkguuNA z2G4~S1_E=fD?{?-bv@+*^9k*FrV5FBsx6NcPB10zpJSclrNYv7EIDb|XSQ+>99PK7 z^*k&v0K$3;BR%BqsjFMEA>-j{3&V5}(zAJ1e@sPPn`8aE7+FfdM%Ms>abI0K6eUD0 z_9`TW*^$2hfq4DWVns<~u6A@8n^M3DrWIKLdKa&D`}*tm(2Mpj(&;MsaB$+jDj=S# zjEwJ(%+CPci$J)(N_rrJJ+qpe3|kwq@=0Q9K=7xpu2VN5fi#_W*ubtczlV)YiA9#9 zV^%k4I*tiM82KLN>nRb*3aD+qGP!Ru6R<^z^3t=T+p_^)7yC0K1eH27DveZ(EpVynmwgSjbs?1V$ z&kxd1*zhPYFMG>b;^`7@Lr4c1QE;;1oGvJzeRC7~t*4jy=H2rJ&mp-5Ips*QftMVP z93TT|T4?MoE1m~u9R$G01QIl)R6AXRMlQNi!%(SK2PcZ4Hs$ZZ?l(v8x>bu0rX4n?aGWuJ` z_6U7oPO4{Hj#$E)?3L46SoUH=Rbbq*R&owHOYQ))w*@)mf=>WWGi#o*A`1rJi65Jl&> z$(&|eH_N0I*|3R^6jayV_V$#h!l4nX*5)_gxm%np*KBiz#GWSY<$*lccu z8WH|MpkqUsy@+?1eZd)B@a$J@!#C`j!zrVD{jM1t3c$DSoh^@D-VkQT1mT`Xt0U;+=ZAuN zrDb zz0f5Nbw3-BFeK14u!-VgEUnZtqz)!%LCy8bZFuf9P=R!1qm=*YkIwQBf6>vX_nhop zv!RRTh))#8$xqRgilnzp1_5b_HZDc%?Bly;Dlv=Kn77P<6LTPGZpz3M{sW5K8i{K| zyE8U*Or%Z-99&3$w3^in=hqF@qmLqp`{xVe6p`x|M>08JX!N0avav0Y;mL+ms%kt* zjYNTkll3`droHsgf`q>=U)3XFg(#~gzj?RXz2H&UnsAoYAN1tPW+0b|sTbF1e&nl@ zN;oh{H9G{#{xXtHDzGylP{H(oD&L-tu^Xeqw%w#U!Q zPOfje)s_92+i;J^staa6Z%#lOx zmK30TF{HC;8XN-YqUEClqF=CBGwk#|;(Qy)E;7-!<}IES&wo}8UzC}JrlSx51=kz= z9?Go4Q(JkohrO ziSV9>eO||oO0OgHhU+1Tv*HsuC)!ynkHW-pvnTPiTyJ{Mr;ThUl=Y?2x@&6Fw4yL6 z5>_~?@eI>*eFGzPO9Tt9C0AlTk12ZXHoJM>^@fUw+$JbtR_-JboGI=6EOuX?A(AqU zCtMm(%L(@%9Njfo!p-i;sDRVlS=%m8zWa~PmUsN^<3>~*ZOcxsk!ov%j$ogSOu}2i z1*N*L)5l0t7JKW-Xve{!G$0NqfgJsd6b2t&yB#*@(rEb}UwHXFnex_>H z>vxajki8l+<-Kzn!$rl?Czm&>W*!*nq?pahjOF~b&HCJ&oZD43yi`%aT7ALwyURPD zUZ$%R!e212_eamI34y}i1oQ`k*VwV_{XSj^eRT#>TlMF}rgCg0f1DvR(I{bW681U6 zUva+n^Zc56vH?wt2^}I8JU7*MR(*!{7T5nvGBq7UHYZtYTiKP55 z+MQriE|67)<=92Fx+O5X_Ge6Pv$+|E8e|C59bcT2-BmyR*tvr6;Oma=kv;9ded;p3 zaNi=o?&xmXi`3x?+1JKnP1>28k^Szgn_E(41>rF4;;QSzJ2CZJA2}mskEn$Fp`PMP z_i=IUX=x{dkSQ^#JrfMAZZ(dqHB#YM*xg&yl2jbdTbOwVZ9hTwz&J}9{iU>Ykt|v` ze`vhu*}~0BM@$+0Q%S=zlAehtRBNj< zXst}hoeeXlg`lATk3(zQOLQZLYN^jXvcT_qcGb)gvdtnA(=OzJNQ-6)!<*C4{+5Y_ z5I9#IVRzQs;V?t{8(W3v^ek*CgH-Kl*$DzfXoc$!U*0IccWQ+_I0QJ0`9Sgt`YAtcO{Cw2L_n-m zi3ov#46C_8>&vZdmEz_N3Ns$AVWM?8w`^<~U|gzA%1>9RKEQvz6_Zx3JAn!l3z9qsRJH+NSg;nMNv1>Thu89+K#JfhLZA=Y^8Ek7x z3aN1gO`D(ciD^7HHQBfRCLQIVmnc1_w4T_=bxNo3O(>+4lDuOKS=BS8byL}9YEwX{ z{oXC_4L41k4f|ve=A%GnlXvK2ku%^NkSD%~jJrwY(uy4^J(a{Dz7kP;dgoxj0kAv4%qR zpL^XBefaFE*z?4yAtf|tP#UWu+fR!g9taQp1!Qm)3569~FBL{bwdTyAY6WA0{fzO0 zagcrsrnyv61gu4Y_tl97I1iK~Oj!}mrcM0$YS&&AZ0x5MTfV`dNj`p9qM;?p`5*jG z){QnGf}!0bp}*TI3fW(O;sg&=sd?z~8Y5|Bq4wqT4laoRMI_)?p1veU9m6s9#^byB zmDlY?1{rNsO^xsmvfi%LIk{Np9C`eQ_vKN%a>(MxNYbOk%T?QUdb+e& zZtDb3IrgP3Q{)-ebIy+qJ`EQ*ooy9Jo6#21dAu2q_RA5_pjsKPP zs+Ex?2YVKTeqcc}kE^|3P=~w5r5S#SJyk2Ck8{9IPKdxGdp>Y)?%Tkn*B@qZO z0@4^vu*=-bCR>)>Qx#cA(LijVIl4F(=cu8Lb2ZfUNR5LdyAbhgZ0fju&py;W<4;{) z!=@P_l)LvX@JkLXiev3h&s?P!>|3gWk$L}|;d_c8j{^Z0H7}(X`M=^BGLA1QsgF+=I_45t%=rn0>AF z(g%UYsG|)whlx)11@q3^)+3TvOTTgQ-^zfBy z^#01YxB&NGxNm`9eRwxLb8UlutJ>8t7ryECLsCC(Q8NDoxk&%C;%J55n_ChhMcp#Y zB?SGVRUCDqMH0#nJn)?JUenSBa=qe?`lcc+SsblF%Z&rIZL^DmVNF4^p_OiD7kk;Q z5G1C1i$UuIgq!yrBsf7Fdy8_Xo!?xJ7!8Lubp}iw4|ewc6whVJ9&wKNkiM=^HaOTS zD42u5EU^L&Gk$2t?b;I=)v@wdtBo96@2F&azNs!+-p5Y zY!w<7XJf&h_fAxyg3Tipg_O!P66?mZy9=?_ydKN+yy5t6`qd{c+s-7NnX!Q!K-#gv zDU-TI5^9VJ#>C0o(DBc)dZXbDN5<9!9(1YhG9#Y+WvQJ$2J=a~m_xv1pb5a-OpVS zbHsBAs*|u(yIS^H|H7ZoISetK<#S@MR@LYbNNSH+<&4E5IDE(nG?pwrVONz0$xKaS9A?#V1wGCy>P59 zY+{Avn>b`Zs()P8tSLE?X3GN7h?)P^la;yaXOlE1lM-28Dj)4{{i!0om>n)Cqo{uX z@Dw=!ILu((a;)9?9 z@$ocvpXZ{{zoz$s=yD=0yEHJ8;YW zpkRySfF|m7V0fpe^TQizO)q8p#qI6PYo-JJc_B^gO!+nV6f`TtUBQLA@K^4Vsq(O` z-Byb@l*g}A5=z@wN(om}gZr=ObBFfWkZWxAlLea1u5Mtu=a~Ngfi{5N&f1{>KpsbNG|!gq z0S7ck%INg<_8JG5EtwuvYTR6GEUs#sc}Z?~I+OSA^Fo0<-Ar|sqGpemE+iy-BrVUw z>jP40$j60(VOagY-nj*h{Yd@%F4vgUSh$#BTVm^F|2=t(E%1nsqW7#9?`Crtcrr}d zSmEumTY(MZXJy|HoRWir=^O9fNB{H750Q@^**92BJ(YtsQ>d-7iH2tG~`-Q7^vt%Iy0d+{+SnEL_ZK}`TXmbB<71f(pV(GLNF}SI$=Xr zhKBPDAurMPD1Hx^UZHn^osLix;?$>PH&=m!tvt87B`KhQrYK?jt0yjtfQw`in<>%G zlSq-Zeo_hHVTF_5S~)GIKy@XkIoPv;(JbArH{(ykOW_;rDi(h#uH;T zSPi}w%ac}tfYP`x*uPMYRSk`z|IomQw;~*Mq-q>MTc8P~k)EoaS_mJ6$<9e^dxX8% z(iR9AHX&?9gu{@{wq;C;obg|I@=9fdEep+gD7A#3$q6YMCX-IvPu-+4Gw4pP4I89C zw;-V|t@i~1S?909Wi8+^Zh5Jev#;wlhcZ!>BX+^4~jQ!AuYrkGTwJ&r#&rPYzLaC!^;DO++<58rb!SiUorLj^71Ce^B z%`>EFg2D&O0c5-bJ`sTApL=9c9>INJ6iDeJrHOUZ2!0|oDFqNJalskn3k^aR4ON-@ zKRtA*R4=Q1F`<&Qu@1IO%(+I~F;7dgRA%|Y>vsvXmXyfNi9mhSDg%+iH-J+s7R`YK zhCMJZC7bAC)#i8(Ob%}}Y)9m052A5UpGyk^pf{+${mUmV$uU75mcg8i6GJN#7aE&g zt+GcQI%9Lw&RW<9@cPHTR&u#R(w|(kk+eftQvdWQAhlq{YyDSs^_KEwZ6PH ziJS!0)U%){1qw*NxC``x{UC0&})3((QhX%To2jg69x250M;=zy8vi$ z>R>FIw&i=}NiGbQD{cQgp!grx8^6GN+`QvV({R`a5E8^RZ=MKhgQwIdcE5byi)J13 zQ3j-OEuDGS3^g5}g`RRyJ~Vsn6N;j|?Tt4|hzO6(=&`v1$vy0)HOZCn=12P7F3S|N zJ=Nl9`^edA^zLU?#MJRgZ`!Ugut0E|A7^}bO5ykFX|FNp#d2eP$#az%5%=AI?90p^B14oE8p#a&YmG_K`p-~RfOmj!Z8 z)K?~g2mKq9_g~k`yyO}NBiY>*s6-N8>SVwnh8y6dS{IM`BnvIs=799YUFIxXlI@_n zNdrO?jJl?I`62`cgWp(}McF7b2pPcxj*2mO{@k@qN$NouYF!hdE$m3LVFw#KUU_(z zgsmQ1UYC4)Kyd8FLc{yaKd`*1eRcYAO+7Pk2YvDT_dl4NWzB(=%uh;+ik&u#$t7#c zz2-{h{Tm9UI5ctT`6lak4O*uC*W~`FBxEQ#(~@_+{(h-0%75YOe(0g!sb7C-*t?t^ zUz8nR#(@*=HFvh%DGP#a(uCS%PTA{rLqa#Sv2xf2rS8yMG_8%*_%$1Y16t3lWjn$Hp}1n$~r5=boD86DrF z3{r*L1?7=YI023x#EDT<;fQ2KO{6FT`iy*Vz|yXt; zTiw*{w}80sKYf)xd~Ve|;coYv4}jWdPBgeiHb)ryruYj?Q`bSlTfb*U^U09cjf}I) zo8o4cY9~f)XRO1~6Zxz0?WuGZ*H=;*nQNDBUVrA$V*&6!9 z$~rw^wan1HfBDkm;y{BS!TXohKI!NJ!sZ5J>P#!RkS_ZDQopQZJgMZr-J}dA;*1oB z_?#$|uW?AWwWIA|X1%8)WMyouc*b_c)^!2ezDQ zoiU}Ca5pQu25Bb9O^nds9rLw7opmIS$cA7pzd#(xvBy3Ud6KA;V7fGr{vWLN@t5DS zM?B!-!5%bRBgg*?TK@~*$>|&%TGrAE^&g(Qu`=3&7uS*0;b}wcV%t0EwqBA`TI!VB zsG(tQ#`(-M=J~FFBbEwAL^uWq@S!g_qz^XbD$IfU0ZJsV=@CH^8mr!M);-sQ92N&# z9X2_%CVt1*ZnIKH$a79!3i8Suu?k>3>pHYP#t$aIzGP_Zu{|eFyb85ZqYQ~G(5y%t zf#>;foiMedVJq8PxMecpLhKwS1JKfjLVG9C2)AZ`v>GfHl81?op=PSCIkcEi<)WZ~gQc`o8<0wUy(b z{`Pd*>cEjT7OPs~F^MtT!v?N7S+%W2<4LtdlxGJ|ItUG{eM90p(DD;X`Lu=?|Y>mA7X* zn=J7|wX%7j-K|*KK4gFH_PsbIb3ts#^9q+O#Z=4m!2W}S4fV+nRhUs>yL^7EI?>Kp z>6jyucRm(aj>;wH?ejgBgn_vB*y6{JURkH#KXpZH;P8rDcqPmT{nz63>W&bz1)(jp zzOKkZV(LWXyTyv50My=wgAL`CQ9KensofH(Z`{A@=n{>Lw#I8Wd6 z!KaK24GL1(iOS>Ymb1}=%l@t@;OX~pf1kHw#2sWCj_`zRi;XH2r_vW?)4|$)uKw{u z@QVLX{qsAd1IcdXM7Z)oHrO3nizwS1+n74Rhp;V*YKqA;0+@o;eQP!i;jrjtCJo%4 zL_axPbuQ|3$s+vxC^p*zt7`485uc1RPu#Bwrj% zib8y6uj#(1V_JwoI)1b=`sq~-Mkr?h>8D7b*eqHwi%xKX((QZl9R1y z%`s#4VqWn*^D{D^*lP~kf3=3;JOns4p`j*<3pzu4MM7u+O$Q#_v1z&EtQv@=+H0_m z#=VITR@?c${Re|c?_KIf%n7a%o^_Q;rv3&e8G2! zNy7t;G)zxIkxdCjwbF*2PRK(`NA(R5qrnO^j7auSfBMq8IEEatq02)%QMDhe56=kK zy9f=7V|QQ*pqM|xGjpR+!LBbY$BT^@BZ1*yK6rP%3h4Eku_iV(!e+3~QGfu9j#%!v z=viR!6pP1yqV?dgZy60gRUL5N7fusa6wNeOFH>m_QI@b83P`Ms)xm~snMW(5q2@Mf zd(TUqFyc)MptB{QkY`?+U!)O`edh{5CsTIA;WUrC_buWWU4H4ICCLuMhK)b2%!o}N z*r)xypK~MQfB>G-q%OQ~Sdzo~D3Niz_rZ&+^!NYxiLws#p}lhu$Z>D1q4QM%&^<50 z`XU>UJHTyA`nGkC_Br=OJ%W}L##0&LloS*Zh&DJAh&f&=Y)>!mhWh&sm{#yv^WFC=Ah=X-*^OTY}Fh^B;3nr*)` zL8e%=FFL-9{*PB5tFT-{U|^apIcR`{DP}Fd98FlAG`Sa_fkA-ykQ(|+k6$U+vgn$M zLt{~BEI9?Vw6x4FxuyPctYTA?97gT1%BvF2s@g|3Lmu#h-txhxi{yYO2%8y% zl1ICRWVM1onWpo^h>g&lhqA8n7E^y%C$o9SISY}X7fz-fPn*P3FQ_Qzu`-$b8$R+Q z5B<~n_m9dwW-nW(I5iOc2@UC9awFc*l58DtrVh{glmhQ0S!lKfYD;oHw1A(d!sV8d zWHssL3qjouGwH=W`{((sZ@5tolAkdn5JV5zzL82Oja2dq@!?)*OeA99-jCK0*tL|M1T1=&No%&`Eau zn6;f+2%)vM+b{U_?I+HOP2DseQ=*SvP;i34Q~eF|G+HX#zH@b&y69vYaH;LZaN*IJ zmK};9cYtQIHRX1s)a{4s`v3FM?!WULH`Y78(cbTvyZzsqv7YgeG0!w~Ve9)2SGf0> zTb#g{e`qh__?I)-yjPAwm|G8g-c+2jyBn$DK+Y3{`eJ|b+$y$bN<0{ZG@}SXt9XtT zo^M%`u){URJ(vJs$?HbTr?3T++XM{FeQGZ*5d??_W|c zgSQ!l1OUU)2DGwnr7Qx`p$+lO26z+sZys3ha|w})Tx|`^HgB*GTgYti^UmB{mT%oGb0%$I)7Dg*bR;UIMEvZ z|4p|YqOZU8AT`3%_aCqtW$-T~3d+C8zUqJZk*9U{RkJ~&k8KqbwM>VSh|+4CyPOqM zhF9DpJq<=nYr!IeEgV~Soo)hmgm{U4rTRho_wvF4T z$Eed6*6zc(T3|x)EYC{q5IJHOr*GnSz-ndgK|Tjl9;plpVFS&+C`a?^;RMk7(B$|I zcD97TW;7&*Yt`7bjKU z%+AdH+-TQV{L>on9KMUSCZDd)z-4-N-0p5Fh#U9YSa*OA!LsI1Py!gj@ty@)=^IU) zF@(@4NeFOqoFolT+#xOQoDwb}xe|VP*U1(B-0K$Qh#Rl1g-1sg?Hm0r+lYYXAic=Y;4|yVP5+x*}O*hE_S96jI~j?*E$rW zr@C*s+yN+OZOK{FgV^fe<6~BR6YcB~ z$j^}>rv)C>#1IBA>OIH#Rc%xb~$RK+15%(-gg7Amk4N1EDncO z3GaI_1T}7UlR!WCw!04Vx8A9m8jd+sK!I~?&JUo^S5lKWc<|f)=&|yvPhIdn+5(i! z)GkO^m0mCk1#KF(;KoAE8BG|prR6q03N9~hlNBqL;7r{Kl<+qN>TAu0Hg70M$7^CU zL=kEofBNX$uAdqmee+v*YrOT3>R0^)R#3y@lw^1bdniNp_9oce*evjhBTH?;$u#$b%*n>lFtqq?Ihal% z`wI^&@Do?orD%Z91$Xn*$i;;an$u{mI*xEiJ3?~M+_=DqXdBsS*bcckqanBq#jk2* zP07H5CSSL@;kX}K8JS}6J_F)Qb(2Xk2G_#>=nC5NI&os#vu2**WXd<(uvbeV)vn7? zyg~;}I66>tr1M{mG)f@XJy4;rdHWW!?VgWH*KSVd*3m-^cCOAyLkR<>W4QuaxJYP=ZxuHUz6~p|KQ6j)1SkN`H3Y*@ zXM4tWy=fI!^{_kSN0AE53pQN5;_$A@JxT0c74rf|T?!&hrW;(bp% zxbj;}Z!e=G7a#t~+R<-yIane@)ucl(TbFi9+pf}>&u%DLn>W`;{At3l*~mnZWMkAq zjYsv_l2%E?t${=$j$Mcf$*pnVSr6ffcxhsgl#4TigB6BMpp;LlmI>BHQq0}yG)Au2I?63 z_A1D**Saz^{jKZw(v6E+M=dc5b)3`vtrzA1n>l`>dcU__+z6pboK3ksq zzl^aT`d77$f0r95fwGdSH|>^s5D9Ju(u@_;1X7>#N}aH9v|gy8Xa{(0RL*#5bS3vR zHymB}drD!RdULc&g$iZdrJ^1E`DgbXrtf~qVY^4~1;0oRwPmBZ#)Ia@-^c<3!@uwJ zD*e%OSBq^KWRK_ei5*sz#YLPAcw)W8Mx`&lVGm;i2sAQkWn)_>zJv|FCfhkDs)&H=Pg2 z*G$mEiIsJ6_Q59r0b*e~Sz!@WI8Xr4dJ$Dj=Pb0RZ9$0anHfTOv<2(aP^Ou>LT>uU z?|nj#wq@=~!Fh{0>WR64m3A$TZhFv2wUZ-lha+{#B$OG?bx_#XDc7R^W$KD%+OwgP zR4--tbOeLJ34ULnaLv zzYWw9M5y_Nrv@X^#SG>WYCKjTYw{ROgX=q$RSMW+eCkp)?x##F)00XRVLYF#dZG5& zfBBM|1di&|=u7kn+%_%*%w{ZD3x^zD^T`Wq^t&gQ+jdFfwm-X+U}RxtTi7UuXnn%Z zR~}uW7gv}MAp$0VQ)^vpYponDF>KS;S2wj7Ai{C*7~{Kjt(8o>MtZ0oGOp~SC{PH2 zgsLcL2U>d8_N?PQO{nWnAcF-dB_;L3HBMfdgVpEN|K{*5wC~$b{Y{D{jHJ{sQFl+z zV>?~go%Oxd^W7fIeixlkhq;!%MVV2$FkPdk;`Xt9*)%vQuWNH9;??#p>L8(`fE{_j zfo3xo$=%~bk+r`K5Ew5+p147Fo6Co)G5kNAdf;{M5_|ZDAA9I?>e~Kk(W8*VPh{fG z$L-c|Qs%uB*#;fjpo-67()T6WFXDai@-U@)n&Xc&(PhWB{C&>k4!zwYsgEWc?n85} zkR@iJ5KhqE-}BPLLd0V`B1n!FVzt4+fWC7Kp=dp)S>wh(|JX&*v^x}GkZWS37tow? z6&BUtm9ZInxpD2E(@Lz%o4V@@wl>m3p^W#;Y8tA0#UThVG6(}NUxXcC_-r+*iYP~nXaHlC)EDm(e2A*gK_*T7M{%NwZUvUntwZp>Q;tl1zaW-l=cJH7!MANs|Tk z@?w~rbH-Q@;8dR*qetJnedZyP94zn$s`2!#C%^7hkIGj^e*BY1X*~LP{rae9#5|u3 zQDP8*@!Le&O^v@o3WXFc)TC0H$Fdy)xd*Yut4+$~0r_Y-c$sYz`!;RueGaIjy-VqV zQxg`GhUaeDHOoKuB{xg$?@c$L*>cTO79%v;$n0-GQ{-lU;LKIJ|Dx>*%QkgrbN$^b zo%kG&BQ=wx$iY|MvX`{*K1q>=^8(FvFE;oNwlo+7PPXwxH;T+8JKC9chPB!W=48c$ z0Ded278n)K|LW#AN(Rqc{x`-8=)O4#)04uD#VXu@7NGHUnOQ0tj!P><(HLD%6ZInf zoG1Kwe^Xl;b~NniaJxFO56xmTbFROfHWPCgUHEI1f?Tuhpxuj6V3GpKN=yuIH%##+ zIWtpAyXNe>nsU}r>W47O_P+b;HtTr;R4br;3JS^S#+aj5w3jrGZ!J&}Z% zJEwo?=GPq70XOV--hn;zD^4A3qixPI7v|IkrO}8ciAe|78(+-%;kMN7dF<4MFE%$3 zaRkey;LIkX^kxyBamLN=P$RB)z+9X@CKx)JLJ^F6b=scET= zx|1NI&}PwDn^1Vp5x#nSWwR{UnNxttU~(|risX;$IobFe1smsN7eP@^HTmR@W`kiMkQQ}haFcvv$P*9aO)p)JNM;cy_jA`PG z=Hi2tRFXSq1Zb3>9R4X>>>m}E7}loeI>>j?&ob{Jy|4>g~tSwtemEsP5@WM5E=gAc?c6sTc1@;Xj zVAic9o$>p~iUNYU0h#aGH!lozUN&AtqK*B`gl3)ZXp`jG6V>|+#}wkrxF4*6wgIEz zr~=dmjxKZ{J_~?{Nb@Mw#2e&bhtkKRU9Kvct2j-x=5W!4MkYCNi}B?p9+^^&2!RhZ88ck z{erO028T8arfxSyAGD3c|EXPai5K68L`{9>{A%O8ZbLg#HQ#x1nSb%|D+MP&-d8oa z7f8fwl;~CSjVyCx-j1|IXa_N86#Z|7?>RclHYaY=#UO^r9Vi#p$Nc#6W|0#kZ8sS9 zaxYXSTOE%@WA4(kvcxe4#R+B?-++}SLcMd%O;?87w5^+S2Hn}Vwc^^cEHym8VA^`b z8h4+VhWEUB@IQnAOp@6_Lxn)`i-RW4^fkw(LOf{!@bAjj7Jc6*pP_&D{uA_cW$r{@ zZA}9GFzvvA!KCCh07UjQjmpjVr}%B!$22q{>kivkuY@$+d~O`lJMt{gBidz-|4jRS z9D9d7Zp7cwsWX_>4mnl*`#<;igUgT6yMKD3v4+q4e`x=%bEt;#&GoPOCToH>i88@+ zg9X+NA4vDo5}4hUx%=v!7s(&9$!vavrnRG0GkXfBTyq~k_d_-Lz?__Kwy-Aoxb=8C z97n{x*Wagf+UkS7hr{T9@$pOiIhDClE*EAj4kPi3DQY`6#~O$oMG>U%0c(8v+Qg!U zp?zGmiWboK`_@JyYB1Vk%j;K_Tou==6-;))7uTb}`Er8Egc$BkH4rNyQ7jjz9{F7~dImvoMm5rn+iQbR^9GJ0# z>&myKf1aqBiPU{eAt=1x9Eycf`l;SfhQ?k>m!*vB^AA_g_fKCr^X?1u?sqrhg#)cj zM!#7bc)2H)Ww`7T(j8LqvcSE;$ie73H{Rbg!T#sq?scN}XiQ_bI33uJHk!@ulk15s zoEWh*2~3nzO0$E|LvXp1(@w;a@8iVf8h&`qktKf99sB63Zr!g8J}9AQn)cGSPW%@( zw&phV^v zoMTt40;<1D$6S(MN4_bP99GHPGjf_*!>O0LP^T2}iF=ZVnlp{1HeK0wYB?#OLS6PF zWd==f^Z6zw(Vic?N3;#%=tx)UdwywVp5OA+SHA8dK8%7;^I!Uw(acP_yFT}da6pAk zKn)i*FLN)zsA)E$?WlrWU&b>=6|SDrgDx}u^}U1*N#d*Q{fQrmnB0D4pX?-YB1u2p zN-6D;iF<+we5SRKIEVi5F)D&Wga7gqmt_0o3->REwhnwpc5bynD0QyQAtjYjCh7Fr zSa)LO$+UU#f5qp&w23}@ZId6nvSB;76k^YsHil_{TyaTOfd`#PY;v^Yb-BQ&LQNWrj5SIVX^NJzC$FW^#dEyVy%h26lI`j3QjfC0 z=n??ZXGws0n1iPzcBkQSUe}xM*iT<`>pnTq9+z(c6+4_-s7z~_t9JkN<#qbVxm6+f z91@G_s43<>wk#^Ge4^EC;0VJu%$HOxE?YQfMxu?Oo%I;uK~Eqp8oolG^oS$v*Vrn^ zfH?uV-6QT7`Z#ME+NI{)&~|V2OH3KB#b>wxv%mlTXXIxjQ(2f(akgg4sT`h$)tWIR zK9Km%X(MD}(CSn_nzlBE&MBYMSLvQ{MH0ze@mxvw1OjNvBoK!YiBcvdfszqMIH3~_ zP9~h7l48~(b=mK)|NWi=lV|_%fj7T-BhN8O2HK4e-CO7KSL@xk`D{#=A=(B{{k~~# zcZN&nbWJa^&wb$QQZjc^!wlU+Nq#0r3Ac&0=}2{4OKvh)+Z`6k5mP+=P1)Rd9-(B7 zaGQiAm`WO8z211!9{&2<_tBRe-y6rxIE)1&4Z=j~h}xPsG@Z~>)sFt?g*AGtGJCOJ zL~5!vE{w0zwHR!dH{&RtyZ6rX>yGV~jV*x2a!A5N8G24mBUN9LoISvLSRMYsqrPRO zR>9!2rq#SonI8i*;^}E=*G_2DX$n2k(M8f=dP6C_7k687i4#e$RO0t+9B>|^>p1!HTK)BRXK3@gpSu4~ zPSE#$?sah-(Q>iW)AF_UOG+O9oywjyW;c=u?SV|EGcsfHaU}~f!&ip z)Aw@fSKpIn(`2d6+s2`+8IN*2-3V>l<<4oD*eq9~|Kx1n=?NV+_FTgj{`%YY@vDyP zvh$;r!M!8KL&oH)&Effe>gqZ@ba9=YTiYO%EzUK`@I=S#MYGzd=%$mqYpCs|hZgCD z`xX>Vk4zI!w9D{zQ&H!b7;$p;F@YWJ4_?t=f8a9U}hXm`#aBEDepV8Y?I>q z0kS{LW{N|VWn@OaCQ;}zXsErdnFghcE`+9~?Y1L4xuZZ!{IkPkYa8b4sUFqSbv}M{VVL~jiNE{$OMcT#eq#2}!R3uhm)=?3=3ia^ z+!aDgXunMJR0;i;tUcS&6Vz#HEu0hzaa#)I6wD1K4kq+si6!8H>h;j*rQ;s&kcyKM z&X(qRC`q3>d8EgS3HvNe+Y%PDa+}Z31xtz9F^@b^C+zGK?1*~$>SlSz;}@~_S{!a) zciVn?Wo7cvPGrZ;)+Yw@f*_Q&XBLdPs99tvWwg2}wZ-uhg$hrvZCXJF?eh@gy;OLA zjUK+dF2@VqQiTilT4Q4gY-|9kEwk(4#U5tJHjvPc@cOg4o`%2S)Fm)8z4!Do*~F%~ zU_Em@G95#Tmy%W`7}{AP;<=%AI)`nvXP$0-8^?yG%+yM2&eS{D@5xm3#q9`<5>tl> zM_ueY@FV4)2}HOQ%L2ehwZqgW;RC|MS-+>MNPqv#{MuT+uVd_!58k+b?AYZGPX_)( z?cZxkB!y(&nQot=Qzc6+PY7vj(*~a&E^hzYKr*xEc?~V>(5V}A@j!)}FYZEAl}&Eh zv@lu;Ms1Fcu+>WRje1vPQB_@tLb~RB`6L1w2q=yCoOG=(mC5-RD%5`aw|n+4$d*eNQ!vHl7$StsifYq+v-5P<-g$YCo&>7zyl`!kF6b^TUK*(* zYh;d?Q97i@PW;)?=*GY`jb!WU)xyw&f497U7wzJ zK|7^gTZfW^pk(cvolo|ryvwRIVH>GBF%IG;(vl!Z2Cp?&+-+D2W!oy*JtmILUEU;O zo@Gw7LJ=3uO>CPdG&o64klyd$!l1uW#~Q$M&#a38>m#;i0;<3I$P&Hiz@ij#fFo>_ z<`Zp=ORTg0oPK9wnob%Q>WQ1wDJBj*CgRVisaZ7g`_HThGb7dWfs1R@!O|uV8}?Dj ztYeLkRZJjtAH?TD6TS|nY?3fejNe9=R{{huC_9&?1h~DT3FPF+b?Eq+9Bb`$J~c+3 zX?)o!%pop_dgxs;Px<{*Hg~2qoYX{*R&oCC4;`d;Jn;J0cLD#I=`Nnwb?fqx%}>3n zj_e!j6ugM2myp&4o|-$#kVEd#Gpuyf+zld}PKCqFYSYq2RXY2_od0Q(Jxo!uKD0Lm zgLqh>t3>IuCjK#|&Q|73^Jdz)v`o;xQA1G*lfJ0w5Ob3khA0wqT)o(@jcAjAK^*sY zPhBDWv$63#2Nuflr8%iZe((MT*}D0n`V)?^Zg7bTOTwNL>+ht$CC!LlaGZ^_53;f3 zp-ZduR5c+UynL;Ikzr2wq4z!-pEKQ#JA?rBHM8`nhU^mYmOxoFjpm4g2@?}E$=;?m zp{wGo5(zBKp^%z`l(&nvLX9XLAs9Uu+U6u~6W8ox2YV4F!;Qm`1^=|~%IPWPWMxs| zvL}sSsdAMjqxr@x&6aW705<>&4%~U6 zDd592lcq^q3c3*IQ-C|caCO;|E{)lACU<%GY%TB zIjkw%#ANCF?8+GbsxReAG|Y>bF+AC&Y5-yH5Hox?rodW;9YWhB|BMOF_|!D zB5WP#gi@NSrfG{4VFC`x3o4pSa+DQ#NXX0L{^~@c?&1&)#3e-#lR#>Kp*D}0H>Gf4 zNkr1AR!(V{CF_aC5c+32Y_SO$Qb**Jwn`eMd9XeDk%^zOGs&IVp8l)-56URLPsW(i z8KcA;wXTOB#{Taz*=RJ@s1yJ3WYh8?AhJMbS8U>mE{S`Y9DSOv+kt}$UMJRA74Bel zv(D^y`)Vh})0TF%zVPG2gx-5%{6)*O!!_*mA-(?fTQdub57rUg(Y#iD~ zZfxJspQ%h1Vk{Y4xX9)b%F+Df>y^JX!zcFpWY!&O>P}2ia4Y9ORnGfML^&+>PHyonFyota6Voao@If!;?F7E}8)G<%qsb z3tVRBA%98Ci*k?g!E&M^$ zR;qPT8b3+H@Y}Pqbb{W@W7^@`nX&8j-0qKFn2g8otnGXx25N-hRBUhM(oJk)y2d=t zu{wle$NH2K_jAb;ZL`5tl1UW|Z*3M6CdC~hjkww(Cw&tzj*@ojoJ1XWbL+F48$NDl zMA%MB7)HFwePg~?&rNJ%*)?8hj}kXHD&e-VMLF7WKo&CbiO*ySji&QowvEaAhq)6&~7xP@=7 zR3FLv%Vg`lC%)#3uF&&b&&$~K<~NUb^R5R=nY_Ke;R1D1UEXE{O=+REcdp4i2bXM8 zVv?IMxwH5EJ3E?fvTMxM{DpsQ!4@p5Bs~n$3Qq z_@_dH(~d0WE}0@}8ts3v-8=SGm>{Kar!h)N7GHv)yaO{N^bj+j7OjQ*l25b=txh6( zuU$AK5BoGRwE2&9;xcsuC+Wr^AWBmRWoph#(K3*JXY@Z2y0zqURl>=oEte!53aP11_03drja2Yzrp8 z>7H3b^Cg^^Zqs-?u_P2B4@}Ht@Lnl76eog4BL0B!*bd__jwm>Bx-Dq2h17JF)NgZa zIS;TAY{?_$HV0wh4i4v3FqBwLf6h=tNGdS(g#DSe29AT&5O79wL*0eepo@~V2T=;Q zl%##F_bz{mYZuPL=9LlX*(?;`AIZ}Ro)q*LoiON0pXiMsX4(`kKm$1W!ZD~+EF zJaYFs6T%TbE!u$?uim3{U=vPZBeDxwB1NxoDB4VPkh-+R_QZbpOLXK*z@>;) zoW(DX&B>pw(CC~#m25-nzAswrY4(96Z>hD|AdrD97kXqi zplcRuJK$!(l;(*I*_u{6(OHS^#iZv6w7*NVqYUTJDr`lA+ie|6J2H3LN(~7H}+chEJbeKT_D2+GG0^GSvxf>MiF-!3R)?!u|*^JGj^s3~O^gxu^&_YqLc zF;j0Z8o$IH%xzsv&@@P2(s;|X0Ucg!XN1ghc`VJZKr}VwiBGe?Bz+@#D4PT>-*oNG zsps^@jg)9w^g7-5uXle56{b8(K3CpunG*DNuL;x5>d$$&{`roj_4)gs&taCY|2kvW z>*Tk;_(EO0U#orojoSE1)XX8G6y$Z+K;ei%N-s?S7=zn{1zm7(BD+=2LCzG0*0nW09UQx7#kkl)i}Y7!J#uCy8ErurJe`v(p?y_FDd zY%9Kv8Oaoc!|+K)=}ilF^V;lx(vY4ip9Yd9!LgK`oTWq`3T1JXCWrd*%5o)*S<+XD zMr_fhM0JWv#45W+H?8jk$=2~oM7VZf-L6P#}cnhCSK}yX^h{q9C^Ii zw0Z}3$xNLz_X^F(hAn zXb>~?&7EdpYlLR8fj5dEG6!SH?_`RdOm3pEBNiip&?#DR7%LDRn}T+kF|%mKFw8bk!tv zr>lwmUDRM==oS0Ggj&!w?+{)qO?)JRXhsl`DYE8&3uZQf)X@$jfk~o*SL)mTbVT$E zyLON7r_bO8pT0hO#;(`NH@{|WZWw;6(9BQQ?mbc8J~n2ia3E;UiceG-B zQ)^%R_eM~q`L}jt)e@RO|7?iC!46Gnd;b~X5Mh`m#rylb5fj=a4qBk(;A0O< zmQGXdpc!O7I(~HPm=4TfL9b>X=6s8#?&FD zlhmWE#dC^eYN&)%Cx@o40w_G}opYuwqVsWaSw+6IhEUwu(xgTFr(_E`+iToQ6V8m^ zzk#zC-V;QOL7RsBq zaGf2Cs5k@LPp7UBoL#TY-&1m2oO(g)&!&shtd&NJ#eF4t{(O#I7?e(GQV4)FM5bxM z=?J4VGuG~wUA1+-KFO1ZpIQP6$p! z8xyslej#VpwvfKiz9>!EOi6Z^(rK_dn-P*}lFm(VjhzpwIx{s&j_d1uX#?vekw;WP#Y2Hwnbd-v0j3j%Mp76>5{2<#tGaP7ve4o&5&WaKXHBC^#EZM0;`4!r}Sw!jO?f&2) z>Xl+Nm%GFrto9!Padlqeo<7dnwdqVR@Ln+nhg?+ie`vGk2#2T4#F^F-^MhWz$~Uv``ZQ(GYF#iniP|NDx|^NV|DkkD8`V zOzAdiey5|Rj<`dCcbX;>s1m-9u}saGtHcz#>82*=+eYZj8cfYeD4@+jg}Iw?sW0B| z4#996e}fIwjjbg*QOib3D6S=y38SFDN{O57e)6Fh+=~IbGiQb*v!B` zKbr8bJ^6KC_$>VeufG(=#&zRQKYV(5g+`d9%rs@!9npZT+3LnO(&ZOwzhpnHb|ILDqy@W7jfe zYbQH}-AOPd(xT018?B71LcCDd=>0==M(>^(|K1~iPMdoA`b%SMTsQv2{RcN`{AIP- zZ>jx$RUPHQI@X~l2I>~GB;x24kxhE1*Rwh3lDgNn`BhoVG?fr>VdqtUnSJ0y=Uijj zqAhIc$6V0NB}%)W+M7D95pnG_I?4!OipkP_Y0jqRZf8gx{6I7{ zfU*OX?Cjrg?VCbS*8L;^90_z??6rFWVbpoH!>)~-U-G*G3FQbwYTNAlL>#3F*c~-= zuko3JnXOw1{Y;^|Hs5LNlpUm^2{tFCoUC`edtm;BJvF5D=kb0&-}RTu*tl+d`+FC+ zF7J6&^%=jp4)t%>SKLrXh+{Cglm5wTYeo%skg?O=PCwI9F#pcQ{hvL~E%|%i_KaMm zU#zJs!@JBi%8XMGcFGClOkiz?!tUDdwTSzY!VV5))_EnA#4}UyPGgiW6Vq=e!jK(t z&-61pTv>2=E3Ub+*@9mu)WZo`^D-rf*i~$ti&o-3J9L7lP(wyMB?*1*&9l(>+fj*9 zTfB;>6Wk3!Idg{4+fg{SQBO}+M~N8cO6|@gwXNTvGX8&;7S|v6v&O0aYu8^oW6SmS zw~ubPc+XwqIeufAly9h0c2Av#{Xx7=q!FNKn$NCGY$-@nvcrTZ*;=K0G+k{G`Zk-k z?&H@PTDEXUvd7x99i)zTsY4`e%qhqg&`&$A`of0CxgY6Lrrq|*)K8+8S6;GRNi8(C z12IlJfAak8xE^S1Xl->)IG?PNdZxc+!OWb9Jom)rnYM-NLco>Ovk;u_s46AHXG(Fa z{FG~GYPtMVv*Mp+NL|vJrW;Eedlpu<^wveA5DaRo{(idt^^tndFLBv=kEDtI88XT~ z+v~4}vF*C??e`ztSlN0-oszGs!+Jv<`t@~Ci~WL_W$Mxve-C}_umT} z1W$2a%!_?$Z4BfB%Lmn)8+ez`59K*;##0Gl!qh}@woM&L6ryPdSmxGla?>}3Qn)mk zD4pHS?O{g_g{A;kzVCTZNx&y(Y|fo&{hnB4#>X%%oC8tF!8Wv9jnYT!Z2u0A%Wq(p z)@Qq+mp*Mavai27<5Y4t%BJ+*)U}pOvZLcGjn80t z)?)JZois>O5&lq{lju({R~lRwH|Hr~S(=LN1Z0~@nuQGwFo^5RRq^V8iL6(<7>V6}dJvNH9b(s=S4 zJkSSZKlNv^_3|%r{naxzt~%h`f9%71S9x?d&Cu)XXurJvdqI8qq59o?KVjRURiqe& zqiWkIlB1&W-_EvBtaATdcdxH8HR7~!`1=f8Qc2oMeQ%VQmZ-C}n_^`5<=6mYe5Qh_ z{Z8CCsBB}Rgi7)Vla|^pz~r9nIqbau(~M2&`I1DQs-}3eIiRxBD{qYH<7gC+yLqvV zGdufu%7+mBq2&E+77|Bvp^*sptW9kq)4c7lzLXr8U4(?A6_@+l1Z}b7)+!Quwto0= z;?XI}c7qbSIB1G^gPWb;9nh^LDU) zyGThJN$0{&Z4x;q)cW2`^^Q18w@8^evSfW81kjUFNzK|PO>I$!0Q}wF_Y|XTXdv0t zouh_2Wg5~cQd~)yJrEvsLTG#c?Z?nL?!56lH`HJ?O$j?n!_tA5JVsK8ZcsjiDsy7_ zjw&T zNcM^EYGX=alwPZw@Nznikc%-z?pG2U6dlgQG(~7Tgr*dla?eNx={w;PK7f#v-FtlV zBMsh`{Ko{=XW@YB9Eue7dT916zg6`oMxB(#aE>U$p#J4ZC-`{(2bOueX=c z?H511`wGt-ot@+RCgaI#%P_p8&f5+3+XGbq?yd7VU*~lQI&5k?92MK`&5|HxeAbP% zBS`-sXih%R_vILS*nm0GQjBO*T4Q@-z|G{$k+vJ+OxlPOgzWT5(@<%I6smX1o+k5A z5Ot899QtpEmfTMjW#(&wAkA$?0o-Y7^F8?8Q=*7+8%#Mj-e$C01M1hn1XM_tZGw=d zUOMHA@Xv?mF-;nB*-5xHt(noD z(R_3~nx%gS0ci&~y(2_-#F(I9(#C#6%U+>~pm*k5>&{ z;r8egvvYHgmF2US=FShRk6!q|rXN24*LnTTVC;NVCV$br56+x9duDEK_tN~h%pa>^ zg}drHys&;+jAwcE&La7s?}KW* z=Ch+_6Gj^pk)2?GuFW#71wjkP*FxgkGCB&Tkktj-5rmV&B?=~g@wn4(eCqG zr%tS2|G778e)!jaef&3pne+8GkFlq(%INgB-aXv8cH?OI-0I9sxwf#y3)fG^8#nVX z+%!!1cwLxdb#0E;!R)UKjY9f+4R)taq~HB*OK|H;o$UE@(*OB}w0BcpK!B=)4U zM9=^)S3u4 zapM$Yq=%K?xuJlPLxfFBGj-^P$hk9cMAGPeIFHCPaEglxYLSgJ+?Xs1gih8yjdIns zrqZftx>S&lO!?aIWShJ=vWUf*g_7B*7l3Mpn+l`LR$cV1i8Jz~pv>1=?dFBr#nbhj z&(_zS+!~Kh&MquHHSp58ty3H82M#Q5J@DX}+Rl3>f72Lw`udyC*gIX2(Eqmf{oY@+ z{=bP{L=TQm&yVKkF0UNe;^DxsN&Cw%-Z${Dx32205tUt)I+m*TuTY-n3(F3)`MU74 zHO4qwX{b8vCbJXlwc&~fkW+x)1*)McH!H)}PpVWHq5|ljsZ>SQ7>I-=DF+OxxZ~dMfUu7gUtcl8cS@*>qQ|i{jUiXV?D+Dc03) T9QuCD00000NkvXXu0mjfo{nUn literal 0 HcmV?d00001 diff --git a/meta.json b/meta.json index 1cd02407..7c953c6a 100644 --- a/meta.json +++ b/meta.json @@ -7109,6 +7109,24 @@ "os" ] }, + { + "id": "windshift", + "name": "Windshift", + "version": "v0.5.0", + "description": "Self-hosted work management platform with projects, tasks, sprints, and team collaboration.", + "logo": "windshift.png", + "links": { + "github": "https://github.com/Windshiftapp/core", + "website": "https://windshift.sh/", + "docs": "https://windshift.sh/docs" + }, + "tags": [ + "project-management", + "collaboration", + "productivity", + "self-hosted" + ] + }, { "id": "wordpress", "name": "Wordpress", From 22f1fe5ee6a5874d2c125787d201ed6377b953d8 Mon Sep 17 00:00:00 2001 From: Benjamin Nussbaum Date: Tue, 7 Jul 2026 23:02:58 +0200 Subject: [PATCH 41/78] feat(blueprint): update name of trmnl-byos-laravel and version (#789) --- .../docker-compose.yml | 4 ++-- .../larapaper.svg} | 0 .../template.toml | 2 +- meta.json | 14 +++++++------- 4 files changed, 10 insertions(+), 10 deletions(-) rename blueprints/{trmnl-byos-laravel => trmnl-larapaper}/docker-compose.yml (88%) rename blueprints/{trmnl-byos-laravel/byos-laravel.svg => trmnl-larapaper/larapaper.svg} (100%) rename blueprints/{trmnl-byos-laravel => trmnl-larapaper}/template.toml (87%) diff --git a/blueprints/trmnl-byos-laravel/docker-compose.yml b/blueprints/trmnl-larapaper/docker-compose.yml similarity index 88% rename from blueprints/trmnl-byos-laravel/docker-compose.yml rename to blueprints/trmnl-larapaper/docker-compose.yml index 61674d23..ffd54306 100644 --- a/blueprints/trmnl-byos-laravel/docker-compose.yml +++ b/blueprints/trmnl-larapaper/docker-compose.yml @@ -1,6 +1,6 @@ services: - trmnl-byos-laravel: - image: ghcr.io/usetrmnl/byos_laravel:0.27.0 + trmnl-larapaper: + image: ghcr.io/usetrmnl/larapaper:0.32.1 environment: - APP_URL=${APP_URL} - PHP_OPCACHE_ENABLE=${PHP_OPCACHE_ENABLE} diff --git a/blueprints/trmnl-byos-laravel/byos-laravel.svg b/blueprints/trmnl-larapaper/larapaper.svg similarity index 100% rename from blueprints/trmnl-byos-laravel/byos-laravel.svg rename to blueprints/trmnl-larapaper/larapaper.svg diff --git a/blueprints/trmnl-byos-laravel/template.toml b/blueprints/trmnl-larapaper/template.toml similarity index 87% rename from blueprints/trmnl-byos-laravel/template.toml rename to blueprints/trmnl-larapaper/template.toml index 6c55be9b..1ce919c1 100644 --- a/blueprints/trmnl-byos-laravel/template.toml +++ b/blueprints/trmnl-larapaper/template.toml @@ -5,7 +5,7 @@ main_domain = "${domain}" mounts = [] [[config.domains]] -serviceName = "trmnl-byos-laravel" +serviceName = "trmnl-larapaper" port = 8080 host = "${main_domain}" diff --git a/meta.json b/meta.json index 7c953c6a..7eb91a2c 100644 --- a/meta.json +++ b/meta.json @@ -6702,15 +6702,15 @@ ] }, { - "id": "trmnl-byos-laravel", - "name": "TRMNL BYOS Laravel", - "version": "0.27.0", - "description": "TRMNL BYOS Laravel is a self-hosted application to manage TRMNL e-ink devices.", - "logo": "byos-laravel.svg", + "id": "trmnl-larapaper", + "name": "TRMNL LaraPaper", + "version": "0.32.1", + "description": "LaraPaper is a self-hosted application (BYOS) to manage TRMNL e-ink devices.", + "logo": "larapaper.svg", "links": { - "github": "https://github.com/usetrmnl/byos_laravel", + "github": "https://github.com/usetrmnl/larapaper", "website": "https://docs.usetrmnl.com/go/diy/byos", - "docs": "https://github.com/usetrmnl/byos_laravel/blob/main/README.md" + "docs": "https://github.com/usetrmnl/larapaper/blob/main/README.md" }, "tags": [ "e-ink" From 989bf839b6214e06cb3400c3d6ede6b623c2fea6 Mon Sep 17 00:00:00 2001 From: Luis <71575458+luis030821@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:03:09 -0500 Subject: [PATCH 42/78] feat: add Agent Zero autonomous AI agent blueprint (#791) * feat: add Agent Zero autonomous AI agent blueprint Added Agent Zero blueprint with SVG logo, Docker Compose configuration for v1.9, and template for deployment. Agent Zero is an open-source autonomous AI agent framework with memory and tool use capabilities. * feat(meta): add documentation link to agent-zero metadata Added docs URL to the links section in meta.json to provide direct access to the project documentation at https://www.agent-zero.ai/p/docs/ * fix: add length parameter to password helper --- blueprints/agent-zero/agent-zero.svg | 1 + blueprints/agent-zero/docker-compose.yml | 14 ++++++++++++++ blueprints/agent-zero/template.toml | 16 ++++++++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 blueprints/agent-zero/agent-zero.svg create mode 100644 blueprints/agent-zero/docker-compose.yml create mode 100644 blueprints/agent-zero/template.toml diff --git a/blueprints/agent-zero/agent-zero.svg b/blueprints/agent-zero/agent-zero.svg new file mode 100644 index 00000000..1d2aea94 --- /dev/null +++ b/blueprints/agent-zero/agent-zero.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/agent-zero/docker-compose.yml b/blueprints/agent-zero/docker-compose.yml new file mode 100644 index 00000000..fceed3cf --- /dev/null +++ b/blueprints/agent-zero/docker-compose.yml @@ -0,0 +1,14 @@ +version: "3.8" +services: + agent-zero: + image: agent0ai/agent-zero:v1.9 + restart: unless-stopped + expose: + - "80" + environment: + - AUTH_LOGIN=${AUTH_LOGIN} + - AUTH_PASSWORD=${AUTH_PASSWORD} + volumes: + - agent-zero-data:/a0/usr +volumes: + agent-zero-data: \ No newline at end of file diff --git a/blueprints/agent-zero/template.toml b/blueprints/agent-zero/template.toml new file mode 100644 index 00000000..bb84517b --- /dev/null +++ b/blueprints/agent-zero/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +auth_login = "${username}" +auth_password = "${password:32}" + +[config] +env = [ + "AUTH_LOGIN=${auth_login}", + "AUTH_PASSWORD=${auth_password}", +] +mounts = [] + +[[config.domains]] +serviceName = "agent-zero" +port = 80 +host = "${main_domain}" \ No newline at end of file diff --git a/meta.json b/meta.json index 7eb91a2c..23dff7cb 100644 --- a/meta.json +++ b/meta.json @@ -122,6 +122,23 @@ "project-management" ] }, + { + "id": "agent-zero", + "name": "Agent Zero", + "version": "v1.9", + "description": "Agent Zero is an open-source autonomous AI agent framework with memory and tool use capabilities.", + "logo": "agent-zero.svg", + "links": { + "github": "https://github.com/agent0ai/agent-zero", + "website": "https://www.agent-zero.ai", + "docs": "https://www.agent-zero.ai/p/docs/" + }, + "tags": [ + "ai", + "agent", + "automation" + ] + }, { "id": "agentdvr", "name": "Agent DVR", From 091ea7afdaa72a8548e22f69f014628d9fb79c17 Mon Sep 17 00:00:00 2001 From: Vitaliy Kukharik <37010174+vitabaks@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:42:13 +0200 Subject: [PATCH 43/78] Update Autobase to version 2.7 (#830) * Update Autobase to version 2.7 Update autobase console image tag in blueprints/autobase/docker-compose.yml from 2.5.2 to 2.7.2 and bump the corresponding package version in meta.json to 2.7.2 to keep metadata in sync with the deployed image. * Add dbdesk_studio_data Docker volume --------- Co-authored-by: Mauricio Siu --- blueprints/autobase/docker-compose.yml | 4 +++- meta.json | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/blueprints/autobase/docker-compose.yml b/blueprints/autobase/docker-compose.yml index 13b23d57..7582c9c6 100644 --- a/blueprints/autobase/docker-compose.yml +++ b/blueprints/autobase/docker-compose.yml @@ -1,6 +1,6 @@ services: autobase-console: - image: autobase/console:2.5.2 + image: autobase/console:2.7.2 restart: unless-stopped ports: - "80" @@ -10,8 +10,10 @@ services: - PG_CONSOLE_AUTHORIZATION_TOKEN=${PG_CONSOLE_AUTHORIZATION_TOKEN} volumes: - console_postgres:/var/lib/postgresql + - dbdesk_studio_data:/opt/dbdesk-studio - /var/run/docker.sock:/var/run/docker.sock - /tmp/ansible:/tmp/ansible volumes: console_postgres: + dbdesk_studio_data: diff --git a/meta.json b/meta.json index 23dff7cb..e94efea2 100644 --- a/meta.json +++ b/meta.json @@ -561,8 +561,8 @@ { "id": "autobase", "name": "Autobase", - "version": "2.5.2", - "description": "Autobase for PostgreSQL® is an open-source alternative to cloud-managed databases (self-hosted DBaaS).", + "version": "2.7.2", + "description": "Autobase for PostgreSQL® is an open-source alternative to cloud-managed databases (self-hosted DBaaS).", "links": { "github": "https://github.com/vitabaks/autobase", "website": "https://autobase.tech/", From 1e99d46afe04d6856e402cc5c96ab3e14ea4845a Mon Sep 17 00:00:00 2001 From: Juan Vargas <108702207+JuansesDev@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:42:47 -0500 Subject: [PATCH 44/78] feat: add SendDock template (#821) * feat(senddock): add SendDock template SendDock is an open-source, self-hosted email marketing platform with BYO SMTP. The template ships postgres + redis + the canonical ghcr.io/arkhe-systems/senddock image, with healthchecks, named volumes and the env vars required for a working install. The senddock service runs migrations on startup via goose and serves the Vue dashboard on port 8080. PUBLIC_URL is wired to the Dokploy domain helper so unsubscribe links and broadcasts work out of the box. License key is left empty by default, which gives users the free Core experience; setting SENDDOCK_LICENSE_KEY unlocks the Pro features. Running dedupe-and-sort-meta.js per CONTRIBUTING removed an unrelated duplicate strapi entry that already existed in the index (same version, same logo, listed twice). The senddock entry is the intended addition. * fix(senddock): apply review fixes and bump to 0.6.4.1 - Add required `version: "3.8"` declaration to docker-compose.yml per AGENTS.md - Convert template.toml env from sub-table to array-of-strings syntax - Bump image and meta.json version to 0.6.4.1 (latest stable, fixes Redis rate-limiter restart loop affecting 0.4.x-0.6.4) * fix(senddock): correct website and docs URLs to senddock.dev * fix(senddock): bump to 0.6.5.1 --------- Co-authored-by: Sebastian Vargas Co-authored-by: Mauricio Siu --- blueprints/senddock/docker-compose.yml | 57 ++++++++++++++++++++++++++ blueprints/senddock/senddock.svg | 16 ++++++++ blueprints/senddock/template.toml | 17 ++++++++ meta.json | 18 ++++++++ 4 files changed, 108 insertions(+) create mode 100644 blueprints/senddock/docker-compose.yml create mode 100644 blueprints/senddock/senddock.svg create mode 100644 blueprints/senddock/template.toml diff --git a/blueprints/senddock/docker-compose.yml b/blueprints/senddock/docker-compose.yml new file mode 100644 index 00000000..c4ff03d0 --- /dev/null +++ b/blueprints/senddock/docker-compose.yml @@ -0,0 +1,57 @@ +version: "3.8" +services: + postgres: + image: postgres:17-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=senddock + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=senddock + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U senddock"] + interval: 10s + timeout: 5s + retries: 6 + + redis: + image: redis:7-alpine + restart: unless-stopped + volumes: + - redisdata:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 5 + + senddock: + image: ghcr.io/arkhe-systems/senddock:0.6.5.1 + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + environment: + - DATABASE_URL=postgres://senddock:${POSTGRES_PASSWORD}@postgres:5432/senddock?sslmode=disable + - REDIS_URL=redis://redis:6379 + - JWT_SECRET=${JWT_SECRET} + - PUBLIC_URL=${PUBLIC_URL} + - FRONTEND_URL=${PUBLIC_URL} + - DEPLOYMENT_MODE=self-hosted + - SENDDOCK_LICENSE_KEY=${SENDDOCK_LICENSE_KEY} + - PORT=8080 + expose: + - 8080 + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/health"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 15s + +volumes: + pgdata: + redisdata: diff --git a/blueprints/senddock/senddock.svg b/blueprints/senddock/senddock.svg new file mode 100644 index 00000000..9dc916eb --- /dev/null +++ b/blueprints/senddock/senddock.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/blueprints/senddock/template.toml b/blueprints/senddock/template.toml new file mode 100644 index 00000000..4a8c456e --- /dev/null +++ b/blueprints/senddock/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +jwt_secret = "${password:64}" + +[config] +env = [ + "POSTGRES_PASSWORD=${postgres_password}", + "JWT_SECRET=${jwt_secret}", + "PUBLIC_URL=https://${main_domain}", + "SENDDOCK_LICENSE_KEY=" +] + +[[config.domains]] +serviceName = "senddock" +port = 8080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index e94efea2..3f70aea8 100644 --- a/meta.json +++ b/meta.json @@ -6175,6 +6175,24 @@ "file-system" ] }, + { + "id": "senddock", + "name": "SendDock", + "version": "0.6.5.1", + "description": "Open-source, self-hosted email marketing platform with BYO SMTP. Manage subscribers, templates, broadcasts, and scheduled campaigns from one place.", + "logo": "senddock.svg", + "links": { + "github": "https://github.com/Arkhe-Systems/senddock", + "website": "https://senddock.dev", + "docs": "https://docs.senddock.dev" + }, + "tags": [ + "email", + "newsletter", + "mailing-list", + "marketing" + ] + }, { "id": "sftpgo", "name": "SFTPGo", From 8a528251674b96e00dc2aeee90ef012ee6efb86e Mon Sep 17 00:00:00 2001 From: Abdelatif Djeddou <155508467+BinkyTwin@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:43:07 +0200 Subject: [PATCH 45/78] feat: add Joomla template (#891) * feat: add Joomla template * docs: clarify Joomla admin credential fields --------- Co-authored-by: Mauricio Siu --- blueprints/joomla/docker-compose.yml | 44 ++++++++++++++++++++++++ blueprints/joomla/joomla.svg | 5 +++ blueprints/joomla/template.toml | 51 ++++++++++++++++++++++++++++ meta.json | 18 ++++++++++ 4 files changed, 118 insertions(+) create mode 100644 blueprints/joomla/docker-compose.yml create mode 100644 blueprints/joomla/joomla.svg create mode 100644 blueprints/joomla/template.toml diff --git a/blueprints/joomla/docker-compose.yml b/blueprints/joomla/docker-compose.yml new file mode 100644 index 00000000..e83a420b --- /dev/null +++ b/blueprints/joomla/docker-compose.yml @@ -0,0 +1,44 @@ +version: "3.8" + +services: + joomla: + image: joomla:6.1.0-apache + restart: unless-stopped + depends_on: + joomla-db: + condition: service_healthy + expose: + - "80" + environment: + JOOMLA_DB_HOST: joomla-db + JOOMLA_DB_USER: ${JOOMLA_DB_USER} + JOOMLA_DB_PASSWORD: ${JOOMLA_DB_PASSWORD} + JOOMLA_DB_NAME: ${JOOMLA_DB_NAME} + JOOMLA_SITE_NAME: ${JOOMLA_SITE_NAME} + JOOMLA_ADMIN_USER: ${JOOMLA_ADMIN_USER} + JOOMLA_ADMIN_USERNAME: ${JOOMLA_ADMIN_USERNAME} + JOOMLA_ADMIN_PASSWORD: ${JOOMLA_ADMIN_PASSWORD} + JOOMLA_ADMIN_EMAIL: ${JOOMLA_ADMIN_EMAIL} + volumes: + - joomla-data:/var/www/html + + joomla-db: + image: mariadb:11.4 + restart: unless-stopped + environment: + MARIADB_DATABASE: ${JOOMLA_DB_NAME} + MARIADB_USER: ${JOOMLA_DB_USER} + MARIADB_PASSWORD: ${JOOMLA_DB_PASSWORD} + MARIADB_RANDOM_ROOT_PASSWORD: "1" + volumes: + - joomla-db:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + +volumes: + joomla-data: + joomla-db: diff --git a/blueprints/joomla/joomla.svg b/blueprints/joomla/joomla.svg new file mode 100644 index 00000000..27239ba8 --- /dev/null +++ b/blueprints/joomla/joomla.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/blueprints/joomla/template.toml b/blueprints/joomla/template.toml new file mode 100644 index 00000000..4f360d66 --- /dev/null +++ b/blueprints/joomla/template.toml @@ -0,0 +1,51 @@ +[variables] +main_domain = "${domain}" +site_name = "Joomla" +admin_name = "Joomla Administrator" +admin_username = "${username}" +admin_password = "${password:24}" +admin_email = "${email}" +db_user = "joomla" +db_password = "${password:32}" +db_name = "joomla" + +[[config.domains]] +serviceName = "joomla" +port = 80 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "README.md" +content = """# Joomla + +This template deploys Joomla CMS with MariaDB using the official Joomla Apache image. + +## Access + +- URL: `http://${main_domain}` +- Administrator username: the configured `admin_username` value +- Administrator password: the generated `admin_password` value +- Administrator email: the configured `admin_email` value + +The template uses Joomla's auto-deployment environment variables so the site and administrator account are initialized on first start. + +## Storage + +The template creates persistent volumes for: + +- Joomla application files at `/var/www/html` +- MariaDB data at `/var/lib/mysql` + +Back up both volumes together before upgrades or migrations. +""" + +[config.env] +JOOMLA_SITE_NAME = "${site_name}" +JOOMLA_ADMIN_USER = "${admin_name}" +JOOMLA_ADMIN_USERNAME = "${admin_username}" +JOOMLA_ADMIN_PASSWORD = "${admin_password}" +JOOMLA_ADMIN_EMAIL = "${admin_email}" +JOOMLA_DB_USER = "${db_user}" +JOOMLA_DB_PASSWORD = "${db_password}" +JOOMLA_DB_NAME = "${db_name}" diff --git a/meta.json b/meta.json index 3f70aea8..431878e4 100644 --- a/meta.json +++ b/meta.json @@ -3588,6 +3588,24 @@ "open-source" ] }, + { + "id": "joomla", + "name": "Joomla", + "version": "6.1.0-apache", + "description": "Joomla is a flexible open-source content management system for building websites, portals, and online applications.", + "logo": "joomla.svg", + "links": { + "github": "https://github.com/joomla/joomla-cms", + "website": "https://www.joomla.org/", + "docs": "https://manual.joomla.org/" + }, + "tags": [ + "cms", + "website", + "php", + "mariadb" + ] + }, { "id": "joplin-server", "name": "Joplin Server", From 3ebc2f850f621e7d61e8d2a05d7a9561be31be24 Mon Sep 17 00:00:00 2001 From: Abdelatif Djeddou <155508467+BinkyTwin@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:43:15 +0200 Subject: [PATCH 46/78] feat: add TimescaleDB template (#892) --- blueprints/timescaledb/docker-compose.yml | 25 ++++++++++++++ blueprints/timescaledb/template.toml | 40 +++++++++++++++++++++++ blueprints/timescaledb/timescaledb.svg | 10 ++++++ meta.json | 19 +++++++++++ 4 files changed, 94 insertions(+) create mode 100644 blueprints/timescaledb/docker-compose.yml create mode 100644 blueprints/timescaledb/template.toml create mode 100644 blueprints/timescaledb/timescaledb.svg diff --git a/blueprints/timescaledb/docker-compose.yml b/blueprints/timescaledb/docker-compose.yml new file mode 100644 index 00000000..bd136801 --- /dev/null +++ b/blueprints/timescaledb/docker-compose.yml @@ -0,0 +1,25 @@ +version: "3.8" + +services: + timescaledb: + image: timescale/timescaledb:2.26.4-pg17 + restart: unless-stopped + expose: + - 5432 + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} + TIMESCALEDB_TELEMETRY: ${TIMESCALEDB_TELEMETRY} + volumes: + - timescaledb-data:/var/lib/postgresql/data + - ../files/001_create_timescaledb.sql:/docker-entrypoint-initdb.d/001_create_timescaledb.sql:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + +volumes: + timescaledb-data: diff --git a/blueprints/timescaledb/template.toml b/blueprints/timescaledb/template.toml new file mode 100644 index 00000000..7ebe8fc3 --- /dev/null +++ b/blueprints/timescaledb/template.toml @@ -0,0 +1,40 @@ +[variables] +postgres_user = "${username}" +postgres_password = "${password:32}" +postgres_db = "timescaledb" +timescaledb_telemetry = "off" + +[config] +mounts = [] + +[config.env] +POSTGRES_USER = "${postgres_user}" +POSTGRES_PASSWORD = "${postgres_password}" +POSTGRES_DB = "${postgres_db}" +TIMESCALEDB_TELEMETRY = "${timescaledb_telemetry}" + +[[config.mounts]] +filePath = "001_create_timescaledb.sql" +content = """ +CREATE EXTENSION IF NOT EXISTS timescaledb; +""" + +[[config.mounts]] +filePath = "README.md" +content = """ +# TimescaleDB + +TimescaleDB is deployed as a PostgreSQL-compatible database on the internal `timescaledb:5432` service. + +Connection values: + +- Host: `timescaledb` +- Port: `5432` +- Database: `${postgres_db}` +- Username: `${postgres_user}` +- Password: generated by Dokploy + +The template creates the TimescaleDB extension during first initialization. PostgreSQL initialization variables only apply when the data volume is empty. + +Telemetry is disabled by default with `TIMESCALEDB_TELEMETRY=off`. The image runs `timescaledb-tune` automatically on first initialization and reads Docker cgroup limits when available. +""" diff --git a/blueprints/timescaledb/timescaledb.svg b/blueprints/timescaledb/timescaledb.svg new file mode 100644 index 00000000..d7bb10c4 --- /dev/null +++ b/blueprints/timescaledb/timescaledb.svg @@ -0,0 +1,10 @@ + + TimescaleDB + A layered database mark with a time-series pulse. + + + + + + + diff --git a/meta.json b/meta.json index 431878e4..19cc4f45 100644 --- a/meta.json +++ b/meta.json @@ -6634,6 +6634,25 @@ "uptime" ] }, + { + "id": "timescaledb", + "name": "TimescaleDB", + "version": "2.26.4-pg17", + "description": "TimescaleDB is an open-source time-series database packaged as a PostgreSQL extension, optimized for fast ingest and real-time analytics with standard SQL.", + "logo": "timescaledb.svg", + "links": { + "github": "https://github.com/timescale/timescaledb", + "website": "https://www.tigerdata.com/", + "docs": "https://docs.timescale.com/" + }, + "tags": [ + "database", + "postgresql", + "time-series", + "analytics", + "sql" + ] + }, { "id": "tolgee", "name": "Tolgee", From 5523024b9529e35bda51bb12d434fb559ae33939 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 8 Jul 2026 03:13:53 +0530 Subject: [PATCH 47/78] feat(appwrite): update template to 1.9.5 (#970) * feat(appwrite): update template to 1.9.5 Move default datastore to MongoDB (DocumentsDB), add worker-screenshots, worker-executions and task-interval, bump all images to 1.9.5, expand v5 function runtimes, and provide mongo bootstrap files via config.mounts. MariaDB kept as a supported alternative. PostgreSQL/embedding (VectorsDB) and bundled Traefik intentionally excluded. * fix(appwrite): re-add compose version header and add mongodb tag Address review: restore "version: 3.8" header (repo convention across blueprints) and add the mongodb tag to meta.json to reflect the new default datastore. * (fix): add container_name to appwrite executor so it can find itself The openruntimes executor v0.25 discovers its own container by listing containers whose name matches its hostname (exc1). Without container_name, compose names the container -openruntimes-executor-1, the lookup fails with 'Own container not found' and the executor crash-loops, breaking all function/site builds and executions with 'Could not resolve host: exc1'. Matches upstream appwrite 1.9.5 compose. Verified end-to-end on a fresh Dokploy v0.29.8 server: executor healthy, function build + execution succeed. * (fix): route wildcard sites/functions subdomains to appwrite Dokploy domains only support exact hosts, so Appwrite-generated preview and execution URLs (.sites., .functions.) hit Dokploy traefik with no matching router: 404 over HTTP and the traefik default self-signed certificate over HTTPS ('site is insecure'). Add HostRegexp routers on the appwrite service for *.sites and *.functions domains on both web and websecure entrypoints. HTTPS for these subdomains still requires the user to provide a wildcard certificate (DNS-01 resolver or uploaded wildcard/Origin CA cert); with tls=true routers in place traefik picks it up via SNI once added. Verified on a live Dokploy v0.29.8 deployment: site subdomains route correctly over HTTP and HTTPS after adding the labels. * (chore): remove wildcard routing comment * (fix): drop container_name, use service-name hostname for executor self-discovery container_name is disallowed by repo conventions and fails CI validation. The executor discovers its own container by matching gethostname() against Docker container names (substring filter), so hostname openruntimes-executor matches -openruntimes-executor-1 and _APP_EXECUTOR_HOST can use the DNS-resolvable service name. Also read the MongoDB healthcheck password from the container env (MONGO_INITDB_ROOT_PASSWORD) instead of interpolating _APP_DB_ROOT_PASS at render time, so shell-special characters in generated passwords cannot break authentication. Both verified on a live Dokploy v0.29.8 deployment: executor healthy, function execution succeeds, mongodb healthcheck passes. --------- Co-authored-by: ChiragAgg5k --- blueprints/appwrite/docker-compose.yml | 493 +++++++++++++++++++++---- blueprints/appwrite/template.toml | 145 +++++--- meta.json | 3 +- 3 files changed, 511 insertions(+), 130 deletions(-) diff --git a/blueprints/appwrite/docker-compose.yml b/blueprints/appwrite/docker-compose.yml index cb622284..2da23f52 100644 --- a/blueprints/appwrite/docker-compose.yml +++ b/blueprints/appwrite/docker-compose.yml @@ -8,12 +8,34 @@ x-logging: &x-logging max-size: "10m" services: appwrite: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 <<: *x-logging restart: unless-stopped labels: - traefik.enable=true - traefik.constraint-label-stack=appwrite + - 'traefik.http.routers.appwrite-sites-wildcard.rule=HostRegexp(`^.+\.${_APP_DOMAIN_SITES}$$`)' + - traefik.http.routers.appwrite-sites-wildcard.entrypoints=web + - traefik.http.routers.appwrite-sites-wildcard.service=appwrite-wildcard + - 'traefik.http.routers.appwrite-sites-wildcard-secure.rule=HostRegexp(`^.+\.${_APP_DOMAIN_SITES}$$`)' + - traefik.http.routers.appwrite-sites-wildcard-secure.entrypoints=websecure + - traefik.http.routers.appwrite-sites-wildcard-secure.tls=true + - traefik.http.routers.appwrite-sites-wildcard-secure.service=appwrite-wildcard + - 'traefik.http.routers.appwrite-functions-wildcard.rule=HostRegexp(`^.+\.${_APP_DOMAIN_FUNCTIONS}$$`)' + - traefik.http.routers.appwrite-functions-wildcard.entrypoints=web + - traefik.http.routers.appwrite-functions-wildcard.service=appwrite-wildcard + - 'traefik.http.routers.appwrite-functions-wildcard-secure.rule=HostRegexp(`^.+\.${_APP_DOMAIN_FUNCTIONS}$$`)' + - traefik.http.routers.appwrite-functions-wildcard-secure.entrypoints=websecure + - traefik.http.routers.appwrite-functions-wildcard-secure.tls=true + - traefik.http.routers.appwrite-functions-wildcard-secure.service=appwrite-wildcard + - traefik.http.services.appwrite-wildcard.loadbalancer.server.port=80 + healthcheck: + test: + - CMD + - doctor + interval: 5s + timeout: 5s + retries: 12 volumes: - appwrite-uploads:/storage/uploads:rw - appwrite-imports:/storage/imports:rw @@ -24,21 +46,25 @@ services: - appwrite-sites:/storage/sites:rw - appwrite-builds:/storage/builds:rw depends_on: - - mariadb - redis - # - clamav + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_EDITION - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_LOCALE + - _APP_COMPRESSION_ENABLED - _APP_COMPRESSION_MIN_SIZE_BYTES - _APP_CONSOLE_WHITELIST_ROOT - _APP_CONSOLE_WHITELIST_EMAILS - _APP_CONSOLE_SESSION_ALERTS - _APP_CONSOLE_WHITELIST_IPS - _APP_CONSOLE_HOSTNAMES + - _APP_CONSOLE_SCHEMA - _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_SYSTEM_TEAM_EMAIL - _APP_EMAIL_SECURITY - _APP_SYSTEM_RESPONSE_FORMAT - _APP_OPTIONS_ABUSE @@ -47,6 +73,8 @@ services: - _APP_OPTIONS_ROUTER_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_CONSOLE_TRUSTED_PROJECTS - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A @@ -57,11 +85,18 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DB_ADAPTER_VECTORSDB + - _APP_DB_HOST_VECTORSDB + - _APP_DB_PORT_VECTORSDB + - _APP_DB_SCHEMA_VECTORSDB + - _APP_DB_USER_VECTORSDB + - _APP_DB_PASS_VECTORSDB - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE @@ -108,8 +143,6 @@ services: - _APP_EXECUTOR_HOST - _APP_LOGGING_CONFIG - _APP_MAINTENANCE_INTERVAL - - _APP_MAINTENANCE_DELAY - - _APP_MAINTENANCE_START_TIME - _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_CACHE - _APP_MAINTENANCE_RETENTION_ABUSE @@ -119,6 +152,7 @@ services: - _APP_MAINTENANCE_RETENTION_SCHEDULES - _APP_SMS_PROVIDER - _APP_SMS_FROM + - _APP_GRAPHQL_INTROSPECTION - _APP_GRAPHQL_MAX_BATCH_SIZE - _APP_GRAPHQL_MAX_COMPLEXITY - _APP_GRAPHQL_MAX_DEPTH @@ -131,16 +165,26 @@ services: - _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET - _APP_ASSISTANT_OPENAI_API_KEY + - _APP_CONSOLE_COUNTRIES_DENYLIST + - _APP_EXPERIMENT_LOGGING_PROVIDER + - _APP_EXPERIMENT_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES + - _APP_DATABASE_SHARED_NAMESPACE + - _APP_FUNCTIONS_CREATION_ABUSE_LIMIT + - _APP_CUSTOM_DOMAIN_DENY_LIST + - _APP_TRUSTED_HEADERS + - _APP_MIGRATION_HOST + appwrite-console: <<: *x-logging - image: appwrite/console:7.4.7 + image: appwrite/console:8.7.5 restart: unless-stopped labels: - "traefik.enable=true" - "traefik.constraint-label-stack=appwrite" appwrite-realtime: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: realtime <<: *x-logging restart: unless-stopped @@ -148,7 +192,7 @@ services: - "traefik.enable=true" - "traefik.constraint-label-stack=appwrite" depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV @@ -160,51 +204,69 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DB_ADAPTER_VECTORSDB + - _APP_DB_HOST_VECTORSDB + - _APP_DB_PORT_VECTORSDB + - _APP_DB_SCHEMA_VECTORSDB + - _APP_DB_USER_VECTORSDB + - _APP_DB_PASS_VECTORSDB - _APP_USAGE_STATS - _APP_LOGGING_CONFIG + - _APP_LOGGING_CONFIG_REALTIME + - _APP_DATABASE_SHARED_TABLES + - _APP_POOL_ADAPTER=swoole appwrite-worker-audits: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-audits <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES appwrite-worker-webhooks: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-webhooks <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_EMAIL_SECURITY - - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -215,15 +277,17 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_LOGGING_CONFIG + - _APP_WEBHOOK_MAX_FAILED_ATTEMPTS + - _APP_DATABASE_SHARED_TABLES appwrite-worker-deletes: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-deletes <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} volumes: - appwrite-uploads:/storage/uploads:rw - appwrite-cache:/storage/cache:rw @@ -233,12 +297,16 @@ services: - appwrite-certificates:/storage/certificates:rw environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -269,52 +337,64 @@ services: - _APP_LOGGING_CONFIG - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST - - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_DATABASE_SHARED_TABLES + - _APP_EMAIL_CERTIFICATES - _APP_MAINTENANCE_RETENTION_AUDIT - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE - - _APP_MAINTENANCE_RETENTION_EXECUTION - - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - - _APP_EMAIL_CERTIFICATES appwrite-worker-databases: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-databases <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DB_ADAPTER_VECTORSDB + - _APP_DB_HOST_VECTORSDB + - _APP_DB_PORT_VECTORSDB + - _APP_DB_SCHEMA_VECTORSDB + - _APP_DB_USER_VECTORSDB + - _APP_DB_PASS_VECTORSDB - _APP_LOGGING_CONFIG + - _APP_QUEUE_NAME + - _APP_DATABASE_SHARED_TABLES appwrite-worker-builds: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-builds <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} volumes: - appwrite-functions:/storage/functions:rw - appwrite-sites:/storage/sites:rw - appwrite-builds:/storage/builds:rw - - appwrite-uploads:/storage/uploads:rw environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST @@ -322,6 +402,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -336,10 +417,13 @@ services: - _APP_COMPUTE_BUILD_TIMEOUT - _APP_COMPUTE_CPUS - _APP_COMPUTE_MEMORY + - _APP_OPEN_RUNTIMES_NFT - _APP_COMPUTE_SIZE_LIMIT - _APP_OPTIONS_FORCE_HTTPS - _APP_OPTIONS_ROUTER_FORCE_HTTPS - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_CONSOLE_TRUSTED_PROJECTS - _APP_STORAGE_DEVICE - _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_SECRET @@ -362,22 +446,85 @@ services: - _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_BUCKET + - _APP_DATABASE_SHARED_TABLES - _APP_DOMAIN_SITES + extra_hosts: + - host.docker.internal:host-gateway + + appwrite-worker-screenshots: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-screenshots + <<: *x-logging + restart: unless-stopped + depends_on: + - redis + - ${_APP_DB_HOST:-mongodb} + volumes: + - appwrite-uploads:/storage/uploads:rw + environment: + - _APP_BROWSER_HOST + - _APP_WORKER_SCREENSHOTS_ROUTER + - _APP_OPTIONS_FORCE_HTTPS + - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_LOGGING_CONFIG + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + extra_hosts: + - host.docker.internal:host-gateway appwrite-worker-certificates: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-certificates <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} volumes: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_TARGET_CNAME @@ -386,30 +533,64 @@ services: - _APP_DOMAIN_TARGET_CAA - _APP_DNS - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES - _APP_EMAIL_CERTIFICATES - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES - appwrite-worker-functions: - image: appwrite/appwrite:1.8.0 - entrypoint: worker-functions + appwrite-worker-executions: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-executions <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} + environment: + - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES + + appwrite-worker-functions: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-functions + <<: *x-logging + restart: unless-stopped + depends_on: + - ${_APP_DB_HOST:-mongodb} + - redis - openruntimes-executor environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_OPTIONS_FORCE_HTTPS @@ -417,6 +598,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -433,9 +615,11 @@ services: - _APP_DOCKER_HUB_USERNAME - _APP_DOCKER_HUB_PASSWORD - _APP_LOGGING_CONFIG + - _APP_LOGGING_PROVIDER + - _APP_DATABASE_SHARED_TABLES appwrite-worker-mails: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-mails <<: *x-logging restart: unless-stopped @@ -443,19 +627,23 @@ services: - redis environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE @@ -464,9 +652,10 @@ services: - _APP_LOGGING_CONFIG - _APP_DOMAIN - _APP_OPTIONS_FORCE_HTTPS + - _APP_DATABASE_SHARED_TABLES appwrite-worker-messaging: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-messaging <<: *x-logging restart: unless-stopped @@ -476,12 +665,16 @@ services: - redis environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -490,6 +683,7 @@ services: - _APP_LOGGING_CONFIG - _APP_SMS_FROM - _APP_SMS_PROVIDER + - _APP_SMS_PROJECTS_DENY_LIST - _APP_STORAGE_DEVICE - _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_SECRET @@ -512,19 +706,24 @@ services: - _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_BUCKET + - _APP_DATABASE_SHARED_TABLES appwrite-worker-migrations: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-migrations <<: *x-logging restart: unless-stopped volumes: - appwrite-imports:/storage/imports:rw + - appwrite-uploads:/storage/uploads:rw depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_TARGET_CNAME @@ -537,6 +736,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -545,17 +745,22 @@ services: - _APP_LOGGING_CONFIG - _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET + - _APP_DATABASE_SHARED_TABLES + - _APP_OPTIONS_FORCE_HTTPS + - _APP_MIGRATION_HOST appwrite-task-maintenance: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: maintenance <<: *x-logging restart: unless-stopped depends_on: + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_DOMAIN - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA @@ -568,6 +773,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -581,19 +787,58 @@ services: - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_SCHEDULES + - _APP_MAINTENANCE_START_TIME + - _APP_DATABASE_SHARED_TABLES + + appwrite-task-interval: + image: appwrite/appwrite:1.9.5 + entrypoint: interval + <<: *x-logging + restart: unless-stopped + depends_on: + - ${_APP_DB_HOST:-mongodb} + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_DOMAIN + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS + - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES + - _APP_INTERVAL_DOMAIN_VERIFICATION + - _APP_INTERVAL_CLEANUP_STALE_EXECUTIONS appwrite-task-stats-resources: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: stats-resources <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -609,42 +854,21 @@ services: - _APP_STATS_RESOURCES_INTERVAL appwrite-worker-stats-resources: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-stats-resources <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb - environment: - - _APP_ENV - - _APP_WORKER_PER_CORE - - _APP_OPENSSL_KEY_V1 - - _APP_DB_HOST - - _APP_DB_PORT - - _APP_DB_SCHEMA - - _APP_DB_USER - - _APP_DB_PASS - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS - - _APP_USAGE_STATS - - _APP_LOGGING_CONFIG - - _APP_STATS_RESOURCES_INTERVAL - - appwrite-worker-stats-usage: - image: appwrite/appwrite:1.8.0 - entrypoint: worker-stats-usage - <<: *x-logging - restart: unless-stopped - depends_on: - - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -657,91 +881,145 @@ services: - _APP_USAGE_STATS - _APP_LOGGING_CONFIG - _APP_USAGE_AGGREGATION_INTERVAL + - _APP_DATABASE_SHARED_TABLES + + appwrite-worker-stats-usage: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-stats-usage + <<: *x-logging + restart: unless-stopped + depends_on: + - redis + - ${_APP_DB_HOST:-mongodb} + environment: + - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_USAGE_AGGREGATION_INTERVAL + - _APP_DATABASE_SHARED_TABLES appwrite-task-scheduler-functions: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: schedule-functions <<: *x-logging restart: unless-stopped depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_OPTIONS_FORCE_HTTPS + - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES + - _APP_MIGRATION_HOST + - _APP_CONSOLE_SCHEMA - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES appwrite-task-scheduler-executions: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: schedule-executions <<: *x-logging restart: unless-stopped depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_OPTIONS_FORCE_HTTPS + - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES + - _APP_MIGRATION_HOST + - _APP_CONSOLE_SCHEMA - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES appwrite-task-scheduler-messages: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: schedule-messages <<: *x-logging restart: unless-stopped depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES appwrite-assistant: - image: appwrite/assistant:0.8.3 + image: appwrite/assistant:0.8.4 <<: *x-logging restart: unless-stopped environment: - _APP_ASSISTANT_OPENAI_API_KEY appwrite-browser: - image: appwrite/browser:0.2.4 + image: appwrite/browser:0.3.2 <<: *x-logging restart: unless-stopped openruntimes-executor: - hostname: exc1 + hostname: openruntimes-executor <<: *x-logging restart: unless-stopped stop_signal: SIGINT - image: openruntimes/executor:0.7.22 + image: openruntimes/executor:0.25.1 volumes: - /var/run/docker.sock:/var/run/docker.sock - appwrite-builds:/storage/builds:rw @@ -751,7 +1029,10 @@ services: # It's not possible to share mount file between 2 containers without host mount (copying is too slow) - /tmp:/tmp:rw environment: - - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD + - OPR_EXECUTOR_CONNECTION_STORAGE=local://localhost + - OPR_EXECUTOR_CONNECTION_BUILD_CACHE_STORAGE= + - OPR_EXECUTOR_IMAGES=$_APP_EXECUTOR_IMAGES + - OPR_EXECUTOR_INACTIVE_THRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_COMPUTE_MAINTENANCE_INTERVAL - OPR_EXECUTOR_NETWORK=$_APP_COMPUTE_RUNTIMES_NETWORK - OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME @@ -760,6 +1041,7 @@ services: - OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES - OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET - OPR_EXECUTOR_RUNTIME_VERSIONS=v5 + - OPEN_RUNTIMES_NFT=$_APP_OPEN_RUNTIMES_NFT - OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG - OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE - OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY @@ -783,6 +1065,55 @@ services: - OPR_EXECUTOR_STORAGE_WASABI_SECRET=$_APP_STORAGE_WASABI_SECRET - OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION - OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET + healthcheck: + test: + - CMD-SHELL + - 'curl -fsS -H "Authorization: Bearer $$OPR_EXECUTOR_SECRET" http://localhost/v1/health >/dev/null' + interval: 5s + timeout: 3s + retries: 20 + start_period: 5s + + mongodb: + image: mongo:8.2.5 + <<: *x-logging + restart: unless-stopped + volumes: + - appwrite-mongodb:/data/db + - appwrite-mongodb-keyfile:/data/keyfile + - ../files/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro + - ../files/mongo-entrypoint.sh:/mongo-entrypoint.sh:ro + environment: + - MONGO_INITDB_ROOT_USERNAME=root + - MONGO_INITDB_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} + - MONGO_INITDB_DATABASE=${_APP_DB_SCHEMA} + - MONGO_INITDB_USERNAME=${_APP_DB_USER} + - MONGO_INITDB_PASSWORD=${_APP_DB_PASS} + entrypoint: + - /bin/bash + - /mongo-entrypoint.sh + healthcheck: + test: | + bash -c " + mongosh -u root -p \"$$MONGO_INITDB_ROOT_PASSWORD\" --authenticationDatabase admin --quiet --eval \" + try { + const hello = db.adminCommand({hello: 1}); + if (hello.isWritablePrimary) { + quit(0); + } + } catch (e) {} + + try { + rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'mongodb:27017'}]}); + } catch (e) {} + + quit(1); + \" 2>/dev/null + " + interval: 10s + timeout: 10s + retries: 10 + start_period: 30s mariadb: image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p @@ -799,7 +1130,7 @@ services: command: "mysqld --innodb-flush-method=fsync" redis: - image: redis:7.2.4-alpine + image: redis:7.4.7-alpine <<: *x-logging restart: unless-stopped command: > @@ -818,6 +1149,8 @@ services: volumes: appwrite-mariadb: + appwrite-mongodb: + appwrite-mongodb-keyfile: appwrite-redis: appwrite-cache: appwrite-uploads: diff --git a/blueprints/appwrite/template.toml b/blueprints/appwrite/template.toml index cca2d43d..54d3a8bc 100644 --- a/blueprints/appwrite/template.toml +++ b/blueprints/appwrite/template.toml @@ -10,58 +10,69 @@ executor_secret = "${password:32}" [config] env = [ "_APP_ENV=production", + "_APP_EDITION=self-hosted", "_APP_LOCALE=en", + "_APP_POOL_ADAPTER=stack", + "_APP_WORKER_PER_CORE=6", "_APP_OPTIONS_ABUSE=enabled", "_APP_OPTIONS_FORCE_HTTPS=disabled", - "_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled", "_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled", "_APP_OPTIONS_ROUTER_PROTECTION=disabled", "_APP_OPENSSL_KEY_V1=${openssl_key}", "_APP_DOMAIN=${main_domain}", + "_APP_CONSOLE_DOMAIN=${main_domain}", + "_APP_CONSOLE_TRUSTED_PROJECTS=", "_APP_CUSTOM_DOMAIN_DENY_LIST=example.com,test.com,app.example.com", "_APP_DOMAIN_FUNCTIONS=${functions_domain}", "_APP_DOMAIN_SITES=${sites_domain}", - "_APP_DOMAIN_TARGET=localhost", "_APP_DOMAIN_TARGET_CNAME=localhost", "_APP_DOMAIN_TARGET_AAAA=::1", "_APP_DOMAIN_TARGET_A=127.0.0.1", "_APP_DOMAIN_TARGET_CAA=", "_APP_DNS=8.8.8.8", + "_APP_TRUSTED_HEADERS=x-forwarded-for", "_APP_CONSOLE_WHITELIST_ROOT=enabled", "_APP_CONSOLE_WHITELIST_EMAILS=", "_APP_CONSOLE_WHITELIST_IPS=", "_APP_CONSOLE_HOSTNAMES=", + "_APP_CONSOLE_SCHEMA=appwriteio", + "_APP_CONSOLE_SESSION_ALERTS=disabled", + "_APP_CONSOLE_COUNTRIES_DENYLIST=", "_APP_SYSTEM_EMAIL_NAME=Appwrite", "_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io", "_APP_SYSTEM_TEAM_EMAIL=team@appwrite.io", "_APP_SYSTEM_RESPONSE_FORMAT=", - "_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=certs@appwrite.io", "_APP_EMAIL_SECURITY=", "_APP_EMAIL_CERTIFICATES=", - "_APP_USAGE_STATS=enabled", - "_APP_LOGGING_PROVIDER=", - "_APP_LOGGING_CONFIG=", - "_APP_USAGE_AGGREGATION_INTERVAL=30", - "_APP_USAGE_TIMESERIES_INTERVAL=30", - "_APP_USAGE_DATABASE_INTERVAL=900", - "_APP_WORKER_PER_CORE=6", - "_APP_CONSOLE_SESSION_ALERTS=disabled", "_APP_COMPRESSION_ENABLED=enabled", "_APP_COMPRESSION_MIN_SIZE_BYTES=1024", + "_APP_USAGE_STATS=enabled", + "_APP_USAGE_AGGREGATION_INTERVAL=30", + "_APP_STATS_RESOURCES_INTERVAL=30", + "_APP_LOGGING_PROVIDER=", + "_APP_LOGGING_CONFIG=", + "_APP_LOGGING_CONFIG_REALTIME=", + "_APP_EXPERIMENT_LOGGING_PROVIDER=", + "_APP_EXPERIMENT_LOGGING_CONFIG=", "_APP_REDIS_HOST=redis", "_APP_REDIS_PORT=6379", "_APP_REDIS_USER=", "_APP_REDIS_PASS=", - "_APP_DB_HOST=mariadb", - "_APP_DB_PORT=3306", + "_APP_DB_ADAPTER=mongodb", + "_APP_DB_HOST=mongodb", + "_APP_DB_PORT=27017", "_APP_DB_SCHEMA=appwrite", "_APP_DB_USER=user", "_APP_DB_PASS=${db_user_pw}", "_APP_DB_ROOT_PASS=${db_root_pw}", - "_APP_INFLUXDB_HOST=influxdb", - "_APP_INFLUXDB_PORT=8086", - "_APP_STATSD_HOST=telegraf", - "_APP_STATSD_PORT=8125", + "_APP_DATABASE_SHARED_TABLES=", + "_APP_DATABASE_SHARED_NAMESPACE=", + "_APP_DB_ADAPTER_VECTORSDB=", + "_APP_DB_HOST_VECTORSDB=", + "_APP_DB_PORT_VECTORSDB=", + "_APP_DB_SCHEMA_VECTORSDB=", + "_APP_DB_USER_VECTORSDB=", + "_APP_DB_PASS_VECTORSDB=", "_APP_SMTP_HOST=", "_APP_SMTP_PORT=", "_APP_SMTP_SECURE=", @@ -69,6 +80,7 @@ env = [ "_APP_SMTP_PASSWORD=", "_APP_SMS_PROVIDER=", "_APP_SMS_FROM=", + "_APP_SMS_PROJECTS_DENY_LIST=", "_APP_STORAGE_LIMIT=30000000", "_APP_STORAGE_PREVIEW_LIMIT=20000000", "_APP_STORAGE_ANTIVIRUS=disabled", @@ -96,45 +108,27 @@ env = [ "_APP_STORAGE_WASABI_SECRET=", "_APP_STORAGE_WASABI_REGION=eu-central-1", "_APP_STORAGE_WASABI_BUCKET=", - "_APP_FUNCTIONS_SIZE_LIMIT=30000000", "_APP_COMPUTE_SIZE_LIMIT=30000000", - "_APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000", - "_APP_FUNCTIONS_TIMEOUT=900", - "_APP_FUNCTIONS_BUILD_TIMEOUT=900", "_APP_COMPUTE_BUILD_TIMEOUT=900", - "_APP_FUNCTIONS_CONTAINERS=10", - "_APP_FUNCTIONS_CPUS=0", "_APP_COMPUTE_CPUS=0", - "_APP_FUNCTIONS_MEMORY=0", "_APP_COMPUTE_MEMORY=0", - "_APP_FUNCTIONS_MEMORY_SWAP=0", - "_APP_FUNCTIONS_RUNTIMES=node-16.0,php-8.0,python-3.9,ruby-3.0", - "_APP_EXECUTOR_SECRET=${executor_secret}", - "_APP_EXECUTOR_HOST=http://exc1/v1", - "_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes", - "_APP_FUNCTIONS_ENVS=node-16.0,php-7.4,python-3.9,ruby-3.0", - "_APP_FUNCTIONS_INACTIVE_THRESHOLD=60", "_APP_COMPUTE_INACTIVE_THRESHOLD=60", - "DOCKERHUB_PULL_USERNAME=", - "DOCKERHUB_PULL_PASSWORD=", - "DOCKERHUB_PULL_EMAIL=", - "OPEN_RUNTIMES_NETWORK=appwrite_runtimes", - "_APP_FUNCTIONS_RUNTIMES_NETWORK=dokploy-network", + "_APP_COMPUTE_MAINTENANCE_INTERVAL=3600", "_APP_COMPUTE_RUNTIMES_NETWORK=dokploy-network", + "_APP_FUNCTIONS_TIMEOUT=900", + "_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000", + "_APP_FUNCTIONS_RUNTIMES=node-22,python-3.12,php-8.3,ruby-3.3,dart-3.5,go-1.23,rust-1.83,bun-1.1,deno-1.46,java-21.0,dotnet-8.0", + "_APP_SITES_TIMEOUT=900", + "_APP_SITES_RUNTIMES=static-1,node-22", + "_APP_EXECUTOR_SECRET=${executor_secret}", + "_APP_EXECUTOR_HOST=http://openruntimes-executor/v1", + "_APP_EXECUTOR_IMAGES=openruntimes/node:v5-22,openruntimes/python:v5-3.12,openruntimes/php:v5-8.3,openruntimes/static:v5-1", + "_APP_OPEN_RUNTIMES_NFT=enabled", + "_APP_BROWSER_HOST=http://appwrite-browser:3000/v1", + "_APP_WORKER_SCREENSHOTS_ROUTER=http://appwrite", "_APP_DOCKER_HUB_USERNAME=", "_APP_DOCKER_HUB_PASSWORD=", - "_APP_FUNCTIONS_MAINTENANCE_INTERVAL=3600", - "_APP_COMPUTE_MAINTENANCE_INTERVAL=3600", - "_APP_SITES_TIMEOUT=900", - "_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.29", - "_APP_VCS_GITHUB_APP_NAME=", - "_APP_VCS_GITHUB_PRIVATE_KEY=", - "_APP_VCS_GITHUB_APP_ID=", - "_APP_VCS_GITHUB_CLIENT_ID=", - "_APP_VCS_GITHUB_CLIENT_SECRET=", - "_APP_VCS_GITHUB_WEBHOOK_SECRET=", "_APP_MAINTENANCE_INTERVAL=86400", - "_APP_MAINTENANCE_DELAY=0", "_APP_MAINTENANCE_START_TIME=00:00", "_APP_MAINTENANCE_RETENTION_CACHE=2592000", "_APP_MAINTENANCE_RETENTION_EXECUTION=1209600", @@ -143,14 +137,67 @@ env = [ "_APP_MAINTENANCE_RETENTION_ABUSE=86400", "_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000", "_APP_MAINTENANCE_RETENTION_SCHEDULES=86400", + "_APP_INTERVAL_DOMAIN_VERIFICATION=120", + "_APP_INTERVAL_CLEANUP_STALE_EXECUTIONS=300", + "_APP_GRAPHQL_INTROSPECTION=enabled", "_APP_GRAPHQL_MAX_BATCH_SIZE=10", "_APP_GRAPHQL_MAX_COMPLEXITY=250", - "_APP_GRAPHQL_MAX_DEPTH=3", + "_APP_GRAPHQL_MAX_DEPTH=4", + "_APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10", + "_APP_VCS_GITHUB_APP_NAME=", + "_APP_VCS_GITHUB_PRIVATE_KEY=", + "_APP_VCS_GITHUB_APP_ID=", + "_APP_VCS_GITHUB_CLIENT_ID=", + "_APP_VCS_GITHUB_CLIENT_SECRET=", + "_APP_VCS_GITHUB_WEBHOOK_SECRET=", + "_APP_MIGRATION_HOST=appwrite", "_APP_MIGRATIONS_FIREBASE_CLIENT_ID=", "_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=", "_APP_ASSISTANT_OPENAI_API_KEY=", ] -mounts = [] + +[[config.mounts]] +filePath = "mongo-init.js" +content = """// mongo-init.js + +// Switch to the admin database +const adminDb = db.getSiblingDB('admin'); + +// Get username and password from environment variables +const username = process.env.MONGO_INITDB_USERNAME; +const password = process.env.MONGO_INITDB_PASSWORD; +const database = process.env.MONGO_INITDB_DATABASE; + +// Create the user +adminDb.createUser({ + user: username, + pwd: password, + roles: [ + { role: 'readWrite', db: database } + ] +}); +""" + +[[config.mounts]] +filePath = "mongo-entrypoint.sh" +content = """#!/bin/bash +set -e + +# Fix keyfile permissions if mounted from volume +KEYFILE_PATH="/data/keyfile/mongo-keyfile" + +if [ ! -f "$KEYFILE_PATH" ]; then + echo "Generating random MongoDB keyfile..." + mkdir -p /data/keyfile + openssl rand -base64 756 > "$KEYFILE_PATH" +fi + +chmod 400 "$KEYFILE_PATH" +chown mongodb:mongodb "$KEYFILE_PATH" 2>/dev/null || chown 999:999 "$KEYFILE_PATH" + +# Use MongoDB's standard entrypoint with our command +exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --auth --keyFile "$KEYFILE_PATH" +""" [[config.domains]] serviceName = "appwrite" diff --git a/meta.json b/meta.json index 19cc4f45..452ceff6 100644 --- a/meta.json +++ b/meta.json @@ -399,7 +399,7 @@ { "id": "appwrite", "name": "Appwrite", - "version": "1.8.0", + "version": "1.9.5", "description": "Appwrite is an end-to-end platform for building Web, Mobile, Native, or Backend apps, packaged as a set of Docker microservices. It includes both a backend server and a fully integrated hosting solution for deploying static and server-side rendered frontends. Appwrite abstracts the complexity and repetitiveness required to build modern apps from scratch and allows you to build secure, full-stack applications faster.\nUsing Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.", "links": { "github": "https://github.com/appwrite/appwrite", @@ -411,6 +411,7 @@ "database", "firebase", "mariadb", + "mongodb", "hosting", "self-hosted" ] From a33fa76399456f6ab82bb97b12dab8745aab3cfd Mon Sep 17 00:00:00 2001 From: Cagdas Salur Date: Wed, 8 Jul 2026 00:43:56 +0300 Subject: [PATCH 48/78] =?UTF-8?q?feat(template):=20add=20tela=20=E2=80=94?= =?UTF-8?q?=20markdown-native=20team=20wiki=20with=20MCP=20(#967)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blueprints/tela/docker-compose.yml | 65 +++++++++++++++++++++++++++++ blueprints/tela/logo.png | Bin 0 -> 11508 bytes blueprints/tela/template.toml | 18 ++++++++ meta.json | 20 +++++++++ 4 files changed, 103 insertions(+) create mode 100644 blueprints/tela/docker-compose.yml create mode 100644 blueprints/tela/logo.png create mode 100644 blueprints/tela/template.toml diff --git a/blueprints/tela/docker-compose.yml b/blueprints/tela/docker-compose.yml new file mode 100644 index 00000000..a5117484 --- /dev/null +++ b/blueprints/tela/docker-compose.yml @@ -0,0 +1,65 @@ +services: + proxy: + image: ghcr.io/zcag/tela-proxy:0.7.0 + restart: unless-stopped + environment: + - TELA_SITE_ADDRESS=:80 + - TELA_UPSTREAM_BACKEND=backend:8080 + - TELA_UPSTREAM_FRONTEND=frontend:80 + depends_on: + - backend + - frontend + volumes: + - caddy-data:/data + - caddy-config:/config + + backend: + image: ghcr.io/zcag/tela-backend:0.7.0 + restart: unless-stopped + environment: + - TELA_PUBLIC_BASE_URL=${TELA_PUBLIC_BASE_URL} + - TELA_DATABASE_URL=postgres://tela:${TELA_PG_PASSWORD}@postgres:5432/tela?sslmode=disable + - TELA_SHARE_SECRET=${TELA_SHARE_SECRET} + - TELA_API_KEY_SECRET=${TELA_API_KEY_SECRET} + - TELA_DECK_URL=http://deck:3344 + - TELA_GOTENBERG_URL=http://gotenberg:3000 + - TELA_PDF_RENDER_BASE_URL=http://proxy + depends_on: + postgres: + condition: service_healthy + + frontend: + image: ghcr.io/zcag/tela-frontend:0.7.0 + restart: unless-stopped + + postgres: + image: pgvector/pgvector:pg17 + restart: unless-stopped + environment: + - POSTGRES_USER=tela + - POSTGRES_PASSWORD=${TELA_PG_PASSWORD} + - POSTGRES_DB=tela + healthcheck: + test: ["CMD-SHELL", "pg_isready -U tela -d tela"] + interval: 5s + timeout: 5s + retries: 20 + start_period: 10s + volumes: + - pgdata:/var/lib/postgresql/data + + deck: + image: ghcr.io/zcag/tela-deck:0.7.0 + restart: unless-stopped + volumes: + - deckcache:/data + + gotenberg: + image: gotenberg/gotenberg:8 + restart: unless-stopped + +volumes: + pgdata: + deckcache: + caddy-data: + caddy-config: diff --git a/blueprints/tela/logo.png b/blueprints/tela/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8c90d578b38099e70398e98ce0465f61c0ed25cd GIT binary patch literal 11508 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

bM3cY4zB<6GWlR&rQW0+OrqiB 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 PWuW*#?S7H0*^B=MRX Date: Wed, 8 Jul 2026 01:43:59 +0400 Subject: [PATCH 49/78] feat: add Speedtest Tracker template (#952) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> Co-authored-by: Mauricio Siu --- blueprints/speedtest-tracker/docker-compose.yml | 16 ++++++++++++++++ .../speedtest-tracker/speedtest-tracker.svg | 5 +++++ blueprints/speedtest-tracker/template.toml | 16 ++++++++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 blueprints/speedtest-tracker/docker-compose.yml create mode 100644 blueprints/speedtest-tracker/speedtest-tracker.svg create mode 100644 blueprints/speedtest-tracker/template.toml diff --git a/blueprints/speedtest-tracker/docker-compose.yml b/blueprints/speedtest-tracker/docker-compose.yml new file mode 100644 index 00000000..234eddb4 --- /dev/null +++ b/blueprints/speedtest-tracker/docker-compose.yml @@ -0,0 +1,16 @@ +services: + speedtest-tracker: + image: lscr.io/linuxserver/speedtest-tracker:latest + restart: unless-stopped + environment: + PUID: ${PUID} + PGID: ${PGID} + APP_KEY: ${APP_KEY} + DB_CONNECTION: sqlite + volumes: + - speedtest_config:/config + ports: + - "80" + +volumes: + speedtest_config: diff --git a/blueprints/speedtest-tracker/speedtest-tracker.svg b/blueprints/speedtest-tracker/speedtest-tracker.svg new file mode 100644 index 00000000..1928bbb7 --- /dev/null +++ b/blueprints/speedtest-tracker/speedtest-tracker.svg @@ -0,0 +1,5 @@ + + + + S + diff --git a/blueprints/speedtest-tracker/template.toml b/blueprints/speedtest-tracker/template.toml new file mode 100644 index 00000000..2ae03d8c --- /dev/null +++ b/blueprints/speedtest-tracker/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +app_key = "${base64:32}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "speedtest-tracker" +port = 80 +host = "${main_domain}" + +[config.env] +APP_KEY = "base64:${app_key}" +PUID = "1000" +PGID = "1000" diff --git a/meta.json b/meta.json index d0feeda5..03578429 100644 --- a/meta.json +++ b/meta.json @@ -6370,6 +6370,23 @@ "storage" ] }, + { + "id": "speedtest-tracker", + "name": "Speedtest Tracker", + "version": "latest", + "description": "Speedtest Tracker runs scheduled internet speed tests and stores the results in a self-hosted dashboard.", + "logo": "speedtest-tracker.svg", + "links": { + "github": "https://github.com/alexjustesen/speedtest-tracker", + "website": "https://docs.speedtest-tracker.dev/", + "docs": "https://docs.speedtest-tracker.dev/" + }, + "tags": [ + "monitoring", + "network", + "speedtest" + ] + }, { "id": "spliit", "name": "Spliit", From cdc72377c7262a76c72e0f833f338b3fdb661e25 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 01:44:04 +0400 Subject: [PATCH 50/78] fix: respect stopped Immich deployments (#938) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/immich/docker-compose.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/blueprints/immich/docker-compose.yml b/blueprints/immich/docker-compose.yml index 8f12d6d1..f385b329 100644 --- a/blueprints/immich/docker-compose.yml +++ b/blueprints/immich/docker-compose.yml @@ -28,7 +28,7 @@ services: REDIS_DBINDEX: ${REDIS_DBINDEX} # Server Configuration TZ: ${TZ} - restart: always + restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:2283/server-info/ping"] interval: 30s @@ -44,7 +44,7 @@ services: REDIS_HOSTNAME: ${REDIS_HOSTNAME} REDIS_PORT: ${REDIS_PORT} REDIS_DBINDEX: ${REDIS_DBINDEX} - restart: always + restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3003/ping"] interval: 30s @@ -61,7 +61,7 @@ services: interval: 10s timeout: 5s retries: 5 - restart: always + restart: unless-stopped immich-database: image: tensorchord/pgvecto-rs:pg14-v0.3.0 @@ -94,10 +94,10 @@ services: '-c', 'wal_compression=on', ] - restart: always + restart: unless-stopped volumes: immich-model-cache: immich-postgres: immich-library: - immich-redis-data: + immich-redis-data: From 1c0380d77b4e2ba2316384964308e84a2aeb0b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20G=C3=BCndo=C4=9Fdu?= Date: Wed, 8 Jul 2026 00:44:07 +0300 Subject: [PATCH 51/78] feat(blueprint): add LibreDB Studio (#931) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add libredb-studio compose Adds LibreDB Studio — an MIT-licensed, AI-powered open-source SQL IDE (PostgreSQL, MySQL, Oracle, SQL Server, SQLite, MongoDB, Redis). This commit adds the docker-compose.yml. The template.toml and meta.json entry follow in the same branch. Image: ghcr.io/libredb/libredb-studio Repo: https://github.com/libredb/libredb-studio * feat: add libredb-studio template.toml Adds the Dokploy template.toml for LibreDB Studio. Defines auto-generated secrets (admin password, user password, JWT secret), exposes port 3000 on the primary domain, and persists data via a mount at /app/data. * feat: add libredb-studio to meta.json Registers LibreDB Studio in the template catalog index (meta.json), alphabetically-independent top entry. Completes the LibreDB Studio template (compose + template.toml + meta entry). * fix: sort libredb-studio entry alphabetically in meta.json Moves the libredb-studio entry to its correct alphabetical position between librechat and libredesk so meta.json remains sorted by id. Resolves the failing "Validate and Process Meta.json" check (duplicates/sort-order validation). * fix: expand libredb-studio tags to match meta.json formatting Formats the libredb-studio "tags" array as a multi-line block to match the output of dedupe-and-sort-meta.js, making the entry byte-identical to the canonical processed file. Resolves the failing "Validate and Process Meta.json" check. * fix: remove invalid mounts block from libredb-studio template.toml Removes the [[config.mounts]] block, which failed validation because Dokploy requires a 'content' field for file mounts. Data persistence is already handled by the named volume (libredb-data:/app/data) in docker-compose.yml, so no file mount is needed. Resolves the failing "Validate Docker Compose Files" check. * fix: correct indentation of libredb-studio tags in meta.json Adjusts the indentation of the "tags" key to 4 spaces so the libredb-studio entry is byte-identical to the output of dedupe-and-sort-meta.js. Resolves the remaining "Validate and Process Meta.json" check failure. * feat: add libredb-studio logo Adds the libredb-studio.svg logo to blueprints/libredb-studio/ so it matches the "logo" field in meta.json. Resolves the "Validate Blueprints Structure and Meta" check, which verifies that each meta.json logo has a corresponding file in its blueprint folder. * fix: bump libredb-studio image to v0.9.27 Updates the image tag from v0.9.13 to v0.9.27 (current release) so reviewers can test against the latest version. * fix: update LibreDB Studio version to v0.9.27 Update version field from v0.9.13 to v0.9.27 in meta.json * fix(blueprint): remove v prefix from image tag GHCR tags do not use v prefix (0.9.27 not v0.9.27) * fix(meta): remove v prefix from version field GHCR tags do not use v prefix (0.9.27 not v0.9.27) --- blueprints/libredb-studio/docker-compose.yml | 17 +++++++++++ blueprints/libredb-studio/libredb-studio.svg | 32 ++++++++++++++++++++ blueprints/libredb-studio/template.toml | 18 +++++++++++ meta.json | 16 ++++++++++ 4 files changed, 83 insertions(+) create mode 100644 blueprints/libredb-studio/docker-compose.yml create mode 100644 blueprints/libredb-studio/libredb-studio.svg create mode 100644 blueprints/libredb-studio/template.toml diff --git a/blueprints/libredb-studio/docker-compose.yml b/blueprints/libredb-studio/docker-compose.yml new file mode 100644 index 00000000..00b88766 --- /dev/null +++ b/blueprints/libredb-studio/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.8" +services: + libredb-studio: + image: ghcr.io/libredb/libredb-studio:0.9.27 + restart: unless-stopped + environment: + - ADMIN_EMAIL=admin@libredb.org + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + - USER_EMAIL=user@libredb.org + - USER_PASSWORD=${USER_PASSWORD} + - JWT_SECRET=${JWT_SECRET} + - STORAGE_PROVIDER=sqlite + - STORAGE_SQLITE_PATH=/app/data/libredb-storage.db + volumes: + - libredb-data:/app/data +volumes: + libredb-data: diff --git a/blueprints/libredb-studio/libredb-studio.svg b/blueprints/libredb-studio/libredb-studio.svg new file mode 100644 index 00000000..c36a98eb --- /dev/null +++ b/blueprints/libredb-studio/libredb-studio.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/libredb-studio/template.toml b/blueprints/libredb-studio/template.toml new file mode 100644 index 00000000..9d30084d --- /dev/null +++ b/blueprints/libredb-studio/template.toml @@ -0,0 +1,18 @@ +[variables] +admin_password = "${password:32}" +user_password = "${password:32}" +jwt_secret = "${password:64}" + +[config] +[[config.domains]] +serviceName = "libredb-studio" +port = 3000 + +[[config.env]] +ADMIN_PASSWORD = "${admin_password}" + +[[config.env]] +USER_PASSWORD = "${user_password}" + +[[config.env]] +JWT_SECRET = "${jwt_secret}" diff --git a/meta.json b/meta.json index 03578429..c8a04988 100644 --- a/meta.json +++ b/meta.json @@ -3884,6 +3884,22 @@ "generative-ai" ] }, + { + "id": "libredb-studio", + "name": "LibreDB Studio", + "version": "0.9.27", + "description": "The modern, AI-powered open-source SQL IDE. Query PostgreSQL, MySQL, Oracle, SQL Server, SQLite, MongoDB and Redis from your browser.", + "logo": "libredb-studio.svg", + "links": { + "github": "https://github.com/libredb/libredb-studio", + "website": "https://libredb.org/", + "docs": "https://libredb.org/" + }, + "tags": [ + "database", + "sql" + ] + }, { "id": "libredesk", "name": "Libredesk", From f7960475521ec824dcf2b3c84334dbce0d2a4b58 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 01:44:10 +0400 Subject: [PATCH 52/78] feat: add Prefect template (#930) * feat: add Prefect template * fix: tighten Prefect template routing --------- Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> Co-authored-by: Mauricio Siu --- blueprints/prefect/docker-compose.yml | 90 +++++++++++++++++++++++++++ blueprints/prefect/prefect.svg | 7 +++ blueprints/prefect/template.toml | 15 +++++ meta.json | 17 +++++ 4 files changed, 129 insertions(+) create mode 100644 blueprints/prefect/docker-compose.yml create mode 100644 blueprints/prefect/prefect.svg create mode 100644 blueprints/prefect/template.toml diff --git a/blueprints/prefect/docker-compose.yml b/blueprints/prefect/docker-compose.yml new file mode 100644 index 00000000..242936e8 --- /dev/null +++ b/blueprints/prefect/docker-compose.yml @@ -0,0 +1,90 @@ +version: "3.8" + +services: + prefect: + image: prefecthq/prefect:3.7.5-python3.12 + restart: unless-stopped + depends_on: + prefect-postgres: + condition: service_healthy + prefect-redis: + condition: service_healthy + environment: + - PREFECT_API_DATABASE_CONNECTION_URL=postgresql+asyncpg://prefect:${POSTGRES_PASSWORD}@prefect-postgres:5432/prefect + - PREFECT_SERVER_API_HOST=0.0.0.0 + - PREFECT_SERVER_UI_API_URL=https://${PREFECT_DOMAIN}/api + - PREFECT_MESSAGING_BROKER=prefect_redis.messaging + - PREFECT_MESSAGING_CACHE=prefect_redis.messaging + - PREFECT_REDIS_MESSAGING_HOST=prefect-redis + - PREFECT_REDIS_MESSAGING_PORT=6379 + - PREFECT_REDIS_MESSAGING_DB=0 + command: prefect server start --no-services + expose: + - 4200 + healthcheck: + test: + [ + "CMD", + "python", + "-c", + "import urllib.request as u; u.urlopen('http://localhost:4200/api/health', timeout=1)", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + prefect-services: + image: prefecthq/prefect:3.7.5-python3.12 + restart: unless-stopped + depends_on: + prefect: + condition: service_healthy + environment: + - PREFECT_API_DATABASE_CONNECTION_URL=postgresql+asyncpg://prefect:${POSTGRES_PASSWORD}@prefect-postgres:5432/prefect + - PREFECT_MESSAGING_BROKER=prefect_redis.messaging + - PREFECT_MESSAGING_CACHE=prefect_redis.messaging + - PREFECT_REDIS_MESSAGING_HOST=prefect-redis + - PREFECT_REDIS_MESSAGING_PORT=6379 + - PREFECT_REDIS_MESSAGING_DB=0 + command: prefect server services start + + prefect-worker: + image: prefecthq/prefect:3.7.5-python3.12 + restart: unless-stopped + depends_on: + prefect: + condition: service_healthy + environment: + - PREFECT_API_URL=http://prefect:4200/api + command: prefect worker start --pool local-pool + + prefect-postgres: + image: postgres:14-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=prefect + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=prefect + volumes: + - prefect-postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U prefect -d prefect"] + interval: 5s + timeout: 5s + retries: 5 + + prefect-redis: + image: redis:7.2-alpine + restart: unless-stopped + volumes: + - prefect-redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + prefect-postgres-data: + prefect-redis-data: diff --git a/blueprints/prefect/prefect.svg b/blueprints/prefect/prefect.svg new file mode 100644 index 00000000..07bf5b43 --- /dev/null +++ b/blueprints/prefect/prefect.svg @@ -0,0 +1,7 @@ + + Prefect + Prefect template icon + + + + diff --git a/blueprints/prefect/template.toml b/blueprints/prefect/template.toml new file mode 100644 index 00000000..15f166e5 --- /dev/null +++ b/blueprints/prefect/template.toml @@ -0,0 +1,15 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" + +[config] +env = [ + "PREFECT_DOMAIN=${main_domain}", + "POSTGRES_PASSWORD=${postgres_password}", +] +mounts = [] + +[[config.domains]] +serviceName = "prefect" +port = 4200 +host = "${main_domain}" diff --git a/meta.json b/meta.json index c8a04988..2e6eaefb 100644 --- a/meta.json +++ b/meta.json @@ -5689,6 +5689,23 @@ ], "dokploy_version": "<0.22.5" }, + { + "id": "prefect", + "name": "Prefect", + "version": "3.7.5-python3.12", + "description": "Prefect is an open-source workflow orchestration platform for building, scheduling, monitoring, and managing data pipelines and Python workflows.", + "logo": "prefect.svg", + "links": { + "github": "https://github.com/PrefectHQ/prefect", + "website": "https://www.prefect.io/", + "docs": "https://docs.prefect.io/" + }, + "tags": [ + "automation", + "workflow", + "data" + ] + }, { "id": "privatebin", "name": "PrivateBin", From 38483d467cf6250f55cb9b5d94de036906a0ca9a Mon Sep 17 00:00:00 2001 From: df4p <38404+df4p@users.noreply.github.com> Date: Tue, 7 Jul 2026 18:44:13 -0300 Subject: [PATCH 53/78] feat: Add OmniRoute - Free LLM Gateway Template (#920) * feat: Add OmniRoute - Free LLM Gateway Template Adding the OmniRoute template to Dokploy. Never stop coding. Free AI gateway: one endpoint, 160+ providers (50+ free), connect Claude Code, Codex, Cursor, Cline & Copilot to FREE Claude/GPT/Gemini. RTK+Caveman stacked compression saves 15-95% tokens, smart auto-fallback, MCP/A2A, multimodal APIs, Desktop/PWA. * fix: Testing files updated Testing files with pnpm updated. * fix(omniroute): pin OmniRoute image version, fix template.toml syntax, normalize metadata * chore: Add VS Code setting for git config auto-approval * chore(app): add pnpm-workspace.yaml to fix pnpm workspace config --- .vscode/settings.json | 5 +++ app/pnpm-workspace.yaml | 4 +++ blueprints/omniroute/docker-compose.yml | 40 ++++++++++++++++++++++++ blueprints/omniroute/omniroute.png | Bin 0 -> 7230 bytes blueprints/omniroute/template.toml | 31 ++++++++++++++++++ meta.json | 20 ++++++++++++ 6 files changed, 100 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 app/pnpm-workspace.yaml create mode 100644 blueprints/omniroute/docker-compose.yml create mode 100644 blueprints/omniroute/omniroute.png create mode 100644 blueprints/omniroute/template.toml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..50e5eba2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "chat.tools.terminal.autoApprove": { + "git config": true + } +} \ No newline at end of file diff --git a/app/pnpm-workspace.yaml b/app/pnpm-workspace.yaml new file mode 100644 index 00000000..c4ec45c0 --- /dev/null +++ b/app/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +packages: + - . +allowBuilds: + esbuild: false diff --git a/blueprints/omniroute/docker-compose.yml b/blueprints/omniroute/docker-compose.yml new file mode 100644 index 00000000..552c8f26 --- /dev/null +++ b/blueprints/omniroute/docker-compose.yml @@ -0,0 +1,40 @@ +services: + omniroute: + image: diegosouzapw/omniroute:3.8.17 + restart: unless-stopped + + expose: + - "20128" + - "20129" + + volumes: + - omniroute-prod-data:/app/data + + environment: + - NODE_ENV=production + - PORT=${PORT:-20128} + - DASHBOARD_PORT=${DASHBOARD_PORT:-${PORT:-20128}} + - API_PORT=${API_PORT:-20129} + - API_HOST=${API_HOST:-0.0.0.0} + - HOSTNAME=0.0.0.0 + - DATA_DIR=/app/data + - PATH=/home/node/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + + redis: + image: redis:8.6.2-alpine + restart: unless-stopped + volumes: + - redis-prod-data:/data + command: redis-server --save 60 1 --loglevel warning + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + interval: 10s + timeout: 5s + retries: 3 + +volumes: + omniroute-prod-data: + + redis-prod-data: + + diff --git a/blueprints/omniroute/omniroute.png b/blueprints/omniroute/omniroute.png new file mode 100644 index 0000000000000000000000000000000000000000..4d4a0d1407f0f0db9dc10e3310d0c8893681cc5a GIT binary patch literal 7230 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 E0>SdMVE_OC literal 0 HcmV?d00001 diff --git a/blueprints/omniroute/template.toml b/blueprints/omniroute/template.toml new file mode 100644 index 00000000..ae4a61e0 --- /dev/null +++ b/blueprints/omniroute/template.toml @@ -0,0 +1,31 @@ +[variables] +main_domain = "${domain}" + +[config] +[[config.domains]] +serviceName = "omniroute" +port = 20128 +host = "${main_domain}" + +[config.env] +JWT_SECRET = "${base64:64}" +API_KEY_SECRET = "${base64:64}" +INITIAL_PASSWORD = "${base64:64}" +STORAGE_ENCRYPTION_KEY = "${base64:64}" +STORAGE_ENCRYPTION_KEY_VERSION = "v1" +DISABLE_SQLITE_AUTO_BACKUP = false +PORT = 20128 +API_PORT = 20129 +OMNIROUTE_USE_TURBOPACK = 1 +NODE_ENV = "production" +MACHINE_ID_SALT = "endpoint-proxy-salt" +AUTH_COOKIE_SECURE = false +REQUIRE_API_KEY = false +ALLOW_API_KEY_REVEAL = false +BASE_URL = "http://localhost:20128" +CLOUD_URL = "" +NEXT_PUBLIC_BASE_URL = "http://localhost:20128" +ENABLE_SOCKS5_PROXY = true +NEXT_PUBLIC_ENABLE_SOCKS5_PROXY = true +PROVIDER_LIMITS_SYNC_INTERVAL_MINUTES = 70 +APP_LOG_TO_FILE = true diff --git a/meta.json b/meta.json index 2e6eaefb..0bb03d88 100644 --- a/meta.json +++ b/meta.json @@ -4931,6 +4931,26 @@ "self-hosted" ] }, + { + "id": "omniroute", + "name": "OmniRoute", + "version": "3.8.17", + "description": "The Free AI Gateway - Never stop coding. Connect every AI tool to 177 providers — 50+ free — through one endpoint. Auto-fallback. RTK + Caveman compression saves 15–95% tokens. Never hit limits. ~1.9B+ documented free tokens/month — up to ~2.5B in your first month with signup credits.", + "logo": "omniroute.png", + "links": { + "github": "https://github.com/diegosouzapw/OmniRoute", + "website": "https://github.com/diegosouzapw/OmniRoute", + "docs": "https://github.com/diegosouzapw/OmniRoute/wiki" + }, + "tags": [ + "artificial-intelligence", + "gateway", + "generative-ai", + "llm", + "monitoring", + "routing" + ] + }, { "id": "onedev", "name": "OneDev", From 438a079a6bbed7155d1e204a6685680712651d36 Mon Sep 17 00:00:00 2001 From: Magique <45526481+MagiqueDeveloper@users.noreply.github.com> Date: Wed, 8 Jul 2026 05:44:17 +0800 Subject: [PATCH 54/78] fix(discourse): replace deprecated Bitnami images with official alternatives (#910) * refactor(docker-compose): update PostgreSQL and Redis images, adjust environment variables * chore(meta.json): update Discourse version to 2026.1.4 * chore(docker-compose): comment out DISCOURSE_FORCE_HTTPS for production * fix(docker-compose): update Discourse app port to 80 for production --- blueprints/discourse/docker-compose.yml | 80 +++++++++---------------- blueprints/discourse/template.toml | 6 +- meta.json | 2 +- 3 files changed, 32 insertions(+), 56 deletions(-) diff --git a/blueprints/discourse/docker-compose.yml b/blueprints/discourse/docker-compose.yml index caafab5b..d0353e8a 100644 --- a/blueprints/discourse/docker-compose.yml +++ b/blueprints/discourse/docker-compose.yml @@ -1,29 +1,24 @@ -version: '3.7' - services: discourse-db: - image: docker.io/bitnami/postgresql:17 - + image: pgvector/pgvector:pg16 volumes: - - discourse-postgresql-data:/bitnami/postgresql + - discourse-postgresql-data:/var/lib/postgresql/data environment: - POSTGRESQL_USERNAME: bn_discourse - POSTGRESQL_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRESQL_DATABASE: bitnami_discourse + POSTGRES_USER: discourse + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: discourse healthcheck: - test: ["CMD-SHELL", "pg_isready -U bn_discourse -d bitnami_discourse"] + test: ["CMD-SHELL", "pg_isready -U discourse -d discourse"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped discourse-redis: - image: docker.io/bitnami/redis:7.4 - + image: redis:7-alpine volumes: - - discourse-redis-data:/bitnami/redis - environment: - REDIS_PASSWORD: ${REDIS_PASSWORD} + - discourse-redis-data:/data + command: redis-server --requirepass ${REDIS_PASSWORD} healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 10s @@ -32,59 +27,38 @@ services: restart: unless-stopped discourse-app: - image: docker.io/bitnamilegacy/discourse:3.5.0 - + image: discourse/discourse:2026.1.4 volumes: - - discourse-data:/bitnami/discourse + - discourse-data:/shared depends_on: discourse-db: condition: service_healthy discourse-redis: condition: service_healthy environment: - DISCOURSE_HOST: ${DISCOURSE_HOST} - DISCOURSE_DATABASE_HOST: discourse-db - DISCOURSE_DATABASE_PORT_NUMBER: 5432 - DISCOURSE_DATABASE_USER: bn_discourse - DISCOURSE_DATABASE_PASSWORD: ${POSTGRES_PASSWORD} - DISCOURSE_DATABASE_NAME: bitnami_discourse + DISCOURSE_HOSTNAME: ${DISCOURSE_HOST} + DISCOURSE_DB_HOST: discourse-db + DISCOURSE_DB_PORT: 5432 + DISCOURSE_DB_USERNAME: discourse + DISCOURSE_DB_PASSWORD: ${POSTGRES_PASSWORD} + DISCOURSE_DB_NAME: discourse DISCOURSE_REDIS_HOST: discourse-redis - DISCOURSE_REDIS_PORT_NUMBER: 6379 + DISCOURSE_REDIS_PORT: 6379 DISCOURSE_REDIS_PASSWORD: ${REDIS_PASSWORD} - # Optional: Configure SMTP for email delivery - # DISCOURSE_SMTP_HOST: ${SMTP_HOST} + DISCOURSE_DEVELOPER_EMAILS: ${DISCOURSE_ADMIN_EMAIL} + # Required for email delivery - Discourse will not function correctly without SMTP + # DISCOURSE_SMTP_ADDRESS: ${SMTP_HOST} # DISCOURSE_SMTP_PORT: ${SMTP_PORT} - # DISCOURSE_SMTP_USER: ${SMTP_USER} - # DISCOURSE_SMTP_PASSWORD: ${SMTP_PASSWORD} - restart: unless-stopped - - discourse-sidekiq: - image: docker.io/bitnamilegacy/discourse:3.5.0 - - volumes: - - discourse-sidekiq-data:/bitnami/discourse - depends_on: - - discourse-app - command: /opt/bitnami/scripts/discourse-sidekiq/run.sh - environment: - DISCOURSE_HOST: ${DISCOURSE_HOST} - DISCOURSE_DATABASE_HOST: discourse-db - DISCOURSE_DATABASE_PORT_NUMBER: 5432 - DISCOURSE_DATABASE_USER: bn_discourse - DISCOURSE_DATABASE_PASSWORD: ${POSTGRES_PASSWORD} - DISCOURSE_DATABASE_NAME: bitnami_discourse - DISCOURSE_REDIS_HOST: discourse-redis - DISCOURSE_REDIS_PORT_NUMBER: 6379 - DISCOURSE_REDIS_PASSWORD: ${REDIS_PASSWORD} - # Optional: Configure SMTP for email delivery - # DISCOURSE_SMTP_HOST: ${SMTP_HOST} - # DISCOURSE_SMTP_PORT: ${SMTP_PORT} - # DISCOURSE_SMTP_USER: ${SMTP_USER} + # DISCOURSE_SMTP_USER_NAME: ${SMTP_USER} # DISCOURSE_SMTP_PASSWORD: ${SMTP_PASSWORD} + # DISCOURSE_SMTP_ENABLE_START_TLS: "true" + # DISCOURSE_NOTIFICATION_EMAIL: [email protected] + # DISCOURSE_DEVELOPER_EMAILS: [email protected] + ports: + - 80 restart: unless-stopped volumes: discourse-postgresql-data: discourse-redis-data: discourse-data: - discourse-sidekiq-data: \ No newline at end of file diff --git a/blueprints/discourse/template.toml b/blueprints/discourse/template.toml index c02aa2c7..10358839 100644 --- a/blueprints/discourse/template.toml +++ b/blueprints/discourse/template.toml @@ -1,11 +1,13 @@ [variables] main_domain = "${domain}" +admin_email = "${email}" postgres_password = "${password}" redis_password = "${password}" [config] env = [ "DISCOURSE_HOST=${main_domain}", + "DISCOURSE_ADMIN_EMAIL=${admin_email}", "POSTGRES_PASSWORD=${postgres_password}", "REDIS_PASSWORD=${redis_password}", ] @@ -13,5 +15,5 @@ mounts = [] [[config.domains]] serviceName = "discourse-app" -port = 3_000 -host = "${main_domain}" +port = 80 +host = "${main_domain}" \ No newline at end of file diff --git a/meta.json b/meta.json index 0bb03d88..9e132a03 100644 --- a/meta.json +++ b/meta.json @@ -1871,7 +1871,7 @@ { "id": "discourse", "name": "Discourse", - "version": "3.5.0", + "version": "2026.1.4", "description": "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", "logo": "discourse.svg", "links": { From 599588d095726d3f08b163bdf63ac04f67bb19d8 Mon Sep 17 00:00:00 2001 From: rjnevins Date: Tue, 7 Jul 2026 17:44:20 -0400 Subject: [PATCH 55/78] feat: add Taiga template (#895) Adds a Dokploy template for Taiga (taigaio/taiga-back, taiga-front, taiga-events, taiga-protected) wired through an nginx gateway, with RabbitMQ and Postgres backing services. Diverges from prior PR #547 in two ways: - Strips trailing inline `# ...` comments from env values in template.toml. Those comments were captured as part of the value when Dokploy expanded them into docker-compose, polluting strings like TAIGA_SCHEME, EMAIL_BACKEND, etc. and breaking Django URL/email configuration at runtime. - Adds explicit SUBPATH= and RABBITMQ_VHOST env entries that PR #547 referenced but never set, so variable expansion no longer falls back to empty/undefined. Validated locally with build-scripts/validate-template.ts, build-scripts/validate-docker-compose.ts, and meta.json required-field + dedupe-and-sort checks. No runtime deploy was performed locally. Co-authored-by: Richard Nevins --- blueprints/taiga/docker-compose.yml | 145 ++++++++++++++++++++++++++++ blueprints/taiga/logo.png | Bin 0 -> 15955 bytes blueprints/taiga/template.toml | 114 ++++++++++++++++++++++ meta.json | 18 ++++ 4 files changed, 277 insertions(+) create mode 100644 blueprints/taiga/docker-compose.yml create mode 100644 blueprints/taiga/logo.png create mode 100644 blueprints/taiga/template.toml diff --git a/blueprints/taiga/docker-compose.yml b/blueprints/taiga/docker-compose.yml new file mode 100644 index 00000000..7649d283 --- /dev/null +++ b/blueprints/taiga/docker-compose.yml @@ -0,0 +1,145 @@ +version: "3.8" + +x-environment: &default-back-environment + POSTGRES_DB: taiga + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_HOST: taiga-db + TAIGA_SECRET_KEY: ${SECRET_KEY} + TAIGA_SITES_SCHEME: ${TAIGA_SCHEME} + TAIGA_SITES_DOMAIN: ${TAIGA_DOMAIN} + TAIGA_SUBPATH: ${SUBPATH} + EMAIL_BACKEND: django.core.mail.backends.${EMAIL_BACKEND}.EmailBackend + DEFAULT_FROM_EMAIL: ${EMAIL_DEFAULT_FROM} + EMAIL_USE_TLS: ${EMAIL_USE_TLS} + EMAIL_USE_SSL: ${EMAIL_USE_SSL} + EMAIL_HOST: ${EMAIL_HOST} + EMAIL_PORT: ${EMAIL_PORT} + EMAIL_HOST_USER: ${EMAIL_HOST_USER} + EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD} + RABBITMQ_USER: ${RABBITMQ_USER} + RABBITMQ_PASS: ${RABBITMQ_PASS} + RABBITMQ_VHOST: ${RABBITMQ_VHOST} + ENABLE_TELEMETRY: ${ENABLE_TELEMETRY} + PUBLIC_REGISTER_ENABLED: ${PUBLIC_REGISTER_ENABLED} + +x-back-volumes: &default-back-volumes + - taiga-static-data:/taiga-back/static + - taiga-media-data:/taiga-back/media + +services: + taiga-db: + image: postgres:12.3 + restart: unless-stopped + environment: + POSTGRES_DB: taiga + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] + interval: 2s + timeout: 15s + retries: 5 + start_period: 3s + volumes: + - taiga-db-data:/var/lib/postgresql/data + + taiga-back: + image: taigaio/taiga-back:latest + restart: unless-stopped + environment: *default-back-environment + volumes: *default-back-volumes + depends_on: + taiga-db: + condition: service_healthy + taiga-events-rabbitmq: + condition: service_started + taiga-async-rabbitmq: + condition: service_started + + taiga-async: + image: taigaio/taiga-back:latest + restart: unless-stopped + entrypoint: ["/taiga-back/docker/async_entrypoint.sh"] + environment: *default-back-environment + volumes: *default-back-volumes + depends_on: + taiga-db: + condition: service_healthy + taiga-events-rabbitmq: + condition: service_started + taiga-async-rabbitmq: + condition: service_started + + taiga-async-rabbitmq: + image: rabbitmq:3.8-management-alpine + restart: unless-stopped + hostname: taiga-async-rabbitmq + environment: + RABBITMQ_ERLANG_COOKIE: ${RABBITMQ_ERLANG_COOKIE} + RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER} + RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASS} + RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_VHOST} + volumes: + - taiga-async-rabbitmq-data:/var/lib/rabbitmq + + taiga-front: + image: taigaio/taiga-front:latest + restart: unless-stopped + environment: + TAIGA_URL: ${TAIGA_SCHEME}://${TAIGA_DOMAIN} + TAIGA_WEBSOCKETS_URL: ${WEBSOCKETS_SCHEME}://${TAIGA_DOMAIN} + TAIGA_SUBPATH: ${SUBPATH} + PUBLIC_REGISTER_ENABLED: ${PUBLIC_REGISTER_ENABLED_FRONT} + + taiga-events: + image: taigaio/taiga-events:latest + restart: unless-stopped + environment: + RABBITMQ_USER: ${RABBITMQ_USER} + RABBITMQ_PASS: ${RABBITMQ_PASS} + RABBITMQ_VHOST: ${RABBITMQ_VHOST} + TAIGA_SECRET_KEY: ${SECRET_KEY} + depends_on: + taiga-events-rabbitmq: + condition: service_started + + taiga-events-rabbitmq: + image: rabbitmq:3.8-management-alpine + restart: unless-stopped + hostname: taiga-events-rabbitmq + environment: + RABBITMQ_ERLANG_COOKIE: ${RABBITMQ_ERLANG_COOKIE} + RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER} + RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASS} + RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_VHOST} + volumes: + - taiga-events-rabbitmq-data:/var/lib/rabbitmq + + taiga-protected: + image: taigaio/taiga-protected:latest + restart: unless-stopped + environment: + MAX_AGE: ${ATTACHMENTS_MAX_AGE} + SECRET_KEY: ${SECRET_KEY} + + taiga-gateway: + image: nginx:1.19-alpine + restart: unless-stopped + expose: + - 80 + volumes: + - ../files/volumes/nginx/taiga.conf:/etc/nginx/conf.d/default.conf:ro + - taiga-static-data:/taiga/static:ro + - taiga-media-data:/taiga/media:ro + depends_on: + - taiga-front + - taiga-back + - taiga-events + +volumes: + taiga-static-data: + taiga-media-data: + taiga-db-data: + taiga-async-rabbitmq-data: + taiga-events-rabbitmq-data: diff --git a/blueprints/taiga/logo.png b/blueprints/taiga/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..064c83ef27f2fe493b7eacd3230fbe73012db41a GIT binary patch literal 15955 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 Bx9I=? literal 0 HcmV?d00001 diff --git a/blueprints/taiga/template.toml b/blueprints/taiga/template.toml new file mode 100644 index 00000000..c528fd32 --- /dev/null +++ b/blueprints/taiga/template.toml @@ -0,0 +1,114 @@ +[variables] +main_domain = "${domain}" +postgres_user = "taiga" +postgres_password = "${password:32}" +secret_key = "${password:64}" +rabbitmq_user = "taiga" +rabbitmq_password = "${password:32}" +rabbitmq_erlang_cookie = "${password:32}" + +[config] +env = [ + "TAIGA_SCHEME=https", + "TAIGA_DOMAIN=${main_domain}", + "WEBSOCKETS_SCHEME=wss", + "SUBPATH=", + "SECRET_KEY=${secret_key}", + "POSTGRES_USER=${postgres_user}", + "POSTGRES_PASSWORD=${postgres_password}", + "EMAIL_BACKEND=console", + "EMAIL_HOST=smtp.example.com", + "EMAIL_PORT=587", + "EMAIL_HOST_USER=user", + "EMAIL_HOST_PASSWORD=password", + "EMAIL_DEFAULT_FROM=changeme@example.com", + "EMAIL_USE_TLS=False", + "EMAIL_USE_SSL=False", + "RABBITMQ_USER=${rabbitmq_user}", + "RABBITMQ_PASS=${rabbitmq_password}", + "RABBITMQ_VHOST=taiga", + "RABBITMQ_ERLANG_COOKIE=${rabbitmq_erlang_cookie}", + "ATTACHMENTS_MAX_AGE=360", + "ENABLE_TELEMETRY=False", + "PUBLIC_REGISTER_ENABLED=False", + "PUBLIC_REGISTER_ENABLED_FRONT=false", +] + +[[config.domains]] +serviceName = "taiga-gateway" +port = 80 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "/volumes/nginx/taiga.conf" +content = """ +server { + listen 80 default_server; + + client_max_body_size 100M; + charset utf-8; + + location / { + proxy_pass http://taiga-front/; + proxy_pass_header Server; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + } + + location /api/ { + proxy_pass http://taiga-back:8000/api/; + proxy_pass_header Server; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + } + + location /admin/ { + proxy_pass http://taiga-back:8000/admin/; + proxy_pass_header Server; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + } + + location /static/ { + alias /taiga/static/; + } + + location /_protected/ { + internal; + alias /taiga/media/; + add_header Content-disposition "attachment"; + } + + location /media/exports/ { + alias /taiga/media/exports/; + add_header Content-disposition "attachment"; + } + + location /media/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://taiga-protected:8003/; + proxy_redirect off; + } + + location /events { + proxy_pass http://taiga-events:8888/events; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_connect_timeout 7d; + proxy_send_timeout 7d; + proxy_read_timeout 7d; + } +} +""" diff --git a/meta.json b/meta.json index 9e132a03..80889bb6 100644 --- a/meta.json +++ b/meta.json @@ -6654,6 +6654,24 @@ "backup" ] }, + { + "id": "taiga", + "name": "Taiga", + "version": "latest", + "description": "Taiga is a free and open-source project management platform for agile developers, designers and project managers.", + "logo": "logo.png", + "links": { + "github": "https://github.com/taigaio/taiga-docker", + "website": "https://taiga.io/", + "docs": "https://docs.taiga.io/" + }, + "tags": [ + "project-management", + "agile", + "scrum", + "kanban" + ] + }, { "id": "tailscale-exitnode", "name": "Tailscale Exit nodes", From d1cc00391e1e7bb0c1ade207fe5904a8f3b33c90 Mon Sep 17 00:00:00 2001 From: autonomousventurelab-jpg Date: Tue, 7 Jul 2026 14:44:23 -0700 Subject: [PATCH 56/78] feat: add HedgeDoc template (#893) Co-authored-by: Mauricio Siu --- blueprints/hedgedoc/docker-compose.yml | 37 ++++++++++++++++++++++++++ blueprints/hedgedoc/hedgedoc.svg | 32 ++++++++++++++++++++++ blueprints/hedgedoc/template.toml | 16 +++++++++++ meta.json | 19 +++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 blueprints/hedgedoc/docker-compose.yml create mode 100644 blueprints/hedgedoc/hedgedoc.svg create mode 100644 blueprints/hedgedoc/template.toml diff --git a/blueprints/hedgedoc/docker-compose.yml b/blueprints/hedgedoc/docker-compose.yml new file mode 100644 index 00000000..8ee2e567 --- /dev/null +++ b/blueprints/hedgedoc/docker-compose.yml @@ -0,0 +1,37 @@ +version: "3.8" + +services: + hedgedoc: + image: quay.io/hedgedoc/hedgedoc:1.10.8 + restart: unless-stopped + depends_on: + database: + condition: service_healthy + environment: + - CMD_DB_URL=${CMD_DB_URL} + - CMD_DOMAIN=${CMD_DOMAIN} + - CMD_PROTOCOL_USESSL=${CMD_PROTOCOL_USESSL} + - CMD_URL_ADDPORT=${CMD_URL_ADDPORT} + volumes: + - hedgedoc_uploads:/hedgedoc/public/uploads + expose: + - "3000" + + database: + image: postgres:17.7-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=hedgedoc + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=hedgedoc + volumes: + - hedgedoc_database:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U hedgedoc -d hedgedoc"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + hedgedoc_uploads: + hedgedoc_database: diff --git a/blueprints/hedgedoc/hedgedoc.svg b/blueprints/hedgedoc/hedgedoc.svg new file mode 100644 index 00000000..0d236216 --- /dev/null +++ b/blueprints/hedgedoc/hedgedoc.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/hedgedoc/template.toml b/blueprints/hedgedoc/template.toml new file mode 100644 index 00000000..8e82e9d5 --- /dev/null +++ b/blueprints/hedgedoc/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +database_password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "hedgedoc" +port = 3000 +host = "${main_domain}" + +[config.env] +POSTGRES_PASSWORD = "${database_password}" +CMD_DB_URL = "postgres://hedgedoc:${database_password}@database:5432/hedgedoc" +CMD_DOMAIN = "${main_domain}" +CMD_PROTOCOL_USESSL = "true" +CMD_URL_ADDPORT = "false" diff --git a/meta.json b/meta.json index 80889bb6..f20f46d6 100644 --- a/meta.json +++ b/meta.json @@ -3106,6 +3106,25 @@ "self-hosted" ] }, + { + "id": "hedgedoc", + "name": "HedgeDoc", + "version": "1.10.8", + "description": "HedgeDoc is a collaborative Markdown editor for teams that need real-time notes, documentation, and knowledge sharing.", + "logo": "hedgedoc.svg", + "links": { + "github": "https://github.com/hedgedoc/hedgedoc", + "website": "https://hedgedoc.org/", + "docs": "https://docs.hedgedoc.org/" + }, + "tags": [ + "markdown", + "notes", + "documentation", + "collaboration", + "wiki" + ] + }, { "id": "hermes", "name": "Hermes", From 3d74ad7c086e1d79153b95311d28c634239db676 Mon Sep 17 00:00:00 2001 From: Abdelatif Djeddou <155508467+BinkyTwin@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:44:27 +0200 Subject: [PATCH 57/78] feat: add Soft Serve template (#889) * feat: add Soft Serve template * fix: quote Soft Serve listen addresses --------- Co-authored-by: Mauricio Siu --- blueprints/soft-serve/docker-compose.yml | 27 +++++++++++ blueprints/soft-serve/soft-serve.svg | 10 ++++ blueprints/soft-serve/template.toml | 62 ++++++++++++++++++++++++ meta.json | 18 +++++++ 4 files changed, 117 insertions(+) create mode 100644 blueprints/soft-serve/docker-compose.yml create mode 100644 blueprints/soft-serve/soft-serve.svg create mode 100644 blueprints/soft-serve/template.toml diff --git a/blueprints/soft-serve/docker-compose.yml b/blueprints/soft-serve/docker-compose.yml new file mode 100644 index 00000000..ac875d21 --- /dev/null +++ b/blueprints/soft-serve/docker-compose.yml @@ -0,0 +1,27 @@ +version: "3.8" + +services: + soft-serve: + image: charmcli/soft-serve:v0.11.6 + restart: unless-stopped + expose: + - "23231" + - "23232" + - "23233" + - "9418" + environment: + SOFT_SERVE_NAME: ${SOFT_SERVE_NAME} + SOFT_SERVE_DATA_PATH: /soft-serve + SOFT_SERVE_INITIAL_ADMIN_KEYS: ${SOFT_SERVE_INITIAL_ADMIN_KEYS} + SOFT_SERVE_SSH_LISTEN_ADDR: ":23231" + SOFT_SERVE_SSH_PUBLIC_URL: ${SOFT_SERVE_SSH_PUBLIC_URL} + SOFT_SERVE_HTTP_LISTEN_ADDR: ":23232" + SOFT_SERVE_HTTP_PUBLIC_URL: ${SOFT_SERVE_HTTP_PUBLIC_URL} + SOFT_SERVE_GIT_LISTEN_ADDR: ":9418" + SOFT_SERVE_STATS_LISTEN_ADDR: ":23233" + SOFT_SERVE_LOG_LEVEL: ${SOFT_SERVE_LOG_LEVEL} + volumes: + - soft-serve-data:/soft-serve + +volumes: + soft-serve-data: diff --git a/blueprints/soft-serve/soft-serve.svg b/blueprints/soft-serve/soft-serve.svg new file mode 100644 index 00000000..0bb41d55 --- /dev/null +++ b/blueprints/soft-serve/soft-serve.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/blueprints/soft-serve/template.toml b/blueprints/soft-serve/template.toml new file mode 100644 index 00000000..f18f883c --- /dev/null +++ b/blueprints/soft-serve/template.toml @@ -0,0 +1,62 @@ +[variables] +main_domain = "${domain}" +server_name = "Soft Serve" +initial_admin_keys = "" +http_public_url = "http://${main_domain}" +ssh_public_url = "ssh://${main_domain}:23231" +log_level = "info" + +[[config.domains]] +serviceName = "soft-serve" +port = 23232 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "README.md" +content = """# Soft Serve + +This template deploys Soft Serve, a self-hosted Git server with an SSH TUI, HTTP Git access, Git LFS support, and a lightweight web endpoint. + +## First Start + +Set `initial_admin_keys` to one or more SSH public keys before the first deployment. Soft Serve uses this value to create the initial `admin` user. The keys are consumed during initialization, so changing this variable later does not rewrite an already initialized data volume. + +Example value: + +```text +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... your-name +``` + +## HTTP Access + +The Dokploy domain routes to Soft Serve's HTTP listener: + +- HTTP public URL: `http://${main_domain}` +- HTTP Git clone example: `git clone http://${main_domain}/repo.git` + +Set `http_public_url` to `https://${main_domain}` before deploying if your Dokploy domain enforces TLS. + +Private HTTP access uses tokens created through the SSH admin interface. + +## SSH and Git Protocol Ports + +Soft Serve also listens inside the deployment on: + +- SSH/TUI: `23231` +- Stats: `23233` +- Git protocol: `9418` + +This template does not publish host TCP ports directly. Expose the SSH or Git protocol ports separately in Dokploy if you need direct terminal/TUI access, then use the configured `ssh_public_url` for clone commands. + +## Storage + +Repositories, configuration, keys, and the default SQLite database are stored in the persistent `soft-serve-data` volume mounted at `/soft-serve`. +""" + +[config.env] +SOFT_SERVE_NAME = "${server_name}" +SOFT_SERVE_INITIAL_ADMIN_KEYS = "${initial_admin_keys}" +SOFT_SERVE_HTTP_PUBLIC_URL = "${http_public_url}" +SOFT_SERVE_SSH_PUBLIC_URL = "${ssh_public_url}" +SOFT_SERVE_LOG_LEVEL = "${log_level}" diff --git a/meta.json b/meta.json index f20f46d6..73391677 100644 --- a/meta.json +++ b/meta.json @@ -6410,6 +6410,24 @@ "self-hosted" ] }, + { + "id": "soft-serve", + "name": "Soft Serve", + "version": "v0.11.6", + "description": "Soft Serve is a self-hosted Git server with an SSH TUI, HTTP Git access, Git LFS support, and simple repository management.", + "logo": "soft-serve.svg", + "links": { + "github": "https://github.com/charmbracelet/soft-serve", + "website": "https://charm.sh/", + "docs": "https://github.com/charmbracelet/soft-serve/blob/main/README.md" + }, + "tags": [ + "git", + "ssh", + "developer-tools", + "self-hosted" + ] + }, { "id": "soketi", "name": "Soketi", From 3829b188d842d1da6bc5e175fb1e5175e49eac23 Mon Sep 17 00:00:00 2001 From: Juan del Peral Date: Tue, 7 Jul 2026 18:44:30 -0300 Subject: [PATCH 58/78] feat(meta): add Arche AI workspace platform to meta.json (#875) Introduce Arche, a self-hosted AI workspace platform, with relevant metadata including description, logo, and GitHub link. This addition enhances the diversity of tools available in the project. Co-authored-by: Mauricio Siu --- blueprints/arche/arche.svg | 1 + blueprints/arche/docker-compose.yml | 102 ++++++++++++++++++++++++++++ blueprints/arche/template.toml | 33 +++++++++ build-scripts/pnpm-workspace.yaml | 2 + meta.json | 17 +++++ 5 files changed, 155 insertions(+) create mode 100644 blueprints/arche/arche.svg create mode 100644 blueprints/arche/docker-compose.yml create mode 100644 blueprints/arche/template.toml create mode 100644 build-scripts/pnpm-workspace.yaml diff --git a/blueprints/arche/arche.svg b/blueprints/arche/arche.svg new file mode 100644 index 00000000..06ae9720 --- /dev/null +++ b/blueprints/arche/arche.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/arche/docker-compose.yml b/blueprints/arche/docker-compose.yml new file mode 100644 index 00000000..cbefbe7e --- /dev/null +++ b/blueprints/arche/docker-compose.yml @@ -0,0 +1,102 @@ +version: "3.8" + +# Arche — AI workspace platform that spawns per-user OpenCode containers +# via a Docker socket proxy. +# +# PRE-REQUISITES (run once on the VPS before deploying): +# mkdir -p /opt/arche/kb-content /opt/arche/kb-config /opt/arche/users +# +# Optional (only needed to seed the Knowledge Base): +# git init --bare --initial-branch=main /opt/arche/kb-content +# git init --bare --initial-branch=main /opt/arche/kb-config +# +# POST-DEPLOY (set in Dokploy → Environment, then redeploy): +# 1. Deploy this template once. Dokploy auto-creates a network for the +# compose stack. +# 2. SSH into the host and find the network name: +# docker network ls --format '{{.Name}}' | grep arche +# It looks like `_default` or similar. +# 3. Add the env var OPENCODE_NETWORK= and redeploy. +# Without it, spawned user workspaces will fail to start. +# +# IMAGE ARCHITECTURE: +# Default tags target linux/amd64. For ARM64 hosts (Apple Silicon, +# Ampere, Graviton), override both images to the `-arm64` variants in +# Dokploy → Environment: +# ARCHE_WEB_IMAGE=ghcr.io/peaberry-studio/arche/web:latest-arm64 +# ARCHE_WORKSPACE_IMAGE=ghcr.io/peaberry-studio/arche/workspace:latest-arm64 + +services: + web: + image: ${ARCHE_WEB_IMAGE:-ghcr.io/peaberry-studio/arche/web:latest} + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + docker-socket-proxy: + condition: service_started + environment: + ARCHE_DOMAIN: ${ARCHE_DOMAIN} + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/arche?schema=public + + ARCHE_SESSION_PEPPER: ${ARCHE_SESSION_PEPPER} + ARCHE_ENCRYPTION_KEY: ${ARCHE_ENCRYPTION_KEY} + ARCHE_INTERNAL_TOKEN: ${ARCHE_INTERNAL_TOKEN} + ARCHE_GATEWAY_TOKEN_SECRET: ${ARCHE_GATEWAY_TOKEN_SECRET} + ARCHE_CONNECTOR_OAUTH_STATE_SECRET: ${ARCHE_CONNECTOR_OAUTH_STATE_SECRET} + + ARCHE_GATEWAY_TOKEN_TTL_SECONDS: "86400" + ARCHE_GATEWAY_BASE_URL: http://web:3000 + ARCHE_PUBLIC_BASE_URL: https://${ARCHE_DOMAIN} + ARCHE_SESSION_TTL_DAYS: "7" + ARCHE_COOKIE_SECURE: "true" + + ARCHE_SEED_ADMIN_EMAIL: ${ARCHE_SEED_ADMIN_EMAIL} + ARCHE_SEED_ADMIN_PASSWORD: ${ARCHE_SEED_ADMIN_PASSWORD} + ARCHE_SEED_ADMIN_SLUG: ${ARCHE_SEED_ADMIN_SLUG:-admin} + + CONTAINER_PROXY_HOST: docker-socket-proxy + CONTAINER_PROXY_PORT: "2375" + OPENCODE_IMAGE: ${ARCHE_WORKSPACE_IMAGE:-ghcr.io/peaberry-studio/arche/workspace:latest} + OPENCODE_NETWORK: ${OPENCODE_NETWORK} + + KB_CONTENT_HOST_PATH: /opt/arche/kb-content + KB_CONFIG_HOST_PATH: /opt/arche/kb-config + ARCHE_USERS_PATH: /opt/arche/users + expose: + - "3000" + volumes: + - /opt/arche/kb-content:/kb-content + - /opt/arche/kb-config:/kb-config + - /opt/arche/users:/opt/arche/users + + postgres: + image: postgres:16 + restart: unless-stopped + environment: + POSTGRES_DB: arche + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + + docker-socket-proxy: + image: ghcr.io/tecnativa/docker-socket-proxy:master + restart: unless-stopped + environment: + CONTAINERS: 1 + NETWORKS: 1 + IMAGES: 1 + INFO: 1 + POST: 1 + VOLUMES: 1 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + +volumes: + postgres_data: diff --git a/blueprints/arche/template.toml b/blueprints/arche/template.toml new file mode 100644 index 00000000..5772c7b3 --- /dev/null +++ b/blueprints/arche/template.toml @@ -0,0 +1,33 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +session_pepper = "${base64:32}" +encryption_key = "${base64:32}" +internal_token = "${base64:32}" +gateway_token_secret = "${base64:32}" +connector_oauth_state_secret = "${base64:32}" +seed_admin_email = "${email}" +seed_admin_password = "${password:16}" + +[config] +env = [ + "ARCHE_DOMAIN=${main_domain}", + "POSTGRES_PASSWORD=${postgres_password}", + "ARCHE_SESSION_PEPPER=${session_pepper}", + "ARCHE_ENCRYPTION_KEY=${encryption_key}", + "ARCHE_INTERNAL_TOKEN=${internal_token}", + "ARCHE_GATEWAY_TOKEN_SECRET=${gateway_token_secret}", + "ARCHE_CONNECTOR_OAUTH_STATE_SECRET=${connector_oauth_state_secret}", + "ARCHE_SEED_ADMIN_EMAIL=${seed_admin_email}", + "ARCHE_SEED_ADMIN_PASSWORD=${seed_admin_password}", + "ARCHE_SEED_ADMIN_SLUG=admin", + "ARCHE_WEB_IMAGE=ghcr.io/peaberry-studio/arche/web:latest", + "ARCHE_WORKSPACE_IMAGE=ghcr.io/peaberry-studio/arche/workspace:latest", + "OPENCODE_NETWORK=", +] +mounts = [] + +[[config.domains]] +serviceName = "web" +port = 3000 +host = "${main_domain}" diff --git a/build-scripts/pnpm-workspace.yaml b/build-scripts/pnpm-workspace.yaml new file mode 100644 index 00000000..00f6fc47 --- /dev/null +++ b/build-scripts/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + esbuild: set this to true or false diff --git a/meta.json b/meta.json index 73391677..2022bca3 100644 --- a/meta.json +++ b/meta.json @@ -449,6 +449,23 @@ "nosql" ] }, + { + "id": "arche", + "name": "Arche", + "version": "latest", + "description": "Arche is a self-hosted AI workspace platform that spawns per-user OpenCode coding containers backed by a shared Knowledge Base. Requires the `arche-internal` external Docker network and `/opt/arche/{kb-content,kb-config,users}` directories on the host before deploy. Images are linux/amd64 by default; override `ARCHE_WEB_IMAGE` and `ARCHE_WORKSPACE_IMAGE` to the `-arm64` tag variants for ARM64 hosts.", + "logo": "arche.svg", + "links": { + "github": "https://github.com/peaberry-studio/arche" + }, + "tags": [ + "ai", + "coding", + "workspace", + "self-hosted", + "llm" + ] + }, { "id": "archivebox", "name": "ArchiveBox", From 641fd75bf72689cbcefe282054a32f7ee8de1cd7 Mon Sep 17 00:00:00 2001 From: Abian Suarez <33789675+AbianS@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:44:33 +0200 Subject: [PATCH 59/78] feat: add rustrak and rustrak-full templates (#866) * feat: add rustrak and rustrak-full templates - rustrak: server-only with SQLite, single service, no external DB - rustrak-full: server + Next.js dashboard + PostgreSQL Both use abians7/rustrak-server:v0.2.1, Sentry-compatible error tracking * chore: update rustrak logo to new design --- blueprints/rustrak-full/docker-compose.yml | 49 ++++++++++++++++++++++ blueprints/rustrak-full/rustrak.svg | 6 +++ blueprints/rustrak-full/template.toml | 27 ++++++++++++ blueprints/rustrak/docker-compose.yml | 20 +++++++++ blueprints/rustrak/rustrak.svg | 6 +++ blueprints/rustrak/template.toml | 17 ++++++++ meta.json | 39 +++++++++++++++++ 7 files changed, 164 insertions(+) create mode 100644 blueprints/rustrak-full/docker-compose.yml create mode 100644 blueprints/rustrak-full/rustrak.svg create mode 100644 blueprints/rustrak-full/template.toml create mode 100644 blueprints/rustrak/docker-compose.yml create mode 100644 blueprints/rustrak/rustrak.svg create mode 100644 blueprints/rustrak/template.toml diff --git a/blueprints/rustrak-full/docker-compose.yml b/blueprints/rustrak-full/docker-compose.yml new file mode 100644 index 00000000..16f734c7 --- /dev/null +++ b/blueprints/rustrak-full/docker-compose.yml @@ -0,0 +1,49 @@ +version: "3.8" + +services: + rustrak-server: + image: abians7/rustrak-server:v0.2.1-postgres + restart: unless-stopped + environment: + - HOST=0.0.0.0 + - PORT=8080 + - RUST_LOG=info + - SSL_PROXY=true + - DATABASE_URL=postgres://rustrak:${POSTGRES_PASSWORD}@postgres:5432/rustrak + - SESSION_SECRET_KEY=${SESSION_SECRET_KEY} + - CREATE_SUPERUSER=${CREATE_SUPERUSER} + ports: + - 8080 + depends_on: + postgres: + condition: service_healthy + + rustrak-ui: + image: abians7/rustrak-ui:v0.1.6 + restart: unless-stopped + environment: + - RUSTRAK_API_URL=${RUSTRAK_API_URL} + ports: + - 3000 + depends_on: + - rustrak-server + + postgres: + image: postgres:16-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=rustrak + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=rustrak + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U rustrak -d rustrak"] + interval: 10s + timeout: 5s + retries: 5 + expose: + - 5432 + +volumes: + postgres_data: diff --git a/blueprints/rustrak-full/rustrak.svg b/blueprints/rustrak-full/rustrak.svg new file mode 100644 index 00000000..11d87e47 --- /dev/null +++ b/blueprints/rustrak-full/rustrak.svg @@ -0,0 +1,6 @@ + + + + diff --git a/blueprints/rustrak-full/template.toml b/blueprints/rustrak-full/template.toml new file mode 100644 index 00000000..383f486f --- /dev/null +++ b/blueprints/rustrak-full/template.toml @@ -0,0 +1,27 @@ +[variables] +main_domain = "${domain}" +api_domain = "${domain}" +postgres_password = "${password:32}" +session_secret_key = "${password:64}" +admin_email = "${email}" +admin_password = "${password:16}" +create_superuser = "${admin_email}:${admin_password}" +rustrak_api_url = "https://${api_domain}" + +[config] +env = [ + "POSTGRES_PASSWORD=${postgres_password}", + "SESSION_SECRET_KEY=${session_secret_key}", + "CREATE_SUPERUSER=${create_superuser}", + "RUSTRAK_API_URL=${rustrak_api_url}", +] + +[[config.domains]] +serviceName = "rustrak-ui" +port = 3000 +host = "${main_domain}" + +[[config.domains]] +serviceName = "rustrak-server" +port = 8080 +host = "${api_domain}" diff --git a/blueprints/rustrak/docker-compose.yml b/blueprints/rustrak/docker-compose.yml new file mode 100644 index 00000000..e4c52b0f --- /dev/null +++ b/blueprints/rustrak/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + rustrak: + image: abians7/rustrak-server:v0.2.1 + restart: unless-stopped + environment: + - HOST=0.0.0.0 + - PORT=8080 + - RUST_LOG=info + - SSL_PROXY=true + - SESSION_SECRET_KEY=${SESSION_SECRET_KEY} + - CREATE_SUPERUSER=${CREATE_SUPERUSER} + volumes: + - rustrak_data:/data + ports: + - 8080 + +volumes: + rustrak_data: diff --git a/blueprints/rustrak/rustrak.svg b/blueprints/rustrak/rustrak.svg new file mode 100644 index 00000000..11d87e47 --- /dev/null +++ b/blueprints/rustrak/rustrak.svg @@ -0,0 +1,6 @@ + + + + diff --git a/blueprints/rustrak/template.toml b/blueprints/rustrak/template.toml new file mode 100644 index 00000000..819f6f85 --- /dev/null +++ b/blueprints/rustrak/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +session_secret_key = "${password:64}" +admin_email = "${email}" +admin_password = "${password:16}" +create_superuser = "${admin_email}:${admin_password}" + +[config] +env = [ + "SESSION_SECRET_KEY=${session_secret_key}", + "CREATE_SUPERUSER=${create_superuser}", +] + +[[config.domains]] +serviceName = "rustrak" +port = 8080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 2022bca3..653a7985 100644 --- a/meta.json +++ b/meta.json @@ -6127,6 +6127,45 @@ "rust" ] }, + { + "id": "rustrak", + "name": "Rustrak", + "version": "0.2.1", + "description": "Self-hosted error tracking compatible with Sentry SDKs. Server only, written in Rust with <100MB memory footprint and SQLite storage. No external database required.", + "logo": "rustrak.svg", + "links": { + "github": "https://github.com/AbianS/rustrak", + "website": "https://abians.github.io/rustrak/", + "docs": "https://abians.github.io/rustrak/" + }, + "tags": [ + "monitoring", + "error-tracking", + "sentry", + "rust", + "observability" + ] + }, + { + "id": "rustrak-full", + "name": "Rustrak (Full Stack)", + "version": "0.2.1", + "description": "Self-hosted error tracking compatible with Sentry SDKs. Includes server, web dashboard UI, and PostgreSQL. Written in Rust with <100MB memory footprint.", + "logo": "rustrak.svg", + "links": { + "github": "https://github.com/AbianS/rustrak", + "website": "https://abians.github.io/rustrak/", + "docs": "https://abians.github.io/rustrak/" + }, + "tags": [ + "monitoring", + "error-tracking", + "sentry", + "rust", + "observability", + "postgresql" + ] + }, { "id": "rutorrent", "name": "ruTorrent", From 31bff09eaaf3f0ca134579013f3591c249d7f843 Mon Sep 17 00:00:00 2001 From: luolink <97769040+abel-yelin@users.noreply.github.com> Date: Wed, 8 Jul 2026 05:44:36 +0800 Subject: [PATCH 60/78] Add Marketing Dashboard template (#861) --- .../marketing-dashboard/docker-compose.yml | 47 +++++++++++++++++++ blueprints/marketing-dashboard/logo.svg | 8 ++++ blueprints/marketing-dashboard/template.toml | 30 ++++++++++++ meta.json | 19 ++++++++ 4 files changed, 104 insertions(+) create mode 100644 blueprints/marketing-dashboard/docker-compose.yml create mode 100644 blueprints/marketing-dashboard/logo.svg create mode 100644 blueprints/marketing-dashboard/template.toml diff --git a/blueprints/marketing-dashboard/docker-compose.yml b/blueprints/marketing-dashboard/docker-compose.yml new file mode 100644 index 00000000..637849e7 --- /dev/null +++ b/blueprints/marketing-dashboard/docker-compose.yml @@ -0,0 +1,47 @@ +services: + marketing-dashboard: + image: ghcr.io/abel-yelin/marketing-dashboard:latest + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + DATABASE_URL: ${DATABASE_URL} + DIRECT_URL: ${DIRECT_URL} + MARKETING_APP_URL: ${MARKETING_APP_URL} + MARKETING_COLLECTOR_UPSTREAM_URL: ${MARKETING_COLLECTOR_UPSTREAM_URL} + MARKETING_SESSION_TIMEOUT_MINUTES: ${MARKETING_SESSION_TIMEOUT_MINUTES} + MARKETING_PROXY_TIMEOUT_MS: ${MARKETING_PROXY_TIMEOUT_MS} + MARKETING_INGEST_MAX_BODY_BYTES: ${MARKETING_INGEST_MAX_BODY_BYTES} + MARKETING_INGEST_IP_RATE_LIMIT: ${MARKETING_INGEST_IP_RATE_LIMIT} + MARKETING_INGEST_CLIENT_RATE_LIMIT: ${MARKETING_INGEST_CLIENT_RATE_LIMIT} + MARKETING_INGEST_RATE_LIMIT_WINDOW_MS: ${MARKETING_INGEST_RATE_LIMIT_WINDOW_MS} + MARKETING_ENABLE_PILOT_API: ${MARKETING_ENABLE_PILOT_API} + MARKETING_DASHBOARD_ADMIN_USER: ${MARKETING_DASHBOARD_ADMIN_USER} + MARKETING_DASHBOARD_ADMIN_PASSWORD: ${MARKETING_DASHBOARD_ADMIN_PASSWORD} + ports: + - 3000 + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:3000/api/health > /dev/null || exit 1"] + interval: 15s + timeout: 5s + retries: 10 + start_period: 30s + + postgres: + image: postgres:16 + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - marketing-dashboard-postgres:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 10 + +volumes: + marketing-dashboard-postgres: diff --git a/blueprints/marketing-dashboard/logo.svg b/blueprints/marketing-dashboard/logo.svg new file mode 100644 index 00000000..07a4ae88 --- /dev/null +++ b/blueprints/marketing-dashboard/logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/blueprints/marketing-dashboard/template.toml b/blueprints/marketing-dashboard/template.toml new file mode 100644 index 00000000..531874ea --- /dev/null +++ b/blueprints/marketing-dashboard/template.toml @@ -0,0 +1,30 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +admin_password = "${password:32}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "marketing-dashboard" +port = 3000 +host = "${main_domain}" + +[config.env] +POSTGRES_DB = "marketing_dashboard" +POSTGRES_USER = "marketing_dashboard" +POSTGRES_PASSWORD = "${postgres_password}" +DATABASE_URL = "postgresql://marketing_dashboard:${postgres_password}@postgres:5432/marketing_dashboard?schema=public" +DIRECT_URL = "postgresql://marketing_dashboard:${postgres_password}@postgres:5432/marketing_dashboard?schema=public" +MARKETING_APP_URL = "https://${main_domain}" +MARKETING_COLLECTOR_UPSTREAM_URL = "https://${main_domain}/api/ingest/browser" +MARKETING_SESSION_TIMEOUT_MINUTES = "30" +MARKETING_PROXY_TIMEOUT_MS = "4000" +MARKETING_INGEST_MAX_BODY_BYTES = "65536" +MARKETING_INGEST_IP_RATE_LIMIT = "300" +MARKETING_INGEST_CLIENT_RATE_LIMIT = "1200" +MARKETING_INGEST_RATE_LIMIT_WINDOW_MS = "60000" +MARKETING_ENABLE_PILOT_API = "false" +MARKETING_DASHBOARD_ADMIN_USER = "admin" +MARKETING_DASHBOARD_ADMIN_PASSWORD = "${admin_password}" diff --git a/meta.json b/meta.json index 653a7985..6e2a3a34 100644 --- a/meta.json +++ b/meta.json @@ -4245,6 +4245,25 @@ "smtp" ] }, + { + "id": "marketing-dashboard", + "name": "Marketing Dashboard", + "version": "latest", + "description": "Self-hosted marketing operations dashboard for product-led growth, checkout analytics, paid media, SEO signals, and Stripe readiness.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/abel-yelin/marketing-dashboard", + "website": "https://github.com/abel-yelin/marketing-dashboard", + "docs": "https://github.com/abel-yelin/marketing-dashboard/blob/main/docs/production-deployment.md" + }, + "tags": [ + "analytics", + "marketing", + "self-hosted", + "postgresql", + "nextjs" + ] + }, { "id": "mattermost", "name": "Mattermost", From 7095691571ca4c3432dc9c2a1bda0c558f717ec2 Mon Sep 17 00:00:00 2001 From: Naterfute <106989409+naterfute@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:44:39 -0700 Subject: [PATCH 61/78] updated Pyrodactyl logo (#855) --- blueprints/pyrodactyl/pyrodactyl.png | Bin 4283 -> 1669 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/blueprints/pyrodactyl/pyrodactyl.png b/blueprints/pyrodactyl/pyrodactyl.png index 50c98fdd6cc5eddc02caab84dd2adb4d257bcbbc..81513193e0c3ef0fa738d9644ebce9837b52b656 100644 GIT binary patch literal 1669 zcmZ`(eLT~79RInMhgMnSdN7&@6WKfs&C~gX}!gU>am<*H5 z5xK&l5Jv7&Ce&1uhjn%90fkd(Gj_lF<6f`(qu1;6{k}h+_rvG=-}fw?wtLO0&8q+a zSVJL${xDX-Cs`W--(Ovku7d#+N%r3ZbLIfRJp%wrnBu+y05%Q)Cc^>1l@9P*?t)|l#DI_(W438f_^GYfSZaz{;N~*V#0xtS~_QTAl z0%7&D6a2K{lmC;dR;$Zu{i@;ZXvm)y0Ql9-42GdRFsEc?I8kiQdCyC;PvgStyN2jo zl&Vq%`!o*u>;{dd5b?S2paoI(khCUOY8#l?hb>{60bs=#1@sC${^ViF)P*z*ii^#< zW9{93X?A(AmEnZcM+fJRD4N-!ih-_Bev!7>vZ0jj}jPk?YSb`*_SE1&yzCQxHmg_PzTK)8q+<~;jC>Q}heOv$l^Yi^s_%vVlnh*VUeMlD_4O@l*~PlTu9F58(De}}SMZQ>I3njt zm=xc)otUKe!&&DwzP;L>XmHblNBm|)UG0Utx{tk3Zhdv1KeKypy=^wHB7EOMpc-Rj*P%b`l-$ zcz_d&Z^xH!X4Ftqv z`8B~kbo7jS34~-&H478pRz7bwIC;@p7VNT*m{-+Hv-W{eNcIRuD}mE{6(M z1w8`9e_atIwT&SIEOw%*YG_ICg1hw&BRy!PZ!mn4G-aoqc1?%Z>#>oOMH+5q2W@Y> zh-{jdH$ly7W-bW4F^psBz`)U9rJ0ez*2d=%Q;UxVqJ&LDCV583IAp_ZISFJm!pw!S zc5$g7>6FS{elNzd+|&no~iot z2a9AE_%Gug5X^M5~8Wxc4*`|;&ueZBKJsm_va->~wGjEiF}hHkLy*_;ZN zPuOBMS{7BVo4YU(LC*FM$<1p$pgt--TzBL3GR&;0W=2UO; zdq;du*UQW=5+!ZuyW=@wg&s^gw3_B#9Fj5+p|%QWUn~p#;~9Eo?_=r2kvE0$$y@<}@l7=XRKhOGQ=n;RQovrV5%q6(}(^9)l zCwpTfC-edK<@))HiQiWyom($derx^?ar^H`7mYvoZt&N)4I%|Y%m^sTH8LRz2EYzy z=VXI(w81$C+TmU8?Oh%5+i^Ho94?H&Hxk=4ZQYEc-p_74*AUh literal 4283 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 From 708b3019bf8bfdbbc99ab35fd0fd0c8d1b66523c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ya=C4=9F=C4=B1z=20Berke=20G=C3=BCven=C3=A7?= Date: Wed, 8 Jul 2026 00:44:42 +0300 Subject: [PATCH 62/78] feat(postiz): update docker-compose and template for Temporal integration (#851) --- blueprints/postiz/docker-compose.yml | 80 ++++++++++++++++++++++++++-- blueprints/postiz/template.toml | 26 ++++++--- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/blueprints/postiz/docker-compose.yml b/blueprints/postiz/docker-compose.yml index dfbf3ae9..a00832dd 100644 --- a/blueprints/postiz/docker-compose.yml +++ b/blueprints/postiz/docker-compose.yml @@ -13,10 +13,25 @@ services: DATABASE_URL: "postgresql://${DB_USER}:${DB_PASSWORD}@postiz-postgres:5432/${DB_NAME}" REDIS_URL: "redis://postiz-redis:6379" BACKEND_INTERNAL_URL: "http://localhost:3000" + TEMPORAL_ADDRESS: "temporal:7233" IS_GENERAL: "true" + DISABLE_REGISTRATION: "false" + RUN_CRON: "true" + NOT_SECURED: "true" STORAGE_PROVIDER: "local" UPLOAD_DIRECTORY: "/uploads" NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads" + + # === Social Media API Settings === + # These variables should be defined in the Dokploy Environment tab. + # For more configuration options and details, please refer to the official documentation. + FACEBOOK_APP_ID: ${FACEBOOK_APP_ID} + FACEBOOK_APP_SECRET: ${FACEBOOK_APP_SECRET} + LINKEDIN_CLIENT_ID: ${LINKEDIN_CLIENT_ID} + LINKEDIN_CLIENT_SECRET: ${LINKEDIN_CLIENT_SECRET} + X_API_KEY: ${X_API_KEY} + X_API_SECRET: ${X_API_SECRET} + volumes: - postiz-config:/config/ - postiz-uploads:/uploads/ @@ -25,6 +40,8 @@ services: condition: service_healthy postiz-redis: condition: service_healthy + temporal: + condition: service_started postiz-postgres: image: postgres:17-alpine @@ -35,27 +52,80 @@ services: POSTGRES_USER: ${DB_USER} POSTGRES_DB: ${DB_NAME} volumes: - - postiz-postgres-data:/var/lib/postgresql/data + - postgres-volume:/var/lib/postgresql/data healthcheck: - test: pg_isready -U ${DB_USER} -d ${DB_NAME} + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] interval: 10s timeout: 3s retries: 3 + start_period: 10s postiz-redis: image: redis:7.2 restart: always healthcheck: - test: redis-cli ping + test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 3 volumes: - postiz-redis-data:/data + temporal-elasticsearch: + image: elasticsearch:7.17.27 + environment: + - cluster.routing.allocation.disk.threshold_enabled=true + - cluster.routing.allocation.disk.watermark.low=512mb + - cluster.routing.allocation.disk.watermark.high=256mb + - cluster.routing.allocation.disk.watermark.flood_stage=128mb + - discovery.type=single-node + - ES_JAVA_OPTS=-Xms256m -Xmx256m + - xpack.security.enabled=false + volumes: + - temporal-es-data:/usr/share/elasticsearch/data + + temporal-postgresql: + image: postgres:16 + environment: + POSTGRES_PASSWORD: ${TEMPORAL_PASSWORD} + POSTGRES_USER: ${TEMPORAL_USER} + volumes: + - temporal-pg-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${TEMPORAL_USER}"] + interval: 10s + timeout: 5s + retries: 5 + + temporal: + image: temporalio/auto-setup:1.28.1 + depends_on: + temporal-postgresql: + condition: service_healthy + temporal-elasticsearch: + condition: service_started + environment: + - DB=postgres12 + - DB_PORT=5432 + - POSTGRES_USER=${TEMPORAL_USER} + - POSTGRES_PWD=${TEMPORAL_PASSWORD} + - POSTGRES_SEEDS=temporal-postgresql + - ENABLE_ES=true + - ES_SEEDS=temporal-elasticsearch + - ES_VERSION=v7 + - TEMPORAL_NAMESPACE=default + healthcheck: + test: ["CMD", "tctl", "--address", "127.0.0.1:7233", "workflow", "list"] + interval: 15s + timeout: 10s + retries: 30 + start_period: 120s + volumes: - postiz-postgres-data: + postgres-volume: postiz-redis-data: postiz-config: - postiz-uploads: \ No newline at end of file + postiz-uploads: + temporal-es-data: + temporal-pg-data: \ No newline at end of file diff --git a/blueprints/postiz/template.toml b/blueprints/postiz/template.toml index 260b77af..4528e909 100644 --- a/blueprints/postiz/template.toml +++ b/blueprints/postiz/template.toml @@ -3,19 +3,29 @@ main_domain = "${domain}" db_password = "${password}" db_user = "postiz" db_name = "postiz" +temporal_password = "${password}" +temporal_user = "temporal" jwt_secret = "${base64:32}" [config] +env = [ + "POSTIZ_HOST=${main_domain}", + "JWT_SECRET=${jwt_secret}", + "DB_USER=${db_user}", + "DB_PASSWORD=${db_password}", + "DB_NAME=${db_name}", + "TEMPORAL_USER=${temporal_user}", + "TEMPORAL_PASSWORD=${temporal_password}", + "FACEBOOK_APP_ID=", + "FACEBOOK_APP_SECRET=", + "LINKEDIN_CLIENT_ID=", + "LINKEDIN_CLIENT_SECRET=", + "X_API_KEY=", + "X_API_SECRET=" +] mounts = [] [[config.domains]] serviceName = "postiz-app" -port = 5_000 +port = 5000 host = "${main_domain}" - -[config.env] -POSTIZ_HOST = "${main_domain}" -DB_PASSWORD = "${db_password}" -DB_USER = "${db_user}" -DB_NAME = "${db_name}" -JWT_SECRET = "${jwt_secret}" From 06904b17bea8ee3f48eafcfecaeb25a465f028c7 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 7 Jul 2026 15:50:39 -0600 Subject: [PATCH 63/78] fix: repair CI broken by #875 (arche meta links + stray pnpm-workspace.yaml) (#977) * fix: repair CI broken by #875 merge - remove stray build-scripts/pnpm-workspace.yaml (placeholder content broke pnpm install in validate-docker-compose) - add missing links.website/docs to arche entry in meta.json (validate check) Co-Authored-By: Claude Fable 5 * chore: remove stray .vscode/settings.json introduced by #920 Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Fable 5 --- .vscode/settings.json | 5 ----- build-scripts/pnpm-workspace.yaml | 2 -- meta.json | 4 +++- 3 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 .vscode/settings.json delete mode 100644 build-scripts/pnpm-workspace.yaml diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 50e5eba2..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "chat.tools.terminal.autoApprove": { - "git config": true - } -} \ No newline at end of file diff --git a/build-scripts/pnpm-workspace.yaml b/build-scripts/pnpm-workspace.yaml deleted file mode 100644 index 00f6fc47..00000000 --- a/build-scripts/pnpm-workspace.yaml +++ /dev/null @@ -1,2 +0,0 @@ -allowBuilds: - esbuild: set this to true or false diff --git a/meta.json b/meta.json index 6e2a3a34..00ceae04 100644 --- a/meta.json +++ b/meta.json @@ -456,7 +456,9 @@ "description": "Arche is a self-hosted AI workspace platform that spawns per-user OpenCode coding containers backed by a shared Knowledge Base. Requires the `arche-internal` external Docker network and `/opt/arche/{kb-content,kb-config,users}` directories on the host before deploy. Images are linux/amd64 by default; override `ARCHE_WEB_IMAGE` and `ARCHE_WORKSPACE_IMAGE` to the `-arm64` tag variants for ARM64 hosts.", "logo": "arche.svg", "links": { - "github": "https://github.com/peaberry-studio/arche" + "github": "https://github.com/peaberry-studio/arche", + "website": "https://github.com/peaberry-studio/arche", + "docs": "https://github.com/peaberry-studio/arche#readme" }, "tags": [ "ai", From 292716be4b1aed96fbb7874dffd86d68f999b3f9 Mon Sep 17 00:00:00 2001 From: Abdelatif Djeddou <155508467+BinkyTwin@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:50:58 +0200 Subject: [PATCH 64/78] feat: add OpenSourcePOS template (#890) --- blueprints/opensourcepos/docker-compose.yml | 45 ++++++++++++++++++ blueprints/opensourcepos/opensourcepos.svg | 9 ++++ blueprints/opensourcepos/template.toml | 51 +++++++++++++++++++++ meta.json | 19 ++++++++ 4 files changed, 124 insertions(+) create mode 100644 blueprints/opensourcepos/docker-compose.yml create mode 100644 blueprints/opensourcepos/opensourcepos.svg create mode 100644 blueprints/opensourcepos/template.toml diff --git a/blueprints/opensourcepos/docker-compose.yml b/blueprints/opensourcepos/docker-compose.yml new file mode 100644 index 00000000..95d58960 --- /dev/null +++ b/blueprints/opensourcepos/docker-compose.yml @@ -0,0 +1,45 @@ +version: "3.8" + +services: + opensourcepos: + image: jekkos/opensourcepos:master + restart: unless-stopped + depends_on: + opensourcepos-db: + condition: service_healthy + expose: + - "80" + environment: + CI_ENVIRONMENT: production + ALLOWED_HOSTNAMES: ${ALLOWED_HOSTNAMES} + FORCE_HTTPS: ${FORCE_HTTPS} + PHP_TIMEZONE: ${PHP_TIMEZONE} + MYSQL_USERNAME: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + MYSQL_DB_NAME: ${MYSQL_DATABASE} + MYSQL_HOST_NAME: opensourcepos-db + volumes: + - opensourcepos-uploads:/app/public/uploads + - opensourcepos-logs:/app/writable/logs + + opensourcepos-db: + image: mariadb:10.11 + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + volumes: + - opensourcepos-db:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + +volumes: + opensourcepos-db: + opensourcepos-uploads: + opensourcepos-logs: diff --git a/blueprints/opensourcepos/opensourcepos.svg b/blueprints/opensourcepos/opensourcepos.svg new file mode 100644 index 00000000..d63b2009 --- /dev/null +++ b/blueprints/opensourcepos/opensourcepos.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/blueprints/opensourcepos/template.toml b/blueprints/opensourcepos/template.toml new file mode 100644 index 00000000..d925ad0c --- /dev/null +++ b/blueprints/opensourcepos/template.toml @@ -0,0 +1,51 @@ +[variables] +main_domain = "${domain}" +mysql_root_password = "${password:32}" +mysql_password = "${password:32}" +php_timezone = "UTC" +force_https = "false" +allowed_hostnames = "${main_domain}" + +[[config.domains]] +serviceName = "opensourcepos" +port = 80 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "README.md" +content = """# OpenSourcePOS + +This template deploys Open Source Point of Sale with MariaDB using the upstream Docker image and persistent storage for uploads, logs, and database data. + +## Access + +- URL: `http://${main_domain}` +- Default application user: `admin` +- Default application password: `pointofsale` + +Change the default application password immediately after the first login. + +## Hostnames and HTTPS + +OpenSourcePOS validates incoming host headers in production. The `allowed_hostnames` variable defaults to `${main_domain}` and can be set to a comma-separated list such as `pos.example.com,www.pos.example.com`. + +The template defaults `force_https` to `false` because Dokploy terminates TLS outside the container. Set it to `true` only when your deployment always reaches OpenSourcePOS through HTTPS and the reverse proxy sends the expected headers. + +## Storage + +The template creates persistent volumes for: + +- MariaDB data +- uploaded files +- application logs +""" + +[config.env] +MYSQL_ROOT_PASSWORD = "${mysql_root_password}" +MYSQL_DATABASE = "ospos" +MYSQL_USER = "admin" +MYSQL_PASSWORD = "${mysql_password}" +PHP_TIMEZONE = "${php_timezone}" +FORCE_HTTPS = "${force_https}" +ALLOWED_HOSTNAMES = "${allowed_hostnames}" diff --git a/meta.json b/meta.json index 00ceae04..0fb24a51 100644 --- a/meta.json +++ b/meta.json @@ -5219,6 +5219,25 @@ "nginx" ] }, + { + "id": "opensourcepos", + "name": "OpenSourcePOS", + "version": "master", + "description": "OpenSourcePOS is a web-based point of sale system for managing sales, inventory, customers, and reports.", + "logo": "opensourcepos.svg", + "links": { + "github": "https://github.com/opensourcepos/opensourcepos", + "website": "https://opensourcepos.org/", + "docs": "https://github.com/opensourcepos/opensourcepos/blob/master/README.md" + }, + "tags": [ + "pos", + "retail", + "inventory", + "sales", + "business" + ] + }, { "id": "openspeedtest", "name": "OpenSpeedTest", From f2f4d85d4351095b2b6ce2e16fb40a119c13b675 Mon Sep 17 00:00:00 2001 From: Abdullah Istiak Date: Wed, 8 Jul 2026 03:51:01 +0600 Subject: [PATCH 65/78] Update Supabase Docker configuration and template (#872) * Update Supabase Docker configuration and template - Updated Supabase Studio and GoTrue images to the latest versions. - Modified healthcheck commands for the Studio service. - Enhanced environment variable configuration in docker-compose.yml for better flexibility. - Updated Logflare API key references and added new S3 protocol access keys in template.toml. - Introduced custom entrypoint script for Kong to handle Lua expressions and environment variable substitution. - Added new routes and plugins for enhanced authentication and authorization in Kong configuration. * Refactor Supabase Docker Compose and Template Configuration - Removed container name prefixes from services in docker-compose.yml for consistency. - Increased healthcheck retries and start periods for several services to improve stability. - Updated healthcheck timeout and retry configurations for the analytics service. - Adjusted appname generation logic in template.toml to ensure proper naming for services. - Cleaned up commented sections in template.toml for clarity. --- blueprints/supabase/docker-compose.yml | 306 +++++++++----------- blueprints/supabase/template.toml | 386 ++++++++++++++++++++----- 2 files changed, 440 insertions(+), 252 deletions(-) diff --git a/blueprints/supabase/docker-compose.yml b/blueprints/supabase/docker-compose.yml index 506ddc90..3e03fb2c 100644 --- a/blueprints/supabase/docker-compose.yml +++ b/blueprints/supabase/docker-compose.yml @@ -1,89 +1,107 @@ # Usage -# Start: docker compose up -# With helpers: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up +# Start: docker compose up -d # Stop: docker compose down -# Destroy: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans -# Reset everything: ./reset.sh +# Reset everything: ./reset.sh +# +# Dokploy: Kong is exposed on port 8000 for domain routing. Storage uses local file +# backend by default; edit the storage service env in this file to switch to S3/MinIO. name: supabase services: studio: - container_name: ${CONTAINER_PREFIX}-studio - image: supabase/studio:2025.04.21-sha-173cc56 + image: supabase/studio:2026.04.27-sha-5f60601 restart: unless-stopped healthcheck: test: [ - "CMD", - "node", - "-e", - "fetch('http://studio:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})" + "CMD-SHELL", + "node -e \"fetch('http://localhost:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})\"" ] timeout: 10s interval: 5s - retries: 3 + retries: 5 + start_period: 30s depends_on: analytics: condition: service_healthy environment: + HOSTNAME: "0.0.0.0" + STUDIO_PG_META_URL: http://meta:8080 + POSTGRES_PORT: ${POSTGRES_PORT} + POSTGRES_HOST: ${POSTGRES_HOST} + POSTGRES_DB: ${POSTGRES_DB} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY} + PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} + PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000} + PGRST_DB_EXTRA_SEARCH_PATH: ${PGRST_DB_EXTRA_SEARCH_PATH:-public} + DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION} DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT} OPENAI_API_KEY: ${OPENAI_API_KEY:-} - SUPABASE_URL: ${SUPABASE_PUBLIC_URL} + SUPABASE_URL: http://kong:8000 SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} AUTH_JWT_SECRET: ${JWT_SECRET} - LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + LOGFLARE_API_KEY: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} + LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} + LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN} + LOGFLARE_URL: http://analytics:4000 - NEXT_PUBLIC_ENABLE_LOGS: true - # Comment to use Big Query backend for analytics + NEXT_PUBLIC_ENABLE_LOGS: "true" NEXT_ANALYTICS_BACKEND_PROVIDER: postgres - # Uncomment to use Big Query backend for analytics - # NEXT_ANALYTICS_BACKEND_PROVIDER: bigquery + SNIPPETS_MANAGEMENT_FOLDER: /app/snippets + EDGE_FUNCTIONS_MANAGEMENT_FOLDER: /app/edge-functions + volumes: + - ../files/volumes/snippets:/app/snippets:Z + - ../files/volumes/functions:/app/edge-functions:Z kong: - container_name: ${CONTAINER_PREFIX}-kong - image: kong:2.8.1 + image: kong/kong:3.9.1 restart: unless-stopped - # ports: - # - ${KONG_HTTP_PORT}:8000/tcp - # - ${KONG_HTTPS_PORT}:8443/tcp + healthcheck: + test: ["CMD", "kong", "health"] + interval: 5s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + studio: + condition: service_healthy expose: - 8000 - 8443 volumes: - # https://github.com/supabase/supabase/issues/12661 - ../files/volumes/api/kong.yml:/home/kong/temp.yml:ro,z - depends_on: - analytics: - condition: service_healthy + - ../files/volumes/api/kong-entrypoint.sh:/home/kong/kong-entrypoint.sh:ro,z environment: KONG_DATABASE: "off" - KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml - # https://github.com/supabase/cli/issues/14 + KONG_DECLARATIVE_CONFIG: /usr/local/kong/kong.yml KONG_DNS_ORDER: LAST,A,CNAME - KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth + KONG_DNS_NOT_FOUND_TTL: 1 + KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth,request-termination,ip-restriction,post-function KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k + KONG_PROXY_ACCESS_LOG: /dev/stdout combined SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} + SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY:-} + SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-} + ANON_KEY_ASYMMETRIC: ${ANON_KEY_ASYMMETRIC:-} + SERVICE_ROLE_KEY_ASYMMETRIC: ${SERVICE_ROLE_KEY_ASYMMETRIC:-} DASHBOARD_USERNAME: ${DASHBOARD_USERNAME} DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD} - CONTAINER_PREFIX: ${CONTAINER_PREFIX} - # https://unix.stackexchange.com/a/294837 - entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start' + entrypoint: ["/bin/bash", "/home/kong/kong-entrypoint.sh"] auth: - container_name: ${CONTAINER_PREFIX}-auth - image: supabase/gotrue:v2.171.0 + image: supabase/gotrue:v2.186.0 restart: unless-stopped healthcheck: test: @@ -100,12 +118,8 @@ services: retries: 3 depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy environment: - # the next line seems required if you want to be able to send mails from the supabase GUI GOTRUE_MAILER_EXTERNAL_HOSTS: kong,${SUPABASE_HOST} GOTRUE_API_HOST: 0.0.0.0 GOTRUE_API_PORT: 9999 @@ -123,16 +137,12 @@ services: GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated GOTRUE_JWT_EXP: ${JWT_EXPIRY} GOTRUE_JWT_SECRET: ${JWT_SECRET} + GOTRUE_JWT_KEYS: ${JWT_KEYS:-[]} GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP} GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS} GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM} - # Uncomment to bypass nonce check in ID Token flow. Commonly set to true when using Google Sign In on mobile. - # GOTRUE_EXTERNAL_SKIP_NONCE_CHECK: true - - # GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true - # GOTRUE_SMTP_MAX_FREQUENCY: 1s GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL} GOTRUE_SMTP_HOST: ${SMTP_HOST} GOTRUE_SMTP_PORT: ${SMTP_PORT} @@ -146,39 +156,18 @@ services: GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP} GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM} - # Uncomment to enable custom access token hook. Please see: https://supabase.com/docs/guides/auth/auth-hooks for full list of hooks and additional details about custom_access_token_hook - - # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED: "true" - # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI: "pg-functions://postgres/public/custom_access_token_hook" - # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS: "" - - # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED: "true" - # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/mfa_verification_attempt" - - # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_ENABLED: "true" - # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/password_verification_attempt" - - # GOTRUE_HOOK_SEND_SMS_ENABLED: "false" - # GOTRUE_HOOK_SEND_SMS_URI: "pg-functions://postgres/public/custom_access_token_hook" - # GOTRUE_HOOK_SEND_SMS_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n" - - # GOTRUE_HOOK_SEND_EMAIL_ENABLED: "false" - # GOTRUE_HOOK_SEND_EMAIL_URI: "http://host.docker.internal:54321/functions/v1/email_sender" - # GOTRUE_HOOK_SEND_EMAIL_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n" rest: - container_name: ${CONTAINER_PREFIX}-rest - image: postgrest/postgrest:v12.2.11 + image: postgrest/postgrest:v14.8 restart: unless-stopped depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy environment: PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} + PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000} + PGRST_DB_EXTRA_SEARCH_PATH: ${PGRST_DB_EXTRA_SEARCH_PATH:-public} PGRST_DB_ANON_ROLE: anon PGRST_JWT_SECRET: ${JWT_SECRET} PGRST_DB_USE_LEGACY_GUCS: "false" @@ -190,32 +179,21 @@ services: ] realtime: - # This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain - container_name: realtime-dev.${CONTAINER_PREFIX}-realtime - image: supabase/realtime:v2.34.47 + image: supabase/realtime:v2.76.5 restart: unless-stopped depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy healthcheck: test: [ - "CMD", - "curl", - "-sSfL", - "--head", - "-o", - "/dev/null", - "-H", - "Authorization: Bearer ${ANON_KEY}", - "http://localhost:4000/api/tenants/realtime-dev/health" + "CMD-SHELL", + "curl -sSfL --head -o /dev/null -H \"Authorization: Bearer ${ANON_KEY}\" http://localhost:4000/api/tenants/realtime-dev/health" ] timeout: 5s - interval: 5s + interval: 30s retries: 3 + start_period: 10s environment: PORT: 4000 DB_HOST: ${POSTGRES_HOST} @@ -227,20 +205,25 @@ services: DB_ENC_KEY: supabaserealtime API_JWT_SECRET: ${JWT_SECRET} SECRET_KEY_BASE: ${SECRET_KEY_BASE} + METRICS_JWT_SECRET: ${JWT_SECRET} ERL_AFLAGS: -proto_dist inet_tcp DNS_NODES: "''" RLIMIT_NOFILE: "10000" APP_NAME: realtime - SEED_SELF_HOST: true - RUN_JANITOR: true + SEED_SELF_HOST: "true" + RUN_JANITOR: "true" + DISABLE_HEALTHCHECK_LOGGING: "true" - # To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up storage: - container_name: ${CONTAINER_PREFIX}-storage - image: supabase/storage-api:v1.22.7 + image: supabase/storage-api:v1.48.26 restart: unless-stopped - volumes: - - ../files/volumes/storage:/var/lib/storage:z + depends_on: + db: + condition: service_healthy + rest: + condition: service_started + imgproxy: + condition: service_started healthcheck: test: [ @@ -254,33 +237,30 @@ services: timeout: 5s interval: 5s retries: 3 - depends_on: - db: - # Disable this if you are using an external Postgres database - condition: service_healthy - rest: - condition: service_started - imgproxy: - condition: service_started + start_period: 10s + volumes: + - ../files/volumes/storage:/var/lib/storage:z environment: ANON_KEY: ${ANON_KEY} SERVICE_KEY: ${SERVICE_ROLE_KEY} POSTGREST_URL: http://rest:3000 - PGRST_JWT_SECRET: ${JWT_SECRET} + AUTH_JWT_SECRET: ${JWT_SECRET} DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} + STORAGE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} + REQUEST_ALLOW_X_FORWARDED_PATH: "true" FILE_SIZE_LIMIT: 52428800 STORAGE_BACKEND: file + GLOBAL_S3_BUCKET: ${GLOBAL_S3_BUCKET} FILE_STORAGE_BACKEND_PATH: /var/lib/storage - TENANT_ID: stub - # TODO: https://github.com/supabase/storage-api/issues/55 - REGION: stub - GLOBAL_S3_BUCKET: stub + TENANT_ID: ${STORAGE_TENANT_ID} + REGION: ${REGION} ENABLE_IMAGE_TRANSFORMATION: "true" IMGPROXY_URL: http://imgproxy:5001 + S3_PROTOCOL_ACCESS_KEY_ID: ${S3_PROTOCOL_ACCESS_KEY_ID} + S3_PROTOCOL_ACCESS_KEY_SECRET: ${S3_PROTOCOL_ACCESS_KEY_SECRET} imgproxy: - container_name: ${CONTAINER_PREFIX}-imgproxy - image: darthsim/imgproxy:v3.8.0 + image: darthsim/imgproxy:v3.30.1 restart: unless-stopped volumes: - ../files/volumes/storage:/var/lib/storage:z @@ -298,17 +278,14 @@ services: IMGPROXY_BIND: ":5001" IMGPROXY_LOCAL_FILESYSTEM_ROOT: / IMGPROXY_USE_ETAG: "true" - IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION} + IMGPROXY_AUTO_WEBP: ${IMGPROXY_AUTO_WEBP} + IMGPROXY_MAX_SRC_RESOLUTION: 16.8 meta: - container_name: ${CONTAINER_PREFIX}-meta - image: supabase/postgres-meta:v0.88.9 + image: supabase/postgres-meta:v0.96.3 restart: unless-stopped depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy environment: PG_META_PORT: 8080 @@ -317,23 +294,26 @@ services: PG_META_DB_NAME: ${POSTGRES_DB} PG_META_DB_USER: supabase_admin PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD} + CRYPTO_KEY: ${PG_META_CRYPTO_KEY} functions: - container_name: ${CONTAINER_PREFIX}-edge-functions - image: supabase/edge-runtime:v1.67.4 + image: supabase/edge-runtime:v1.71.2 restart: unless-stopped volumes: - ../files/volumes/functions:/home/deno/functions:Z + - deno-cache:/root/.cache/deno depends_on: - analytics: + kong: condition: service_healthy environment: JWT_SECRET: ${JWT_SECRET} SUPABASE_URL: http://kong:8000 + SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY} + SUPABASE_PUBLISHABLE_KEYS: "{\"default\":\"${SUPABASE_PUBLISHABLE_KEY:-}\"}" + SUPABASE_SECRET_KEYS: "{\"default\":\"${SUPABASE_SECRET_KEY:-}\"}" SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} - # TODO: Allow configuring VERIFY_JWT per function. This PR might help: https://github.com/supabase/cli/pull/786 VERIFY_JWT: "${FUNCTIONS_VERIFY_JWT}" command: [ @@ -343,32 +323,23 @@ services: ] analytics: - container_name: ${CONTAINER_PREFIX}-analytics - image: supabase/logflare:1.12.0 + image: supabase/logflare:1.36.1 restart: unless-stopped - # ports: - # - 4000:4000 expose: - 4000 - # Uncomment to use Big Query backend for analytics - # volumes: - # - type: bind - # source: ${PWD}/gcloud.json - # target: /opt/app/rel/logflare/bin/gcloud.json - # read_only: true + # First boot: Logflare seeds _analytics in Postgres; allow extra time before marking unhealthy. healthcheck: test: [ - "CMD", - "curl", - "http://localhost:4000/health" + "CMD-SHELL", + "curl -sSfL -o /dev/null http://localhost:4000/health" ] - timeout: 5s + timeout: 10s interval: 5s - retries: 10 + retries: 15 + start_period: 90s depends_on: db: - # Disable this if you are using an external Postgres database condition: service_healthy environment: LOGFLARE_NODE_HOST: 127.0.0.1 @@ -378,55 +349,42 @@ services: DB_PORT: ${POSTGRES_PORT} DB_PASSWORD: ${POSTGRES_PASSWORD} DB_SCHEMA: _analytics - LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} - LOGFLARE_SINGLE_TENANT: true - LOGFLARE_SUPABASE_MODE: true + LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} + LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN} + LOGFLARE_SINGLE_TENANT: "true" + LOGFLARE_SUPABASE_MODE: "true" LOGFLARE_MIN_CLUSTER_SIZE: 1 - - # Comment variables to use Big Query backend for analytics POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase POSTGRES_BACKEND_SCHEMA: _analytics LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true - # Uncomment to use Big Query backend for analytics - # GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID} - # GOOGLE_PROJECT_NUMBER: ${GOOGLE_PROJECT_NUMBER} - # Comment out everything below this point if you are using an external Postgres database db: - container_name: ${CONTAINER_PREFIX}-db - image: supabase/postgres:15.8.1.060 + image: supabase/postgres:15.8.1.085 restart: unless-stopped volumes: - ../files/volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z - # Must be superuser to create event trigger - ../files/volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z - # Must be superuser to alter reserved role - ../files/volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z - # Initialize the database settings with JWT_SECRET and JWT_EXP - ../files/volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z - # PGDATA directory is persisted between restarts - ../files/volumes/db/data:/var/lib/postgresql/data:Z - # Changes required for internal supabase data such as _analytics - ../files/volumes/db/_supabase.sql:/docker-entrypoint-initdb.d/migrations/97-_supabase.sql:Z - # Changes required for Analytics support - ../files/volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z - # Changes required for Pooler support - ../files/volumes/db/pooler.sql:/docker-entrypoint-initdb.d/migrations/99-pooler.sql:Z - # Use named volume to persist pgsodium decryption key between restarts - db-config:/etc/postgresql-custom healthcheck: test: [ - "CMD", - "pg_isready", - "-U", - "postgres", - "-h", - "localhost" + "CMD", + "pg_isready", + "-U", + "postgres", + "-h", + "localhost" ] interval: 5s timeout: 5s - retries: 10 + retries: 15 + start_period: 60s depends_on: vector: condition: service_healthy @@ -446,12 +404,11 @@ services: "-c", "config_file=/etc/postgresql/postgresql.conf", "-c", - "log_min_messages=fatal" # prevents Realtime polling queries from appearing in logs + "log_min_messages=fatal" ] vector: - container_name: ${CONTAINER_PREFIX}-vector - image: timberio/vector:0.28.1-alpine + image: timberio/vector:0.53.0-alpine restart: unless-stopped volumes: - ../files/volumes/logs/vector.yml:/etc/vector/vector.yml:ro,z @@ -468,9 +425,10 @@ services: ] timeout: 5s interval: 5s - retries: 3 + retries: 5 + start_period: 10s environment: - LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} command: [ "--config", @@ -479,14 +437,12 @@ services: security_opt: - "label=disable" - # Update the DATABASE_URL if you are using an external Postgres database supavisor: - container_name: ${CONTAINER_PREFIX}-pooler - image: supabase/supavisor:2.5.1 + image: supabase/supavisor:2.7.4 restart: unless-stopped - ports: # expose supavisor to the host to enable db pooler connection - - ${POSTGRES_PORT}:5432 - - ${POOLER_PROXY_PORT_TRANSACTION}:6543 + expose: + - "5432" + - "6543" volumes: - ../files/volumes/pooler/pooler.exs:/etc/pooler/pooler.exs:ro,z healthcheck: @@ -502,19 +458,19 @@ services: ] interval: 10s timeout: 5s - retries: 5 + retries: 10 + start_period: 30s depends_on: db: condition: service_healthy - analytics: - condition: service_healthy environment: PORT: 4000 POSTGRES_PORT: ${POSTGRES_PORT} + POSTGRES_HOST: ${POSTGRES_HOST} POSTGRES_DB: ${POSTGRES_DB} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/_supabase - CLUSTER_POSTGRES: true + DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase + CLUSTER_POSTGRES: "true" SECRET_KEY_BASE: ${SECRET_KEY_BASE} VAULT_ENC_KEY: ${VAULT_ENC_KEY} API_JWT_SECRET: ${JWT_SECRET} @@ -525,6 +481,7 @@ services: POOLER_DEFAULT_POOL_SIZE: ${POOLER_DEFAULT_POOL_SIZE} POOLER_MAX_CLIENT_CONN: ${POOLER_MAX_CLIENT_CONN} POOLER_POOL_MODE: transaction + DB_POOL_SIZE: ${POOLER_DB_POOL_SIZE} command: [ "/bin/sh", @@ -533,4 +490,5 @@ services: ] volumes: - db-config: \ No newline at end of file + db-config: + deno-cache: diff --git a/blueprints/supabase/template.toml b/blueprints/supabase/template.toml index f70c93cc..0e76b7dd 100644 --- a/blueprints/supabase/template.toml +++ b/blueprints/supabase/template.toml @@ -2,11 +2,15 @@ main_domain = "${domain}" postgres_password = "${password:32}" dashboard_password = "${password:32}" -logflare_api_key = "${password:32}" +logflare_public_access_token = "${password:32}" +logflare_private_access_token = "${password:32}" +pg_meta_crypto_key = "${password:32}" +s3_protocol_access_key_id = "${password:24}" +s3_protocol_access_key_secret = "${password:48}" secret_key_base = "${password:64}" vault_enc_key = "${password:32}" jwt_secret = "${password:32}" -container_name_prefix = "${APP_NAME}-supabase" +pooler_tenant_id = "${uuid}" anon_key_payload = """{ "role": "anon", "iss": "supabase", @@ -34,12 +38,7 @@ env = [ '# - ADDITIONAL_REDIRECT_URLS, SITE_URL should point to application using supabase for authentication', '# They are used for redirecting after login/signup and gotrue will check them before sending emails', '# - POSTGRES_PORT, POOLER_PROXY_PORT_TRANSACTION should be changed if you are already running other instances of supabase', -'#', -'# Supabase uses container names in part of its configuration so it is important to keep them', -'# This template generates a random prefix for the container names to avoid conflicts', -'# If you change it you will need to update routes in the vector.yml file in advanced->mounts section', '############', -'CONTAINER_PREFIX=${container_name_prefix}', '', '############', '# Secrets', @@ -59,6 +58,9 @@ env = [ 'DASHBOARD_PASSWORD=${dashboard_password}', 'SECRET_KEY_BASE=${secret_key_base}', 'VAULT_ENC_KEY=${vault_enc_key}', +'PG_META_CRYPTO_KEY=${pg_meta_crypto_key}', +'LOGFLARE_PUBLIC_ACCESS_TOKEN=${logflare_public_access_token}', +'LOGFLARE_PRIVATE_ACCESS_TOKEN=${logflare_private_access_token}', '', '', '############', @@ -68,7 +70,7 @@ env = [ 'POSTGRES_HOST=db', 'POSTGRES_DB=postgres', 'POSTGRES_PORT=5432', -'# default user is postgres', +'POSTGRES_USER=postgres', '', '', '############', @@ -77,7 +79,8 @@ env = [ 'POOLER_PROXY_PORT_TRANSACTION=6543', 'POOLER_DEFAULT_POOL_SIZE=20', 'POOLER_MAX_CLIENT_CONN=100', -'POOLER_TENANT_ID=your-tenant-id', +'POOLER_TENANT_ID=${pooler_tenant_id}', +'POOLER_DB_POOL_SIZE=5', '', '', '############', @@ -94,6 +97,8 @@ env = [ '############', '', 'PGRST_DB_SCHEMAS=public,storage,graphql_public', +'PGRST_DB_MAX_ROWS=1000', +'PGRST_DB_EXTRA_SEARCH_PATH=public', '', '', '############', @@ -102,10 +107,11 @@ env = [ '', '## General', 'SITE_URL=http://localhost:3000', -'ADDITIONAL_REDIRECT_URLS=http://${main_domain}/*,http://localhost:3000/*', +'ADDITIONAL_REDIRECT_URLS=https://${main_domain}/*,http://localhost:3000/*', 'JWT_EXPIRY=3600', 'DISABLE_SIGNUP=false', -'API_EXTERNAL_URL=http://${main_domain}', +'API_EXTERNAL_URL=https://${main_domain}', +'JWT_KEYS=[]', '', '## Mailer Config', 'MAILER_URLPATHS_CONFIRMATION="/auth/v1/verify"', @@ -136,12 +142,19 @@ env = [ 'STUDIO_DEFAULT_ORGANIZATION=Default Organization', 'STUDIO_DEFAULT_PROJECT=Default Project', '', -'STUDIO_PORT=3000', -'# replace if you intend to use Studio outside of localhost', -'SUPABASE_PUBLIC_URL=http://${main_domain}', +'SUPABASE_PUBLIC_URL=https://${main_domain}', '', -'# Enable webp support', -'IMGPROXY_ENABLE_WEBP_DETECTION=true', +'############', +'# Storage - local file backend (default). Edit docker-compose.yml storage env for S3/MinIO.', +'############', +'', +'GLOBAL_S3_BUCKET=stub', +'REGION=stub', +'STORAGE_TENANT_ID=stub', +'S3_PROTOCOL_ACCESS_KEY_ID=${s3_protocol_access_key_id}', +'S3_PROTOCOL_ACCESS_KEY_SECRET=${s3_protocol_access_key_secret}', +'', +'IMGPROXY_AUTO_WEBP=true', '', '# Add your OpenAI API key to enable SQL Editor Assistant', 'OPENAI_API_KEY=', @@ -156,14 +169,8 @@ env = [ '', '############', '# Logs - Configuration for Logflare', -'# Please refer to https://supabase.com/docs/reference/self-hosting-analytics/introduction', '############', '', -'LOGFLARE_LOGGER_BACKEND_API_KEY=your-super-secret-and-long-logflare-key', -'', -'# Change vector.toml sinks to reflect this change', -'LOGFLARE_API_KEY=${logflare_api_key}', -'', '# Docker socket location - this value will differ depending on your OS', 'DOCKER_SOCKET_LOCATION=/var/run/docker.sock', '', @@ -171,6 +178,40 @@ env = [ 'GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID', 'GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER'] +[[config.mounts]] +filePath = "/volumes/api/kong-entrypoint.sh" +content = """#!/bin/bash +# Custom entrypoint for Kong that builds Lua expressions for request-transformer +# and performs environment variable substitution in the declarative config. + +if [ -n "$SUPABASE_SECRET_KEY" ] && [ -n "$SUPABASE_PUBLISHABLE_KEY" ]; then + export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or (headers.apikey == '$SUPABASE_SECRET_KEY' and 'Bearer $SERVICE_ROLE_KEY_ASYMMETRIC') or (headers.apikey == '$SUPABASE_PUBLISHABLE_KEY' and 'Bearer $ANON_KEY_ASYMMETRIC') or headers.apikey)" + export LUA_RT_WS_EXPR="\$((query_params.apikey == '$SUPABASE_SECRET_KEY' and '$SERVICE_ROLE_KEY_ASYMMETRIC') or (query_params.apikey == '$SUPABASE_PUBLISHABLE_KEY' and '$ANON_KEY_ASYMMETRIC') or query_params.apikey)" +else + export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or headers.apikey)" + export LUA_RT_WS_EXPR="\$(query_params.apikey)" +fi + +awk '{ + result = "" + rest = $0 + while (match(rest, /\$[A-Za-z_][A-Za-z_0-9]*/)) { + varname = substr(rest, RSTART + 1, RLENGTH - 1) + if (varname in ENVIRON) { + result = result substr(rest, 1, RSTART - 1) ENVIRON[varname] + } else { + result = result substr(rest, 1, RSTART + RLENGTH - 1) + } + rest = substr(rest, RSTART + RLENGTH) + } + print result rest +}' /home/kong/temp.yml > "$KONG_DECLARATIVE_CONFIG" + +sed -i '/^[[:space:]]*- key:[[:space:]]*$/d' "$KONG_DECLARATIVE_CONFIG" + +exec /entrypoint.sh kong docker-start +""" + [[config.mounts]] filePath = "/volumes/api/kong.yml" content = """_format_version: '2.1' @@ -184,9 +225,11 @@ consumers: - username: anon keyauth_credentials: - key: $SUPABASE_ANON_KEY + - key: $SUPABASE_PUBLISHABLE_KEY - username: service_role keyauth_credentials: - key: $SUPABASE_SERVICE_KEY + - key: $SUPABASE_SECRET_KEY ### ### Access Control List @@ -202,8 +245,8 @@ acls: ### basicauth_credentials: - consumer: DASHBOARD - username: $DASHBOARD_USERNAME - password: $DASHBOARD_PASSWORD + username: '$DASHBOARD_USERNAME' + password: '$DASHBOARD_PASSWORD' ### ### API Routes @@ -237,6 +280,33 @@ services: - /auth/v1/authorize plugins: - name: cors + - name: auth-v1-open-jwks + url: http://auth:9999/.well-known/jwks.json + routes: + - name: auth-v1-open-jwks + strip_path: true + paths: + - /auth/v1/.well-known/jwks.json + plugins: + - name: cors + - name: auth-v1-open-sso-acs + url: http://auth:9999/sso/saml/acs + routes: + - name: auth-v1-open-sso-acs + strip_path: true + paths: + - /sso/saml/acs + plugins: + - name: cors + - name: auth-v1-open-sso-metadata + url: http://auth:9999/sso/saml/metadata + routes: + - name: auth-v1-open-sso-metadata + strip_path: true + paths: + - /sso/saml/metadata + plugins: + - name: cors ## Secure Auth routes - name: auth-v1 @@ -252,6 +322,14 @@ services: - name: key-auth config: hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true @@ -272,7 +350,15 @@ services: - name: cors - name: key-auth config: - hide_credentials: true + hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true @@ -298,7 +384,11 @@ services: config: add: headers: - - Content-Profile:graphql_public + - "Content-Profile: graphql_public" + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true @@ -309,7 +399,7 @@ services: ## Secure Realtime routes - name: realtime-v1-ws _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' - url: http://realtime-dev.${CONTAINER_PREFIX}-realtime:4000/socket + url: http://realtime:4000/socket protocol: ws routes: - name: realtime-v1-ws @@ -321,6 +411,14 @@ services: - name: key-auth config: hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "x-api-key:$LUA_RT_WS_EXPR" + replace: + querystring: + - "apikey:$LUA_RT_WS_EXPR" - name: acl config: hide_groups_header: true @@ -328,8 +426,8 @@ services: - admin - anon - name: realtime-v1-rest - _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' - url: http://realtime-dev.${CONTAINER_PREFIX}-realtime:4000/api + _comment: 'Realtime: /realtime/v1/api/* -> http://realtime:4000/api/*' + url: http://realtime:4000/api protocol: http routes: - name: realtime-v1-rest @@ -341,13 +439,22 @@ services: - name: key-auth config: hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true allow: - admin - anon - ## Storage routes: the storage server manages its own auth + + ## Storage routes - name: storage-v1 _comment: 'Storage: /storage/v1/* -> http://storage:5000/*' url: http://storage:5000/ @@ -358,11 +465,28 @@ services: - /storage/v1/ plugins: - name: cors + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" + - name: post-function + config: + access: + - | + local auth = kong.request.get_header("authorization") + if auth == nil or auth == "" or auth:find("^%s*$") then + kong.service.request.clear_header("authorization") + end ## Edge Functions routes - name: functions-v1 _comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*' url: http://functions:9000/ + read_timeout: 150000 routes: - name: functions-v1-all strip_path: true @@ -371,15 +495,16 @@ services: plugins: - name: cors - ## Analytics routes - - name: analytics-v1 - _comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*' - url: http://analytics:4000/ + ## OAuth 2.0 Authorization Server Metadata + - name: well-known-oauth + url: http://auth:9999/.well-known/oauth-authorization-server routes: - - name: analytics-v1-all + - name: well-known-oauth strip_path: true paths: - - /analytics/v1/ + - /.well-known/oauth-authorization-server + plugins: + - name: cors ## Secure Database routes - name: meta @@ -400,6 +525,32 @@ services: allow: - admin + - name: mcp-blocker + url: http://studio:3000/api/mcp + routes: + - name: mcp-blocker-route + strip_path: true + paths: + - /api/mcp + plugins: + - name: request-termination + config: + status_code: 403 + message: "Access is forbidden." + + - name: mcp + url: http://studio:3000/api/mcp + routes: + - name: mcp + strip_path: true + paths: + - /mcp + plugins: + - name: request-termination + config: + status_code: 403 + message: "Access is forbidden." + ## Protected Dashboard - catch all remaining routes - name: dashboard _comment: 'Studio: /* -> http://studio:3000/*' @@ -416,6 +567,10 @@ services: hide_credentials: true """ +[[config.mounts]] +filePath = "/volumes/snippets/.gitkeep" +content = "" + [[config.mounts]] filePath = "/volumes/db/init/data.sql" content = "" @@ -717,14 +872,25 @@ serve(async () => { [[config.mounts]] filePath = "/volumes/functions/main/index.ts" -content = """import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' -import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' +content = """import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' console.log('main function started') const JWT_SECRET = Deno.env.get('JWT_SECRET') +const SUPABASE_URL = Deno.env.get('SUPABASE_URL') const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true' +let SUPABASE_JWT_KEYS: ReturnType | null = null +if (SUPABASE_URL) { + try { + SUPABASE_JWT_KEYS = jose.createRemoteJWKSet( + new URL('/auth/v1/.well-known/jwks.json', SUPABASE_URL) + ) + } catch (e) { + console.error('Failed to fetch JWKS from SUPABASE_URL:', e) + } +} + function getAuthToken(req: Request) { const authHeader = req.headers.get('authorization') if (!authHeader) { @@ -737,25 +903,53 @@ function getAuthToken(req: Request) { return token } -async function verifyJWT(jwt: string): Promise { +async function isValidLegacyJWT(jwt: string): Promise { + if (!JWT_SECRET) { + console.error('JWT_SECRET not available for HS256 token verification') + return false + } const encoder = new TextEncoder() const secretKey = encoder.encode(JWT_SECRET) try { await jose.jwtVerify(jwt, secretKey) - } catch (err) { - console.error(err) + } catch (e) { + console.error('Symmetric Legacy JWT verification error', e) return false } return true } -serve(async (req: Request) => { +async function isValidJWT(jwt: string): Promise { + if (!SUPABASE_JWT_KEYS) { + console.error('JWKS not available for ES256/RS256 token verification') + return false + } + try { + await jose.jwtVerify(jwt, SUPABASE_JWT_KEYS) + } catch (e) { + console.error('Asymmetric JWT verification error', e) + return false + } + return true +} + +async function isValidHybridJWT(jwt: string): Promise { + const { alg: jwtAlgorithm } = jose.decodeProtectedHeader(jwt) + if (jwtAlgorithm === 'HS256') { + return await isValidLegacyJWT(jwt) + } + if (jwtAlgorithm === 'ES256' || jwtAlgorithm === 'RS256') { + return await isValidJWT(jwt) + } + return false +} + +Deno.serve(async (req: Request) => { if (req.method !== 'OPTIONS' && VERIFY_JWT) { try { const token = getAuthToken(req) - const isValidJWT = await verifyJWT(token) - - if (!isValidJWT) { + const isValid = await isValidHybridJWT(token) + if (!isValid) { return new Response(JSON.stringify({ msg: 'Invalid JWT' }), { status: 401, headers: { 'Content-Type': 'application/json' }, @@ -822,8 +1016,6 @@ content = """api: sources: docker_host: type: docker_logs - exclude_containers: - - ${container_name_prefix}-vector transforms: project_logs: @@ -833,7 +1025,15 @@ transforms: source: |- .project = "default" .event_message = del(.message) - .appname = replace!(del(.container_name), "${container_name_prefix}", "supabase") + compose_service, label_err = get(.label, ["com.docker.compose.service"]) + if label_err != null || compose_service == null { + abort + } + compose_service = to_string!(compose_service) + if compose_service == "vector" { + abort + } + .appname = "supabase-" + compose_service del(.container_created_at) del(.container_id) del(.source_type) @@ -847,12 +1047,12 @@ transforms: inputs: - project_logs route: - kong: '.appname == "supabase-kong"' + kong: '.appname == "supabase-kong" || .appname == "supabase-envoy"' auth: '.appname == "supabase-auth"' rest: '.appname == "supabase-rest"' - realtime: '.appname == "realtime-dev.${CONTAINER_PREFIX}-realtime"' + realtime: '.appname == "supabase-realtime"' storage: '.appname == "supabase-storage"' - functions: '.appname == "supabase-edge-functions"' + functions: '.appname == "supabase-functions"' db: '.appname == "supabase-db"' # Ignores non nginx errors since they are related with kong booting up kong_logs: @@ -866,15 +1066,17 @@ transforms: .metadata.request.headers.referer = req.referer .metadata.request.headers.user_agent = req.agent .metadata.request.headers.cf_connecting_ip = req.client - .metadata.request.method = req.method - .metadata.request.path = req.path - .metadata.request.protocol = req.protocol .metadata.response.status_code = req.status + url, split_err = split(req.request, " ") + if split_err == null { + .metadata.request.method = url[0] + .metadata.request.path = url[1] + .metadata.request.protocol = url[2] + } } if err != null { abort } - # Ignores non nginx errors since they are related with kong booting up kong_err: type: remap inputs: @@ -918,14 +1120,18 @@ transforms: parsed, err = parse_regex(.event_message, r'^(?P