mirror of
https://github.com/kossakovsky/n8n-install.git
synced 2026-03-07 14:23:08 +00:00
feat: add local installation mode with http-only caddy and .local domains
This commit is contained in:
21
.env.example
21
.env.example
@@ -6,6 +6,27 @@
|
||||
# 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]
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -21,3 +21,5 @@ certs/*
|
||||
# Custom Caddy addons (user configurations)
|
||||
caddy-addon/*.conf
|
||||
!caddy-addon/*.example
|
||||
hosts.txt
|
||||
welcome/data.json
|
||||
|
||||
61
CLAUDE.md
61
CLAUDE.md
@@ -17,9 +17,10 @@ This is **n8n-install**, a Docker Compose-based installer that provides a compre
|
||||
|
||||
- `Makefile`: Common commands (install, update, logs, etc.)
|
||||
- `docker-compose.yml`: Service definitions with profiles
|
||||
- `Caddyfile`: Reverse proxy configuration with automatic HTTPS
|
||||
- `Caddyfile`: Reverse proxy configuration (HTTPS for VPS, HTTP for local via `CADDY_AUTO_HTTPS`)
|
||||
- `.env`: Generated secrets and configuration (from `.env.example`)
|
||||
- `scripts/install.sh`: Main installation orchestrator (runs numbered scripts 01-08 in sequence)
|
||||
- `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/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
|
||||
@@ -30,6 +31,7 @@ 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
|
||||
@@ -45,8 +47,17 @@ This is **n8n-install**, a Docker Compose-based installer that provides a compre
|
||||
|
||||
### Installation Flow
|
||||
|
||||
`scripts/install.sh` orchestrates the installation by running numbered scripts in sequence:
|
||||
Two independent installation paths:
|
||||
|
||||
```
|
||||
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
|
||||
@@ -56,8 +67,23 @@ This is **n8n-install**, a Docker Compose-based installer that provides a compre
|
||||
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.
|
||||
|
||||
## Common Development Commands
|
||||
|
||||
### Makefile Commands
|
||||
@@ -65,6 +91,9 @@ The update flow (`scripts/update.sh`) similarly orchestrates: git fetch + reset
|
||||
```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)
|
||||
@@ -189,7 +218,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 binary)
|
||||
- `generate_bcrypt_hash "password"` - Create Caddy-compatible bcrypt hash (uses Caddy Docker image)
|
||||
- `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
|
||||
@@ -203,6 +232,22 @@ 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:
|
||||
@@ -305,8 +350,8 @@ These are backed up before `git reset --hard` and restored after.
|
||||
- Verify `LETSENCRYPT_EMAIL` is set in `.env`
|
||||
|
||||
### Password hash generation fails
|
||||
- Ensure Caddy container is running: `docker compose -p localai up -d caddy`
|
||||
- Script uses: `docker exec caddy caddy hash-password --plaintext "$password"`
|
||||
- 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"`
|
||||
|
||||
## File Locations
|
||||
|
||||
@@ -326,6 +371,7 @@ 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
|
||||
@@ -337,7 +383,8 @@ 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.sh
|
||||
bash -n scripts/install-vps.sh
|
||||
bash -n scripts/install-local.sh
|
||||
```
|
||||
|
||||
### Full Testing
|
||||
|
||||
79
Caddyfile
79
Caddyfile
@@ -1,32 +1,32 @@
|
||||
{
|
||||
# Global options - works for both environments
|
||||
# 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}
|
||||
email {$LETSENCRYPT_EMAIL}
|
||||
}
|
||||
|
||||
# N8N
|
||||
{$N8N_HOSTNAME} {
|
||||
# For domains, Caddy will automatically use Let's Encrypt
|
||||
# For localhost/port addresses, HTTPS won't be enabled
|
||||
http://{$N8N_HOSTNAME} {
|
||||
reverse_proxy n8n:5678
|
||||
}
|
||||
|
||||
# Open WebUI
|
||||
{$WEBUI_HOSTNAME} {
|
||||
http://{$WEBUI_HOSTNAME} {
|
||||
reverse_proxy open-webui:8080
|
||||
}
|
||||
|
||||
# Flowise
|
||||
{$FLOWISE_HOSTNAME} {
|
||||
http://{$FLOWISE_HOSTNAME} {
|
||||
reverse_proxy flowise:3001
|
||||
}
|
||||
|
||||
# Dify
|
||||
{$DIFY_HOSTNAME} {
|
||||
http://{$DIFY_HOSTNAME} {
|
||||
reverse_proxy nginx:80
|
||||
}
|
||||
|
||||
# RAGApp
|
||||
{$RAGAPP_HOSTNAME} {
|
||||
http://{$RAGAPP_HOSTNAME} {
|
||||
basic_auth {
|
||||
{$RAGAPP_USERNAME} {$RAGAPP_PASSWORD_HASH}
|
||||
}
|
||||
@@ -34,50 +34,45 @@
|
||||
}
|
||||
|
||||
# RAGFlow
|
||||
{$RAGFLOW_HOSTNAME} {
|
||||
http://{$RAGFLOW_HOSTNAME} {
|
||||
reverse_proxy ragflow:80
|
||||
}
|
||||
|
||||
# Langfuse
|
||||
{$LANGFUSE_HOSTNAME} {
|
||||
http://{$LANGFUSE_HOSTNAME} {
|
||||
reverse_proxy langfuse-web:3000
|
||||
}
|
||||
|
||||
# # Ollama API
|
||||
# {$OLLAMA_HOSTNAME} {
|
||||
# reverse_proxy ollama:11434
|
||||
# }
|
||||
|
||||
# Supabase
|
||||
{$SUPABASE_HOSTNAME} {
|
||||
http://{$SUPABASE_HOSTNAME} {
|
||||
reverse_proxy kong:8000
|
||||
}
|
||||
|
||||
# Grafana
|
||||
{$GRAFANA_HOSTNAME} {
|
||||
http://{$GRAFANA_HOSTNAME} {
|
||||
reverse_proxy grafana:3000
|
||||
}
|
||||
|
||||
# WAHA (WhatsApp HTTP API)
|
||||
{$WAHA_HOSTNAME} {
|
||||
http://{$WAHA_HOSTNAME} {
|
||||
reverse_proxy waha:3000
|
||||
}
|
||||
|
||||
# Prometheus
|
||||
{$PROMETHEUS_HOSTNAME} {
|
||||
basic_auth {
|
||||
http://{$PROMETHEUS_HOSTNAME} {
|
||||
basic_auth {
|
||||
{$PROMETHEUS_USERNAME} {$PROMETHEUS_PASSWORD_HASH}
|
||||
}
|
||||
reverse_proxy prometheus:9090
|
||||
}
|
||||
|
||||
# Portainer
|
||||
{$PORTAINER_HOSTNAME} {
|
||||
http://{$PORTAINER_HOSTNAME} {
|
||||
reverse_proxy portainer:9000
|
||||
}
|
||||
|
||||
# Postiz
|
||||
{$POSTIZ_HOSTNAME} {
|
||||
http://{$POSTIZ_HOSTNAME} {
|
||||
reverse_proxy postiz:5000
|
||||
}
|
||||
|
||||
@@ -95,27 +90,27 @@
|
||||
}
|
||||
|
||||
# Letta
|
||||
{$LETTA_HOSTNAME} {
|
||||
http://{$LETTA_HOSTNAME} {
|
||||
reverse_proxy letta:8283
|
||||
}
|
||||
|
||||
# LightRAG (Graph-based RAG with Knowledge Extraction)
|
||||
{$LIGHTRAG_HOSTNAME} {
|
||||
http://{$LIGHTRAG_HOSTNAME} {
|
||||
reverse_proxy lightrag:9621
|
||||
}
|
||||
|
||||
# Weaviate
|
||||
{$WEAVIATE_HOSTNAME} {
|
||||
http://{$WEAVIATE_HOSTNAME} {
|
||||
reverse_proxy weaviate:8080
|
||||
}
|
||||
|
||||
# Qdrant
|
||||
{$QDRANT_HOSTNAME} {
|
||||
http://{$QDRANT_HOSTNAME} {
|
||||
reverse_proxy qdrant:6333
|
||||
}
|
||||
|
||||
# ComfyUI
|
||||
{$COMFYUI_HOSTNAME} {
|
||||
http://{$COMFYUI_HOSTNAME} {
|
||||
basic_auth {
|
||||
{$COMFYUI_USERNAME} {$COMFYUI_PASSWORD_HASH}
|
||||
}
|
||||
@@ -123,30 +118,30 @@
|
||||
}
|
||||
|
||||
# LibreTranslate (Self-hosted Translation API)
|
||||
{$LT_HOSTNAME} {
|
||||
http://{$LT_HOSTNAME} {
|
||||
basic_auth {
|
||||
{$LT_USERNAME} {$LT_PASSWORD_HASH}
|
||||
}
|
||||
reverse_proxy libretranslate:5000
|
||||
}
|
||||
|
||||
# Neo4j
|
||||
{$NEO4J_HOSTNAME} {
|
||||
# Neo4j Browser
|
||||
http://{$NEO4J_HOSTNAME} {
|
||||
reverse_proxy neo4j:7474
|
||||
}
|
||||
|
||||
# Neo4j Bolt Protocol (wss)
|
||||
https://{$NEO4J_HOSTNAME}:7687 {
|
||||
# Neo4j Bolt Protocol
|
||||
{$NEO4J_HOSTNAME}:7687 {
|
||||
reverse_proxy neo4j:7687
|
||||
}
|
||||
|
||||
# NocoDB
|
||||
{$NOCODB_HOSTNAME} {
|
||||
http://{$NOCODB_HOSTNAME} {
|
||||
reverse_proxy nocodb:8080
|
||||
}
|
||||
|
||||
# PaddleOCR (PaddleX Basic Serving)
|
||||
{$PADDLEOCR_HOSTNAME} {
|
||||
http://{$PADDLEOCR_HOSTNAME} {
|
||||
basic_auth {
|
||||
{$PADDLEOCR_USERNAME} {$PADDLEOCR_PASSWORD_HASH}
|
||||
}
|
||||
@@ -154,7 +149,7 @@ https://{$NEO4J_HOSTNAME}:7687 {
|
||||
}
|
||||
|
||||
# Docling (Document Conversion API)
|
||||
{$DOCLING_HOSTNAME} {
|
||||
http://{$DOCLING_HOSTNAME} {
|
||||
basic_auth {
|
||||
{$DOCLING_USERNAME} {$DOCLING_PASSWORD_HASH}
|
||||
}
|
||||
@@ -184,8 +179,8 @@ http://{$WELCOME_HOSTNAME} {
|
||||
|
||||
import /etc/caddy/addons/*.conf
|
||||
|
||||
# # SearXNG
|
||||
{$SEARXNG_HOSTNAME} {
|
||||
# SearXNG
|
||||
http://{$SEARXNG_HOSTNAME} {
|
||||
@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 {
|
||||
@@ -193,7 +188,7 @@ import /etc/caddy/addons/*.conf
|
||||
}
|
||||
|
||||
encode zstd gzip
|
||||
|
||||
|
||||
@api {
|
||||
path /config
|
||||
path /healthz
|
||||
@@ -209,7 +204,7 @@ import /etc/caddy/addons/*.conf
|
||||
@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;"
|
||||
@@ -226,12 +221,12 @@ import /etc/caddy/addons/*.conf
|
||||
# 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"
|
||||
@@ -239,7 +234,7 @@ import /etc/caddy/addons/*.conf
|
||||
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,11 +2,15 @@
|
||||
|
||||
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 Full installation"
|
||||
@echo " make update Update system and services (resets to origin)"
|
||||
@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 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)"
|
||||
@@ -28,8 +32,11 @@ help:
|
||||
@echo " make switch-beta Switch to beta (develop branch)"
|
||||
@echo " make switch-stable Switch to stable (main branch)"
|
||||
|
||||
install:
|
||||
sudo bash ./scripts/install.sh
|
||||
install-vps:
|
||||
sudo bash ./scripts/install-vps.sh
|
||||
|
||||
install-local:
|
||||
$(BASH_CMD) ./scripts/install-local.sh
|
||||
|
||||
update:
|
||||
sudo bash ./scripts/update.sh
|
||||
|
||||
66
README.md
66
README.md
@@ -144,13 +144,18 @@ Get started quickly with a vast library of pre-built automations (optional impor
|
||||
|
||||
### Running the Install
|
||||
|
||||
The recommended way to install is using the provided main installation script.
|
||||
The recommended way to install on a VPS is using the provided 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 && sudo bash ./scripts/install.sh
|
||||
git clone https://github.com/kossakovsky/n8n-install && cd n8n-install && make install-vps
|
||||
```
|
||||
|
||||
Or run directly:
|
||||
```bash
|
||||
sudo bash ./scripts/install-vps.sh
|
||||
```
|
||||
|
||||
This single command automates the entire setup process, including:
|
||||
@@ -162,16 +167,60 @@ This single command automates the entire setup process, including:
|
||||
|
||||
During the installation, the script will prompt you for:
|
||||
|
||||
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).
|
||||
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).
|
||||
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 (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).
|
||||
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).
|
||||
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!**
|
||||
|
||||
## Quick Start and Usage
|
||||
### 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
|
||||
|
||||
After successful installation, your services are up and running! Here's how to get started:
|
||||
|
||||
@@ -304,8 +353,9 @@ The project includes a Makefile for simplified command execution:
|
||||
|
||||
| Command | Description |
|
||||
| --------------------- | ---------------------------------------------------- |
|
||||
| `make install` | Full installation |
|
||||
| `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 without applying (dry-run) |
|
||||
| `make git-pull` | Update for forks (merges from upstream/main) |
|
||||
| `make clean` | Remove unused Docker resources |
|
||||
|
||||
@@ -98,6 +98,7 @@ 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
|
||||
@@ -105,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: ${N8N_HOSTNAME:+https://}${N8N_HOSTNAME:-http://localhost:5678}/
|
||||
WEBHOOK_URL: ${PROTOCOL:-https}://${N8N_HOSTNAME}/
|
||||
|
||||
x-ollama: &service-ollama
|
||||
image: ollama/ollama:latest
|
||||
@@ -286,7 +287,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: https://${NOCODB_HOSTNAME}
|
||||
NC_PUBLIC_URL: ${PROTOCOL:-https}://${NOCODB_HOSTNAME}
|
||||
NC_REDIS_URL: redis://redis:6379
|
||||
volumes:
|
||||
- nocodb_data:/usr/app/data
|
||||
@@ -318,6 +319,7 @@ services:
|
||||
- caddy-data:/data:rw
|
||||
- caddy-config:/config:rw
|
||||
environment:
|
||||
CADDY_AUTO_HTTPS: ${CADDY_AUTO_HTTPS:-on}
|
||||
COMFYUI_HOSTNAME: ${COMFYUI_HOSTNAME}
|
||||
COMFYUI_PASSWORD_HASH: ${COMFYUI_PASSWORD_HASH}
|
||||
COMFYUI_USERNAME: ${COMFYUI_USERNAME}
|
||||
@@ -465,7 +467,7 @@ services:
|
||||
depends_on: *langfuse-depends-on
|
||||
environment:
|
||||
<<: *langfuse-worker-env
|
||||
NEXTAUTH_URL: https://${LANGFUSE_HOSTNAME}
|
||||
NEXTAUTH_URL: ${PROTOCOL:-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}
|
||||
@@ -563,7 +565,7 @@ services:
|
||||
volumes:
|
||||
- ./searxng:/etc/searxng:rw
|
||||
environment:
|
||||
SEARXNG_BASE_URL: https://${SEARXNG_HOSTNAME:-localhost}/
|
||||
SEARXNG_BASE_URL: ${PROTOCOL:-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
|
||||
@@ -839,11 +841,11 @@ services:
|
||||
BACKEND_INTERNAL_URL: http://postiz:3000
|
||||
DATABASE_URL: "postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/${POSTIZ_DB_NAME:-postiz}?schema=postiz"
|
||||
DISABLE_REGISTRATION: ${POSTIZ_DISABLE_REGISTRATION}
|
||||
FRONTEND_URL: ${POSTIZ_HOSTNAME:+https://}${POSTIZ_HOSTNAME}
|
||||
FRONTEND_URL: ${PROTOCOL:-https}://${POSTIZ_HOSTNAME}
|
||||
IS_GENERAL: "true" # Required for self-hosting.
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
MAIN_URL: ${POSTIZ_HOSTNAME:+https://}${POSTIZ_HOSTNAME}
|
||||
NEXT_PUBLIC_BACKEND_URL: ${POSTIZ_HOSTNAME:+https://}${POSTIZ_HOSTNAME}/api
|
||||
MAIN_URL: ${PROTOCOL:-https}://${POSTIZ_HOSTNAME}
|
||||
NEXT_PUBLIC_BACKEND_URL: ${PROTOCOL:-https}://${POSTIZ_HOSTNAME}/api
|
||||
NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads"
|
||||
REDIS_URL: "redis://redis:6379"
|
||||
STORAGE_PROVIDER: "local"
|
||||
|
||||
@@ -23,9 +23,15 @@ 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() {
|
||||
@@ -153,15 +159,13 @@ if [ -f "$OUTPUT_FILE" ]; then
|
||||
done < "$OUTPUT_FILE"
|
||||
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."
|
||||
# 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
|
||||
|
||||
require_whiptail
|
||||
|
||||
@@ -169,42 +173,51 @@ require_whiptail
|
||||
log_subheader "Domain Configuration"
|
||||
DOMAIN="" # Initialize DOMAIN variable
|
||||
|
||||
# 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.
|
||||
# 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)"
|
||||
generated_values["USER_DOMAIN_NAME"]="$DOMAIN"
|
||||
log_info "Using local development domain: .$DOMAIN"
|
||||
else
|
||||
while true; do
|
||||
DOMAIN_INPUT=$(wt_input "Primary Domain" "Enter the primary domain name for your services (e.g., example.com)." "") || true
|
||||
# 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
|
||||
|
||||
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" # 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
|
||||
# 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
|
||||
fi
|
||||
|
||||
# Prompt for user email
|
||||
# Prompt for user email (used for service logins and SSL certificates in VPS mode)
|
||||
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 logins and Let's Encrypt SSL."
|
||||
wt_msg "Email Required" "Please enter your email address. It will be used for service logins."
|
||||
fi
|
||||
|
||||
if [[ -n "${existing_env_vars[LETSENCRYPT_EMAIL]}" ]]; then
|
||||
@@ -592,9 +605,6 @@ 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"
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ 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"
|
||||
"$BASH" "$SCRIPT_DIR/generate_n8n_workers.sh"
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@@ -23,13 +23,20 @@ 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
|
||||
@@ -58,12 +65,25 @@ 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" "https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
print_credential "URL" "${PROTOCOL}://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
print_credential "Username" "${WELCOME_USERNAME:-<not_set>}"
|
||||
print_credential "Password" "${WELCOME_PASSWORD:-<not_set>}"
|
||||
echo ""
|
||||
@@ -74,7 +94,7 @@ 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}https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}${NC}"
|
||||
echo -e " ${CYAN}${PROTOCOL}://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}${NC}"
|
||||
echo ""
|
||||
echo -e " ${WHITE}2.${NC} Store the Welcome Page credentials securely"
|
||||
echo ""
|
||||
|
||||
@@ -17,8 +17,33 @@ 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.
|
||||
}
|
||||
|
||||
90
scripts/generate_hosts.sh
Executable file
90
scripts/generate_hosts.sh
Executable file
@@ -0,0 +1,90 @@
|
||||
#!/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,11 +8,17 @@ 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"
|
||||
|
||||
@@ -133,7 +139,7 @@ if is_profile_active "dify"; then
|
||||
\"note\": \"Create account on first login\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"api_endpoint\": \"https://$(json_escape "$DIFY_HOSTNAME")/v1\",
|
||||
\"api_endpoint\": \"${PROTOCOL}://$(json_escape "$DIFY_HOSTNAME")/v1\",
|
||||
\"internal_api\": \"http://dify-api:5001\"
|
||||
}
|
||||
}")
|
||||
@@ -147,7 +153,7 @@ if is_profile_active "qdrant"; then
|
||||
\"api_key\": \"$(json_escape "$QDRANT_API_KEY")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"dashboard\": \"https://$(json_escape "$QDRANT_HOSTNAME")/dashboard\",
|
||||
\"dashboard\": \"${PROTOCOL}://$(json_escape "$QDRANT_HOSTNAME")/dashboard\",
|
||||
\"internal_api\": \"http://qdrant:6333\"
|
||||
}
|
||||
}")
|
||||
@@ -213,8 +219,8 @@ if is_profile_active "ragapp"; then
|
||||
\"password\": \"$(json_escape "$RAGAPP_PASSWORD")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"admin\": \"https://$(json_escape "$RAGAPP_HOSTNAME")/admin\",
|
||||
\"docs\": \"https://$(json_escape "$RAGAPP_HOSTNAME")/docs\",
|
||||
\"admin\": \"${PROTOCOL}://$(json_escape "$RAGAPP_HOSTNAME")/admin\",
|
||||
\"docs\": \"${PROTOCOL}://$(json_escape "$RAGAPP_HOSTNAME")/docs\",
|
||||
\"internal_api\": \"http://ragapp:8000\"
|
||||
}
|
||||
}")
|
||||
@@ -243,7 +249,7 @@ if is_profile_active "lightrag"; then
|
||||
\"api_key\": \"$(json_escape "$LIGHTRAG_API_KEY")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"docs\": \"https://$(json_escape "$LIGHTRAG_HOSTNAME")/docs\",
|
||||
\"docs\": \"${PROTOCOL}://$(json_escape "$LIGHTRAG_HOSTNAME")/docs\",
|
||||
\"internal_api\": \"http://lightrag:9621\"
|
||||
}
|
||||
}")
|
||||
@@ -293,8 +299,8 @@ if is_profile_active "docling"; then
|
||||
\"password\": \"$(json_escape "$DOCLING_PASSWORD")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"ui\": \"https://$(json_escape "$DOCLING_HOSTNAME")/ui\",
|
||||
\"docs\": \"https://$(json_escape "$DOCLING_HOSTNAME")/docs\",
|
||||
\"ui\": \"${PROTOCOL}://$(json_escape "$DOCLING_HOSTNAME")/ui\",
|
||||
\"docs\": \"${PROTOCOL}://$(json_escape "$DOCLING_HOSTNAME")/docs\",
|
||||
\"internal_api\": \"http://docling:5001\"
|
||||
}
|
||||
}")
|
||||
@@ -351,7 +357,7 @@ if is_profile_active "waha"; then
|
||||
\"api_key\": \"$(json_escape "$WAHA_API_KEY_PLAIN")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"dashboard\": \"https://$(json_escape "$WAHA_HOSTNAME")/dashboard\",
|
||||
\"dashboard\": \"${PROTOCOL}://$(json_escape "$WAHA_HOSTNAME")/dashboard\",
|
||||
\"swagger_user\": \"$(json_escape "$WHATSAPP_SWAGGER_USERNAME")\",
|
||||
\"swagger_pass\": \"$(json_escape "$WHATSAPP_SWAGGER_PASSWORD")\",
|
||||
\"internal_api\": \"http://waha:3000\"
|
||||
@@ -543,6 +549,7 @@ done
|
||||
cat > "$OUTPUT_FILE" << EOF
|
||||
{
|
||||
"domain": "$(json_escape "$USER_DOMAIN_NAME")",
|
||||
"protocol": "$PROTOCOL",
|
||||
"generated_at": "$GENERATED_AT",
|
||||
"services": {
|
||||
$SERVICES_JSON
|
||||
@@ -582,3 +589,4 @@ 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}}"
|
||||
|
||||
331
scripts/install-local.sh
Normal file
331
scripts/install-local.sh
Normal file
@@ -0,0 +1,331 @@
|
||||
#!/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,50 +1,30 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# install.sh - Main installation orchestrator for n8n-install
|
||||
# install-vps.sh - VPS installation orchestrator for n8n-install
|
||||
# =============================================================================
|
||||
# This script runs the complete installation process by sequentially executing
|
||||
# 8 installation steps:
|
||||
# Runs the complete VPS installation process:
|
||||
# 1. System Preparation - updates packages, installs utilities, configures firewall
|
||||
# 2. Docker Installation - installs Docker and Docker Compose
|
||||
# 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
|
||||
# 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
|
||||
#
|
||||
# Usage: sudo bash scripts/install.sh
|
||||
# Usage: sudo bash scripts/install-vps.sh
|
||||
# Note: This script should be called from install.sh (entry point)
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# VPS mode is fixed for this script
|
||||
export INSTALL_MODE="vps"
|
||||
|
||||
# Source the utilities file
|
||||
source "$(dirname "$0")/utils.sh"
|
||||
|
||||
# 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
|
||||
# Initialize paths
|
||||
init_paths
|
||||
|
||||
# Source telemetry functions
|
||||
@@ -111,52 +91,63 @@ if [ ${#non_executable_scripts[@]} -gt 0 ]; then
|
||||
log_success "Scripts successfully made executable."
|
||||
fi
|
||||
|
||||
# Run installation steps sequentially using their full paths
|
||||
# =============================================================================
|
||||
# Run VPS installation steps sequentially (8 steps total)
|
||||
# =============================================================================
|
||||
TOTAL_STEPS=8
|
||||
|
||||
show_step 1 8 "System Preparation"
|
||||
log_header "VPS Installation"
|
||||
log_info "Starting VPS installation..."
|
||||
|
||||
# Step 1: System Preparation
|
||||
show_step 1 $TOTAL_STEPS "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!"
|
||||
|
||||
show_step 2 8 "Installing Docker"
|
||||
# Step 2: Docker Installation
|
||||
show_step 2 $TOTAL_STEPS "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!"
|
||||
|
||||
show_step 3 8 "Generating Secrets and Configuration"
|
||||
# Step 3: Secrets Generation
|
||||
show_step 3 $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; }
|
||||
"$BASH" "$SCRIPT_DIR/03_generate_secrets.sh" || { log_error "Secret/Config Generation failed"; exit 1; }
|
||||
log_success "Secret/Config Generation complete!"
|
||||
|
||||
show_step 4 8 "Running Service Selection Wizard"
|
||||
# Step 4: Service Selection Wizard
|
||||
show_step 4 $TOTAL_STEPS "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!"
|
||||
|
||||
show_step 5 8 "Configure Services"
|
||||
# Step 5: Configure Services
|
||||
show_step 5 $TOTAL_STEPS "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!"
|
||||
|
||||
show_step 6 8 "Running Services"
|
||||
# Step 6: Running Services
|
||||
show_step 6 $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)
|
||||
# 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!"
|
||||
|
||||
show_step 7 8 "Generating Final Report"
|
||||
# Step 7: Final Report
|
||||
show_step 7 $TOTAL_STEPS "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"
|
||||
@@ -166,12 +157,13 @@ 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!"
|
||||
|
||||
show_step 8 8 "Fixing File Permissions"
|
||||
# Step 8: Fix Permissions
|
||||
show_step 8 $TOTAL_STEPS "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
Normal file
294
scripts/local.sh
Normal file
@@ -0,0 +1,294 @@
|
||||
#!/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
|
||||
}
|
||||
@@ -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
|
||||
@@ -472,7 +472,8 @@ restore_debian_frontend() {
|
||||
gen_random() {
|
||||
local length="$1"
|
||||
local characters="$2"
|
||||
head /dev/urandom | tr -dc "$characters" | head -c "$length"
|
||||
# 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"
|
||||
}
|
||||
|
||||
# Generate alphanumeric password
|
||||
@@ -497,13 +498,18 @@ gen_base64() {
|
||||
openssl rand -base64 "$bytes" | head -c "$length"
|
||||
}
|
||||
|
||||
# Generate bcrypt hash using Caddy
|
||||
# Generate bcrypt hash using Caddy Docker image
|
||||
# Usage: hash=$(generate_bcrypt_hash "plaintext_password")
|
||||
generate_bcrypt_hash() {
|
||||
local plaintext="$1"
|
||||
if [[ -n "$plaintext" ]]; then
|
||||
caddy hash-password --algorithm bcrypt --plaintext "$plaintext" 2>/dev/null
|
||||
[[ -z "$plaintext" ]] && return 1
|
||||
|
||||
if ! command -v docker &> /dev/null; then
|
||||
log_error "Docker is required for bcrypt generation"
|
||||
return 1
|
||||
fi
|
||||
|
||||
docker run --rm caddy:latest caddy hash-password --algorithm bcrypt --plaintext "$plaintext" 2>/dev/null
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
|
||||
@@ -144,6 +144,14 @@
|
||||
</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
|
||||
// ============================================
|
||||
@@ -771,7 +779,7 @@
|
||||
// External link (if hostname exists)
|
||||
if (serviceData.hostname) {
|
||||
const link = document.createElement('a');
|
||||
link.href = `https://${serviceData.hostname}`;
|
||||
link.href = `${CONFIG.protocol}://${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';
|
||||
@@ -1039,6 +1047,14 @@
|
||||
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