mirror of
https://github.com/kossakovsky/n8n-install.git
synced 2026-04-12 17:34:31 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b28093b5cd | ||
|
|
361a726a07 | ||
|
|
0b4c9d5dda | ||
|
|
0e4b46ec31 | ||
|
|
e8f7934224 |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -11,4 +11,13 @@ dify/
|
||||
volumes/
|
||||
docker-compose.override.yml
|
||||
docker-compose.n8n-workers.yml
|
||||
welcome/data.json
|
||||
welcome/data.json
|
||||
welcome/changelog.json
|
||||
|
||||
# Custom TLS certificates
|
||||
certs/*
|
||||
!certs/.gitkeep
|
||||
|
||||
# Custom Caddy addons (user configurations)
|
||||
caddy-addon/*.conf
|
||||
!caddy-addon/*.example
|
||||
|
||||
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,11 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project are documented in this file.
|
||||
## [1.2.0] - 2026-01-12
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
### Added
|
||||
- Changelog section on Welcome Page dashboard
|
||||
|
||||
## [Unreleased]
|
||||
## [1.1.0] - 2026-01-11
|
||||
|
||||
### Added
|
||||
- **Custom TLS certificates** - Support for corporate/internal certificates via `caddy-addon/` mechanism
|
||||
- New `make stop` and `make start` commands for stopping/starting all services without restart
|
||||
- New `make setup-tls` command and `scripts/setup_custom_tls.sh` helper script for easy certificate configuration
|
||||
- New `make git-pull` command for fork workflows - merges from upstream instead of hard reset
|
||||
|
||||
## [1.0.0] - 2026-01-07
|
||||
|
||||
@@ -211,3 +217,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
- Langfuse - LLM observability and analytics platform
|
||||
- Initial fork from coleam00/local-ai-packager with enhanced service support
|
||||
|
||||
---
|
||||
|
||||
All notable changes to this project are documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
@@ -64,8 +64,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
|
||||
make update # Update system and services (resets to origin)
|
||||
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)
|
||||
make clean-all # Remove ALL Docker resources including data (DANGEROUS)
|
||||
|
||||
@@ -74,10 +75,13 @@ make logs s=<service> # View logs for specific service
|
||||
make status # Show container status
|
||||
make monitor # Live CPU/memory monitoring (docker stats)
|
||||
make restart # Restart all services
|
||||
make stop # Stop all services
|
||||
make start # Start all services
|
||||
make show-restarts # Show restart count per container
|
||||
make doctor # Run system diagnostics (DNS, SSL, containers, disk, memory)
|
||||
make import # Import n8n workflows from backup
|
||||
make import n=10 # Import first N workflows only
|
||||
make setup-tls # Configure custom TLS certificates
|
||||
|
||||
make switch-beta # Switch to develop branch and update
|
||||
make switch-stable # Switch to main branch and update
|
||||
|
||||
20
Makefile
20
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: help install update update-preview clean clean-all logs status monitor restart show-restarts doctor switch-beta switch-stable import
|
||||
.PHONY: help install update update-preview git-pull clean clean-all logs status monitor restart stop start show-restarts doctor switch-beta switch-stable import setup-tls
|
||||
|
||||
PROJECT_NAME := localai
|
||||
|
||||
@@ -6,8 +6,9 @@ help:
|
||||
@echo "n8n-install - Available commands:"
|
||||
@echo ""
|
||||
@echo " make install Full installation"
|
||||
@echo " make update Update system and services"
|
||||
@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)"
|
||||
@echo " make clean-all Remove ALL Docker resources including data (DANGEROUS)"
|
||||
@echo ""
|
||||
@@ -16,10 +17,13 @@ help:
|
||||
@echo " make status Show container status"
|
||||
@echo " make monitor Live CPU/memory monitoring"
|
||||
@echo " make restart Restart all services"
|
||||
@echo " make stop Stop all services"
|
||||
@echo " make start Start all services"
|
||||
@echo " make show-restarts Show restart count per container"
|
||||
@echo " make doctor Run system diagnostics"
|
||||
@echo " make import Import n8n workflows from backup"
|
||||
@echo " make import n=10 Import first N workflows only"
|
||||
@echo " make setup-tls Configure custom TLS certificates"
|
||||
@echo ""
|
||||
@echo " make switch-beta Switch to beta (develop branch)"
|
||||
@echo " make switch-stable Switch to stable (main branch)"
|
||||
@@ -33,6 +37,9 @@ update:
|
||||
update-preview:
|
||||
bash ./scripts/update_preview.sh
|
||||
|
||||
git-pull:
|
||||
sudo GIT_MODE=merge bash ./scripts/update.sh
|
||||
|
||||
clean:
|
||||
sudo bash ./scripts/docker_cleanup.sh
|
||||
|
||||
@@ -58,6 +65,12 @@ monitor:
|
||||
restart:
|
||||
bash ./scripts/restart.sh
|
||||
|
||||
stop:
|
||||
docker compose -p $(PROJECT_NAME) stop
|
||||
|
||||
start:
|
||||
docker compose -p $(PROJECT_NAME) start
|
||||
|
||||
show-restarts:
|
||||
@docker ps -q | while read id; do \
|
||||
name=$$(docker inspect --format '{{.Name}}' $$id | sed 's/^\/\(.*\)/\1/'); \
|
||||
@@ -84,3 +97,6 @@ ifdef n
|
||||
else
|
||||
docker compose -p $(PROJECT_NAME) run --rm -e FORCE_IMPORT=true n8n-import
|
||||
endif
|
||||
|
||||
setup-tls:
|
||||
bash ./scripts/setup_custom_tls.sh
|
||||
|
||||
20
README.md
20
README.md
@@ -271,6 +271,12 @@ To update all components (n8n, Open WebUI, etc.) to their latest versions and in
|
||||
make update
|
||||
```
|
||||
|
||||
**For forks**: If you maintain a fork with custom changes and want to merge updates from upstream instead of resetting:
|
||||
|
||||
```bash
|
||||
make git-pull
|
||||
```
|
||||
|
||||
This script will:
|
||||
|
||||
1. Fetch the latest updates for the installer from the Git repository.
|
||||
@@ -298,8 +304,9 @@ The project includes a Makefile for simplified command execution:
|
||||
| Command | Description |
|
||||
| --------------------- | ---------------------------------------------------- |
|
||||
| `make install` | Full installation |
|
||||
| `make update` | Update system and services |
|
||||
| `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 |
|
||||
|
||||
### Monitoring & Logs
|
||||
@@ -311,15 +318,18 @@ The project includes a Makefile for simplified command execution:
|
||||
| `make status` | Show container status |
|
||||
| `make monitor` | Live CPU/memory monitoring |
|
||||
| `make restart` | Restart all services |
|
||||
| `make stop` | Stop all services |
|
||||
| `make start` | Start all services |
|
||||
| `make show-restarts` | Show restart count per container |
|
||||
| `make import` | Import n8n workflows from backup |
|
||||
| `make import n=10` | Import first N workflows only |
|
||||
|
||||
### Diagnostics
|
||||
### Diagnostics & Configuration
|
||||
|
||||
| Command | Description |
|
||||
| ------------- | ------------------------------------------------------------------ |
|
||||
| `make doctor` | Run system diagnostics (checks DNS, SSL, containers, disk, memory) |
|
||||
| Command | Description |
|
||||
| ---------------- | ------------------------------------------------------------------ |
|
||||
| `make doctor` | Run system diagnostics (checks DNS, SSL, containers, disk, memory) |
|
||||
| `make setup-tls` | Configure custom TLS certificates for corporate/internal use |
|
||||
|
||||
Run `make help` for the full list of available commands.
|
||||
|
||||
|
||||
97
caddy-addon/README.md
Normal file
97
caddy-addon/README.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Caddy Addons
|
||||
|
||||
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.
|
||||
|
||||
## Use Cases
|
||||
|
||||
- Custom TLS certificates (corporate/internal CA)
|
||||
- Additional reverse proxy rules
|
||||
- Custom headers or middleware
|
||||
- Rate limiting or access control
|
||||
|
||||
## Custom TLS Certificates
|
||||
|
||||
For corporate/internal deployments where Let's Encrypt is not available, you can use your own certificates.
|
||||
|
||||
### Quick Setup
|
||||
|
||||
1. Place your certificates in the `certs/` directory:
|
||||
```bash
|
||||
cp /path/to/your/cert.crt ./certs/wildcard.crt
|
||||
cp /path/to/your/key.key ./certs/wildcard.key
|
||||
```
|
||||
|
||||
2. Run the setup script:
|
||||
```bash
|
||||
make setup-tls
|
||||
```
|
||||
|
||||
3. Restart Caddy:
|
||||
```bash
|
||||
docker compose -p localai restart caddy
|
||||
```
|
||||
|
||||
### Manual Setup
|
||||
|
||||
1. Copy the example file:
|
||||
```bash
|
||||
cp caddy-addon/custom-tls.conf.example caddy-addon/custom-tls.conf
|
||||
```
|
||||
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
Make sure your `.env` file has `N8N_HOSTNAME=n8n.internal.company.com`.
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
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)
|
||||
|
||||
certs/
|
||||
├── .gitkeep # Keeps directory in git
|
||||
├── wildcard.crt # Your certificate (gitignored)
|
||||
└── wildcard.key # Your private key (gitignored)
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Files in `caddy-addon/*.conf` 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
|
||||
```
|
||||
|
||||
## Caddy Documentation
|
||||
|
||||
- [Caddyfile Syntax](https://caddyserver.com/docs/caddyfile)
|
||||
- [TLS Directive](https://caddyserver.com/docs/caddyfile/directives/tls)
|
||||
- [Reverse Proxy](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy)
|
||||
114
caddy-addon/custom-tls.conf.example
Normal file
114
caddy-addon/custom-tls.conf.example
Normal file
@@ -0,0 +1,114 @@
|
||||
# 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
|
||||
# }
|
||||
0
certs/.gitkeep
Normal file
0
certs/.gitkeep
Normal file
@@ -309,6 +309,7 @@ services:
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- ./caddy-addon:/etc/caddy/addons:ro
|
||||
- ./certs:/etc/caddy/certs:ro
|
||||
- ./welcome:/srv/welcome:ro
|
||||
- caddy-data:/data:rw
|
||||
- caddy-config:/config:rw
|
||||
|
||||
@@ -541,3 +541,30 @@ EOF
|
||||
|
||||
log_success "Welcome page data generated at: $OUTPUT_FILE"
|
||||
log_info "Access it at: https://${WELCOME_HOSTNAME:-welcome.${USER_DOMAIN_NAME}}"
|
||||
|
||||
# Generate changelog.json with CHANGELOG.md content
|
||||
CHANGELOG_JSON_FILE="$PROJECT_ROOT/welcome/changelog.json"
|
||||
CHANGELOG_SOURCE="$PROJECT_ROOT/CHANGELOG.md"
|
||||
|
||||
if [ -f "$CHANGELOG_SOURCE" ]; then
|
||||
# Read and escape content for JSON (preserve newlines as \n)
|
||||
# Using awk for cross-platform compatibility (macOS + Linux)
|
||||
CHANGELOG_CONTENT=$(awk '
|
||||
BEGIN { ORS="" }
|
||||
{
|
||||
gsub(/\\/, "\\\\") # Escape backslashes first
|
||||
gsub(/"/, "\\\"") # Escape double quotes
|
||||
gsub(/\t/, "\\t") # Escape tabs
|
||||
gsub(/\r/, "") # Remove carriage returns (CRLF → LF)
|
||||
if (NR > 1) printf "\\n"
|
||||
printf "%s", $0
|
||||
}
|
||||
' "$CHANGELOG_SOURCE")
|
||||
|
||||
# Write changelog.json file
|
||||
printf '{\n "content": "%s"\n}\n' "$CHANGELOG_CONTENT" > "$CHANGELOG_JSON_FILE"
|
||||
|
||||
log_success "Changelog JSON generated at: $CHANGELOG_JSON_FILE"
|
||||
else
|
||||
log_warning "CHANGELOG.md not found, skipping changelog.json generation"
|
||||
fi
|
||||
|
||||
@@ -71,7 +71,7 @@ git_get_current_branch() {
|
||||
# SYNC OPERATIONS
|
||||
#=============================================================================
|
||||
|
||||
# Sync local repository with origin
|
||||
# Sync local repository with origin using hard reset
|
||||
# Fetches latest changes and resets to origin/<branch>
|
||||
# This discards any local commits to ensure clean sync with remote
|
||||
# Usage: git_sync_with_origin [target_branch]
|
||||
@@ -102,6 +102,40 @@ git_sync_with_origin() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# Merge changes from upstream remote (for forks)
|
||||
# Fetches from upstream and merges main branch into current branch
|
||||
# Preserves local commits - suitable for fork workflows
|
||||
# Usage: git_merge_from_upstream
|
||||
# Returns: 0 on success, 1 on failure
|
||||
git_merge_from_upstream() {
|
||||
local upstream_branch="main"
|
||||
|
||||
# Check if upstream remote exists
|
||||
if ! git remote get-url upstream &>/dev/null; then
|
||||
log_error "Remote 'upstream' not configured."
|
||||
log_info "Add it with: git remote add upstream <original-repo-url>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Fetch latest changes from upstream
|
||||
log_info "Fetching latest changes from upstream..."
|
||||
if ! git fetch upstream; then
|
||||
log_error "Git fetch from upstream failed. Check your internet connection."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Merge upstream changes into current branch
|
||||
log_info "Merging upstream/$upstream_branch into current branch..."
|
||||
if ! git merge "upstream/$upstream_branch" --no-edit; then
|
||||
log_error "Git merge from upstream/$upstream_branch failed."
|
||||
log_error "You may need to resolve conflicts manually."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "Successfully merged changes from upstream/$upstream_branch"
|
||||
return 0
|
||||
}
|
||||
|
||||
#=============================================================================
|
||||
# CONFIGURATION
|
||||
#=============================================================================
|
||||
|
||||
351
scripts/setup_custom_tls.sh
Executable file
351
scripts/setup_custom_tls.sh
Executable file
@@ -0,0 +1,351 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# setup_custom_tls.sh - Configure custom TLS certificates for Caddy
|
||||
# =============================================================================
|
||||
# Generates caddy-addon/custom-tls.conf for using 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
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Place certificate files in ./certs/ directory
|
||||
# - Certificate paths are relative to container (/etc/caddy/certs/)
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(dirname "$0")/utils.sh" && init_paths
|
||||
|
||||
ADDON_FILE="$PROJECT_ROOT/caddy-addon/custom-tls.conf"
|
||||
CERTS_DIR="$PROJECT_ROOT/certs"
|
||||
|
||||
# =============================================================================
|
||||
# FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Setup Custom TLS Certificates for Caddy
|
||||
|
||||
Usage: $(basename "$0") [OPTIONS] [CERT_FILE] [KEY_FILE]
|
||||
|
||||
Options:
|
||||
-h, --help Show this help message
|
||||
--remove Remove custom TLS configuration
|
||||
|
||||
Arguments:
|
||||
CERT_FILE Path to certificate file (relative to ./certs/)
|
||||
KEY_FILE Path to private key file (relative to ./certs/)
|
||||
|
||||
Examples:
|
||||
$(basename "$0") # Interactive mode
|
||||
$(basename "$0") wildcard.crt wildcard.key # Use specific files
|
||||
$(basename "$0") --remove # Remove custom TLS config
|
||||
|
||||
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
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
find_certificates() {
|
||||
# Find certificate files in certs directory
|
||||
local certs=()
|
||||
if [[ -d "$CERTS_DIR" ]]; then
|
||||
while IFS= read -r -d '' file; do
|
||||
certs+=("$(basename "$file")")
|
||||
done < <(find "$CERTS_DIR" -maxdepth 1 -type f \( -name "*.crt" -o -name "*.pem" -o -name "*.cer" \) -print0 2>/dev/null)
|
||||
fi
|
||||
echo "${certs[*]:-}"
|
||||
}
|
||||
|
||||
find_keys() {
|
||||
# Find key files in certs directory
|
||||
local keys=()
|
||||
if [[ -d "$CERTS_DIR" ]]; then
|
||||
while IFS= read -r -d '' file; do
|
||||
keys+=("$(basename "$file")")
|
||||
done < <(find "$CERTS_DIR" -maxdepth 1 -type f \( -name "*.key" -o -name "*-key.pem" \) -print0 2>/dev/null)
|
||||
fi
|
||||
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")
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${services[*]:-}"
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
# Reusable TLS snippet
|
||||
(custom_tls) {
|
||||
HEADER
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
restart_caddy() {
|
||||
if wt_yesno "Restart Caddy" "Do you want to restart Caddy to apply the new configuration?" "yes"; then
|
||||
log_info "Restarting Caddy..."
|
||||
docker compose -p localai restart caddy
|
||||
log_success "Caddy restarted"
|
||||
else
|
||||
log_info "Skipped Caddy restart. Run manually: docker compose -p localai restart caddy"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MAIN
|
||||
# =============================================================================
|
||||
|
||||
main() {
|
||||
# Handle arguments
|
||||
case "${1:-}" in
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
--remove)
|
||||
remove_config
|
||||
restart_caddy
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Ensure certs directory exists
|
||||
mkdir -p "$CERTS_DIR"
|
||||
|
||||
local cert_file=""
|
||||
local key_file=""
|
||||
|
||||
# Non-interactive mode
|
||||
if [[ $# -ge 2 ]]; then
|
||||
cert_file="$1"
|
||||
key_file="$2"
|
||||
|
||||
if [[ ! -f "$CERTS_DIR/$cert_file" ]]; then
|
||||
log_error "Certificate not found: $CERTS_DIR/$cert_file"
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$CERTS_DIR/$key_file" ]]; then
|
||||
log_error "Key not found: $CERTS_DIR/$key_file"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Interactive mode
|
||||
require_whiptail
|
||||
|
||||
# Find available certificates
|
||||
local certs_arr
|
||||
IFS=' ' read -ra certs_arr <<< "$(find_certificates)"
|
||||
|
||||
if [[ ${#certs_arr[@]} -eq 0 ]]; then
|
||||
wt_msg "No Certificates Found" "No certificate files found in ./certs/\n\nPlease place your certificate (.crt, .pem, .cer) and key (.key) files in the certs/ directory first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build menu items for certificates
|
||||
local cert_items=()
|
||||
for cert in "${certs_arr[@]}"; do
|
||||
cert_items+=("$cert" "")
|
||||
done
|
||||
|
||||
cert_file=$(wt_menu "Select Certificate" "Choose your TLS certificate file:" "${cert_items[@]}")
|
||||
[[ -z "$cert_file" ]] && exit 1
|
||||
|
||||
# Find available keys
|
||||
local keys_arr
|
||||
IFS=' ' read -ra keys_arr <<< "$(find_keys)"
|
||||
|
||||
if [[ ${#keys_arr[@]} -eq 0 ]]; then
|
||||
wt_msg "No Keys Found" "No key files found in ./certs/\n\nPlease place your private key (.key) file in the certs/ directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build menu items for keys
|
||||
local key_items=()
|
||||
for key in "${keys_arr[@]}"; do
|
||||
key_items+=("$key" "")
|
||||
done
|
||||
|
||||
key_file=$(wt_menu "Select Private Key" "Choose your TLS private key file:" "${key_items[@]}")
|
||||
[[ -z "$key_file" ]] && exit 1
|
||||
fi
|
||||
|
||||
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"
|
||||
|
||||
# Generate configuration
|
||||
generate_config "$cert_file" "$key_file" "${services_arr[@]}"
|
||||
|
||||
# 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
|
||||
echo ""
|
||||
|
||||
# Restart Caddy
|
||||
restart_caddy
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -4,7 +4,7 @@
|
||||
# =============================================================================
|
||||
# Performs a full system and service update:
|
||||
# 1. Backs up user-customizable directories (e.g., python-runner/)
|
||||
# 2. Fetches and resets to origin/<branch> (discards any local commits)
|
||||
# 2. Syncs with remote repository (method depends on GIT_MODE)
|
||||
# 3. Restores backed up directories to preserve user modifications
|
||||
# 4. Updates Ubuntu system packages (apt-get update && upgrade)
|
||||
# 5. Delegates to apply_update.sh for service updates
|
||||
@@ -12,14 +12,19 @@
|
||||
# This two-stage approach ensures apply_update.sh itself gets updated before
|
||||
# running, so new update logic is always applied.
|
||||
#
|
||||
# Git strategy: We use `git fetch` + `git reset --hard origin/<branch>` instead
|
||||
# of `git pull` to ensure we always sync with remote, even if the user has
|
||||
# accidental local commits that would cause rebase conflicts.
|
||||
# Git modes (set via GIT_MODE environment variable):
|
||||
# - reset (default): git fetch + reset --hard origin/<branch>
|
||||
# Best for: Standard installations, always syncs cleanly with remote
|
||||
# - merge: git fetch upstream + merge upstream/<branch>
|
||||
# Best for: Forks that maintain their own changes and merge from upstream
|
||||
#
|
||||
# Preserved directories: Defined in PRESERVE_DIRS array in utils.sh.
|
||||
# These directories contain user-customizable content that survives git reset.
|
||||
#
|
||||
# Usage: make update OR sudo bash scripts/update.sh
|
||||
# Usage:
|
||||
# make update - Standard update (reset mode)
|
||||
# make git-pull - Fork update (merge mode)
|
||||
# GIT_MODE=merge sudo bash scripts/update.sh - Manual merge mode
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
@@ -85,10 +90,20 @@ if [ -n "$BACKUP_PATH" ]; then
|
||||
log_info "Backup created at: $BACKUP_PATH"
|
||||
fi
|
||||
|
||||
# Sync with origin (fetch + reset to remote branch)
|
||||
if ! git_sync_with_origin; then
|
||||
restore_preserved_dirs "$BACKUP_PATH"
|
||||
exit 1
|
||||
# Sync with remote repository based on GIT_MODE
|
||||
if [[ "${GIT_MODE:-reset}" == "merge" ]]; then
|
||||
# Fork workflow: merge from upstream (preserves local commits)
|
||||
log_info "Using merge mode (for forks)..."
|
||||
if ! git_merge_from_upstream; then
|
||||
restore_preserved_dirs "$BACKUP_PATH"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Standard workflow: reset to origin (discards local commits)
|
||||
if ! git_sync_with_origin; then
|
||||
restore_preserved_dirs "$BACKUP_PATH"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Restore user-customizable directories after git reset
|
||||
|
||||
@@ -136,6 +136,11 @@
|
||||
warning: (className = '') => `
|
||||
<svg class="${className}" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||
</svg>`,
|
||||
|
||||
changelog: (className = '') => `
|
||||
<svg class="${className}" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"/>
|
||||
</svg>`
|
||||
};
|
||||
|
||||
@@ -415,9 +420,12 @@
|
||||
{ cmd: 'make logs s=<service>', desc: 'View logs for specific service' },
|
||||
{ cmd: 'make monitor', desc: 'Live CPU/memory monitoring' },
|
||||
{ cmd: 'make restart', desc: 'Restart all services' },
|
||||
{ cmd: 'make stop', desc: 'Stop all services' },
|
||||
{ cmd: 'make start', desc: 'Start all services' },
|
||||
{ cmd: 'make show-restarts', desc: 'Show restart count per container' },
|
||||
{ cmd: 'make doctor', desc: 'Run system diagnostics' },
|
||||
{ cmd: 'make update', desc: 'Update system and services' },
|
||||
{ cmd: 'make git-pull', desc: 'Update for forks (merge from upstream)' },
|
||||
{ cmd: 'make import', desc: 'Import n8n workflows (use n=10 to limit)' },
|
||||
{ cmd: 'make clean', desc: 'Remove unused Docker resources' }
|
||||
];
|
||||
@@ -841,6 +849,7 @@
|
||||
const servicesContainer = document.getElementById('services-container');
|
||||
const quickstartContainer = document.getElementById('quickstart-container');
|
||||
const commandsContainer = document.getElementById('commands-container');
|
||||
const changelogContainer = document.getElementById('changelog-container');
|
||||
const domainInfo = document.getElementById('domain-info');
|
||||
|
||||
/**
|
||||
@@ -956,6 +965,26 @@
|
||||
commandsContainer.appendChild(grid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render changelog content
|
||||
*/
|
||||
function renderChangelog(content) {
|
||||
if (!changelogContainer) return;
|
||||
changelogContainer.innerHTML = '';
|
||||
|
||||
if (!content) {
|
||||
changelogContainer.innerHTML = `
|
||||
<p class="text-gray-500 text-center py-8">Changelog not available</p>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
const pre = document.createElement('pre');
|
||||
pre.className = 'text-sm text-gray-300 font-mono whitespace-pre-wrap break-words leading-relaxed';
|
||||
pre.textContent = content;
|
||||
changelogContainer.appendChild(pre);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render error state in services container
|
||||
*/
|
||||
@@ -981,14 +1010,26 @@
|
||||
// Always render commands (static content)
|
||||
renderCommands();
|
||||
|
||||
try {
|
||||
const response = await fetch('data.json');
|
||||
// Fetch both JSON files in parallel for better performance
|
||||
// Each fetch is handled independently - changelog failure won't affect main data
|
||||
const [changelogResult, dataResult] = await Promise.allSettled([
|
||||
fetch('changelog.json').then(r => r.ok ? r.json() : null),
|
||||
fetch('data.json').then(r => r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)))
|
||||
]);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load data (${response.status})`);
|
||||
// Handle changelog (independent - failures don't break the page)
|
||||
if (changelogResult.status === 'fulfilled' && changelogResult.value?.content) {
|
||||
renderChangelog(changelogResult.value.content);
|
||||
} else {
|
||||
if (changelogResult.status === 'rejected') {
|
||||
console.error('Error loading changelog:', changelogResult.reason);
|
||||
}
|
||||
renderChangelog(null);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
// Handle main data
|
||||
if (dataResult.status === 'fulfilled' && dataResult.value) {
|
||||
const data = dataResult.value;
|
||||
|
||||
// Update domain info
|
||||
if (domainInfo) {
|
||||
@@ -1006,9 +1047,8 @@
|
||||
|
||||
// Render quick start
|
||||
renderQuickStart(data.quick_start);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading data:', error);
|
||||
} else {
|
||||
console.error('Error loading data:', dataResult.reason);
|
||||
|
||||
// Show error in UI
|
||||
renderServicesError();
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(17, 17, 17, 0.8);
|
||||
background: transparent;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
@@ -198,6 +198,23 @@
|
||||
|
||||
<div class="gradient-line my-8" aria-hidden="true"></div>
|
||||
|
||||
<!-- Changelog Section -->
|
||||
<section class="mb-16">
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div class="w-10 h-10 rounded-lg bg-brand/10 border border-brand/20 flex items-center justify-center"
|
||||
data-section-icon="changelog"></div>
|
||||
<h2 class="text-2xl font-semibold text-white">Changelog</h2>
|
||||
</div>
|
||||
<div id="changelog-container"
|
||||
class="bg-surface-100 rounded-xl border border-surface-400 p-6 overflow-y-auto"
|
||||
style="max-height: 444px;">
|
||||
<!-- Changelog content will be injected here by JavaScript -->
|
||||
<div class="animate-pulse h-32"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="gradient-line my-8" aria-hidden="true"></div>
|
||||
|
||||
<!-- Documentation Section -->
|
||||
<section class="mb-16">
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
|
||||
Reference in New Issue
Block a user