mirror of
https://github.com/kossakovsky/n8n-install.git
synced 2026-03-09 15:25:33 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a1301bfc0 | ||
|
|
19325191c3 | ||
|
|
107f18296a | ||
|
|
059e141daa | ||
|
|
6505c5cdf4 | ||
|
|
f8e665f85f | ||
|
|
f2f51c6e13 | ||
|
|
ceaa970273 | ||
|
|
6f1aaa0555 | ||
|
|
0dec31539e | ||
|
|
b990b09681 | ||
|
|
de8df8a0b7 | ||
|
|
543593de36 | ||
|
|
50bd817b56 | ||
|
|
611591dc0f | ||
|
|
ad9c7aa57d | ||
|
|
6e283c508c | ||
|
|
adc5b94f1c |
43
.env.example
43
.env.example
@@ -6,27 +6,6 @@
|
||||
# Set to false to disable (default: true)
|
||||
############
|
||||
# SCARF_ANALYTICS=false
|
||||
# Installation Mode Configuration
|
||||
# INSTALL_MODE: vps | local
|
||||
# vps - Production mode with real domain and Let's Encrypt SSL
|
||||
# local - Local installation with .local domains (no SSL, HTTP only)
|
||||
############
|
||||
INSTALL_MODE=vps
|
||||
|
||||
# Caddy HTTPS mode (auto-set based on INSTALL_MODE during installation)
|
||||
# VPS mode: on (Let's Encrypt certificates)
|
||||
# Local mode: off (HTTP only)
|
||||
CADDY_AUTO_HTTPS=on
|
||||
|
||||
# n8n cookie security (auto-set based on INSTALL_MODE)
|
||||
# VPS mode: true (requires HTTPS)
|
||||
# Local mode: false (allows HTTP)
|
||||
N8N_SECURE_COOKIE=true
|
||||
|
||||
# Protocol for service URLs (auto-set based on INSTALL_MODE)
|
||||
# VPS mode: https
|
||||
# Local mode: http
|
||||
PROTOCOL=https
|
||||
|
||||
############
|
||||
# [required]
|
||||
@@ -120,6 +99,15 @@ NEO4J_AUTH_PASSWORD=
|
||||
NOCODB_JWT_SECRET=
|
||||
|
||||
|
||||
############
|
||||
# [required]
|
||||
# Appsmith encryption credentials (auto-generated)
|
||||
############
|
||||
|
||||
APPSMITH_ENCRYPTION_PASSWORD=
|
||||
APPSMITH_ENCRYPTION_SALT=
|
||||
|
||||
|
||||
############
|
||||
# [required]
|
||||
# Langfuse credentials
|
||||
@@ -169,6 +157,7 @@ LT_PASSWORD_HASH=
|
||||
|
||||
USER_DOMAIN_NAME=
|
||||
LETSENCRYPT_EMAIL=
|
||||
APPSMITH_HOSTNAME=appsmith.yourdomain.com
|
||||
COMFYUI_HOSTNAME=comfyui.yourdomain.com
|
||||
DATABASUS_HOSTNAME=databasus.yourdomain.com
|
||||
DIFY_HOSTNAME=dify.yourdomain.com
|
||||
@@ -451,11 +440,13 @@ GOST_PROXY_URL=
|
||||
|
||||
# External upstream proxy (REQUIRED - asked during wizard if gost is selected)
|
||||
# Examples: socks5://user:pass@proxy.com:1080, http://user:pass@proxy.com:8080
|
||||
# IMPORTANT: For HTTP proxies use http://, NOT https://
|
||||
# The protocol refers to proxy type, not connection security.
|
||||
GOST_UPSTREAM_PROXY=
|
||||
|
||||
# Internal services bypass list (prevents internal Docker traffic from going through proxy)
|
||||
# Includes: Docker internal networks (172.16-31.*, 10.*), Docker DNS (127.0.0.11), and all service hostnames
|
||||
GOST_NO_PROXY=localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.local,postgres,postgres:5432,redis,redis:6379,caddy,ollama,neo4j,qdrant,weaviate,clickhouse,minio,searxng,crawl4ai,gotenberg,langfuse-web,langfuse-worker,flowise,n8n,n8n-import,n8n-worker-1,n8n-worker-2,n8n-worker-3,n8n-worker-4,n8n-worker-5,n8n-worker-6,n8n-worker-7,n8n-worker-8,n8n-worker-9,n8n-worker-10,n8n-runner-1,n8n-runner-2,n8n-runner-3,n8n-runner-4,n8n-runner-5,n8n-runner-6,n8n-runner-7,n8n-runner-8,n8n-runner-9,n8n-runner-10,letta,lightrag,docling,postiz,temporal,temporal-ui,ragflow,ragflow-mysql,ragflow-minio,ragflow-redis,ragflow-elasticsearch,ragapp,open-webui,comfyui,waha,libretranslate,paddleocr,nocodb,db,studio,kong,auth,rest,realtime,storage,imgproxy,meta,functions,analytics,vector,supavisor,gost
|
||||
GOST_NO_PROXY=localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.local,appsmith,postgres,postgres:5432,redis,redis:6379,caddy,ollama,neo4j,qdrant,weaviate,clickhouse,minio,searxng,crawl4ai,gotenberg,langfuse-web,langfuse-worker,flowise,n8n,n8n-import,n8n-worker-1,n8n-worker-2,n8n-worker-3,n8n-worker-4,n8n-worker-5,n8n-worker-6,n8n-worker-7,n8n-worker-8,n8n-worker-9,n8n-worker-10,n8n-runner-1,n8n-runner-2,n8n-runner-3,n8n-runner-4,n8n-runner-5,n8n-runner-6,n8n-runner-7,n8n-runner-8,n8n-runner-9,n8n-runner-10,letta,lightrag,docling,postiz,temporal,temporal-ui,ragflow,ragflow-mysql,ragflow-minio,ragflow-redis,ragflow-elasticsearch,ragapp,open-webui,comfyui,waha,libretranslate,paddleocr,nocodb,db,studio,kong,auth,rest,realtime,storage,imgproxy,meta,functions,analytics,vector,supavisor,gost,api.telegram.org,telegram.org,t.me,core.telegram.org
|
||||
|
||||
############
|
||||
# Functions - Configuration for Functions
|
||||
@@ -496,6 +487,14 @@ DIFY_SECRET_KEY=
|
||||
DIFY_EXPOSE_NGINX_PORT=8080
|
||||
DIFY_EXPOSE_NGINX_SSL_PORT=9443
|
||||
|
||||
############
|
||||
# Docker Compose parallel limit
|
||||
# Limits the number of simultaneous Docker image pulls to prevent
|
||||
# "net/http: TLS handshake timeout" errors when many services are selected.
|
||||
# Increase this value if you have a fast network connection.
|
||||
############
|
||||
COMPOSE_PARALLEL_LIMIT=3
|
||||
|
||||
###########################################################################################
|
||||
COMPOSE_PROFILES="n8n,portainer,monitoring,databasus"
|
||||
PROMETHEUS_PASSWORD_HASH=
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -21,5 +21,3 @@ certs/*
|
||||
# Custom Caddy addons (user configurations)
|
||||
caddy-addon/*.conf
|
||||
!caddy-addon/*.example
|
||||
hosts.txt
|
||||
welcome/data.json
|
||||
|
||||
31
AGENTS.md
Normal file
31
AGENTS.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
- Core runtime config lives at the repo root: `docker-compose.yml`, `docker-compose.n8n-workers.yml`, and `Caddyfile`.
|
||||
- Installer and maintenance logic is in `scripts/` (install, update, doctor, cleanup, and helpers).
|
||||
- Service-specific assets are grouped by folder (examples: `n8n/`, `grafana/`, `prometheus/`, `searxng/`, `ragflow/`, `python-runner/`, `welcome/`).
|
||||
- Shared files for workflows are stored in `shared/` and mounted inside containers as `/data/shared`.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
- `make install`: run the full installation wizard.
|
||||
- `make update` or `make git-pull`: refresh images and configuration (fork-friendly via `make git-pull`).
|
||||
- `make logs s=<service>`: tail a specific service’s logs (example: `make logs s=n8n`).
|
||||
- `make doctor`: run system checks for DNS/SSL/containers.
|
||||
- `make restart`, `make stop`, `make start`, `make status`: manage the compose stack.
|
||||
- `make clean` or `make clean-all`: remove unused Docker resources (`clean-all` is destructive).
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
- Bash scripts in `scripts/` use `#!/bin/bash`, 4-space indentation, and uppercase constants. Match existing formatting.
|
||||
- Environment variable patterns are consistent: hostnames use `_HOSTNAME`, secrets use `_PASSWORD` or `_KEY`, and bcrypt hashes use `_PASSWORD_HASH`.
|
||||
- Services should not publish ports directly; external access goes through Caddy.
|
||||
|
||||
## Testing Guidelines
|
||||
- There is no unit-test suite. Use syntax checks instead:
|
||||
- `docker compose -p localai config --quiet`
|
||||
- `bash -n scripts/install.sh` (and other edited scripts)
|
||||
- For installer changes, validate on a clean Ubuntu 24.04 LTS host and confirm profile selections start correctly.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
- Commit messages follow Conventional Commits: `type(scope): summary` (examples in history include `fix(caddy): ...`, `docs(readme): ...`, `feat(postiz): ...`).
|
||||
- PRs should include a short summary, affected services/profiles, and test commands run.
|
||||
- Update `README.md` and `CHANGELOG.md` for user-facing changes or new services.
|
||||
50
CHANGELOG.md
50
CHANGELOG.md
@@ -2,6 +2,56 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.3.2] - 2026-02-27
|
||||
|
||||
### Fixed
|
||||
- **Docker Compose** - Respect `docker-compose.override.yml` for user customizations (#44). All compose file assembly points now include the override file when present.
|
||||
|
||||
## [1.3.1] - 2026-02-27
|
||||
|
||||
### Fixed
|
||||
- **Installer** - Skip n8n workflow import and worker configuration prompts when n8n profile is not selected
|
||||
|
||||
## [1.3.0] - 2026-02-27
|
||||
|
||||
### Added
|
||||
- **Appsmith** - Low-code platform for building internal tools, dashboards, and admin panels
|
||||
|
||||
## [1.2.8] - 2026-02-27
|
||||
|
||||
### Fixed
|
||||
- **Ragflow** - Fix nginx config mount path (`sites-available/default` → `conf.d/default.conf`) to resolve default "Welcome to nginx!" page (#41)
|
||||
|
||||
## [1.2.7] - 2026-02-27
|
||||
|
||||
### Fixed
|
||||
- **Docker** - Limit parallel image pulls (`COMPOSE_PARALLEL_LIMIT=3`) to prevent `TLS handshake timeout` errors when many services are selected
|
||||
|
||||
## [1.2.6] - 2026-02-10
|
||||
|
||||
### Changed
|
||||
- **ComfyUI** - Update Docker image to CUDA 12.8 (`cu128-slim`)
|
||||
|
||||
## [1.2.5] - 2026-02-03
|
||||
|
||||
### Fixed
|
||||
- **n8n** - Use static ffmpeg binaries for Alpine/musl compatibility (fixes glibc errors)
|
||||
|
||||
## [1.2.4] - 2026-01-30
|
||||
|
||||
### Fixed
|
||||
- **Postiz** - Fix `BACKEND_INTERNAL_URL` to use `localhost` instead of Docker hostname (internal nginx requires localhost)
|
||||
|
||||
## [1.2.3] - 2026-01-29
|
||||
|
||||
### Fixed
|
||||
- **Gost proxy** - Add Telegram domains to `GOST_NO_PROXY` bypass list for n8n Telegram triggers
|
||||
|
||||
## [1.2.2] - 2026-01-26
|
||||
|
||||
### Fixed
|
||||
- **Custom TLS** - Fix duplicate hostname error when using custom certificates. Changed architecture from generating separate site blocks to using a shared TLS snippet that all services import.
|
||||
|
||||
## [1.2.1] - 2026-01-16
|
||||
|
||||
### Added
|
||||
|
||||
80
CLAUDE.md
80
CLAUDE.md
@@ -10,17 +10,16 @@ This is **n8n-install**, a Docker Compose-based installer that provides a compre
|
||||
|
||||
- **Profile-based service management**: Services are activated via Docker Compose profiles (e.g., `n8n`, `flowise`, `monitoring`). Profiles are stored in the `.env` file's `COMPOSE_PROFILES` variable.
|
||||
- **No exposed ports**: Services do NOT publish ports directly. All external HTTPS access is routed through Caddy reverse proxy on ports 80/443.
|
||||
- **Shared secrets**: Core services (Postgres, Redis/Valkey, Caddy) are always included. Other services are optional and selected during installation.
|
||||
- **Shared secrets**: Core services (Postgres, Valkey (Redis-compatible, container named `redis` for backward compatibility), Caddy) are always included. Other services are optional and selected during installation.
|
||||
- **Queue-based n8n**: n8n runs in `queue` mode with Redis, Postgres, and dynamically scaled workers (`N8N_WORKER_COUNT`).
|
||||
|
||||
### Key Files
|
||||
|
||||
- `Makefile`: Common commands (install, update, logs, etc.)
|
||||
- `docker-compose.yml`: Service definitions with profiles
|
||||
- `Caddyfile`: Reverse proxy configuration (HTTPS for VPS, HTTP for local via `CADDY_AUTO_HTTPS`)
|
||||
- `Caddyfile`: Reverse proxy configuration with automatic HTTPS
|
||||
- `.env`: Generated secrets and configuration (from `.env.example`)
|
||||
- `scripts/install-vps.sh`: VPS installation orchestrator (runs scripts 01-08 in sequence)
|
||||
- `scripts/install-local.sh`: Local installation orchestrator (inline prerequisites check, then 03-08)
|
||||
- `scripts/install.sh`: Main installation orchestrator (runs numbered scripts 01-08 in sequence)
|
||||
- `scripts/utils.sh`: Shared utility functions (sourced by all scripts via `source "$(dirname "$0")/utils.sh" && init_paths`)
|
||||
- `scripts/01_system_preparation.sh`: System updates, firewall, security hardening
|
||||
- `scripts/02_install_docker.sh`: Docker and Docker Compose installation
|
||||
@@ -31,7 +30,6 @@ This is **n8n-install**, a Docker Compose-based installer that provides a compre
|
||||
- `scripts/databases.sh`: Creates isolated PostgreSQL databases for services (library)
|
||||
- `scripts/telemetry.sh`: Anonymous telemetry functions (Scarf integration)
|
||||
- `scripts/06_run_services.sh`: Starts Docker Compose stack
|
||||
- `scripts/generate_hosts.sh`: Generates /etc/hosts entries for local mode
|
||||
- `scripts/07_final_report.sh`: Post-install credential summary
|
||||
- `scripts/08_fix_permissions.sh`: Fixes file ownership for non-root access
|
||||
- `scripts/generate_n8n_workers.sh`: Generates dynamic worker/runner compose file
|
||||
@@ -42,22 +40,17 @@ This is **n8n-install**, a Docker Compose-based installer that provides a compre
|
||||
- `scripts/docker_cleanup.sh`: Removes unused Docker resources (used by `make clean`)
|
||||
- `scripts/download_top_workflows.sh`: Downloads community n8n workflows
|
||||
- `scripts/import_workflows.sh`: Imports workflows from `n8n/backup/workflows/` into n8n (used by `make import`)
|
||||
- `scripts/restart.sh`: Restarts services with proper compose file handling (used by `make restart`)
|
||||
- `scripts/setup_custom_tls.sh`: Configures custom TLS certificates (used by `make setup-tls`)
|
||||
|
||||
**Project Name**: All docker-compose commands use `-p localai` (defined in Makefile as `PROJECT_NAME := localai`).
|
||||
|
||||
**Version**: Stored in `VERSION` file at repository root.
|
||||
|
||||
### Installation Flow
|
||||
|
||||
Two independent installation paths:
|
||||
`scripts/install.sh` orchestrates the installation by running numbered scripts in sequence:
|
||||
|
||||
```
|
||||
make install-vps → install-vps.sh (Ubuntu VPS)
|
||||
└── 01 → 02 → 03 → 04 → 05 → 06 → 07 → 08
|
||||
|
||||
make install-local → install-local.sh (macOS/Linux local)
|
||||
└── [inline prereq check] → 03 → 04 → 05 → 06 → 07 → 08
|
||||
```
|
||||
|
||||
**VPS Installation** (`install-vps.sh`):
|
||||
1. `01_system_preparation.sh` - System updates, firewall, security hardening
|
||||
2. `02_install_docker.sh` - Docker and Docker Compose installation
|
||||
3. `03_generate_secrets.sh` - Generate passwords, API keys, bcrypt hashes
|
||||
@@ -67,22 +60,9 @@ make install-local → install-local.sh (macOS/Linux local)
|
||||
7. `07_final_report.sh` - Display credentials and URLs
|
||||
8. `08_fix_permissions.sh` - Fix file ownership for non-root access
|
||||
|
||||
**Local Installation** (`install-local.sh`):
|
||||
1. Inline prerequisites check - Verify Docker, whiptail, openssl, git
|
||||
2. `03_generate_secrets.sh` - Generate passwords, API keys, bcrypt hashes
|
||||
3. `04_wizard.sh` - Interactive service selection (whiptail UI)
|
||||
4. `05_configure_services.sh` - Service-specific configuration
|
||||
5. `06_run_services.sh` - Start Docker Compose stack
|
||||
6. `07_final_report.sh` - Display credentials and URLs
|
||||
|
||||
The update flow (`scripts/update.sh`) similarly orchestrates: git fetch + reset → service selection → `apply_update.sh` → restart.
|
||||
|
||||
### Installation Modes
|
||||
|
||||
Two independent entry points (no shared entrypoint):
|
||||
|
||||
- **VPS Mode** (`make install-vps`): Production with real domains, Let's Encrypt SSL, full system prep. Run with `sudo`.
|
||||
- **Local Mode** (`make install-local`): Development with `.local` domains, HTTP only, no system prep. Requires Docker pre-installed.
|
||||
**Git update modes**: Default is `reset` (hard reset to origin). Set `GIT_MODE=merge` in `.env` for fork workflows (merges from upstream instead of hard reset). The `make git-pull` command uses merge mode.
|
||||
|
||||
## Common Development Commands
|
||||
|
||||
@@ -91,9 +71,6 @@ Two independent entry points (no shared entrypoint):
|
||||
```bash
|
||||
make install # Full installation (runs scripts/install.sh)
|
||||
make update # Update system and services (resets to origin)
|
||||
make install-vps # VPS installation (Ubuntu with SSL)
|
||||
make install-local # Local installation (macOS/Linux, no sudo)
|
||||
make update # Update system and services
|
||||
make update-preview # Preview available updates (dry-run)
|
||||
make git-pull # Update for forks (merges from upstream/main)
|
||||
make clean # Remove unused Docker resources (preserves data)
|
||||
@@ -185,6 +162,7 @@ This project uses [Semantic Versioning](https://semver.org/). When updating `CHA
|
||||
- Configuration stored in `docker-compose.n8n-workers.yml` (auto-generated, gitignored)
|
||||
- Runner connects to its worker via `network_mode: "service:n8n-worker-N"` (localhost:5679)
|
||||
- Runner image `n8nio/runners` must match n8n version
|
||||
- **Template profile pattern**: `docker-compose.yml` defines `n8n-worker-template` and `n8n-runner-template` with `profiles: ["n8n-template"]` (never activated directly). `generate_n8n_workers.sh` uses these as templates to generate `docker-compose.n8n-workers.yml` with the actual worker/runner services.
|
||||
- **Scaling**: Change `N8N_WORKER_COUNT` in `.env` and run `bash scripts/generate_n8n_workers.sh`
|
||||
- **Code node libraries**: Configured via `n8n/n8n-task-runners.json` and `n8n/Dockerfile.runner`:
|
||||
- JS packages installed via `pnpm add` in Dockerfile.runner
|
||||
@@ -199,6 +177,16 @@ This project uses [Semantic Versioning](https://semver.org/). When updating `CHA
|
||||
- Hostnames are passed via environment variables (e.g., `N8N_HOSTNAME`, `FLOWISE_HOSTNAME`)
|
||||
- Basic auth uses bcrypt hashes generated by `scripts/03_generate_secrets.sh` via Caddy's hash command
|
||||
- Never add `ports:` to services in docker-compose.yml; let Caddy handle all external access
|
||||
- **Caddy Addons** (`caddy-addon/`): Extend Caddy config without modifying the main Caddyfile. Files matching `site-*.conf` are auto-imported. TLS is controlled via `tls-snippet.conf` (all service blocks use `import service_tls`). See `caddy-addon/README.md` for details.
|
||||
|
||||
### External Compose Files (Supabase/Dify)
|
||||
|
||||
Complex services like Supabase and Dify maintain their own upstream docker-compose files:
|
||||
- `start_services.py` handles cloning repos, preparing `.env` files, and starting services
|
||||
- Each external service needs: `is_*_enabled()`, `clone_*_repo()`, `prepare_*_env()`, `start_*()` functions in `start_services.py`
|
||||
- `scripts/utils.sh` provides `get_*_compose()` getter functions and `build_compose_files_array()` includes them
|
||||
- `stop_all_services()` in `start_services.py` checks compose file existence (not profile) to ensure cleanup when a profile is removed
|
||||
- All external compose files use the same project name (`-p localai`) so containers appear together
|
||||
|
||||
### Secret Generation
|
||||
|
||||
@@ -218,7 +206,7 @@ Key functions:
|
||||
- `load_env` - Source .env file to make variables available
|
||||
- `update_compose_profiles "profile1,profile2"` - Update COMPOSE_PROFILES in .env
|
||||
- `gen_password 32` / `gen_hex 64` / `gen_base64 64` - Secret generation
|
||||
- `generate_bcrypt_hash "password"` - Create Caddy-compatible bcrypt hash (uses Caddy Docker image)
|
||||
- `generate_bcrypt_hash "password"` - Create Caddy-compatible bcrypt hash (uses Caddy binary)
|
||||
- `json_escape "string"` - Escape string for JSON output
|
||||
- `wt_input`, `wt_password`, `wt_yesno`, `wt_msg` - Whiptail dialog wrappers
|
||||
- `wt_checklist`, `wt_radiolist`, `wt_menu` - Whiptail selection dialogs
|
||||
@@ -232,22 +220,6 @@ Key functions:
|
||||
- `get_n8n_workers_compose` / `get_supabase_compose` / `get_dify_compose` - Get compose file path if profile active AND file exists
|
||||
- `build_compose_files_array` - Build global `COMPOSE_FILES` array with all active compose files (main + external)
|
||||
|
||||
### Local Mode Utilities (scripts/local.sh)
|
||||
|
||||
Source with: `source "$SCRIPT_DIR/local.sh"` (after sourcing utils.sh)
|
||||
|
||||
Encapsulates all VPS vs Local mode logic in one place:
|
||||
- `get_install_mode` - Get current mode from INSTALL_MODE env or .env file (defaults to "vps")
|
||||
- `is_local_mode` / `is_vps_mode` - Check current installation mode
|
||||
- `get_protocol` - Get protocol based on mode ("http" for local, "https" for VPS)
|
||||
- `get_caddy_auto_https` - Get Caddy setting ("off" for local, "on" for VPS)
|
||||
- `get_n8n_secure_cookie` - Get cookie security ("false" for local, "true" for VPS)
|
||||
- `get_local_domain` - Get default domain for local mode (".local")
|
||||
- `configure_mode_env` - Populate associative array with all mode-specific settings
|
||||
- `get_all_hostname_vars` - Get list of all hostname variables (single source of truth)
|
||||
- `print_local_hosts_instructions` - Display /etc/hosts setup instructions
|
||||
- `check_local_prerequisites` - Verify Docker, whiptail, openssl, git are installed
|
||||
|
||||
### Service Profiles
|
||||
|
||||
Common profiles:
|
||||
@@ -321,6 +293,8 @@ healthcheck:
|
||||
test: ["CMD-SHELL", "http_proxy= https_proxy= HTTP_PROXY= HTTPS_PROXY= wget -qO- http://localhost:8080/health || exit 1"]
|
||||
```
|
||||
|
||||
**GOST_NO_PROXY**: ALL service container names must be listed in `GOST_NO_PROXY` in `.env.example`. This prevents internal Docker network traffic from routing through the proxy. This applies to every service, not just those using `<<: *proxy-env`.
|
||||
|
||||
### Welcome Page Dashboard
|
||||
|
||||
The welcome page (`welcome/`) provides a post-install dashboard showing all active services:
|
||||
@@ -350,8 +324,8 @@ These are backed up before `git reset --hard` and restored after.
|
||||
- Verify `LETSENCRYPT_EMAIL` is set in `.env`
|
||||
|
||||
### Password hash generation fails
|
||||
- Ensure Docker is running (bcrypt hashes are generated via `docker run caddy:latest`)
|
||||
- Script uses: `docker run --rm caddy:latest caddy hash-password --algorithm bcrypt --plaintext "$password"`
|
||||
- Ensure Caddy container is running: `docker compose -p localai up -d caddy`
|
||||
- Script uses: `docker exec caddy caddy hash-password --plaintext "$password"`
|
||||
|
||||
## File Locations
|
||||
|
||||
@@ -371,7 +345,6 @@ docker compose -p localai config --quiet
|
||||
|
||||
# Bash script syntax (validate all key scripts)
|
||||
bash -n scripts/utils.sh
|
||||
bash -n scripts/local.sh
|
||||
bash -n scripts/git.sh
|
||||
bash -n scripts/databases.sh
|
||||
bash -n scripts/telemetry.sh
|
||||
@@ -383,8 +356,7 @@ bash -n scripts/generate_welcome_page.sh
|
||||
bash -n scripts/generate_n8n_workers.sh
|
||||
bash -n scripts/apply_update.sh
|
||||
bash -n scripts/update.sh
|
||||
bash -n scripts/install-vps.sh
|
||||
bash -n scripts/install-local.sh
|
||||
bash -n scripts/install.sh
|
||||
```
|
||||
|
||||
### Full Testing
|
||||
|
||||
110
Caddyfile
110
Caddyfile
@@ -1,32 +1,46 @@
|
||||
{
|
||||
# Global options - works for both VPS and local environments
|
||||
# CADDY_AUTO_HTTPS: "on" for VPS (Let's Encrypt), "off" for local (HTTP only)
|
||||
auto_https {$CADDY_AUTO_HTTPS:on}
|
||||
# Global options - works for both environments
|
||||
email {$LETSENCRYPT_EMAIL}
|
||||
}
|
||||
|
||||
# Import TLS snippet (must be before service blocks)
|
||||
# Default: Let's Encrypt automatic certificates
|
||||
# Custom: Run 'make setup-tls' to use your own certificates
|
||||
import /etc/caddy/addons/tls-snippet.conf
|
||||
|
||||
# Appsmith
|
||||
{$APPSMITH_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy appsmith:80
|
||||
}
|
||||
|
||||
# N8N
|
||||
http://{$N8N_HOSTNAME} {
|
||||
{$N8N_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy n8n:5678
|
||||
}
|
||||
|
||||
# Open WebUI
|
||||
http://{$WEBUI_HOSTNAME} {
|
||||
{$WEBUI_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy open-webui:8080
|
||||
}
|
||||
|
||||
# Flowise
|
||||
http://{$FLOWISE_HOSTNAME} {
|
||||
{$FLOWISE_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy flowise:3001
|
||||
}
|
||||
|
||||
# Dify
|
||||
http://{$DIFY_HOSTNAME} {
|
||||
{$DIFY_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy nginx:80
|
||||
}
|
||||
|
||||
# RAGApp
|
||||
http://{$RAGAPP_HOSTNAME} {
|
||||
{$RAGAPP_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$RAGAPP_USERNAME} {$RAGAPP_PASSWORD_HASH}
|
||||
}
|
||||
@@ -34,32 +48,38 @@ http://{$RAGAPP_HOSTNAME} {
|
||||
}
|
||||
|
||||
# RAGFlow
|
||||
http://{$RAGFLOW_HOSTNAME} {
|
||||
{$RAGFLOW_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy ragflow:80
|
||||
}
|
||||
|
||||
# Langfuse
|
||||
http://{$LANGFUSE_HOSTNAME} {
|
||||
{$LANGFUSE_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy langfuse-web:3000
|
||||
}
|
||||
|
||||
# Supabase
|
||||
http://{$SUPABASE_HOSTNAME} {
|
||||
{$SUPABASE_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy kong:8000
|
||||
}
|
||||
|
||||
# Grafana
|
||||
http://{$GRAFANA_HOSTNAME} {
|
||||
{$GRAFANA_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy grafana:3000
|
||||
}
|
||||
|
||||
# WAHA (WhatsApp HTTP API)
|
||||
http://{$WAHA_HOSTNAME} {
|
||||
{$WAHA_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy waha:3000
|
||||
}
|
||||
|
||||
# Prometheus
|
||||
http://{$PROMETHEUS_HOSTNAME} {
|
||||
{$PROMETHEUS_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$PROMETHEUS_USERNAME} {$PROMETHEUS_PASSWORD_HASH}
|
||||
}
|
||||
@@ -67,17 +87,20 @@ http://{$PROMETHEUS_HOSTNAME} {
|
||||
}
|
||||
|
||||
# Portainer
|
||||
http://{$PORTAINER_HOSTNAME} {
|
||||
{$PORTAINER_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy portainer:9000
|
||||
}
|
||||
|
||||
# Postiz
|
||||
http://{$POSTIZ_HOSTNAME} {
|
||||
{$POSTIZ_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy postiz:5000
|
||||
}
|
||||
|
||||
# Temporal UI (workflow orchestration for Postiz)
|
||||
{$TEMPORAL_UI_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$TEMPORAL_UI_USERNAME} {$TEMPORAL_UI_PASSWORD_HASH}
|
||||
}
|
||||
@@ -86,31 +109,37 @@ http://{$POSTIZ_HOSTNAME} {
|
||||
|
||||
# Databasus
|
||||
{$DATABASUS_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy databasus:4005
|
||||
}
|
||||
|
||||
# Letta
|
||||
http://{$LETTA_HOSTNAME} {
|
||||
{$LETTA_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy letta:8283
|
||||
}
|
||||
|
||||
# LightRAG (Graph-based RAG with Knowledge Extraction)
|
||||
http://{$LIGHTRAG_HOSTNAME} {
|
||||
{$LIGHTRAG_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy lightrag:9621
|
||||
}
|
||||
|
||||
# Weaviate
|
||||
http://{$WEAVIATE_HOSTNAME} {
|
||||
{$WEAVIATE_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy weaviate:8080
|
||||
}
|
||||
|
||||
# Qdrant
|
||||
http://{$QDRANT_HOSTNAME} {
|
||||
{$QDRANT_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy qdrant:6333
|
||||
}
|
||||
|
||||
# ComfyUI
|
||||
http://{$COMFYUI_HOSTNAME} {
|
||||
{$COMFYUI_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$COMFYUI_USERNAME} {$COMFYUI_PASSWORD_HASH}
|
||||
}
|
||||
@@ -118,30 +147,35 @@ http://{$COMFYUI_HOSTNAME} {
|
||||
}
|
||||
|
||||
# LibreTranslate (Self-hosted Translation API)
|
||||
http://{$LT_HOSTNAME} {
|
||||
{$LT_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$LT_USERNAME} {$LT_PASSWORD_HASH}
|
||||
}
|
||||
reverse_proxy libretranslate:5000
|
||||
}
|
||||
|
||||
# Neo4j Browser
|
||||
http://{$NEO4J_HOSTNAME} {
|
||||
# Neo4j
|
||||
{$NEO4J_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy neo4j:7474
|
||||
}
|
||||
|
||||
# Neo4j Bolt Protocol
|
||||
{$NEO4J_HOSTNAME}:7687 {
|
||||
# Neo4j Bolt Protocol (wss)
|
||||
https://{$NEO4J_HOSTNAME}:7687 {
|
||||
import service_tls
|
||||
reverse_proxy neo4j:7687
|
||||
}
|
||||
|
||||
# NocoDB
|
||||
http://{$NOCODB_HOSTNAME} {
|
||||
{$NOCODB_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy nocodb:8080
|
||||
}
|
||||
|
||||
# PaddleOCR (PaddleX Basic Serving)
|
||||
http://{$PADDLEOCR_HOSTNAME} {
|
||||
{$PADDLEOCR_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$PADDLEOCR_USERNAME} {$PADDLEOCR_PASSWORD_HASH}
|
||||
}
|
||||
@@ -149,7 +183,8 @@ http://{$PADDLEOCR_HOSTNAME} {
|
||||
}
|
||||
|
||||
# Docling (Document Conversion API)
|
||||
http://{$DOCLING_HOSTNAME} {
|
||||
{$DOCLING_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$DOCLING_USERNAME} {$DOCLING_PASSWORD_HASH}
|
||||
}
|
||||
@@ -169,6 +204,7 @@ http://{$WELCOME_HOSTNAME} {
|
||||
|
||||
# HTTPS block for direct access
|
||||
{$WELCOME_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
|
||||
}
|
||||
@@ -177,10 +213,12 @@ http://{$WELCOME_HOSTNAME} {
|
||||
try_files {path} /index.html
|
||||
}
|
||||
|
||||
import /etc/caddy/addons/*.conf
|
||||
# Import custom site addons
|
||||
import /etc/caddy/addons/site-*.conf
|
||||
|
||||
# SearXNG
|
||||
http://{$SEARXNG_HOSTNAME} {
|
||||
{$SEARXNG_HOSTNAME} {
|
||||
import service_tls
|
||||
@protected not remote_ip 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10
|
||||
|
||||
basic_auth @protected {
|
||||
@@ -188,7 +226,7 @@ http://{$SEARXNG_HOSTNAME} {
|
||||
}
|
||||
|
||||
encode zstd gzip
|
||||
|
||||
|
||||
@api {
|
||||
path /config
|
||||
path /healthz
|
||||
@@ -204,7 +242,7 @@ http://{$SEARXNG_HOSTNAME} {
|
||||
@static {
|
||||
path /static/*
|
||||
}
|
||||
|
||||
|
||||
header {
|
||||
# CSP (https://content-security-policy.com)
|
||||
Content-Security-Policy "upgrade-insecure-requests; default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; form-action 'self' https://github.com/searxng/searxng/issues/new; font-src 'self'; frame-ancestors 'self'; base-uri 'self'; connect-src 'self' https://overpass-api.de; img-src * data:; frame-src https://www.youtube-nocookie.com https://player.vimeo.com https://www.dailymotion.com https://www.deezer.com https://www.mixcloud.com https://w.soundcloud.com https://embed.spotify.com;"
|
||||
@@ -221,12 +259,12 @@ http://{$SEARXNG_HOSTNAME} {
|
||||
# Remove "Server" header
|
||||
-Server
|
||||
}
|
||||
|
||||
|
||||
header @api {
|
||||
Access-Control-Allow-Methods "GET, OPTIONS"
|
||||
Access-Control-Allow-Origin "*"
|
||||
}
|
||||
|
||||
|
||||
route {
|
||||
# Cache policy
|
||||
header Cache-Control "max-age=0, no-store"
|
||||
@@ -234,7 +272,7 @@ http://{$SEARXNG_HOSTNAME} {
|
||||
header @imageproxy Cache-Control "max-age=604800, public"
|
||||
header @static Cache-Control "max-age=31536000, public, immutable"
|
||||
}
|
||||
|
||||
|
||||
# SearXNG (uWSGI)
|
||||
reverse_proxy searxng:8080 {
|
||||
header_up X-Forwarded-Port {http.request.port}
|
||||
|
||||
15
Makefile
15
Makefile
@@ -2,15 +2,11 @@
|
||||
|
||||
PROJECT_NAME := localai
|
||||
|
||||
# Detect bash 4+ (macOS ships with bash 3.2, need Homebrew bash; Ubuntu 24+ has bash 5.x)
|
||||
BASH_CMD := $(shell command -v /opt/homebrew/bin/bash 2>/dev/null || command -v /usr/local/bin/bash 2>/dev/null || echo bash)
|
||||
|
||||
help:
|
||||
@echo "n8n-install - Available commands:"
|
||||
@echo ""
|
||||
@echo " make install-vps VPS installation (Ubuntu with SSL)"
|
||||
@echo " make install-local Local installation (macOS/Linux, no sudo)"
|
||||
@echo " make update Update system and services"
|
||||
@echo " make install Full installation"
|
||||
@echo " make update Update system and services (resets to origin)"
|
||||
@echo " make update-preview Preview available updates (dry-run)"
|
||||
@echo " make git-pull Update for forks (merges from upstream)"
|
||||
@echo " make clean Remove unused Docker resources (preserves data)"
|
||||
@@ -32,11 +28,8 @@ help:
|
||||
@echo " make switch-beta Switch to beta (develop branch)"
|
||||
@echo " make switch-stable Switch to stable (main branch)"
|
||||
|
||||
install-vps:
|
||||
sudo bash ./scripts/install-vps.sh
|
||||
|
||||
install-local:
|
||||
$(BASH_CMD) ./scripts/install-local.sh
|
||||
install:
|
||||
sudo bash ./scripts/install.sh
|
||||
|
||||
update:
|
||||
sudo bash ./scripts/update.sh
|
||||
|
||||
69
README.md
69
README.md
@@ -56,6 +56,8 @@ This setup provides a comprehensive suite of cutting-edge services, all pre-conf
|
||||
|
||||
The installer also makes the following powerful open-source tools **available for you to select and deploy** via an interactive wizard during setup:
|
||||
|
||||
✅ [**Appsmith**](https://www.appsmith.com/) - An open-source low-code platform for building internal tools, dashboards, and admin panels with a drag-and-drop UI builder.
|
||||
|
||||
✅ [**n8n**](https://n8n.io/) - A low-code platform with over 400 integrations and advanced AI components to automate workflows.
|
||||
|
||||
✅ [**ComfyUI**](https://github.com/comfyanonymous/ComfyUI) - A powerful, node-based UI for Stable Diffusion workflows. Build and run image-generation pipelines visually, with support for custom nodes and extensions.
|
||||
@@ -144,18 +146,13 @@ Get started quickly with a vast library of pre-built automations (optional impor
|
||||
|
||||
### Running the Install
|
||||
|
||||
The recommended way to install on a VPS is using the provided installation script.
|
||||
The recommended way to install is using the provided main installation script.
|
||||
|
||||
1. Connect to your server via SSH.
|
||||
2. Run the following command:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/kossakovsky/n8n-install && cd n8n-install && make install-vps
|
||||
```
|
||||
|
||||
Or run directly:
|
||||
```bash
|
||||
sudo bash ./scripts/install-vps.sh
|
||||
git clone https://github.com/kossakovsky/n8n-install && cd n8n-install && sudo bash ./scripts/install.sh
|
||||
```
|
||||
|
||||
This single command automates the entire setup process, including:
|
||||
@@ -167,60 +164,16 @@ This single command automates the entire setup process, including:
|
||||
|
||||
During the installation, the script will prompt you for:
|
||||
|
||||
1. Your **primary domain name** (e.g., `yourdomain.com`). This is the domain for which you've configured the wildcard DNS record.
|
||||
2. Your **email address** (used for service logins like Flowise, Supabase dashboard, Grafana, and for SSL certificate registration with Let's Encrypt).
|
||||
1. Your **primary domain name** (Required, e.g., `yourdomain.com`). This is the domain for which you've configured the wildcard DNS record.
|
||||
2. Your **email address** (Required, used for service logins like Flowise, Supabase dashboard, Grafana, and for SSL certificate registration with Let's Encrypt).
|
||||
3. An optional **OpenAI API key** (Not required. If provided, it can be used by Supabase AI features and Crawl4ai. Press Enter to skip).
|
||||
4. Whether you want to **import ~300 ready-made n8n community workflows** (y/n, Optional. This can take 20-30 minutes, depending on your server and network speed).
|
||||
5. The **number of n8n workers** you want to run (e.g., 1, 2, 3, 4. This determines how many workflows can be processed in parallel. Each worker automatically gets its own dedicated task runner sidecar for executing Code nodes. Defaults to 1 if not specified).
|
||||
5. The **number of n8n workers** you want to run (Required, e.g., 1, 2, 3, 4. This determines how many workflows can be processed in parallel. Each worker automatically gets its own dedicated task runner sidecar for executing Code nodes. Defaults to 1 if not specified).
|
||||
6. A **Service Selection Wizard** will then appear, allowing you to choose which of the available services (like Flowise, Supabase, Qdrant, Open WebUI, etc.) you want to deploy. Core services (Caddy, Postgres, Redis) will be set up to support your selections.
|
||||
|
||||
Upon successful completion, the script will display a summary report. This report contains the access URLs and credentials for the deployed services. **Save this information in a safe place!**
|
||||
|
||||
### Local Installation
|
||||
|
||||
You can also run this project on your local machine (macOS, Linux, or Windows via WSL2) for development and testing purposes.
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
- Docker and Docker Compose installed and running
|
||||
- Bash 4.0+ (macOS ships with bash 3.2, install modern bash: `brew install bash`)
|
||||
- Python 3 with PyYAML (`pip3 install pyyaml` - auto-installed if missing)
|
||||
- `whiptail` installed (macOS: `brew install newt`, Linux: `apt install whiptail`)
|
||||
- `openssl` and `git` installed
|
||||
|
||||
#### Running Local Install
|
||||
|
||||
```bash
|
||||
git clone https://github.com/kossakovsky/n8n-install && cd n8n-install
|
||||
make install-local
|
||||
```
|
||||
|
||||
This automatically uses Homebrew bash on macOS (required for bash 4+ features).
|
||||
|
||||
The local installer will:
|
||||
- Check prerequisites (Docker, whiptail, openssl, git, python3)
|
||||
- Use `.local` domains (e.g., `n8n.local`, `flowise.local`)
|
||||
- Configure Caddy for HTTP only (no SSL certificates)
|
||||
- Skip system preparation and Docker installation (assumes Docker is pre-installed)
|
||||
|
||||
#### After Installation
|
||||
|
||||
Add the generated host entries to your system:
|
||||
|
||||
```bash
|
||||
# The installer generates a hosts.txt file with all required entries
|
||||
sudo bash -c 'cat hosts.txt >> /etc/hosts'
|
||||
|
||||
# Flush DNS cache
|
||||
# macOS:
|
||||
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
|
||||
# Linux:
|
||||
sudo systemd-resolve --flush-caches
|
||||
```
|
||||
|
||||
Access your services at `http://n8n.local`, `http://flowise.local`, etc.
|
||||
|
||||
## ⚡️ Quick Start and Usage
|
||||
## Quick Start and Usage
|
||||
|
||||
After successful installation, your services are up and running! Here's how to get started:
|
||||
|
||||
@@ -228,6 +181,7 @@ After successful installation, your services are up and running! Here's how to g
|
||||
The installation script provided a summary report with all access URLs and credentials. Please refer to that report. The main services will be available at the following addresses (replace `yourdomain.com` with your actual domain):
|
||||
|
||||
- **n8n:** `n8n.yourdomain.com` (Log in with the email address you provided during installation and the initial password from the summary report. You may be prompted to change this password on first login.)
|
||||
- **Appsmith:** `appsmith.yourdomain.com` (Low-code app builder)
|
||||
- **ComfyUI:** `comfyui.yourdomain.com` (Node-based Stable Diffusion UI)
|
||||
- **Databasus:** `databasus.yourdomain.com`
|
||||
- **Dify:** `dify.yourdomain.com` (AI application development platform with comprehensive LLMOps capabilities)
|
||||
@@ -353,9 +307,8 @@ The project includes a Makefile for simplified command execution:
|
||||
|
||||
| Command | Description |
|
||||
| --------------------- | ---------------------------------------------------- |
|
||||
| `make install-vps` | VPS installation (Ubuntu with SSL) |
|
||||
| `make install-local` | Local installation (macOS/Linux, no sudo) |
|
||||
| `make update` | Update system and services |
|
||||
| `make install` | Full installation |
|
||||
| `make update` | Update system and services (resets to origin) |
|
||||
| `make update-preview` | Preview available updates without applying (dry-run) |
|
||||
| `make git-pull` | Update for forks (merges from upstream/main) |
|
||||
| `make clean` | Remove unused Docker resources |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This directory allows you to extend or override Caddy configuration without modifying the main `Caddyfile`.
|
||||
|
||||
All `.conf` files in this directory are automatically imported via `import /etc/caddy/addons/*.conf` at the end of the main Caddyfile.
|
||||
Files matching `site-*.conf` in this directory are automatically imported via `import /etc/caddy/addons/site-*.conf` in the main Caddyfile.
|
||||
|
||||
## Use Cases
|
||||
|
||||
@@ -15,6 +15,23 @@ All `.conf` files in this directory are automatically imported via `import /etc/
|
||||
|
||||
For corporate/internal deployments where Let's Encrypt is not available, you can use your own certificates.
|
||||
|
||||
### How It Works
|
||||
|
||||
The main `Caddyfile` imports a TLS snippet that all service blocks use:
|
||||
|
||||
```caddy
|
||||
# In Caddyfile (top)
|
||||
import /etc/caddy/addons/tls-snippet.conf
|
||||
|
||||
# In each service block
|
||||
{$N8N_HOSTNAME} {
|
||||
import service_tls # <-- Uses the snippet
|
||||
reverse_proxy n8n:5678
|
||||
}
|
||||
```
|
||||
|
||||
By default, the snippet is empty (Let's Encrypt is used). When you run `make setup-tls`, the snippet is updated with your certificate paths.
|
||||
|
||||
### Quick Setup
|
||||
|
||||
1. Place your certificates in the `certs/` directory:
|
||||
@@ -28,42 +45,22 @@ For corporate/internal deployments where Let's Encrypt is not available, you can
|
||||
make setup-tls
|
||||
```
|
||||
|
||||
3. Restart Caddy:
|
||||
```bash
|
||||
docker compose -p localai restart caddy
|
||||
```
|
||||
3. The script will:
|
||||
- Update `caddy-addon/tls-snippet.conf` with your certificate paths
|
||||
- Optionally restart Caddy to apply changes
|
||||
|
||||
### Manual Setup
|
||||
### Reset to Let's Encrypt
|
||||
|
||||
1. Copy the example file:
|
||||
```bash
|
||||
cp caddy-addon/custom-tls.conf.example caddy-addon/custom-tls.conf
|
||||
```
|
||||
To switch back to automatic Let's Encrypt certificates:
|
||||
|
||||
2. Edit `custom-tls.conf` with your hostnames and certificate paths
|
||||
|
||||
3. Place certificates in `certs/` directory
|
||||
|
||||
4. Restart Caddy:
|
||||
```bash
|
||||
docker compose -p localai restart caddy
|
||||
```
|
||||
|
||||
## How Site Override Works
|
||||
|
||||
When you define a site block in an addon file with the same hostname as the main Caddyfile, Caddy will use **both** configurations. To completely override a site, use the exact same hostname.
|
||||
|
||||
Example: To override `n8n.yourdomain.com` with a custom certificate:
|
||||
|
||||
```
|
||||
# caddy-addon/custom-tls.conf
|
||||
n8n.internal.company.com {
|
||||
tls /etc/caddy/certs/wildcard.crt /etc/caddy/certs/wildcard.key
|
||||
reverse_proxy n8n:5678
|
||||
}
|
||||
```bash
|
||||
make setup-tls --remove
|
||||
```
|
||||
|
||||
Make sure your `.env` file has `N8N_HOSTNAME=n8n.internal.company.com`.
|
||||
Or run directly:
|
||||
```bash
|
||||
bash scripts/setup_custom_tls.sh --remove
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
@@ -71,8 +68,9 @@ Make sure your `.env` file has `N8N_HOSTNAME=n8n.internal.company.com`.
|
||||
caddy-addon/
|
||||
├── .gitkeep # Keeps directory in git
|
||||
├── README.md # This file
|
||||
├── custom-tls.conf.example # Example for custom certificates
|
||||
└── custom-tls.conf # Your custom config (gitignored)
|
||||
├── tls-snippet.conf.example # Template for TLS snippet (tracked in git)
|
||||
├── tls-snippet.conf # Your TLS config (gitignored, auto-created)
|
||||
└── site-*.conf # Your custom addons (gitignored, must start with "site-")
|
||||
|
||||
certs/
|
||||
├── .gitkeep # Keeps directory in git
|
||||
@@ -80,11 +78,26 @@ certs/
|
||||
└── wildcard.key # Your private key (gitignored)
|
||||
```
|
||||
|
||||
## Adding Custom Addons
|
||||
|
||||
You can create `site-*.conf` files for custom Caddy configurations. They will be automatically loaded by the main Caddyfile.
|
||||
|
||||
**Important:** Custom addon files MUST start with `site-` prefix to be loaded (e.g., `site-custom.conf`, `site-myapp.conf`).
|
||||
|
||||
Example: `caddy-addon/site-custom-headers.conf`
|
||||
```caddy
|
||||
# Add custom headers to all responses
|
||||
(custom_headers) {
|
||||
header X-Custom-Header "My Value"
|
||||
}
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Files in `caddy-addon/*.conf` are gitignored (preserved during updates)
|
||||
- `tls-snippet.conf.example` is tracked in git (template with default Let's Encrypt behavior)
|
||||
- `tls-snippet.conf` is gitignored and auto-created from template (preserved during updates)
|
||||
- `site-*.conf` files are gitignored (preserved during updates)
|
||||
- Files in `certs/` are gitignored (certificates are not committed)
|
||||
- Example files (`*.example`) are tracked in git
|
||||
- Caddy validates configuration on startup - check logs if it fails:
|
||||
```bash
|
||||
docker compose -p localai logs caddy
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
# Custom TLS Configuration for Corporate/Internal Certificates
|
||||
#
|
||||
# This file provides examples for using your own TLS certificates instead of Let's Encrypt.
|
||||
# Copy this file to custom-tls.conf and modify as needed.
|
||||
#
|
||||
# Prerequisites:
|
||||
# 1. Place your certificate files in the ./certs/ directory
|
||||
# 2. Update .env hostnames to match your internal domain
|
||||
# 3. Restart Caddy: docker compose -p localai restart caddy
|
||||
|
||||
# =============================================================================
|
||||
# Option 1: Reusable TLS snippet (recommended for wildcard certificates)
|
||||
# =============================================================================
|
||||
# Define once, import in each service block
|
||||
|
||||
(custom_tls) {
|
||||
tls /etc/caddy/certs/wildcard.crt /etc/caddy/certs/wildcard.key
|
||||
}
|
||||
|
||||
# Then for each service you want to override:
|
||||
#
|
||||
# n8n.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy n8n:5678
|
||||
# }
|
||||
#
|
||||
# flowise.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy flowise:3001
|
||||
# }
|
||||
|
||||
# =============================================================================
|
||||
# Option 2: Individual service configuration
|
||||
# =============================================================================
|
||||
# Use when you have different certificates for different services
|
||||
|
||||
# n8n.internal.company.com {
|
||||
# tls /etc/caddy/certs/n8n.crt /etc/caddy/certs/n8n.key
|
||||
# reverse_proxy n8n:5678
|
||||
# }
|
||||
|
||||
# =============================================================================
|
||||
# Option 3: Internal CA with auto-reload
|
||||
# =============================================================================
|
||||
# Caddy can auto-reload certificates when they change
|
||||
|
||||
# n8n.internal.company.com {
|
||||
# tls /etc/caddy/certs/cert.pem /etc/caddy/certs/key.pem {
|
||||
# # Optional: specify CA certificate for client verification
|
||||
# # client_auth {
|
||||
# # mode require_and_verify
|
||||
# # trusted_ca_cert_file /etc/caddy/certs/ca.pem
|
||||
# # }
|
||||
# }
|
||||
# reverse_proxy n8n:5678
|
||||
# }
|
||||
|
||||
# =============================================================================
|
||||
# Full Example: All common services with wildcard certificate
|
||||
# =============================================================================
|
||||
# Uncomment and modify the hostnames to match your .env configuration
|
||||
|
||||
# # N8N
|
||||
# n8n.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy n8n:5678
|
||||
# }
|
||||
|
||||
# # Flowise
|
||||
# flowise.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy flowise:3001
|
||||
# }
|
||||
|
||||
# # Open WebUI
|
||||
# webui.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy open-webui:8080
|
||||
# }
|
||||
|
||||
# # Grafana
|
||||
# grafana.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy grafana:3000
|
||||
# }
|
||||
|
||||
# # Portainer
|
||||
# portainer.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy portainer:9000
|
||||
# }
|
||||
|
||||
# # Langfuse
|
||||
# langfuse.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy langfuse-web:3000
|
||||
# }
|
||||
|
||||
# # Supabase
|
||||
# supabase.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy kong:8000
|
||||
# }
|
||||
|
||||
# # Welcome Page (with basic auth preserved)
|
||||
# welcome.internal.company.com {
|
||||
# import custom_tls
|
||||
# basic_auth {
|
||||
# {$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
|
||||
# }
|
||||
# root * /srv/welcome
|
||||
# file_server
|
||||
# try_files {path} /index.html
|
||||
# }
|
||||
10
caddy-addon/tls-snippet.conf.example
Normal file
10
caddy-addon/tls-snippet.conf.example
Normal file
@@ -0,0 +1,10 @@
|
||||
# TLS Configuration Snippet
|
||||
# Imported by all service blocks in the main Caddyfile.
|
||||
#
|
||||
# Default: Empty (uses Let's Encrypt automatic certificates)
|
||||
# Custom: Overwritten by 'make setup-tls' with your certificate paths
|
||||
# Reset: Run 'make setup-tls --remove' to restore Let's Encrypt
|
||||
|
||||
(service_tls) {
|
||||
# Default: Let's Encrypt automatic certificates (empty = no override)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
volumes:
|
||||
appsmith_data:
|
||||
caddy-config:
|
||||
caddy-data:
|
||||
comfyui_data:
|
||||
@@ -98,7 +99,6 @@ x-n8n: &service-n8n
|
||||
N8N_SMTP_SSL: ${N8N_SMTP_SSL:-true}
|
||||
N8N_SMTP_STARTTLS: ${N8N_SMTP_STARTTLS:-true}
|
||||
N8N_SMTP_USER: ${N8N_SMTP_USER:-}
|
||||
N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE:-true}
|
||||
N8N_TRUST_PROXY: true
|
||||
N8N_USER_MANAGEMENT_JWT_SECRET: ${N8N_USER_MANAGEMENT_JWT_SECRET}
|
||||
NODE_ENV: production
|
||||
@@ -106,7 +106,7 @@ x-n8n: &service-n8n
|
||||
QUEUE_BULL_REDIS_HOST: ${REDIS_HOST:-redis}
|
||||
QUEUE_BULL_REDIS_PORT: ${REDIS_PORT:-6379}
|
||||
QUEUE_HEALTH_CHECK_ACTIVE: true
|
||||
WEBHOOK_URL: ${PROTOCOL:-https}://${N8N_HOSTNAME}/
|
||||
WEBHOOK_URL: ${N8N_HOSTNAME:+https://}${N8N_HOSTNAME:-http://localhost:5678}/
|
||||
|
||||
x-ollama: &service-ollama
|
||||
image: ollama/ollama:latest
|
||||
@@ -145,6 +145,26 @@ x-n8n-worker-runner: &service-n8n-worker-runner
|
||||
N8N_RUNNERS_TASK_BROKER_URI: http://127.0.0.1:5679
|
||||
|
||||
services:
|
||||
appsmith:
|
||||
image: appsmith/appsmith-ce:release
|
||||
container_name: appsmith
|
||||
profiles: ["appsmith"]
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
environment:
|
||||
<<: *proxy-env
|
||||
APPSMITH_ENCRYPTION_PASSWORD: ${APPSMITH_ENCRYPTION_PASSWORD}
|
||||
APPSMITH_ENCRYPTION_SALT: ${APPSMITH_ENCRYPTION_SALT}
|
||||
APPSMITH_DISABLE_TELEMETRY: "true"
|
||||
volumes:
|
||||
- appsmith_data:/appsmith-stacks
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "http_proxy= https_proxy= HTTP_PROXY= HTTPS_PROXY= wget -qO- http://localhost/api/v1/health || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 120s
|
||||
|
||||
flowise:
|
||||
image: flowiseai/flowise
|
||||
restart: unless-stopped
|
||||
@@ -287,7 +307,7 @@ services:
|
||||
environment:
|
||||
NC_AUTH_JWT_SECRET: ${NOCODB_JWT_SECRET}
|
||||
NC_DB: pg://postgres:5432?u=postgres&p=${POSTGRES_PASSWORD}&d=nocodb
|
||||
NC_PUBLIC_URL: ${PROTOCOL:-https}://${NOCODB_HOSTNAME}
|
||||
NC_PUBLIC_URL: https://${NOCODB_HOSTNAME}
|
||||
NC_REDIS_URL: redis://redis:6379
|
||||
volumes:
|
||||
- nocodb_data:/usr/app/data
|
||||
@@ -319,7 +339,7 @@ services:
|
||||
- caddy-data:/data:rw
|
||||
- caddy-config:/config:rw
|
||||
environment:
|
||||
CADDY_AUTO_HTTPS: ${CADDY_AUTO_HTTPS:-on}
|
||||
APPSMITH_HOSTNAME: ${APPSMITH_HOSTNAME}
|
||||
COMFYUI_HOSTNAME: ${COMFYUI_HOSTNAME}
|
||||
COMFYUI_PASSWORD_HASH: ${COMFYUI_PASSWORD_HASH}
|
||||
COMFYUI_USERNAME: ${COMFYUI_USERNAME}
|
||||
@@ -467,7 +487,7 @@ services:
|
||||
depends_on: *langfuse-depends-on
|
||||
environment:
|
||||
<<: *langfuse-worker-env
|
||||
NEXTAUTH_URL: ${PROTOCOL:-https}://${LANGFUSE_HOSTNAME}
|
||||
NEXTAUTH_URL: https://${LANGFUSE_HOSTNAME}
|
||||
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
||||
LANGFUSE_INIT_ORG_ID: ${LANGFUSE_INIT_ORG_ID:-organization_id}
|
||||
LANGFUSE_INIT_ORG_NAME: ${LANGFUSE_INIT_ORG_NAME:-Organization}
|
||||
@@ -565,7 +585,7 @@ services:
|
||||
volumes:
|
||||
- ./searxng:/etc/searxng:rw
|
||||
environment:
|
||||
SEARXNG_BASE_URL: ${PROTOCOL:-https}://${SEARXNG_HOSTNAME:-localhost}/
|
||||
SEARXNG_BASE_URL: https://${SEARXNG_HOSTNAME:-localhost}/
|
||||
UWSGI_WORKERS: ${SEARXNG_UWSGI_WORKERS:-4}
|
||||
UWSGI_THREADS: ${SEARXNG_UWSGI_THREADS:-4}
|
||||
# cap_drop: - ALL # Temporarily commented out for first run
|
||||
@@ -838,14 +858,14 @@ services:
|
||||
restart: always
|
||||
environment:
|
||||
<<: *proxy-env
|
||||
BACKEND_INTERNAL_URL: http://postiz:3000
|
||||
BACKEND_INTERNAL_URL: http://localhost:3000
|
||||
DATABASE_URL: "postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/${POSTIZ_DB_NAME:-postiz}?schema=postiz"
|
||||
DISABLE_REGISTRATION: ${POSTIZ_DISABLE_REGISTRATION}
|
||||
FRONTEND_URL: ${PROTOCOL:-https}://${POSTIZ_HOSTNAME}
|
||||
FRONTEND_URL: ${POSTIZ_HOSTNAME:+https://}${POSTIZ_HOSTNAME}
|
||||
IS_GENERAL: "true" # Required for self-hosting.
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
MAIN_URL: ${PROTOCOL:-https}://${POSTIZ_HOSTNAME}
|
||||
NEXT_PUBLIC_BACKEND_URL: ${PROTOCOL:-https}://${POSTIZ_HOSTNAME}/api
|
||||
MAIN_URL: ${POSTIZ_HOSTNAME:+https://}${POSTIZ_HOSTNAME}
|
||||
NEXT_PUBLIC_BACKEND_URL: ${POSTIZ_HOSTNAME:+https://}${POSTIZ_HOSTNAME}/api
|
||||
NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads"
|
||||
REDIS_URL: "redis://redis:6379"
|
||||
STORAGE_PROVIDER: "local"
|
||||
@@ -910,7 +930,7 @@ services:
|
||||
start_period: 60s
|
||||
|
||||
comfyui:
|
||||
image: yanwk/comfyui-boot:cu124-slim
|
||||
image: yanwk/comfyui-boot:cu128-slim
|
||||
container_name: comfyui
|
||||
profiles: ["comfyui"]
|
||||
restart: unless-stopped
|
||||
@@ -1032,10 +1052,10 @@ services:
|
||||
REDIS_HOST: ragflow-redis
|
||||
REDIS_PASSWORD: ${RAGFLOW_REDIS_PASSWORD}
|
||||
REDIS_PORT: 6379
|
||||
SVR_HTTP_PORT: 80
|
||||
SVR_HTTP_PORT: 9380
|
||||
volumes:
|
||||
- ragflow_data:/ragflow
|
||||
- ./ragflow/nginx.conf:/etc/nginx/sites-available/default:ro
|
||||
- ./ragflow/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
ragflow-elasticsearch:
|
||||
condition: service_healthy
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# Stage 1: Get static ffmpeg binaries (statically linked, works on Alpine/musl)
|
||||
FROM mwader/static-ffmpeg:latest AS ffmpeg
|
||||
|
||||
# Stage 2: Build final n8n image with ffmpeg
|
||||
FROM n8nio/n8n:stable
|
||||
|
||||
USER root
|
||||
# Install static ffmpeg binary from BtbN GitHub releases
|
||||
RUN wget -qO- --tries=3 --timeout=60 https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz | \
|
||||
tar -xJC /tmp && \
|
||||
mv /tmp/ffmpeg-master-latest-linux64-gpl/bin/ffmpeg /tmp/ffmpeg-master-latest-linux64-gpl/bin/ffprobe /usr/local/bin/ && \
|
||||
rm -rf /tmp/ffmpeg-*
|
||||
# Copy static ffmpeg binaries from the ffmpeg stage
|
||||
COPY --from=ffmpeg /ffmpeg /usr/local/bin/ffmpeg
|
||||
COPY --from=ffmpeg /ffprobe /usr/local/bin/ffprobe
|
||||
USER node
|
||||
|
||||
@@ -23,15 +23,9 @@ set -e
|
||||
source "$(dirname "$0")/utils.sh"
|
||||
init_paths
|
||||
|
||||
# Source local mode utilities
|
||||
source "$SCRIPT_DIR/local.sh"
|
||||
|
||||
# Source telemetry functions
|
||||
source "$SCRIPT_DIR/telemetry.sh"
|
||||
|
||||
# Get installation mode using local.sh helper
|
||||
INSTALL_MODE="$(get_install_mode)"
|
||||
|
||||
# Setup cleanup for temporary files
|
||||
TEMP_FILES=()
|
||||
cleanup_temp_files() {
|
||||
@@ -80,6 +74,8 @@ USER_INPUT_VARS=(
|
||||
# Variables to generate: varName="type:length"
|
||||
# Types: password (alphanum), secret (base64), hex, base64, alphanum
|
||||
declare -A VARS_TO_GENERATE=(
|
||||
["APPSMITH_ENCRYPTION_PASSWORD"]="password:32"
|
||||
["APPSMITH_ENCRYPTION_SALT"]="password:32"
|
||||
["CLICKHOUSE_PASSWORD"]="password:32"
|
||||
["COMFYUI_PASSWORD"]="password:32" # Added ComfyUI basic auth password
|
||||
["DASHBOARD_PASSWORD"]="password:32" # Supabase Dashboard
|
||||
@@ -159,13 +155,15 @@ if [ -f "$OUTPUT_FILE" ]; then
|
||||
done < "$OUTPUT_FILE"
|
||||
fi
|
||||
|
||||
# Setup Caddy for bcrypt hash generation (using Docker for cross-platform support)
|
||||
log_subheader "Setting up Caddy for password hashing"
|
||||
log_info "Using Docker-based Caddy for password hashing..."
|
||||
if ! docker image inspect caddy:latest &>/dev/null; then
|
||||
log_info "Pulling Caddy Docker image..."
|
||||
docker pull caddy:latest
|
||||
fi
|
||||
# Install Caddy
|
||||
log_subheader "Installing Caddy"
|
||||
log_info "Adding Caddy repository and installing..."
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --yes --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
|
||||
apt install -y caddy
|
||||
|
||||
# Check for caddy
|
||||
require_command "caddy" "Caddy installation failed. Please check the installation logs above."
|
||||
|
||||
require_whiptail
|
||||
|
||||
@@ -173,51 +171,42 @@ require_whiptail
|
||||
log_subheader "Domain Configuration"
|
||||
DOMAIN="" # Initialize DOMAIN variable
|
||||
|
||||
# Configure mode-specific environment variables using local.sh
|
||||
configure_mode_env generated_values "$INSTALL_MODE"
|
||||
|
||||
if is_local_mode; then
|
||||
# Local mode: use .local TLD automatically
|
||||
DOMAIN="$(get_local_domain)"
|
||||
# Try to get domain from existing .env file first
|
||||
# Check if USER_DOMAIN_NAME is set in existing_env_vars and is not empty
|
||||
if [[ ${existing_env_vars[USER_DOMAIN_NAME]+_} && -n "${existing_env_vars[USER_DOMAIN_NAME]}" ]]; then
|
||||
DOMAIN="${existing_env_vars[USER_DOMAIN_NAME]}"
|
||||
# Ensure this value is carried over to generated_values for writing and template processing
|
||||
# If it came from existing_env_vars, it might already be there, but this ensures it.
|
||||
generated_values["USER_DOMAIN_NAME"]="$DOMAIN"
|
||||
log_info "Using local development domain: .$DOMAIN"
|
||||
else
|
||||
# VPS mode: prompt for real domain
|
||||
# Try to get domain from existing .env file first
|
||||
if [[ ${existing_env_vars[USER_DOMAIN_NAME]+_} && -n "${existing_env_vars[USER_DOMAIN_NAME]}" ]]; then
|
||||
DOMAIN="${existing_env_vars[USER_DOMAIN_NAME]}"
|
||||
generated_values["USER_DOMAIN_NAME"]="$DOMAIN"
|
||||
else
|
||||
while true; do
|
||||
DOMAIN_INPUT=$(wt_input "Primary Domain" "Enter the primary domain name for your services (e.g., example.com)." "") || true
|
||||
while true; do
|
||||
DOMAIN_INPUT=$(wt_input "Primary Domain" "Enter the primary domain name for your services (e.g., example.com)." "") || true
|
||||
|
||||
DOMAIN_TO_USE="$DOMAIN_INPUT" # Direct assignment, no default fallback
|
||||
DOMAIN_TO_USE="$DOMAIN_INPUT" # Direct assignment, no default fallback
|
||||
|
||||
# Validate domain input
|
||||
if [[ -z "$DOMAIN_TO_USE" ]]; then
|
||||
wt_msg "Validation" "Domain name cannot be empty."
|
||||
continue
|
||||
fi
|
||||
# Validate domain input
|
||||
if [[ -z "$DOMAIN_TO_USE" ]]; then
|
||||
wt_msg "Validation" "Domain name cannot be empty."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Basic check for likely invalid domain characters (very permissive)
|
||||
if [[ "$DOMAIN_TO_USE" =~ [^a-zA-Z0-9.-] ]]; then
|
||||
wt_msg "Validation" "Warning: Domain contains potentially invalid characters: '$DOMAIN_TO_USE'"
|
||||
fi
|
||||
if wt_yesno "Confirm Domain" "Use '$DOMAIN_TO_USE' as the primary domain?" "yes"; then
|
||||
DOMAIN="$DOMAIN_TO_USE"
|
||||
generated_values["USER_DOMAIN_NAME"]="$DOMAIN"
|
||||
log_info "Domain set to '$DOMAIN'. It will be saved in .env."
|
||||
break # Confirmed, exit loop
|
||||
fi
|
||||
done
|
||||
fi
|
||||
# Basic check for likely invalid domain characters (very permissive)
|
||||
if [[ "$DOMAIN_TO_USE" =~ [^a-zA-Z0-9.-] ]]; then
|
||||
wt_msg "Validation" "Warning: Domain contains potentially invalid characters: '$DOMAIN_TO_USE'"
|
||||
fi
|
||||
if wt_yesno "Confirm Domain" "Use '$DOMAIN_TO_USE' as the primary domain?" "yes"; then
|
||||
DOMAIN="$DOMAIN_TO_USE" # Set the final DOMAIN variable
|
||||
generated_values["USER_DOMAIN_NAME"]="$DOMAIN" # Using USER_DOMAIN_NAME
|
||||
log_info "Domain set to '$DOMAIN'. It will be saved in .env."
|
||||
break # Confirmed, exit loop
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Prompt for user email (used for service logins and SSL certificates in VPS mode)
|
||||
# Prompt for user email
|
||||
log_subheader "Email Configuration"
|
||||
|
||||
if [[ -z "${existing_env_vars[LETSENCRYPT_EMAIL]}" ]]; then
|
||||
wt_msg "Email Required" "Please enter your email address. It will be used for service logins."
|
||||
wt_msg "Email Required" "Please enter your email address. It will be used for logins and Let's Encrypt SSL."
|
||||
fi
|
||||
|
||||
if [[ -n "${existing_env_vars[LETSENCRYPT_EMAIL]}" ]]; then
|
||||
@@ -605,6 +594,9 @@ log_success ".env file generated successfully in the project root ($OUTPUT_FILE)
|
||||
# Save installation ID for telemetry correlation
|
||||
save_installation_id "$OUTPUT_FILE"
|
||||
|
||||
# Uninstall caddy
|
||||
apt remove -y caddy
|
||||
|
||||
# Cleanup any .bak files
|
||||
cleanup_bak_files "$PROJECT_ROOT"
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ current_profiles_for_matching=",$CURRENT_PROFILES_VALUE,"
|
||||
# --- Define available services and their descriptions ---
|
||||
# Base service definitions (tag, description)
|
||||
base_services_data=(
|
||||
"appsmith" "Appsmith (Low-code Platform for Internal Tools & Dashboards)"
|
||||
"cloudflare-tunnel" "Cloudflare Tunnel (Zero-Trust Secure Access)"
|
||||
"comfyui" "ComfyUI (Node-based Stable Diffusion UI)"
|
||||
"crawl4ai" "Crawl4ai (Web Crawler for AI)"
|
||||
@@ -215,7 +216,7 @@ if [ $gost_selected -eq 1 ]; then
|
||||
EXISTING_UPSTREAM=$(read_env_var "GOST_UPSTREAM_PROXY")
|
||||
|
||||
GOST_UPSTREAM_INPUT=$(wt_input "Gost Upstream Proxy" \
|
||||
"Enter your external proxy URL for geo-bypass.\n\nExamples:\n socks5://user:pass@proxy.com:1080\n http://user:pass@proxy.com:8080\n\nThis proxy should be located outside restricted regions." \
|
||||
"Enter your external proxy URL for geo-bypass.\n\nExamples:\n socks5://user:pass@proxy.com:1080\n http://user:pass@proxy.com:8080\n\nIMPORTANT: For HTTP proxies use http://, NOT https://.\nThe protocol refers to proxy type, not connection security.\n\nThis proxy should be located outside restricted regions." \
|
||||
"$EXISTING_UPSTREAM") || true
|
||||
|
||||
if [ -n "$GOST_UPSTREAM_INPUT" ]; then
|
||||
|
||||
@@ -27,6 +27,10 @@ init_paths
|
||||
# Ensure .env exists
|
||||
ensure_file_exists "$ENV_FILE"
|
||||
|
||||
# Load COMPOSE_PROFILES early so is_profile_active works for all sections
|
||||
COMPOSE_PROFILES_VALUE="$(read_env_var COMPOSE_PROFILES)"
|
||||
COMPOSE_PROFILES="$COMPOSE_PROFILES_VALUE"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Prompt for OpenAI API key (optional) using .env value as source of truth
|
||||
# ----------------------------------------------------------------
|
||||
@@ -48,87 +52,89 @@ fi
|
||||
# ----------------------------------------------------------------
|
||||
# Logic for n8n workflow import (RUN_N8N_IMPORT)
|
||||
# ----------------------------------------------------------------
|
||||
log_subheader "n8n Workflow Import"
|
||||
final_run_n8n_import_decision="false"
|
||||
require_whiptail
|
||||
if wt_yesno "Import n8n Workflows" "Import ~300 ready-made n8n workflows now? This can take ~30 minutes." "no"; then
|
||||
final_run_n8n_import_decision="true"
|
||||
else
|
||||
if is_profile_active "n8n"; then
|
||||
log_subheader "n8n Workflow Import"
|
||||
final_run_n8n_import_decision="false"
|
||||
fi
|
||||
require_whiptail
|
||||
if wt_yesno "Import n8n Workflows" "Import ~300 ready-made n8n workflows now? This can take ~30 minutes." "no"; then
|
||||
final_run_n8n_import_decision="true"
|
||||
else
|
||||
final_run_n8n_import_decision="false"
|
||||
fi
|
||||
|
||||
# Persist RUN_N8N_IMPORT to .env
|
||||
write_env_var "RUN_N8N_IMPORT" "$final_run_n8n_import_decision"
|
||||
# Persist RUN_N8N_IMPORT to .env
|
||||
write_env_var "RUN_N8N_IMPORT" "$final_run_n8n_import_decision"
|
||||
else
|
||||
write_env_var "RUN_N8N_IMPORT" "false"
|
||||
fi
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Prompt for number of n8n workers
|
||||
# ----------------------------------------------------------------
|
||||
log_subheader "n8n Worker Configuration"
|
||||
EXISTING_N8N_WORKER_COUNT="$(read_env_var N8N_WORKER_COUNT)"
|
||||
require_whiptail
|
||||
if [[ -n "$EXISTING_N8N_WORKER_COUNT" ]]; then
|
||||
N8N_WORKER_COUNT_CURRENT="$EXISTING_N8N_WORKER_COUNT"
|
||||
N8N_WORKER_COUNT_INPUT_RAW=$(wt_input "n8n Workers (instances)" "Enter new number of n8n workers, or leave as current ($N8N_WORKER_COUNT_CURRENT)." "") || true
|
||||
if [[ -z "$N8N_WORKER_COUNT_INPUT_RAW" ]]; then
|
||||
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
|
||||
else
|
||||
if [[ "$N8N_WORKER_COUNT_INPUT_RAW" =~ ^0*[1-9][0-9]*$ ]]; then
|
||||
N8N_WORKER_COUNT_TEMP="$((10#$N8N_WORKER_COUNT_INPUT_RAW))"
|
||||
if [[ "$N8N_WORKER_COUNT_TEMP" -ge 1 ]]; then
|
||||
if wt_yesno "Confirm Workers" "Update n8n workers to $N8N_WORKER_COUNT_TEMP?" "yes"; then
|
||||
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_TEMP"
|
||||
if is_profile_active "n8n"; then
|
||||
log_subheader "n8n Worker Configuration"
|
||||
EXISTING_N8N_WORKER_COUNT="$(read_env_var N8N_WORKER_COUNT)"
|
||||
require_whiptail
|
||||
if [[ -n "$EXISTING_N8N_WORKER_COUNT" ]]; then
|
||||
N8N_WORKER_COUNT_CURRENT="$EXISTING_N8N_WORKER_COUNT"
|
||||
N8N_WORKER_COUNT_INPUT_RAW=$(wt_input "n8n Workers (instances)" "Enter new number of n8n workers, or leave as current ($N8N_WORKER_COUNT_CURRENT)." "") || true
|
||||
if [[ -z "$N8N_WORKER_COUNT_INPUT_RAW" ]]; then
|
||||
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
|
||||
else
|
||||
if [[ "$N8N_WORKER_COUNT_INPUT_RAW" =~ ^0*[1-9][0-9]*$ ]]; then
|
||||
N8N_WORKER_COUNT_TEMP="$((10#$N8N_WORKER_COUNT_INPUT_RAW))"
|
||||
if [[ "$N8N_WORKER_COUNT_TEMP" -ge 1 ]]; then
|
||||
if wt_yesno "Confirm Workers" "Update n8n workers to $N8N_WORKER_COUNT_TEMP?" "yes"; then
|
||||
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_TEMP"
|
||||
else
|
||||
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
|
||||
log_info "Change declined. Keeping N8N_WORKER_COUNT at $N8N_WORKER_COUNT."
|
||||
fi
|
||||
else
|
||||
log_warning "Invalid input '$N8N_WORKER_COUNT_INPUT_RAW'. Number must be positive. Keeping $N8N_WORKER_COUNT_CURRENT."
|
||||
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
|
||||
log_info "Change declined. Keeping N8N_WORKER_COUNT at $N8N_WORKER_COUNT."
|
||||
fi
|
||||
else
|
||||
log_warning "Invalid input '$N8N_WORKER_COUNT_INPUT_RAW'. Number must be positive. Keeping $N8N_WORKER_COUNT_CURRENT."
|
||||
log_warning "Invalid input '$N8N_WORKER_COUNT_INPUT_RAW'. Please enter a positive integer. Keeping $N8N_WORKER_COUNT_CURRENT."
|
||||
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
|
||||
fi
|
||||
else
|
||||
log_warning "Invalid input '$N8N_WORKER_COUNT_INPUT_RAW'. Please enter a positive integer. Keeping $N8N_WORKER_COUNT_CURRENT."
|
||||
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_CURRENT"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
while true; do
|
||||
N8N_WORKER_COUNT_INPUT_RAW=$(wt_input "n8n Workers" "Enter number of n8n workers to run (default 1)." "1") || true
|
||||
N8N_WORKER_COUNT_CANDIDATE="${N8N_WORKER_COUNT_INPUT_RAW:-1}"
|
||||
if [[ "$N8N_WORKER_COUNT_CANDIDATE" =~ ^0*[1-9][0-9]*$ ]]; then
|
||||
N8N_WORKER_COUNT_VALIDATED="$((10#$N8N_WORKER_COUNT_CANDIDATE))"
|
||||
if [[ "$N8N_WORKER_COUNT_VALIDATED" -ge 1 ]]; then
|
||||
if wt_yesno "Confirm Workers" "Run $N8N_WORKER_COUNT_VALIDATED n8n worker(s)?" "yes"; then
|
||||
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_VALIDATED"
|
||||
break
|
||||
else
|
||||
while true; do
|
||||
N8N_WORKER_COUNT_INPUT_RAW=$(wt_input "n8n Workers" "Enter number of n8n workers to run (default 1)." "1") || true
|
||||
N8N_WORKER_COUNT_CANDIDATE="${N8N_WORKER_COUNT_INPUT_RAW:-1}"
|
||||
if [[ "$N8N_WORKER_COUNT_CANDIDATE" =~ ^0*[1-9][0-9]*$ ]]; then
|
||||
N8N_WORKER_COUNT_VALIDATED="$((10#$N8N_WORKER_COUNT_CANDIDATE))"
|
||||
if [[ "$N8N_WORKER_COUNT_VALIDATED" -ge 1 ]]; then
|
||||
if wt_yesno "Confirm Workers" "Run $N8N_WORKER_COUNT_VALIDATED n8n worker(s)?" "yes"; then
|
||||
N8N_WORKER_COUNT="$N8N_WORKER_COUNT_VALIDATED"
|
||||
break
|
||||
fi
|
||||
else
|
||||
log_error "Number of workers must be a positive integer."
|
||||
fi
|
||||
else
|
||||
log_error "Number of workers must be a positive integer."
|
||||
log_error "Invalid input '$N8N_WORKER_COUNT_CANDIDATE'. Please enter a positive integer (e.g., 1, 2)."
|
||||
fi
|
||||
else
|
||||
log_error "Invalid input '$N8N_WORKER_COUNT_CANDIDATE'. Please enter a positive integer (e.g., 1, 2)."
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
# Ensure N8N_WORKER_COUNT is definitely set (should be by logic above)
|
||||
N8N_WORKER_COUNT="${N8N_WORKER_COUNT:-1}"
|
||||
|
||||
# Persist N8N_WORKER_COUNT to .env
|
||||
write_env_var "N8N_WORKER_COUNT" "$N8N_WORKER_COUNT"
|
||||
|
||||
# Generate worker-runner pairs configuration
|
||||
# Each worker gets its own dedicated task runner sidecar
|
||||
log_info "Generating n8n worker-runner pairs configuration..."
|
||||
bash "$SCRIPT_DIR/generate_n8n_workers.sh"
|
||||
fi
|
||||
# Ensure N8N_WORKER_COUNT is definitely set (should be by logic above)
|
||||
N8N_WORKER_COUNT="${N8N_WORKER_COUNT:-1}"
|
||||
|
||||
# Persist N8N_WORKER_COUNT to .env
|
||||
write_env_var "N8N_WORKER_COUNT" "$N8N_WORKER_COUNT"
|
||||
|
||||
# Generate worker-runner pairs configuration
|
||||
# Each worker gets its own dedicated task runner sidecar
|
||||
log_info "Generating n8n worker-runner pairs configuration..."
|
||||
"$BASH" "$SCRIPT_DIR/generate_n8n_workers.sh"
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Cloudflare Tunnel Token (if cloudflare-tunnel profile is active)
|
||||
# ----------------------------------------------------------------
|
||||
COMPOSE_PROFILES_VALUE="$(read_env_var COMPOSE_PROFILES)"
|
||||
# Set COMPOSE_PROFILES for is_profile_active to work
|
||||
COMPOSE_PROFILES="$COMPOSE_PROFILES_VALUE"
|
||||
|
||||
if is_profile_active "cloudflare-tunnel"; then
|
||||
log_subheader "Cloudflare Tunnel"
|
||||
existing_cf_token="$(read_env_var CLOUDFLARE_TUNNEL_TOKEN)"
|
||||
|
||||
@@ -32,6 +32,23 @@ require_file "$PROJECT_ROOT/docker-compose.yml" "docker-compose.yml file not fou
|
||||
require_file "$PROJECT_ROOT/Caddyfile" "Caddyfile not found in project root. Reverse proxy might not work."
|
||||
require_file "$PROJECT_ROOT/start_services.py" "start_services.py file not found in project root."
|
||||
|
||||
# Remove legacy custom-tls.conf that causes duplicate host errors
|
||||
# This is needed for users upgrading from older versions
|
||||
# TODO: Remove this cleanup block after v3.0 release (all users migrated)
|
||||
OLD_TLS_CONFIG="$PROJECT_ROOT/caddy-addon/custom-tls.conf"
|
||||
if [[ -f "$OLD_TLS_CONFIG" ]]; then
|
||||
log_warning "Removing obsolete custom-tls.conf (causes duplicate host errors)"
|
||||
rm -f "$OLD_TLS_CONFIG"
|
||||
fi
|
||||
|
||||
# Ensure TLS snippet exists (auto-create from template if missing)
|
||||
TLS_SNIPPET="$PROJECT_ROOT/caddy-addon/tls-snippet.conf"
|
||||
TLS_TEMPLATE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf.example"
|
||||
if [[ ! -f "$TLS_SNIPPET" ]] && [[ -f "$TLS_TEMPLATE" ]]; then
|
||||
cp "$TLS_TEMPLATE" "$TLS_SNIPPET"
|
||||
log_info "Created tls-snippet.conf from template (Let's Encrypt mode)"
|
||||
fi
|
||||
|
||||
# Check if Docker daemon is running
|
||||
if ! docker info > /dev/null 2>&1; then
|
||||
log_error "Docker daemon is not running. Please start Docker and try again."
|
||||
|
||||
@@ -23,20 +23,13 @@ set -e
|
||||
source "$(dirname "$0")/utils.sh"
|
||||
init_paths
|
||||
|
||||
# Source local mode utilities
|
||||
source "$SCRIPT_DIR/local.sh"
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_env || exit 1
|
||||
|
||||
# Get installation mode and protocol using local.sh helpers
|
||||
INSTALL_MODE="$(get_install_mode)"
|
||||
PROTOCOL="$(get_protocol)"
|
||||
|
||||
# Generate welcome page data
|
||||
if [ -f "$SCRIPT_DIR/generate_welcome_page.sh" ]; then
|
||||
log_info "Generating welcome page..."
|
||||
"$BASH" "$SCRIPT_DIR/generate_welcome_page.sh" || log_warning "Failed to generate welcome page"
|
||||
bash "$SCRIPT_DIR/generate_welcome_page.sh" || log_warning "Failed to generate welcome page"
|
||||
fi
|
||||
|
||||
# Helper function to print a divider line
|
||||
@@ -65,25 +58,12 @@ clear
|
||||
# Header
|
||||
log_box "Installation/Update Complete"
|
||||
|
||||
# --- Local Mode: /etc/hosts Instructions ---
|
||||
if is_local_mode; then
|
||||
print_section "Local Installation Setup"
|
||||
|
||||
# Generate hosts entries
|
||||
if [ -f "$SCRIPT_DIR/generate_hosts.sh" ]; then
|
||||
"$BASH" "$SCRIPT_DIR/generate_hosts.sh" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Print hosts setup instructions using local.sh helper
|
||||
print_local_hosts_instructions
|
||||
fi
|
||||
|
||||
# --- Welcome Page Section ---
|
||||
print_section "Welcome Page"
|
||||
echo ""
|
||||
echo -e " ${WHITE}All your service credentials are available here:${NC}"
|
||||
echo ""
|
||||
print_credential "URL" "${PROTOCOL}://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
print_credential "URL" "https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
print_credential "Username" "${WELCOME_USERNAME:-<not_set>}"
|
||||
print_credential "Password" "${WELCOME_PASSWORD:-<not_set>}"
|
||||
echo ""
|
||||
@@ -94,11 +74,14 @@ echo -e " ${DIM}hostnames, credentials, and internal URLs.${NC}"
|
||||
print_section "Next Steps"
|
||||
echo ""
|
||||
echo -e " ${WHITE}1.${NC} Visit your Welcome Page to view all credentials"
|
||||
echo -e " ${CYAN}${PROTOCOL}://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}${NC}"
|
||||
echo -e " ${CYAN}https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}${NC}"
|
||||
echo ""
|
||||
echo -e " ${WHITE}2.${NC} Store the Welcome Page credentials securely"
|
||||
echo ""
|
||||
echo -e " ${WHITE}3.${NC} Configure services as needed:"
|
||||
if is_profile_active "appsmith"; then
|
||||
echo -e " ${GREEN}*${NC} ${WHITE}Appsmith${NC}: Create admin account on first login (may take a few minutes to start)"
|
||||
fi
|
||||
if is_profile_active "n8n"; then
|
||||
echo -e " ${GREEN}*${NC} ${WHITE}n8n${NC}: Complete first-run setup with your email"
|
||||
fi
|
||||
|
||||
@@ -17,33 +17,8 @@ source "$(dirname "$0")/utils.sh"
|
||||
# Initialize paths
|
||||
init_paths
|
||||
|
||||
# Source local mode utilities
|
||||
source "$SCRIPT_DIR/local.sh"
|
||||
|
||||
# Load environment for INSTALL_MODE
|
||||
load_env 2>/dev/null || true
|
||||
|
||||
log_info "Fixing file permissions..."
|
||||
|
||||
# Local mode: minimal permission fixes (usually not run with sudo)
|
||||
if is_local_mode; then
|
||||
log_info "Local mode - applying minimal permission fixes"
|
||||
|
||||
# Ensure .env has restricted permissions
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
chmod 600 "$ENV_FILE"
|
||||
log_info "Set restrictive permissions on .env file"
|
||||
fi
|
||||
|
||||
# Ensure scripts are executable
|
||||
chmod +x "$SCRIPT_DIR"/*.sh 2>/dev/null || true
|
||||
chmod +x "$PROJECT_ROOT"/*.py 2>/dev/null || true
|
||||
|
||||
log_success "File permissions configured for local development!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# VPS mode: full permission fix with chown
|
||||
# Get the real user who ran the installer
|
||||
REAL_USER=$(get_real_user)
|
||||
REAL_GROUP=$(id -gn "$REAL_USER" 2>/dev/null || echo "$REAL_USER")
|
||||
|
||||
@@ -44,7 +44,7 @@ send_telemetry "update_start"
|
||||
# --- Call 03_generate_secrets.sh in update mode ---
|
||||
set_telemetry_stage "update_env"
|
||||
log_info "Ensuring .env file is up-to-date with all variables..."
|
||||
"$BASH" "$SCRIPT_DIR/03_generate_secrets.sh" --update || {
|
||||
bash "$SCRIPT_DIR/03_generate_secrets.sh" --update || {
|
||||
log_error "Failed to update .env configuration via 03_generate_secrets.sh. Update process cannot continue."
|
||||
exit 1
|
||||
}
|
||||
@@ -54,7 +54,7 @@ log_success ".env file updated successfully."
|
||||
# --- Run Service Selection Wizard FIRST to get updated profiles ---
|
||||
set_telemetry_stage "update_wizard"
|
||||
log_info "Running Service Selection Wizard to update service choices..."
|
||||
"$BASH" "$SCRIPT_DIR/04_wizard.sh" || {
|
||||
bash "$SCRIPT_DIR/04_wizard.sh" || {
|
||||
log_error "Service Selection Wizard failed. Update process cannot continue."
|
||||
exit 1
|
||||
}
|
||||
@@ -64,7 +64,7 @@ log_success "Service selection updated."
|
||||
# --- Configure Services (prompts and .env updates) ---
|
||||
set_telemetry_stage "update_configure"
|
||||
log_info "Configuring services (.env updates for optional inputs)..."
|
||||
"$BASH" "$SCRIPT_DIR/05_configure_services.sh" || {
|
||||
bash "$SCRIPT_DIR/05_configure_services.sh" || {
|
||||
log_error "Configure Services failed. Update process cannot continue."
|
||||
exit 1
|
||||
}
|
||||
@@ -104,21 +104,21 @@ init_all_databases || { log_warning "Database initialization had issues, but con
|
||||
# Start all services using the 06_run_services.sh script (postgres is already running)
|
||||
set_telemetry_stage "update_services_start"
|
||||
log_info "Running Services..."
|
||||
"$BASH" "$RUN_SERVICES_SCRIPT" || { log_error "Failed to start services. Check logs for details."; exit 1; }
|
||||
bash "$RUN_SERVICES_SCRIPT" || { log_error "Failed to start services. Check logs for details."; exit 1; }
|
||||
|
||||
log_success "Update application completed successfully!"
|
||||
|
||||
# --- Fix file permissions ---
|
||||
set_telemetry_stage "update_fix_perms"
|
||||
log_info "Fixing file permissions..."
|
||||
"$BASH" "$SCRIPT_DIR/08_fix_permissions.sh" || {
|
||||
bash "$SCRIPT_DIR/08_fix_permissions.sh" || {
|
||||
log_warning "Failed to fix file permissions. This does not affect the update."
|
||||
}
|
||||
# --- End of Fix permissions ---
|
||||
|
||||
# --- Display Final Report with Credentials ---
|
||||
set_telemetry_stage "update_final_report"
|
||||
"$BASH" "$SCRIPT_DIR/07_final_report.sh" || {
|
||||
bash "$SCRIPT_DIR/07_final_report.sh" || {
|
||||
log_warning "Failed to display the final report. This does not affect the update."
|
||||
# We don't exit 1 here as the update itself was successful.
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# generate_hosts.sh - Generate /etc/hosts entries for local development
|
||||
# =============================================================================
|
||||
# Creates a hosts.txt file with all .local domain entries needed for local
|
||||
# development mode. Users can then add these entries to their /etc/hosts.
|
||||
#
|
||||
# Usage: bash scripts/generate_hosts.sh
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
source "$(dirname "$0")/utils.sh"
|
||||
init_paths
|
||||
|
||||
# Source local mode utilities
|
||||
source "$SCRIPT_DIR/local.sh"
|
||||
|
||||
# Load environment
|
||||
load_env || { log_error "Could not load .env file"; exit 1; }
|
||||
|
||||
# Check if local mode using local.sh helper
|
||||
if ! is_local_mode; then
|
||||
log_info "Not in local mode, skipping hosts generation"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
OUTPUT_FILE="$PROJECT_ROOT/hosts.txt"
|
||||
|
||||
log_info "Generating hosts file entries for local development..."
|
||||
|
||||
# Get hostname variables from local.sh (single source of truth)
|
||||
readarray -t HOSTNAME_VARS < <(get_all_hostname_vars)
|
||||
|
||||
# Create hosts.txt header
|
||||
cat > "$OUTPUT_FILE" << 'EOF'
|
||||
# =============================================================================
|
||||
# n8n-install Local Installation Hosts
|
||||
# =============================================================================
|
||||
# Add these lines to your hosts file:
|
||||
# macOS/Linux: /etc/hosts
|
||||
# Windows: C:\Windows\System32\drivers\etc\hosts
|
||||
#
|
||||
# To add automatically (macOS/Linux):
|
||||
# sudo bash -c 'cat hosts.txt >> /etc/hosts'
|
||||
#
|
||||
# To flush DNS cache after adding:
|
||||
# macOS: sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
|
||||
# Linux: sudo systemd-resolve --flush-caches
|
||||
# Windows: ipconfig /flushdns
|
||||
# =============================================================================
|
||||
|
||||
EOF
|
||||
|
||||
# Collect unique hostnames
|
||||
declare -A HOSTNAMES_MAP
|
||||
for var in "${HOSTNAME_VARS[@]}"; do
|
||||
value=$(read_env_var "$var")
|
||||
if [[ -n "$value" && "$value" =~ \.local$ ]]; then
|
||||
HOSTNAMES_MAP["$value"]=1
|
||||
fi
|
||||
done
|
||||
|
||||
# Write sorted hostnames
|
||||
for hostname in $(echo "${!HOSTNAMES_MAP[@]}" | tr ' ' '\n' | sort); do
|
||||
echo "127.0.0.1 $hostname" >> "$OUTPUT_FILE"
|
||||
done
|
||||
|
||||
# Count entries
|
||||
ENTRY_COUNT=${#HOSTNAMES_MAP[@]}
|
||||
|
||||
if [ "$ENTRY_COUNT" -eq 0 ]; then
|
||||
log_warning "No .local hostnames found in .env"
|
||||
rm -f "$OUTPUT_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "" >> "$OUTPUT_FILE"
|
||||
echo "# Total: $ENTRY_COUNT entries" >> "$OUTPUT_FILE"
|
||||
|
||||
log_success "Generated $ENTRY_COUNT host entries in: $OUTPUT_FILE"
|
||||
log_info ""
|
||||
log_info "To add these entries to your hosts file, run:"
|
||||
log_info " sudo bash -c 'cat $OUTPUT_FILE >> /etc/hosts'"
|
||||
log_info ""
|
||||
log_info "Then flush your DNS cache:"
|
||||
log_info " macOS: sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder"
|
||||
log_info " Linux: sudo systemd-resolve --flush-caches"
|
||||
|
||||
exit 0
|
||||
@@ -8,17 +8,11 @@ set -e
|
||||
source "$(dirname "$0")/utils.sh"
|
||||
init_paths
|
||||
|
||||
# Source local mode utilities
|
||||
source "$SCRIPT_DIR/local.sh"
|
||||
|
||||
OUTPUT_FILE="$PROJECT_ROOT/welcome/data.json"
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_env || exit 1
|
||||
|
||||
# Get protocol using local.sh helper (based on INSTALL_MODE)
|
||||
PROTOCOL="$(get_protocol)"
|
||||
|
||||
# Ensure welcome directory exists
|
||||
mkdir -p "$PROJECT_ROOT/welcome"
|
||||
|
||||
@@ -33,6 +27,19 @@ GENERATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
# Build services array - each entry is a formatted JSON block
|
||||
declare -a SERVICES_ARRAY
|
||||
|
||||
# Appsmith
|
||||
if is_profile_active "appsmith"; then
|
||||
SERVICES_ARRAY+=(" \"appsmith\": {
|
||||
\"hostname\": \"$(json_escape "$APPSMITH_HOSTNAME")\",
|
||||
\"credentials\": {
|
||||
\"note\": \"Create your account on first login\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"docs\": \"https://docs.appsmith.com\"
|
||||
}
|
||||
}")
|
||||
fi
|
||||
|
||||
# n8n
|
||||
if is_profile_active "n8n"; then
|
||||
N8N_WORKER_COUNT_VAL="${N8N_WORKER_COUNT:-1}"
|
||||
@@ -139,7 +146,7 @@ if is_profile_active "dify"; then
|
||||
\"note\": \"Create account on first login\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"api_endpoint\": \"${PROTOCOL}://$(json_escape "$DIFY_HOSTNAME")/v1\",
|
||||
\"api_endpoint\": \"https://$(json_escape "$DIFY_HOSTNAME")/v1\",
|
||||
\"internal_api\": \"http://dify-api:5001\"
|
||||
}
|
||||
}")
|
||||
@@ -153,7 +160,7 @@ if is_profile_active "qdrant"; then
|
||||
\"api_key\": \"$(json_escape "$QDRANT_API_KEY")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"dashboard\": \"${PROTOCOL}://$(json_escape "$QDRANT_HOSTNAME")/dashboard\",
|
||||
\"dashboard\": \"https://$(json_escape "$QDRANT_HOSTNAME")/dashboard\",
|
||||
\"internal_api\": \"http://qdrant:6333\"
|
||||
}
|
||||
}")
|
||||
@@ -219,8 +226,8 @@ if is_profile_active "ragapp"; then
|
||||
\"password\": \"$(json_escape "$RAGAPP_PASSWORD")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"admin\": \"${PROTOCOL}://$(json_escape "$RAGAPP_HOSTNAME")/admin\",
|
||||
\"docs\": \"${PROTOCOL}://$(json_escape "$RAGAPP_HOSTNAME")/docs\",
|
||||
\"admin\": \"https://$(json_escape "$RAGAPP_HOSTNAME")/admin\",
|
||||
\"docs\": \"https://$(json_escape "$RAGAPP_HOSTNAME")/docs\",
|
||||
\"internal_api\": \"http://ragapp:8000\"
|
||||
}
|
||||
}")
|
||||
@@ -249,7 +256,7 @@ if is_profile_active "lightrag"; then
|
||||
\"api_key\": \"$(json_escape "$LIGHTRAG_API_KEY")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"docs\": \"${PROTOCOL}://$(json_escape "$LIGHTRAG_HOSTNAME")/docs\",
|
||||
\"docs\": \"https://$(json_escape "$LIGHTRAG_HOSTNAME")/docs\",
|
||||
\"internal_api\": \"http://lightrag:9621\"
|
||||
}
|
||||
}")
|
||||
@@ -299,8 +306,8 @@ if is_profile_active "docling"; then
|
||||
\"password\": \"$(json_escape "$DOCLING_PASSWORD")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"ui\": \"${PROTOCOL}://$(json_escape "$DOCLING_HOSTNAME")/ui\",
|
||||
\"docs\": \"${PROTOCOL}://$(json_escape "$DOCLING_HOSTNAME")/docs\",
|
||||
\"ui\": \"https://$(json_escape "$DOCLING_HOSTNAME")/ui\",
|
||||
\"docs\": \"https://$(json_escape "$DOCLING_HOSTNAME")/docs\",
|
||||
\"internal_api\": \"http://docling:5001\"
|
||||
}
|
||||
}")
|
||||
@@ -357,7 +364,7 @@ if is_profile_active "waha"; then
|
||||
\"api_key\": \"$(json_escape "$WAHA_API_KEY_PLAIN")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"dashboard\": \"${PROTOCOL}://$(json_escape "$WAHA_HOSTNAME")/dashboard\",
|
||||
\"dashboard\": \"https://$(json_escape "$WAHA_HOSTNAME")/dashboard\",
|
||||
\"swagger_user\": \"$(json_escape "$WHATSAPP_SWAGGER_USERNAME")\",
|
||||
\"swagger_pass\": \"$(json_escape "$WHATSAPP_SWAGGER_PASSWORD")\",
|
||||
\"internal_api\": \"http://waha:3000\"
|
||||
@@ -525,6 +532,16 @@ if is_profile_active "databasus"; then
|
||||
((STEP_NUM++))
|
||||
fi
|
||||
|
||||
# Set up Appsmith (if appsmith active)
|
||||
if is_profile_active "appsmith"; then
|
||||
QUICK_START_ARRAY+=(" {
|
||||
\"step\": $STEP_NUM,
|
||||
\"title\": \"Set up Appsmith\",
|
||||
\"description\": \"Create your admin account and build your first app\"
|
||||
}")
|
||||
((STEP_NUM++))
|
||||
fi
|
||||
|
||||
# Step 4: Monitor system (if monitoring active)
|
||||
if is_profile_active "monitoring"; then
|
||||
QUICK_START_ARRAY+=(" {
|
||||
@@ -549,7 +566,6 @@ done
|
||||
cat > "$OUTPUT_FILE" << EOF
|
||||
{
|
||||
"domain": "$(json_escape "$USER_DOMAIN_NAME")",
|
||||
"protocol": "$PROTOCOL",
|
||||
"generated_at": "$GENERATED_AT",
|
||||
"services": {
|
||||
$SERVICES_JSON
|
||||
@@ -589,4 +605,3 @@ if [ -f "$CHANGELOG_SOURCE" ]; then
|
||||
else
|
||||
log_warning "CHANGELOG.md not found, skipping changelog.json generation"
|
||||
fi
|
||||
log_info "Access it at: ${PROTOCOL}://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
|
||||
@@ -1,331 +0,0 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# install-local.sh - Local installation for n8n-install
|
||||
# =============================================================================
|
||||
# Runs the local development installation process (6 steps):
|
||||
# 1. Secret Generation - generates passwords, API keys, bcrypt hashes
|
||||
# 2. Service Selection Wizard - interactive service selection
|
||||
# 3. Configure Services - service-specific configuration
|
||||
# 4. Run Services - starts Docker Compose stack
|
||||
# 5. Final Report - displays credentials and URLs
|
||||
# 6. Fix Permissions - fixes file permissions
|
||||
#
|
||||
# Requirements:
|
||||
# - Docker and Docker Compose installed and running
|
||||
# - Bash 4.0+ (macOS: brew install bash)
|
||||
# - whiptail (macOS: brew install newt)
|
||||
# - openssl, git, python3
|
||||
#
|
||||
# Usage:
|
||||
# make install-local
|
||||
# Or directly: bash scripts/install-local.sh
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# Check bash version FIRST (requires bash 4+ for associative arrays)
|
||||
# =============================================================================
|
||||
if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
|
||||
echo "[ERROR] Bash 4.0 or higher is required. Current version: $BASH_VERSION"
|
||||
echo ""
|
||||
echo "On macOS, install modern bash and run with it:"
|
||||
echo " brew install bash"
|
||||
echo " /opt/homebrew/bin/bash ./scripts/install-local.sh"
|
||||
echo ""
|
||||
echo "Or use: make install-local"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
# Local mode is fixed for this script
|
||||
export INSTALL_MODE="local"
|
||||
|
||||
# Source the utilities file
|
||||
source "$(dirname "$0")/utils.sh"
|
||||
|
||||
# Initialize paths
|
||||
init_paths
|
||||
|
||||
# Source local mode utilities
|
||||
source "$SCRIPT_DIR/local.sh"
|
||||
|
||||
# Source telemetry functions
|
||||
source "$SCRIPT_DIR/telemetry.sh"
|
||||
|
||||
# =============================================================================
|
||||
# Prerequisites Check (inline)
|
||||
# =============================================================================
|
||||
check_prerequisites() {
|
||||
log_subheader "Checking Prerequisites for Local Installation"
|
||||
|
||||
local MISSING_DEPS=()
|
||||
|
||||
# Check Docker
|
||||
log_info "Checking Docker..."
|
||||
if ! command -v docker &> /dev/null; then
|
||||
MISSING_DEPS+=("docker")
|
||||
print_error "Docker is not installed"
|
||||
case "$(uname)" in
|
||||
Darwin)
|
||||
log_info " Install Docker Desktop: https://www.docker.com/products/docker-desktop"
|
||||
;;
|
||||
Linux)
|
||||
log_info " Install Docker: https://docs.docker.com/engine/install/"
|
||||
;;
|
||||
MINGW*|MSYS*|CYGWIN*)
|
||||
log_info " Install Docker Desktop for Windows: https://www.docker.com/products/docker-desktop"
|
||||
log_info " Then enable WSL2 integration in Docker Desktop settings"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
local docker_version
|
||||
docker_version=$(docker --version 2>/dev/null || echo "unknown")
|
||||
print_ok "Docker is installed: $docker_version"
|
||||
|
||||
# Check Docker daemon
|
||||
if ! docker info > /dev/null 2>&1; then
|
||||
MISSING_DEPS+=("docker-daemon")
|
||||
print_error "Docker daemon is not running"
|
||||
case "$(uname)" in
|
||||
Darwin)
|
||||
log_info " Start Docker Desktop from Applications"
|
||||
;;
|
||||
Linux)
|
||||
log_info " Start Docker: sudo systemctl start docker"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
print_ok "Docker daemon is running"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check Docker Compose
|
||||
log_info "Checking Docker Compose..."
|
||||
if ! docker compose version &> /dev/null 2>&1; then
|
||||
MISSING_DEPS+=("docker-compose")
|
||||
print_error "Docker Compose plugin is not installed"
|
||||
log_info " Docker Compose should be included with Docker Desktop"
|
||||
log_info " Or install: https://docs.docker.com/compose/install/"
|
||||
else
|
||||
local compose_version
|
||||
compose_version=$(docker compose version 2>/dev/null || echo "unknown")
|
||||
print_ok "Docker Compose is installed: $compose_version"
|
||||
fi
|
||||
|
||||
# Check whiptail
|
||||
log_info "Checking whiptail..."
|
||||
if ! command -v whiptail &> /dev/null; then
|
||||
MISSING_DEPS+=("whiptail")
|
||||
print_error "whiptail is not installed"
|
||||
case "$(uname)" in
|
||||
Darwin)
|
||||
log_info " Install with: brew install newt"
|
||||
;;
|
||||
Linux)
|
||||
if command -v apt-get &> /dev/null; then
|
||||
log_info " Install with: sudo apt-get install -y whiptail"
|
||||
elif command -v yum &> /dev/null; then
|
||||
log_info " Install with: sudo yum install -y newt"
|
||||
elif command -v pacman &> /dev/null; then
|
||||
log_info " Install with: sudo pacman -S libnewt"
|
||||
else
|
||||
log_info " Install the 'newt' or 'whiptail' package for your distribution"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
print_ok "whiptail is installed"
|
||||
fi
|
||||
|
||||
# Check openssl
|
||||
log_info "Checking openssl..."
|
||||
if ! command -v openssl &> /dev/null; then
|
||||
MISSING_DEPS+=("openssl")
|
||||
print_error "openssl is not installed"
|
||||
case "$(uname)" in
|
||||
Darwin)
|
||||
log_info " openssl should be pre-installed on macOS"
|
||||
log_info " Or install with: brew install openssl"
|
||||
;;
|
||||
Linux)
|
||||
if command -v apt-get &> /dev/null; then
|
||||
log_info " Install with: sudo apt-get install -y openssl"
|
||||
elif command -v yum &> /dev/null; then
|
||||
log_info " Install with: sudo yum install -y openssl"
|
||||
else
|
||||
log_info " Install openssl for your distribution"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
local openssl_version
|
||||
openssl_version=$(openssl version 2>/dev/null || echo "unknown")
|
||||
print_ok "openssl is installed: $openssl_version"
|
||||
fi
|
||||
|
||||
# Check git
|
||||
log_info "Checking git..."
|
||||
if ! command -v git &> /dev/null; then
|
||||
MISSING_DEPS+=("git")
|
||||
print_error "git is not installed"
|
||||
log_info " Install git: https://git-scm.com/downloads"
|
||||
else
|
||||
local git_version
|
||||
git_version=$(git --version 2>/dev/null || echo "unknown")
|
||||
print_ok "git is installed: $git_version"
|
||||
fi
|
||||
|
||||
# Check Python3 and required modules
|
||||
log_info "Checking Python3..."
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
MISSING_DEPS+=("python3")
|
||||
print_error "Python3 is not installed"
|
||||
log_info " Install Python3: https://www.python.org/downloads/"
|
||||
else
|
||||
local python_version
|
||||
python_version=$(python3 --version 2>/dev/null || echo "unknown")
|
||||
print_ok "Python3 is installed: $python_version"
|
||||
|
||||
# Check and install required Python modules
|
||||
local PYTHON_MODULES=("yaml:pyyaml" "dotenv:python-dotenv")
|
||||
for module_pair in "${PYTHON_MODULES[@]}"; do
|
||||
local import_name="${module_pair%%:*}"
|
||||
local package_name="${module_pair##*:}"
|
||||
log_info "Checking Python module: $package_name..."
|
||||
if ! python3 -c "import $import_name" 2>/dev/null; then
|
||||
print_warning "$package_name not found. Installing..."
|
||||
if python3 -m pip install --user "$package_name" 2>/dev/null; then
|
||||
print_ok "$package_name installed successfully"
|
||||
else
|
||||
MISSING_DEPS+=("$package_name")
|
||||
print_error "Failed to install $package_name"
|
||||
log_info " Install manually: pip3 install $package_name"
|
||||
fi
|
||||
else
|
||||
print_ok "$package_name is available"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
if [ ${#MISSING_DEPS[@]} -gt 0 ]; then
|
||||
log_error "Missing dependencies: ${MISSING_DEPS[*]}"
|
||||
log_info "Please install the missing dependencies and try again."
|
||||
return 1
|
||||
else
|
||||
log_success "All prerequisites are satisfied!"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Setup error telemetry trap for tracking failures
|
||||
setup_error_telemetry_trap
|
||||
|
||||
# Generate installation ID for telemetry correlation
|
||||
INSTALLATION_ID=$(get_installation_id)
|
||||
export INSTALLATION_ID
|
||||
|
||||
# Send telemetry: installation started
|
||||
send_telemetry "install_start"
|
||||
|
||||
# Check required scripts
|
||||
required_scripts=(
|
||||
"03_generate_secrets.sh"
|
||||
"04_wizard.sh"
|
||||
"05_configure_services.sh"
|
||||
"06_run_services.sh"
|
||||
"07_final_report.sh"
|
||||
"08_fix_permissions.sh"
|
||||
)
|
||||
|
||||
missing_scripts=()
|
||||
for script in "${required_scripts[@]}"; do
|
||||
script_path="$SCRIPT_DIR/$script"
|
||||
if [ ! -f "$script_path" ]; then
|
||||
missing_scripts+=("$script")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing_scripts[@]} -gt 0 ]; then
|
||||
log_error "The following required scripts are missing in $SCRIPT_DIR:"
|
||||
printf " - %s\n" "${missing_scripts[@]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Make scripts executable
|
||||
chmod +x "$SCRIPT_DIR"/*.sh 2>/dev/null || true
|
||||
|
||||
# =============================================================================
|
||||
# Run Local installation steps sequentially (6 steps total)
|
||||
# =============================================================================
|
||||
TOTAL_STEPS=6
|
||||
|
||||
log_header "Local Installation"
|
||||
log_info "Starting local development installation..."
|
||||
|
||||
# Step 1: Prerequisites Check (inline)
|
||||
show_step 1 $TOTAL_STEPS "Checking Prerequisites"
|
||||
check_prerequisites || { log_error "Prerequisites check failed"; exit 1; }
|
||||
log_success "Prerequisites check complete!"
|
||||
|
||||
# Pull Caddy image for bcrypt hash generation
|
||||
log_info "Pulling Caddy image for password hashing..."
|
||||
docker pull caddy:latest 2>/dev/null || log_warning "Could not pull Caddy image, will try during installation"
|
||||
|
||||
# Step 2: Secrets Generation
|
||||
show_step 2 $TOTAL_STEPS "Generating Secrets and Configuration"
|
||||
set_telemetry_stage "secrets_gen"
|
||||
"$BASH" "$SCRIPT_DIR/03_generate_secrets.sh" || { log_error "Secret/Config Generation failed"; exit 1; }
|
||||
log_success "Secret/Config Generation complete!"
|
||||
|
||||
# Step 3: Service Selection Wizard
|
||||
show_step 3 $TOTAL_STEPS "Running Service Selection Wizard"
|
||||
set_telemetry_stage "wizard"
|
||||
"$BASH" "$SCRIPT_DIR/04_wizard.sh" || { log_error "Service Selection Wizard failed"; exit 1; }
|
||||
log_success "Service Selection Wizard complete!"
|
||||
|
||||
# Step 4: Configure Services
|
||||
show_step 4 $TOTAL_STEPS "Configure Services"
|
||||
set_telemetry_stage "configure"
|
||||
"$BASH" "$SCRIPT_DIR/05_configure_services.sh" || { log_error "Configure Services failed"; exit 1; }
|
||||
log_success "Configure Services complete!"
|
||||
|
||||
# Step 5: Running Services
|
||||
show_step 5 $TOTAL_STEPS "Running Services"
|
||||
set_telemetry_stage "db_init"
|
||||
# Start PostgreSQL first to initialize databases before other services
|
||||
log_info "Starting PostgreSQL..."
|
||||
docker compose -p localai up -d postgres || { log_error "Failed to start PostgreSQL"; exit 1; }
|
||||
|
||||
# Initialize PostgreSQL databases for services (creates if not exist)
|
||||
source "$SCRIPT_DIR/databases.sh"
|
||||
init_all_databases || { log_warning "Database initialization had issues, but continuing..."; }
|
||||
|
||||
# Now start all services (postgres is already running)
|
||||
set_telemetry_stage "services_start"
|
||||
"$BASH" "$SCRIPT_DIR/06_run_services.sh" || { log_error "Running Services failed"; exit 1; }
|
||||
log_success "Running Services complete!"
|
||||
|
||||
# Step 6: Final Report
|
||||
show_step 6 $TOTAL_STEPS "Generating Final Report"
|
||||
set_telemetry_stage "final_report"
|
||||
log_info "Installation Summary:"
|
||||
echo -e " ${GREEN}*${NC} Prerequisites verified (Docker, whiptail, openssl, git)"
|
||||
echo -e " ${GREEN}*${NC} Local installation mode configured (.local domains)"
|
||||
echo -e " ${GREEN}*${NC} '.env' generated with secure passwords and secrets"
|
||||
echo -e " ${GREEN}*${NC} Services launched via Docker Compose"
|
||||
|
||||
"$BASH" "$SCRIPT_DIR/07_final_report.sh" || { log_error "Final Report Generation failed"; exit 1; }
|
||||
log_success "Final Report generated!"
|
||||
|
||||
# Fix Permissions (run silently, not as a numbered step for local)
|
||||
set_telemetry_stage "fix_perms"
|
||||
"$BASH" "$SCRIPT_DIR/08_fix_permissions.sh" || { log_warning "Fix Permissions had issues"; }
|
||||
|
||||
log_success "Local installation complete!"
|
||||
|
||||
# Send telemetry: installation completed with selected services
|
||||
send_telemetry "install_complete" "$(read_env_var COMPOSE_PROFILES)"
|
||||
|
||||
exit 0
|
||||
@@ -1,30 +1,50 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# install-vps.sh - VPS installation orchestrator for n8n-install
|
||||
# install.sh - Main installation orchestrator for n8n-install
|
||||
# =============================================================================
|
||||
# Runs the complete VPS installation process:
|
||||
# This script runs the complete installation process by sequentially executing
|
||||
# 8 installation steps:
|
||||
# 1. System Preparation - updates packages, installs utilities, configures firewall
|
||||
# 2. Docker Installation - installs Docker and Docker Compose
|
||||
# 3. Secret Generation - generates passwords, API keys, bcrypt hashes
|
||||
# 4. Service Selection Wizard - interactive service selection
|
||||
# 5. Configure Services - service-specific configuration
|
||||
# 6. Run Services - starts Docker Compose stack
|
||||
# 7. Final Report - displays credentials and URLs
|
||||
# 8. Fix Permissions - fixes file ownership
|
||||
# 3. Secret Generation - creates .env file with secure passwords and secrets
|
||||
# 4. Service Wizard - interactive service selection using whiptail
|
||||
# 5. Service Configuration - prompts for API keys and service-specific settings
|
||||
# 6. Service Launch - starts all selected services via Docker Compose
|
||||
# 7. Final Report - displays credentials and access URLs
|
||||
# 8. Fix Permissions - ensures correct file ownership for the invoking user
|
||||
#
|
||||
# Usage: sudo bash scripts/install-vps.sh
|
||||
# Note: This script should be called from install.sh (entry point)
|
||||
# Usage: sudo bash scripts/install.sh
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# VPS mode is fixed for this script
|
||||
export INSTALL_MODE="vps"
|
||||
|
||||
# Source the utilities file
|
||||
source "$(dirname "$0")/utils.sh"
|
||||
|
||||
# Initialize paths
|
||||
# Check for nested n8n-install directory
|
||||
current_path=$(pwd)
|
||||
if [[ "$current_path" == *"/n8n-install/n8n-install" ]]; then
|
||||
log_info "Detected nested n8n-install directory. Correcting..."
|
||||
cd ..
|
||||
log_info "Moved to $(pwd)"
|
||||
log_info "Removing redundant n8n-install directory..."
|
||||
rm -rf "n8n-install"
|
||||
log_info "Redundant directory removed."
|
||||
# Re-evaluate SCRIPT_DIR after potential path correction
|
||||
SCRIPT_DIR_REALPATH_TEMP="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
if [[ "$SCRIPT_DIR_REALPATH_TEMP" == *"/n8n-install/n8n-install/scripts" ]]; then
|
||||
# If SCRIPT_DIR is still pointing to the nested structure's scripts dir, adjust it
|
||||
# This happens if the script was invoked like: sudo bash n8n-install/scripts/install.sh
|
||||
# from the outer n8n-install directory.
|
||||
# We need to ensure that relative paths for other scripts are correct.
|
||||
# The most robust way is to re-execute the script from the corrected location
|
||||
# if the SCRIPT_DIR itself was nested.
|
||||
log_info "Re-executing install script from corrected path..."
|
||||
exec sudo bash "./scripts/install.sh" "$@"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Initialize paths using utils.sh helper
|
||||
init_paths
|
||||
|
||||
# Source telemetry functions
|
||||
@@ -91,63 +111,52 @@ if [ ${#non_executable_scripts[@]} -gt 0 ]; then
|
||||
log_success "Scripts successfully made executable."
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Run VPS installation steps sequentially (8 steps total)
|
||||
# =============================================================================
|
||||
TOTAL_STEPS=8
|
||||
# Run installation steps sequentially using their full paths
|
||||
|
||||
log_header "VPS Installation"
|
||||
log_info "Starting VPS installation..."
|
||||
|
||||
# Step 1: System Preparation
|
||||
show_step 1 $TOTAL_STEPS "System Preparation"
|
||||
show_step 1 8 "System Preparation"
|
||||
set_telemetry_stage "system_prep"
|
||||
"$BASH" "$SCRIPT_DIR/01_system_preparation.sh" || { log_error "System Preparation failed"; exit 1; }
|
||||
bash "$SCRIPT_DIR/01_system_preparation.sh" || { log_error "System Preparation failed"; exit 1; }
|
||||
log_success "System preparation complete!"
|
||||
|
||||
# Step 2: Docker Installation
|
||||
show_step 2 $TOTAL_STEPS "Installing Docker"
|
||||
show_step 2 8 "Installing Docker"
|
||||
set_telemetry_stage "docker_install"
|
||||
"$BASH" "$SCRIPT_DIR/02_install_docker.sh" || { log_error "Docker Installation failed"; exit 1; }
|
||||
bash "$SCRIPT_DIR/02_install_docker.sh" || { log_error "Docker Installation failed"; exit 1; }
|
||||
log_success "Docker installation complete!"
|
||||
|
||||
# Step 3: Secrets Generation
|
||||
show_step 3 $TOTAL_STEPS "Generating Secrets and Configuration"
|
||||
show_step 3 8 "Generating Secrets and Configuration"
|
||||
set_telemetry_stage "secrets_gen"
|
||||
"$BASH" "$SCRIPT_DIR/03_generate_secrets.sh" || { log_error "Secret/Config Generation failed"; exit 1; }
|
||||
bash "$SCRIPT_DIR/03_generate_secrets.sh" || { log_error "Secret/Config Generation failed"; exit 1; }
|
||||
log_success "Secret/Config Generation complete!"
|
||||
|
||||
# Step 4: Service Selection Wizard
|
||||
show_step 4 $TOTAL_STEPS "Running Service Selection Wizard"
|
||||
show_step 4 8 "Running Service Selection Wizard"
|
||||
set_telemetry_stage "wizard"
|
||||
"$BASH" "$SCRIPT_DIR/04_wizard.sh" || { log_error "Service Selection Wizard failed"; exit 1; }
|
||||
bash "$SCRIPT_DIR/04_wizard.sh" || { log_error "Service Selection Wizard failed"; exit 1; }
|
||||
log_success "Service Selection Wizard complete!"
|
||||
|
||||
# Step 5: Configure Services
|
||||
show_step 5 $TOTAL_STEPS "Configure Services"
|
||||
show_step 5 8 "Configure Services"
|
||||
set_telemetry_stage "configure"
|
||||
"$BASH" "$SCRIPT_DIR/05_configure_services.sh" || { log_error "Configure Services failed"; exit 1; }
|
||||
bash "$SCRIPT_DIR/05_configure_services.sh" || { log_error "Configure Services failed"; exit 1; }
|
||||
log_success "Configure Services complete!"
|
||||
|
||||
# Step 6: Running Services
|
||||
show_step 6 $TOTAL_STEPS "Running Services"
|
||||
show_step 6 8 "Running Services"
|
||||
set_telemetry_stage "db_init"
|
||||
# Start PostgreSQL first to initialize databases before other services
|
||||
log_info "Starting PostgreSQL..."
|
||||
docker compose -p localai up -d postgres || { log_error "Failed to start PostgreSQL"; exit 1; }
|
||||
|
||||
# Initialize PostgreSQL databases for services (creates if not exist)
|
||||
# This must run BEFORE other services that depend on these databases
|
||||
source "$SCRIPT_DIR/databases.sh"
|
||||
init_all_databases || { log_warning "Database initialization had issues, but continuing..."; }
|
||||
|
||||
# Now start all services (postgres is already running)
|
||||
set_telemetry_stage "services_start"
|
||||
"$BASH" "$SCRIPT_DIR/06_run_services.sh" || { log_error "Running Services failed"; exit 1; }
|
||||
bash "$SCRIPT_DIR/06_run_services.sh" || { log_error "Running Services failed"; exit 1; }
|
||||
log_success "Running Services complete!"
|
||||
|
||||
# Step 7: Final Report
|
||||
show_step 7 $TOTAL_STEPS "Generating Final Report"
|
||||
show_step 7 8 "Generating Final Report"
|
||||
set_telemetry_stage "final_report"
|
||||
# --- Installation Summary ---
|
||||
log_info "Installation Summary:"
|
||||
echo -e " ${GREEN}*${NC} System updated and basic utilities installed"
|
||||
echo -e " ${GREEN}*${NC} Firewall (UFW) configured and enabled"
|
||||
@@ -157,13 +166,12 @@ echo -e " ${GREEN}*${NC} Docker and Docker Compose installed"
|
||||
echo -e " ${GREEN}*${NC} '.env' generated with secure passwords and secrets"
|
||||
echo -e " ${GREEN}*${NC} Services launched via Docker Compose"
|
||||
|
||||
"$BASH" "$SCRIPT_DIR/07_final_report.sh" || { log_error "Final Report Generation failed"; exit 1; }
|
||||
bash "$SCRIPT_DIR/07_final_report.sh" || { log_error "Final Report Generation failed"; exit 1; }
|
||||
log_success "Final Report generated!"
|
||||
|
||||
# Step 8: Fix Permissions
|
||||
show_step 8 $TOTAL_STEPS "Fixing File Permissions"
|
||||
show_step 8 8 "Fixing File Permissions"
|
||||
set_telemetry_stage "fix_perms"
|
||||
"$BASH" "$SCRIPT_DIR/08_fix_permissions.sh" || { log_error "Fix Permissions failed"; exit 1; }
|
||||
bash "$SCRIPT_DIR/08_fix_permissions.sh" || { log_error "Fix Permissions failed"; exit 1; }
|
||||
log_success "File permissions fixed!"
|
||||
|
||||
log_success "Installation complete!"
|
||||
294
scripts/local.sh
294
scripts/local.sh
@@ -1,294 +0,0 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# local.sh - Local installation mode utilities
|
||||
# =============================================================================
|
||||
# Encapsulates all logic related to local vs VPS installation modes.
|
||||
# Provides functions for mode detection, environment configuration,
|
||||
# and mode-specific settings.
|
||||
#
|
||||
# Usage: source "$(dirname "$0")/local.sh"
|
||||
#
|
||||
# Functions:
|
||||
# - get_install_mode: Get current installation mode (vps|local)
|
||||
# - is_local_mode: Check if running in local mode
|
||||
# - is_vps_mode: Check if running in VPS mode
|
||||
# - get_protocol: Get protocol based on mode (http|https)
|
||||
# - get_caddy_auto_https: Get Caddy auto_https setting (on|off)
|
||||
# - get_n8n_secure_cookie: Get n8n secure cookie setting (true|false)
|
||||
# - get_local_domain: Get default domain for local mode (.local)
|
||||
# - configure_mode_env: Set all mode-specific environment variables
|
||||
# - print_local_hosts_instructions: Display hosts file setup instructions
|
||||
# =============================================================================
|
||||
|
||||
#=============================================================================
|
||||
# CONSTANTS
|
||||
#=============================================================================
|
||||
|
||||
# Local mode defaults
|
||||
LOCAL_MODE_DOMAIN="local"
|
||||
LOCAL_MODE_PROTOCOL="http"
|
||||
LOCAL_MODE_CADDY_AUTO_HTTPS="off"
|
||||
LOCAL_MODE_N8N_SECURE_COOKIE="false"
|
||||
|
||||
# VPS mode defaults
|
||||
VPS_MODE_PROTOCOL="https"
|
||||
VPS_MODE_CADDY_AUTO_HTTPS="on"
|
||||
VPS_MODE_N8N_SECURE_COOKIE="true"
|
||||
|
||||
#=============================================================================
|
||||
# MODE DETECTION
|
||||
#=============================================================================
|
||||
|
||||
# Get the current installation mode
|
||||
# Checks: 1) exported INSTALL_MODE, 2) .env file, 3) defaults to "vps"
|
||||
# Usage: mode=$(get_install_mode)
|
||||
get_install_mode() {
|
||||
local mode="${INSTALL_MODE:-}"
|
||||
|
||||
# If not set, try to read from .env
|
||||
if [[ -z "$mode" && -n "${ENV_FILE:-}" && -f "$ENV_FILE" ]]; then
|
||||
mode=$(grep "^INSTALL_MODE=" "$ENV_FILE" 2>/dev/null | cut -d'=' -f2- | tr -d '"'"'" || true)
|
||||
fi
|
||||
|
||||
# Default to vps for backward compatibility
|
||||
echo "${mode:-vps}"
|
||||
}
|
||||
|
||||
# Check if running in local mode
|
||||
# Usage: is_local_mode && echo "Local mode"
|
||||
is_local_mode() {
|
||||
[[ "$(get_install_mode)" == "local" ]]
|
||||
}
|
||||
|
||||
# Check if running in VPS mode
|
||||
# Usage: is_vps_mode && echo "VPS mode"
|
||||
is_vps_mode() {
|
||||
[[ "$(get_install_mode)" != "local" ]]
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
# MODE-SPECIFIC GETTERS
|
||||
#=============================================================================
|
||||
|
||||
# Get protocol based on installation mode
|
||||
# Usage: protocol=$(get_protocol)
|
||||
get_protocol() {
|
||||
if is_local_mode; then
|
||||
echo "$LOCAL_MODE_PROTOCOL"
|
||||
else
|
||||
echo "$VPS_MODE_PROTOCOL"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get Caddy auto_https setting based on installation mode
|
||||
# Usage: auto_https=$(get_caddy_auto_https)
|
||||
get_caddy_auto_https() {
|
||||
if is_local_mode; then
|
||||
echo "$LOCAL_MODE_CADDY_AUTO_HTTPS"
|
||||
else
|
||||
echo "$VPS_MODE_CADDY_AUTO_HTTPS"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get n8n secure cookie setting based on installation mode
|
||||
# Usage: secure_cookie=$(get_n8n_secure_cookie)
|
||||
get_n8n_secure_cookie() {
|
||||
if is_local_mode; then
|
||||
echo "$LOCAL_MODE_N8N_SECURE_COOKIE"
|
||||
else
|
||||
echo "$VPS_MODE_N8N_SECURE_COOKIE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get default domain for local mode
|
||||
# Usage: domain=$(get_local_domain)
|
||||
get_local_domain() {
|
||||
echo "$LOCAL_MODE_DOMAIN"
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
# ENVIRONMENT CONFIGURATION
|
||||
#=============================================================================
|
||||
|
||||
# Configure all mode-specific environment variables
|
||||
# Populates the provided associative array with mode settings
|
||||
# Usage: declare -A settings; configure_mode_env settings "local"
|
||||
# Arguments:
|
||||
# $1 - nameref to associative array for storing values
|
||||
# $2 - mode (optional, defaults to get_install_mode())
|
||||
configure_mode_env() {
|
||||
local -n _env_ref=$1
|
||||
local mode="${2:-$(get_install_mode)}"
|
||||
|
||||
_env_ref["INSTALL_MODE"]="$mode"
|
||||
|
||||
if [[ "$mode" == "local" ]]; then
|
||||
_env_ref["PROTOCOL"]="$LOCAL_MODE_PROTOCOL"
|
||||
_env_ref["CADDY_AUTO_HTTPS"]="$LOCAL_MODE_CADDY_AUTO_HTTPS"
|
||||
_env_ref["N8N_SECURE_COOKIE"]="$LOCAL_MODE_N8N_SECURE_COOKIE"
|
||||
else
|
||||
_env_ref["PROTOCOL"]="$VPS_MODE_PROTOCOL"
|
||||
_env_ref["CADDY_AUTO_HTTPS"]="$VPS_MODE_CADDY_AUTO_HTTPS"
|
||||
_env_ref["N8N_SECURE_COOKIE"]="$VPS_MODE_N8N_SECURE_COOKIE"
|
||||
fi
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
# HOSTS FILE UTILITIES
|
||||
#=============================================================================
|
||||
|
||||
# All hostname variables used in the project
|
||||
# Used by generate_hosts.sh and other scripts that need hostname list
|
||||
get_all_hostname_vars() {
|
||||
local vars=(
|
||||
"N8N_HOSTNAME"
|
||||
"WEBUI_HOSTNAME"
|
||||
"FLOWISE_HOSTNAME"
|
||||
"DIFY_HOSTNAME"
|
||||
"RAGAPP_HOSTNAME"
|
||||
"RAGFLOW_HOSTNAME"
|
||||
"LANGFUSE_HOSTNAME"
|
||||
"SUPABASE_HOSTNAME"
|
||||
"GRAFANA_HOSTNAME"
|
||||
"WAHA_HOSTNAME"
|
||||
"PROMETHEUS_HOSTNAME"
|
||||
"PORTAINER_HOSTNAME"
|
||||
"POSTIZ_HOSTNAME"
|
||||
"POSTGRESUS_HOSTNAME"
|
||||
"LETTA_HOSTNAME"
|
||||
"LIGHTRAG_HOSTNAME"
|
||||
"WEAVIATE_HOSTNAME"
|
||||
"QDRANT_HOSTNAME"
|
||||
"COMFYUI_HOSTNAME"
|
||||
"LT_HOSTNAME"
|
||||
"NEO4J_HOSTNAME"
|
||||
"NOCODB_HOSTNAME"
|
||||
"PADDLEOCR_HOSTNAME"
|
||||
"DOCLING_HOSTNAME"
|
||||
"WELCOME_HOSTNAME"
|
||||
"SEARXNG_HOSTNAME"
|
||||
)
|
||||
printf '%s\n' "${vars[@]}"
|
||||
}
|
||||
|
||||
# Print instructions for setting up /etc/hosts for local mode
|
||||
# Usage: print_local_hosts_instructions
|
||||
print_local_hosts_instructions() {
|
||||
# Requires color variables from utils.sh
|
||||
local cyan="${CYAN:-}"
|
||||
local white="${WHITE:-}"
|
||||
local dim="${DIM:-}"
|
||||
local nc="${NC:-}"
|
||||
|
||||
echo ""
|
||||
echo -e " ${white}Before accessing services, add entries to your hosts file:${nc}"
|
||||
echo ""
|
||||
echo -e " ${cyan}sudo bash -c 'cat hosts.txt >> /etc/hosts'${nc}"
|
||||
echo ""
|
||||
echo -e " ${dim}Then flush your DNS cache:${nc}"
|
||||
echo -e " ${cyan}macOS: sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder${nc}"
|
||||
echo -e " ${cyan}Linux: sudo systemd-resolve --flush-caches${nc}"
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
# PREREQUISITES CHECK (for local mode)
|
||||
#=============================================================================
|
||||
|
||||
# Check if all prerequisites for local mode are met
|
||||
# Returns 0 if all prerequisites are met, 1 otherwise
|
||||
# Usage: check_local_prerequisites || exit 1
|
||||
check_local_prerequisites() {
|
||||
local missing=()
|
||||
|
||||
# Check Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
missing+=("docker")
|
||||
elif ! docker info > /dev/null 2>&1; then
|
||||
missing+=("docker-daemon")
|
||||
fi
|
||||
|
||||
# Check Docker Compose
|
||||
if ! docker compose version &> /dev/null 2>&1; then
|
||||
missing+=("docker-compose")
|
||||
fi
|
||||
|
||||
# Check whiptail
|
||||
if ! command -v whiptail &> /dev/null; then
|
||||
missing+=("whiptail")
|
||||
fi
|
||||
|
||||
# Check openssl
|
||||
if ! command -v openssl &> /dev/null; then
|
||||
missing+=("openssl")
|
||||
fi
|
||||
|
||||
# Check git
|
||||
if ! command -v git &> /dev/null; then
|
||||
missing+=("git")
|
||||
fi
|
||||
|
||||
# Check Python3
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
missing+=("python3")
|
||||
fi
|
||||
|
||||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||
echo "Missing prerequisites: ${missing[*]}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Print installation instructions for missing prerequisites
|
||||
# Usage: print_prerequisite_instructions "docker" "whiptail"
|
||||
print_prerequisite_instructions() {
|
||||
local os_type
|
||||
os_type=$(uname)
|
||||
|
||||
for dep in "$@"; do
|
||||
case "$dep" in
|
||||
docker)
|
||||
echo " Docker:"
|
||||
case "$os_type" in
|
||||
Darwin) echo " Install Docker Desktop: https://www.docker.com/products/docker-desktop" ;;
|
||||
Linux) echo " Install Docker: https://docs.docker.com/engine/install/" ;;
|
||||
esac
|
||||
;;
|
||||
docker-daemon)
|
||||
echo " Docker daemon is not running:"
|
||||
case "$os_type" in
|
||||
Darwin) echo " Start Docker Desktop from Applications" ;;
|
||||
Linux) echo " Run: sudo systemctl start docker" ;;
|
||||
esac
|
||||
;;
|
||||
docker-compose)
|
||||
echo " Docker Compose:"
|
||||
echo " Should be included with Docker Desktop"
|
||||
echo " Or install: https://docs.docker.com/compose/install/"
|
||||
;;
|
||||
whiptail)
|
||||
echo " whiptail:"
|
||||
case "$os_type" in
|
||||
Darwin) echo " Run: brew install newt" ;;
|
||||
Linux) echo " Run: sudo apt-get install -y whiptail" ;;
|
||||
esac
|
||||
;;
|
||||
openssl)
|
||||
echo " openssl:"
|
||||
case "$os_type" in
|
||||
Darwin) echo " Usually pre-installed, or: brew install openssl" ;;
|
||||
Linux) echo " Run: sudo apt-get install -y openssl" ;;
|
||||
esac
|
||||
;;
|
||||
git)
|
||||
echo " git:"
|
||||
echo " Install: https://git-scm.com/downloads"
|
||||
;;
|
||||
python3)
|
||||
echo " Python3:"
|
||||
echo " Install: https://www.python.org/downloads/"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
# - docker-compose.n8n-workers.yml (if exists and n8n profile active)
|
||||
# - supabase/docker/docker-compose.yml (if exists and supabase profile active)
|
||||
# - dify/docker/docker-compose.yaml (if exists and dify profile active)
|
||||
# - docker-compose.override.yml (if exists, user overrides with highest precedence)
|
||||
#
|
||||
# Usage: bash scripts/restart.sh
|
||||
# =============================================================================
|
||||
@@ -71,6 +72,10 @@ MAIN_COMPOSE_FILES=("-f" "$PROJECT_ROOT/docker-compose.yml")
|
||||
if path=$(get_n8n_workers_compose); then
|
||||
MAIN_COMPOSE_FILES+=("-f" "$path")
|
||||
fi
|
||||
OVERRIDE_COMPOSE="$PROJECT_ROOT/docker-compose.override.yml"
|
||||
if [ -f "$OVERRIDE_COMPOSE" ]; then
|
||||
MAIN_COMPOSE_FILES+=("-f" "$OVERRIDE_COMPOSE")
|
||||
fi
|
||||
|
||||
# Start main services
|
||||
log_info "Starting main services..."
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
# =============================================================================
|
||||
# setup_custom_tls.sh - Configure custom TLS certificates for Caddy
|
||||
# =============================================================================
|
||||
# Generates caddy-addon/custom-tls.conf for using corporate/internal certificates
|
||||
# Updates caddy-addon/tls-snippet.conf to use corporate/internal certificates
|
||||
# instead of Let's Encrypt.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/setup_custom_tls.sh # Interactive mode
|
||||
# bash scripts/setup_custom_tls.sh cert.crt key.key # Non-interactive mode
|
||||
# bash scripts/setup_custom_tls.sh --remove # Reset to Let's Encrypt
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Place certificate files in ./certs/ directory
|
||||
@@ -18,13 +19,27 @@ set -euo pipefail
|
||||
|
||||
source "$(dirname "$0")/utils.sh" && init_paths
|
||||
|
||||
ADDON_FILE="$PROJECT_ROOT/caddy-addon/custom-tls.conf"
|
||||
SNIPPET_FILE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf"
|
||||
SNIPPET_EXAMPLE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf.example"
|
||||
CERTS_DIR="$PROJECT_ROOT/certs"
|
||||
|
||||
# Legacy file that causes duplicate host errors (must be cleaned up on migration)
|
||||
# TODO: Remove OLD_CONFIG and cleanup_legacy_config() after v3.0 release (all users migrated)
|
||||
OLD_CONFIG="$PROJECT_ROOT/caddy-addon/custom-tls.conf"
|
||||
|
||||
# =============================================================================
|
||||
# FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
cleanup_legacy_config() {
|
||||
# Remove old custom-tls.conf that causes duplicate host errors
|
||||
# This is needed for users upgrading from older versions
|
||||
if [[ -f "$OLD_CONFIG" ]]; then
|
||||
log_warning "Removing obsolete custom-tls.conf (causes duplicate host errors)"
|
||||
rm -f "$OLD_CONFIG"
|
||||
fi
|
||||
}
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Setup Custom TLS Certificates for Caddy
|
||||
@@ -33,7 +48,7 @@ Usage: $(basename "$0") [OPTIONS] [CERT_FILE] [KEY_FILE]
|
||||
|
||||
Options:
|
||||
-h, --help Show this help message
|
||||
--remove Remove custom TLS configuration
|
||||
--remove Reset to Let's Encrypt automatic certificates
|
||||
|
||||
Arguments:
|
||||
CERT_FILE Path to certificate file (relative to ./certs/)
|
||||
@@ -42,13 +57,12 @@ Arguments:
|
||||
Examples:
|
||||
$(basename "$0") # Interactive mode
|
||||
$(basename "$0") wildcard.crt wildcard.key # Use specific files
|
||||
$(basename "$0") --remove # Remove custom TLS config
|
||||
$(basename "$0") --remove # Reset to Let's Encrypt
|
||||
|
||||
The script will:
|
||||
1. Detect certificate files in ./certs/
|
||||
2. Read active services from .env
|
||||
3. Generate caddy-addon/custom-tls.conf
|
||||
4. Optionally restart Caddy
|
||||
2. Update caddy-addon/tls-snippet.conf with your certificate paths
|
||||
3. Optionally restart Caddy
|
||||
|
||||
EOF
|
||||
}
|
||||
@@ -75,157 +89,53 @@ find_keys() {
|
||||
echo "${keys[*]:-}"
|
||||
}
|
||||
|
||||
get_active_services() {
|
||||
# Get list of services with their hostnames from .env
|
||||
load_env
|
||||
local services=()
|
||||
|
||||
# Map of service names to their hostname variables
|
||||
declare -A service_map=(
|
||||
["n8n"]="N8N_HOSTNAME"
|
||||
["flowise"]="FLOWISE_HOSTNAME"
|
||||
["webui"]="WEBUI_HOSTNAME"
|
||||
["grafana"]="GRAFANA_HOSTNAME"
|
||||
["prometheus"]="PROMETHEUS_HOSTNAME"
|
||||
["portainer"]="PORTAINER_HOSTNAME"
|
||||
["langfuse"]="LANGFUSE_HOSTNAME"
|
||||
["supabase"]="SUPABASE_HOSTNAME"
|
||||
["dify"]="DIFY_HOSTNAME"
|
||||
["nocodb"]="NOCODB_HOSTNAME"
|
||||
["ragapp"]="RAGAPP_HOSTNAME"
|
||||
["ragflow"]="RAGFLOW_HOSTNAME"
|
||||
["waha"]="WAHA_HOSTNAME"
|
||||
["searxng"]="SEARXNG_HOSTNAME"
|
||||
["comfyui"]="COMFYUI_HOSTNAME"
|
||||
["welcome"]="WELCOME_HOSTNAME"
|
||||
["databasus"]="DATABASUS_HOSTNAME"
|
||||
["letta"]="LETTA_HOSTNAME"
|
||||
["lightrag"]="LIGHTRAG_HOSTNAME"
|
||||
["weaviate"]="WEAVIATE_HOSTNAME"
|
||||
["qdrant"]="QDRANT_HOSTNAME"
|
||||
["neo4j"]="NEO4J_HOSTNAME"
|
||||
["postiz"]="POSTIZ_HOSTNAME"
|
||||
["libretranslate"]="LT_HOSTNAME"
|
||||
["paddleocr"]="PADDLEOCR_HOSTNAME"
|
||||
["docling"]="DOCLING_HOSTNAME"
|
||||
)
|
||||
|
||||
for service in "${!service_map[@]}"; do
|
||||
local hostname_var="${service_map[$service]}"
|
||||
local hostname="${!hostname_var:-}"
|
||||
if [[ -n "$hostname" && "$hostname" != *"yourdomain.com" ]]; then
|
||||
services+=("$service:$hostname")
|
||||
ensure_snippet_exists() {
|
||||
# Create tls-snippet.conf from example if it doesn't exist
|
||||
# This ensures the file survives git updates (it's gitignored)
|
||||
if [[ ! -f "$SNIPPET_FILE" ]]; then
|
||||
if [[ -f "$SNIPPET_EXAMPLE" ]]; then
|
||||
cp "$SNIPPET_EXAMPLE" "$SNIPPET_FILE"
|
||||
log_info "Created tls-snippet.conf from template"
|
||||
else
|
||||
# Fallback: create default content directly
|
||||
remove_config
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${services[*]:-}"
|
||||
fi
|
||||
}
|
||||
|
||||
generate_config() {
|
||||
local cert_file="$1"
|
||||
local key_file="$2"
|
||||
local services=("${@:3}")
|
||||
|
||||
cat > "$ADDON_FILE" << 'HEADER'
|
||||
# Custom TLS Configuration
|
||||
# Generated by setup_custom_tls.sh
|
||||
#
|
||||
# This file overrides default Let's Encrypt certificates with custom ones.
|
||||
# Regenerate with: make setup-tls
|
||||
cat > "$SNIPPET_FILE" << EOF
|
||||
# TLS Configuration Snippet
|
||||
# Generated by setup_custom_tls.sh on $(date -Iseconds)
|
||||
# Using custom certificates instead of Let's Encrypt.
|
||||
# Reset to Let's Encrypt: make setup-tls --remove
|
||||
|
||||
# Reusable TLS snippet
|
||||
(custom_tls) {
|
||||
HEADER
|
||||
(service_tls) {
|
||||
tls /etc/caddy/certs/$cert_file /etc/caddy/certs/$key_file
|
||||
}
|
||||
EOF
|
||||
|
||||
echo " tls /etc/caddy/certs/$cert_file /etc/caddy/certs/$key_file" >> "$ADDON_FILE"
|
||||
echo "}" >> "$ADDON_FILE"
|
||||
echo "" >> "$ADDON_FILE"
|
||||
|
||||
# Service-specific reverse proxy mappings
|
||||
declare -A proxy_map=(
|
||||
["n8n"]="n8n:5678"
|
||||
["flowise"]="flowise:3001"
|
||||
["webui"]="open-webui:8080"
|
||||
["grafana"]="grafana:3000"
|
||||
["prometheus"]="prometheus:9090"
|
||||
["portainer"]="portainer:9000"
|
||||
["langfuse"]="langfuse-web:3000"
|
||||
["supabase"]="kong:8000"
|
||||
["dify"]="nginx:80"
|
||||
["nocodb"]="nocodb:8080"
|
||||
["ragapp"]="ragapp:8000"
|
||||
["ragflow"]="ragflow:80"
|
||||
["waha"]="waha:3000"
|
||||
["searxng"]="searxng:8080"
|
||||
["comfyui"]="comfyui:8188"
|
||||
["welcome"]="file_server"
|
||||
["databasus"]="databasus:4005"
|
||||
["letta"]="letta:8283"
|
||||
["lightrag"]="lightrag:9621"
|
||||
["weaviate"]="weaviate:8080"
|
||||
["qdrant"]="qdrant:6333"
|
||||
["neo4j"]="neo4j:7474"
|
||||
["postiz"]="postiz:5000"
|
||||
["libretranslate"]="libretranslate:5000"
|
||||
["paddleocr"]="paddleocr:8080"
|
||||
["docling"]="docling:5001"
|
||||
)
|
||||
|
||||
# Services that need basic auth (format: USERNAME_VAR:PASSWORD_HASH_VAR)
|
||||
declare -A auth_services=(
|
||||
["prometheus"]="PROMETHEUS_USERNAME:PROMETHEUS_PASSWORD_HASH"
|
||||
["ragapp"]="RAGAPP_USERNAME:RAGAPP_PASSWORD_HASH"
|
||||
["comfyui"]="COMFYUI_USERNAME:COMFYUI_PASSWORD_HASH"
|
||||
["welcome"]="WELCOME_USERNAME:WELCOME_PASSWORD_HASH"
|
||||
["libretranslate"]="LT_USERNAME:LT_PASSWORD_HASH"
|
||||
["paddleocr"]="PADDLEOCR_USERNAME:PADDLEOCR_PASSWORD_HASH"
|
||||
["docling"]="DOCLING_USERNAME:DOCLING_PASSWORD_HASH"
|
||||
)
|
||||
|
||||
for service_entry in "${services[@]}"; do
|
||||
local service="${service_entry%%:*}"
|
||||
local hostname="${service_entry#*:}"
|
||||
local proxy="${proxy_map[$service]:-}"
|
||||
|
||||
[[ -z "$proxy" ]] && continue
|
||||
|
||||
echo "# $service" >> "$ADDON_FILE"
|
||||
echo "$hostname {" >> "$ADDON_FILE"
|
||||
echo " import custom_tls" >> "$ADDON_FILE"
|
||||
|
||||
# Add basic auth if needed
|
||||
if [[ -n "${auth_services[$service]:-}" ]]; then
|
||||
local auth_config="${auth_services[$service]}"
|
||||
local username_var="${auth_config%%:*}"
|
||||
local password_hash_var="${auth_config#*:}"
|
||||
echo " basic_auth {" >> "$ADDON_FILE"
|
||||
echo " {\$${username_var}} {\$${password_hash_var}}" >> "$ADDON_FILE"
|
||||
echo " }" >> "$ADDON_FILE"
|
||||
fi
|
||||
|
||||
# Add reverse proxy or file server
|
||||
if [[ "$proxy" == "file_server" ]]; then
|
||||
echo " root * /srv/welcome" >> "$ADDON_FILE"
|
||||
echo " file_server" >> "$ADDON_FILE"
|
||||
echo " try_files {path} /index.html" >> "$ADDON_FILE"
|
||||
else
|
||||
echo " reverse_proxy $proxy" >> "$ADDON_FILE"
|
||||
fi
|
||||
|
||||
echo "}" >> "$ADDON_FILE"
|
||||
echo "" >> "$ADDON_FILE"
|
||||
done
|
||||
|
||||
log_success "Generated $ADDON_FILE"
|
||||
log_success "Generated $SNIPPET_FILE"
|
||||
}
|
||||
|
||||
remove_config() {
|
||||
if [[ -f "$ADDON_FILE" ]]; then
|
||||
rm -f "$ADDON_FILE"
|
||||
log_success "Removed custom TLS configuration"
|
||||
else
|
||||
log_info "No custom TLS configuration found"
|
||||
fi
|
||||
cat > "$SNIPPET_FILE" << 'EOF'
|
||||
# TLS Configuration Snippet
|
||||
# Imported by all service blocks in the main Caddyfile.
|
||||
#
|
||||
# Default: Empty (uses Let's Encrypt automatic certificates)
|
||||
# Custom: Overwritten by 'make setup-tls' with your certificate paths
|
||||
# Reset: Run 'make setup-tls --remove' to restore Let's Encrypt
|
||||
|
||||
(service_tls) {
|
||||
# Default: Let's Encrypt automatic certificates (empty = no override)
|
||||
}
|
||||
EOF
|
||||
|
||||
log_success "Reset to Let's Encrypt (automatic certificates)"
|
||||
}
|
||||
|
||||
restart_caddy() {
|
||||
@@ -250,12 +160,19 @@ main() {
|
||||
exit 0
|
||||
;;
|
||||
--remove)
|
||||
cleanup_legacy_config
|
||||
remove_config
|
||||
restart_caddy
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Clean up legacy config that causes duplicate hosts
|
||||
cleanup_legacy_config
|
||||
|
||||
# Ensure snippet file exists (survives git updates)
|
||||
ensure_snippet_exists
|
||||
|
||||
# Ensure certs directory exists
|
||||
mkdir -p "$CERTS_DIR"
|
||||
|
||||
@@ -319,29 +236,16 @@ main() {
|
||||
log_info "Using certificate: $cert_file"
|
||||
log_info "Using key: $key_file"
|
||||
|
||||
# Get active services
|
||||
local services_arr
|
||||
IFS=' ' read -ra services_arr <<< "$(get_active_services)"
|
||||
|
||||
if [[ ${#services_arr[@]} -eq 0 ]]; then
|
||||
log_warning "No services with configured hostnames found in .env"
|
||||
log_info "Make sure to update *_HOSTNAME variables in .env with your domain"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Found ${#services_arr[@]} services with configured hostnames"
|
||||
# Ensure certificate files are readable by Caddy container
|
||||
# (Docker volume mounts preserve host permissions, Caddy may run as different UID)
|
||||
chmod 644 "$CERTS_DIR/$cert_file" "$CERTS_DIR/$key_file"
|
||||
|
||||
# Generate configuration
|
||||
generate_config "$cert_file" "$key_file" "${services_arr[@]}"
|
||||
generate_config "$cert_file" "$key_file"
|
||||
|
||||
# Show summary
|
||||
echo ""
|
||||
log_info "Configuration generated for the following services:"
|
||||
for service_entry in "${services_arr[@]}"; do
|
||||
local service="${service_entry%%:*}"
|
||||
local hostname="${service_entry#*:}"
|
||||
echo " - $service: $hostname"
|
||||
done
|
||||
log_info "Custom TLS configured successfully!"
|
||||
log_info "All services will use: /etc/caddy/certs/$cert_file"
|
||||
echo ""
|
||||
|
||||
# Restart Caddy
|
||||
|
||||
@@ -130,6 +130,6 @@ fi
|
||||
|
||||
# Execute the rest of the update process using the (potentially updated) apply_update.sh
|
||||
# Note: apply_update.sh has its own error telemetry trap and stages
|
||||
"$BASH" "$APPLY_UPDATE_SCRIPT"
|
||||
bash "$APPLY_UPDATE_SCRIPT"
|
||||
|
||||
exit 0
|
||||
@@ -134,6 +134,11 @@ if is_profile_active "databasus"; then
|
||||
check_image_update "databasus" "databasus/databasus:latest"
|
||||
fi
|
||||
|
||||
if is_profile_active "appsmith"; then
|
||||
log_subheader "Appsmith"
|
||||
check_image_update "appsmith" "appsmith/appsmith-ce:release"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
log_divider
|
||||
echo ""
|
||||
|
||||
@@ -353,6 +353,7 @@ get_dify_compose() {
|
||||
}
|
||||
|
||||
# Build array of all active compose files (main + external services)
|
||||
# Appends docker-compose.override.yml last if it exists (user overrides, highest precedence)
|
||||
# IMPORTANT: Requires COMPOSE_PROFILES to be set before calling (via load_env)
|
||||
# Usage: build_compose_files_array; docker compose "${COMPOSE_FILES[@]}" up -d
|
||||
# Result is stored in global COMPOSE_FILES array
|
||||
@@ -369,6 +370,12 @@ build_compose_files_array() {
|
||||
if path=$(get_dify_compose); then
|
||||
COMPOSE_FILES+=("-f" "$path")
|
||||
fi
|
||||
|
||||
# Include user overrides last (highest precedence)
|
||||
local override="$PROJECT_ROOT/docker-compose.override.yml"
|
||||
if [ -f "$override" ]; then
|
||||
COMPOSE_FILES+=("-f" "$override")
|
||||
fi
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
@@ -472,8 +479,7 @@ restore_debian_frontend() {
|
||||
gen_random() {
|
||||
local length="$1"
|
||||
local characters="$2"
|
||||
# LC_ALL=C is required on macOS to handle raw bytes from /dev/urandom
|
||||
LC_ALL=C tr -dc "$characters" < /dev/urandom | head -c "$length"
|
||||
head /dev/urandom | tr -dc "$characters" | head -c "$length"
|
||||
}
|
||||
|
||||
# Generate alphanumeric password
|
||||
@@ -498,18 +504,13 @@ gen_base64() {
|
||||
openssl rand -base64 "$bytes" | head -c "$length"
|
||||
}
|
||||
|
||||
# Generate bcrypt hash using Caddy Docker image
|
||||
# Generate bcrypt hash using Caddy
|
||||
# Usage: hash=$(generate_bcrypt_hash "plaintext_password")
|
||||
generate_bcrypt_hash() {
|
||||
local plaintext="$1"
|
||||
[[ -z "$plaintext" ]] && return 1
|
||||
|
||||
if ! command -v docker &> /dev/null; then
|
||||
log_error "Docker is required for bcrypt generation"
|
||||
return 1
|
||||
if [[ -n "$plaintext" ]]; then
|
||||
caddy hash-password --algorithm bcrypt --plaintext "$plaintext" 2>/dev/null
|
||||
fi
|
||||
|
||||
docker run --rm caddy:latest caddy hash-password --algorithm bcrypt --plaintext "$plaintext" 2>/dev/null
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
|
||||
@@ -195,6 +195,11 @@ def stop_existing_containers():
|
||||
if os.path.exists(n8n_workers_compose_path):
|
||||
cmd.extend(["-f", n8n_workers_compose_path])
|
||||
|
||||
# Include user overrides if present
|
||||
override_path = "docker-compose.override.yml"
|
||||
if os.path.exists(override_path):
|
||||
cmd.extend(["-f", override_path])
|
||||
|
||||
cmd.extend(["down"])
|
||||
run_command(cmd)
|
||||
|
||||
@@ -230,6 +235,11 @@ def start_local_ai():
|
||||
if os.path.exists(n8n_workers_compose_path):
|
||||
compose_files.extend(["-f", n8n_workers_compose_path])
|
||||
|
||||
# Include user overrides if present (must be last for highest precedence)
|
||||
override_path = "docker-compose.override.yml"
|
||||
if os.path.exists(override_path):
|
||||
compose_files.extend(["-f", override_path])
|
||||
|
||||
# Explicitly build services and pull newer base images first.
|
||||
print("Checking for newer base images and building services...")
|
||||
build_cmd = ["docker", "compose", "-p", "localai"] + compose_files + ["build", "--pull"]
|
||||
|
||||
@@ -144,18 +144,18 @@
|
||||
</svg>`
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// CONFIG - Global configuration loaded from data.json
|
||||
// ============================================
|
||||
let CONFIG = {
|
||||
protocol: 'https', // Default, will be overridden by data.json
|
||||
domain: ''
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// DATA - Service metadata and commands
|
||||
// ============================================
|
||||
const SERVICE_METADATA = {
|
||||
'appsmith': {
|
||||
name: 'Appsmith',
|
||||
description: 'Low-code Internal Tools',
|
||||
icon: 'AS',
|
||||
color: 'bg-[#5f2dde]',
|
||||
category: 'tools',
|
||||
docsUrl: 'https://docs.appsmith.com'
|
||||
},
|
||||
'n8n': {
|
||||
name: 'n8n',
|
||||
description: 'Workflow Automation',
|
||||
@@ -779,7 +779,7 @@
|
||||
// External link (if hostname exists)
|
||||
if (serviceData.hostname) {
|
||||
const link = document.createElement('a');
|
||||
link.href = `${CONFIG.protocol}://${serviceData.hostname}`;
|
||||
link.href = `https://${serviceData.hostname}`;
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener';
|
||||
link.className = 'text-brand hover:text-brand-400 text-sm font-medium inline-flex items-center gap-1 group transition-colors';
|
||||
@@ -1047,14 +1047,6 @@
|
||||
if (dataResult.status === 'fulfilled' && dataResult.value) {
|
||||
const data = dataResult.value;
|
||||
|
||||
// Update global config from data.json
|
||||
if (data.protocol) {
|
||||
CONFIG.protocol = data.protocol;
|
||||
}
|
||||
if (data.domain) {
|
||||
CONFIG.domain = data.domain;
|
||||
}
|
||||
|
||||
// Update domain info
|
||||
if (domainInfo) {
|
||||
if (data.domain) {
|
||||
|
||||
Reference in New Issue
Block a user