mirror of
https://github.com/kossakovsky/n8n-install.git
synced 2026-03-08 06:43:22 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50bd817b56 | ||
|
|
611591dc0f | ||
|
|
ad9c7aa57d | ||
|
|
6e283c508c | ||
|
|
adc5b94f1c | ||
|
|
a99676e3d5 | ||
|
|
bf7ce20f7b | ||
|
|
36717a45c9 | ||
|
|
31b81b71a4 | ||
|
|
a3e8f26925 | ||
|
|
917afe615c | ||
|
|
641fd04290 | ||
|
|
ca43e7ab12 | ||
|
|
e5db00098a | ||
|
|
4a6f1c0e01 | ||
|
|
19cd6b6f91 | ||
|
|
b28093b5cd | ||
|
|
361a726a07 | ||
|
|
0b4c9d5dda |
@@ -314,14 +314,16 @@ ${SERVICE_NAME_UPPER}_PASSWORD=
|
||||
${SERVICE_NAME_UPPER}_PASSWORD_HASH=
|
||||
```
|
||||
|
||||
### 3.3 GOST_NO_PROXY (if using proxy-env)
|
||||
### 3.3 GOST_NO_PROXY (REQUIRED for ALL services)
|
||||
|
||||
Add service to comma-separated list:
|
||||
**CRITICAL:** Add ALL new service container names to the comma-separated list to prevent internal Docker traffic from going through the proxy:
|
||||
|
||||
```dotenv
|
||||
GOST_NO_PROXY=localhost,127.0.0.1,...existing...,$ARGUMENTS
|
||||
```
|
||||
|
||||
This applies to ALL services, not just those using `<<: *proxy-env`. Internal service-to-service communication must bypass the proxy.
|
||||
|
||||
---
|
||||
|
||||
## STEP 4: scripts/03_generate_secrets.sh
|
||||
@@ -706,6 +708,7 @@ bash -n scripts/07_final_report.sh
|
||||
- [ ] `docker-compose.yml`: caddy environment vars (if external)
|
||||
- [ ] `Caddyfile`: reverse proxy block (if external)
|
||||
- [ ] `.env.example`: hostname added
|
||||
- [ ] `.env.example`: service added to `GOST_NO_PROXY` (ALL internal services must be listed)
|
||||
- [ ] `scripts/03_generate_secrets.sh`: password in `VARS_TO_GENERATE`
|
||||
- [ ] `scripts/04_wizard.sh`: service in `base_services_data`
|
||||
- [ ] `scripts/generate_welcome_page.sh`: `SERVICES_ARRAY` entry
|
||||
@@ -722,7 +725,6 @@ bash -n scripts/07_final_report.sh
|
||||
|
||||
### If Outbound Proxy (AI API calls)
|
||||
- [ ] `docker-compose.yml`: `<<: *proxy-env` in environment
|
||||
- [ ] `.env.example`: service added to `GOST_NO_PROXY`
|
||||
- [ ] `docker-compose.yml`: healthcheck bypasses proxy
|
||||
|
||||
### If Database Required
|
||||
|
||||
10
.env.example
10
.env.example
@@ -164,6 +164,7 @@ NOCODB_HOSTNAME=nocodb.yourdomain.com
|
||||
PADDLEOCR_HOSTNAME=paddleocr.yourdomain.com
|
||||
PORTAINER_HOSTNAME=portainer.yourdomain.com
|
||||
POSTIZ_HOSTNAME=postiz.yourdomain.com
|
||||
TEMPORAL_UI_HOSTNAME=temporal.yourdomain.com
|
||||
PROMETHEUS_HOSTNAME=prometheus.yourdomain.com
|
||||
QDRANT_HOSTNAME=qdrant.yourdomain.com
|
||||
RAGAPP_HOSTNAME=ragapp.yourdomain.com
|
||||
@@ -433,7 +434,7 @@ GOST_UPSTREAM_PROXY=
|
||||
|
||||
# Internal services bypass list (prevents internal Docker traffic from going through proxy)
|
||||
# Includes: Docker internal networks (172.16-31.*, 10.*), Docker DNS (127.0.0.11), and all service hostnames
|
||||
GOST_NO_PROXY=localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.local,postgres,postgres:5432,redis,redis:6379,caddy,ollama,neo4j,qdrant,weaviate,clickhouse,minio,searxng,crawl4ai,gotenberg,langfuse-web,langfuse-worker,flowise,n8n,n8n-import,n8n-worker-1,n8n-worker-2,n8n-worker-3,n8n-worker-4,n8n-worker-5,n8n-worker-6,n8n-worker-7,n8n-worker-8,n8n-worker-9,n8n-worker-10,n8n-runner-1,n8n-runner-2,n8n-runner-3,n8n-runner-4,n8n-runner-5,n8n-runner-6,n8n-runner-7,n8n-runner-8,n8n-runner-9,n8n-runner-10,letta,lightrag,docling,postiz,ragflow,ragflow-mysql,ragflow-minio,ragflow-redis,ragflow-elasticsearch,ragapp,open-webui,comfyui,waha,libretranslate,paddleocr,nocodb,db,studio,kong,auth,rest,realtime,storage,imgproxy,meta,functions,analytics,vector,supavisor,gost
|
||||
GOST_NO_PROXY=localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,.local,postgres,postgres:5432,redis,redis:6379,caddy,ollama,neo4j,qdrant,weaviate,clickhouse,minio,searxng,crawl4ai,gotenberg,langfuse-web,langfuse-worker,flowise,n8n,n8n-import,n8n-worker-1,n8n-worker-2,n8n-worker-3,n8n-worker-4,n8n-worker-5,n8n-worker-6,n8n-worker-7,n8n-worker-8,n8n-worker-9,n8n-worker-10,n8n-runner-1,n8n-runner-2,n8n-runner-3,n8n-runner-4,n8n-runner-5,n8n-runner-6,n8n-runner-7,n8n-runner-8,n8n-runner-9,n8n-runner-10,letta,lightrag,docling,postiz,temporal,temporal-ui,ragflow,ragflow-mysql,ragflow-minio,ragflow-redis,ragflow-elasticsearch,ragapp,open-webui,comfyui,waha,libretranslate,paddleocr,nocodb,db,studio,kong,auth,rest,realtime,storage,imgproxy,meta,functions,analytics,vector,supavisor,gost,api.telegram.org,telegram.org,t.me,core.telegram.org
|
||||
|
||||
############
|
||||
# Functions - Configuration for Functions
|
||||
@@ -489,6 +490,13 @@ RAGAPP_PASSWORD_HASH=
|
||||
|
||||
POSTIZ_DISABLE_REGISTRATION=false
|
||||
|
||||
############
|
||||
# Temporal UI credentials (for Caddy basic auth)
|
||||
############
|
||||
TEMPORAL_UI_USERNAME=
|
||||
TEMPORAL_UI_PASSWORD=
|
||||
TEMPORAL_UI_PASSWORD_HASH=
|
||||
|
||||
############
|
||||
# Postiz Social Media Integrations
|
||||
# Leave blank if not used. Provide credentials from each platform.
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@ volumes/
|
||||
docker-compose.override.yml
|
||||
docker-compose.n8n-workers.yml
|
||||
welcome/data.json
|
||||
welcome/changelog.json
|
||||
|
||||
# Custom TLS certificates
|
||||
certs/*
|
||||
|
||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,16 +1,32 @@
|
||||
# Changelog
|
||||
|
||||
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).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.1.0] - 2026-01-09
|
||||
## [1.2.3] - 2026-01-29
|
||||
|
||||
### Fixed
|
||||
- **Gost proxy** - Add Telegram domains to `GOST_NO_PROXY` bypass list for n8n Telegram triggers
|
||||
|
||||
## [1.2.2] - 2026-01-26
|
||||
|
||||
### Fixed
|
||||
- **Custom TLS** - Fix duplicate hostname error when using custom certificates. Changed architecture from generating separate site blocks to using a shared TLS snippet that all services import.
|
||||
|
||||
## [1.2.1] - 2026-01-16
|
||||
|
||||
### Added
|
||||
- **Temporal** - Temporal server and UI for Postiz workflow orchestration (#33)
|
||||
|
||||
## [1.2.0] - 2026-01-12
|
||||
|
||||
### Added
|
||||
- Changelog section on Welcome Page dashboard
|
||||
|
||||
## [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
|
||||
|
||||
@@ -218,3 +234,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).
|
||||
|
||||
@@ -75,6 +75,8 @@ 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
|
||||
|
||||
68
Caddyfile
68
Caddyfile
@@ -3,30 +3,38 @@
|
||||
email {$LETSENCRYPT_EMAIL}
|
||||
}
|
||||
|
||||
# Import TLS snippet (must be before service blocks)
|
||||
# Default: Let's Encrypt automatic certificates
|
||||
# Custom: Run 'make setup-tls' to use your own certificates
|
||||
import /etc/caddy/addons/tls-snippet.conf
|
||||
|
||||
# N8N
|
||||
{$N8N_HOSTNAME} {
|
||||
# For domains, Caddy will automatically use Let's Encrypt
|
||||
# For localhost/port addresses, HTTPS won't be enabled
|
||||
import service_tls
|
||||
reverse_proxy n8n:5678
|
||||
}
|
||||
|
||||
# Open WebUI
|
||||
{$WEBUI_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy open-webui:8080
|
||||
}
|
||||
|
||||
# Flowise
|
||||
{$FLOWISE_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy flowise:3001
|
||||
}
|
||||
|
||||
# Dify
|
||||
{$DIFY_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy nginx:80
|
||||
}
|
||||
|
||||
# RAGApp
|
||||
{$RAGAPP_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$RAGAPP_USERNAME} {$RAGAPP_PASSWORD_HASH}
|
||||
}
|
||||
@@ -35,37 +43,38 @@
|
||||
|
||||
# RAGFlow
|
||||
{$RAGFLOW_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy ragflow:80
|
||||
}
|
||||
|
||||
# Langfuse
|
||||
{$LANGFUSE_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy langfuse-web:3000
|
||||
}
|
||||
|
||||
# # Ollama API
|
||||
# {$OLLAMA_HOSTNAME} {
|
||||
# reverse_proxy ollama:11434
|
||||
# }
|
||||
|
||||
# Supabase
|
||||
{$SUPABASE_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy kong:8000
|
||||
}
|
||||
|
||||
# Grafana
|
||||
{$GRAFANA_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy grafana:3000
|
||||
}
|
||||
|
||||
# WAHA (WhatsApp HTTP API)
|
||||
{$WAHA_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy waha:3000
|
||||
}
|
||||
|
||||
# Prometheus
|
||||
{$PROMETHEUS_HOSTNAME} {
|
||||
basic_auth {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$PROMETHEUS_USERNAME} {$PROMETHEUS_PASSWORD_HASH}
|
||||
}
|
||||
reverse_proxy prometheus:9090
|
||||
@@ -73,41 +82,58 @@
|
||||
|
||||
# Portainer
|
||||
{$PORTAINER_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy portainer:9000
|
||||
}
|
||||
|
||||
# Postiz
|
||||
{$POSTIZ_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy postiz:5000
|
||||
}
|
||||
|
||||
# Temporal UI (workflow orchestration for Postiz)
|
||||
{$TEMPORAL_UI_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$TEMPORAL_UI_USERNAME} {$TEMPORAL_UI_PASSWORD_HASH}
|
||||
}
|
||||
reverse_proxy temporal-ui:8080
|
||||
}
|
||||
|
||||
# Databasus
|
||||
{$DATABASUS_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy databasus:4005
|
||||
}
|
||||
|
||||
# Letta
|
||||
{$LETTA_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy letta:8283
|
||||
}
|
||||
|
||||
# LightRAG (Graph-based RAG with Knowledge Extraction)
|
||||
{$LIGHTRAG_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy lightrag:9621
|
||||
}
|
||||
|
||||
# Weaviate
|
||||
{$WEAVIATE_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy weaviate:8080
|
||||
}
|
||||
|
||||
# Qdrant
|
||||
{$QDRANT_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy qdrant:6333
|
||||
}
|
||||
|
||||
# ComfyUI
|
||||
{$COMFYUI_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$COMFYUI_USERNAME} {$COMFYUI_PASSWORD_HASH}
|
||||
}
|
||||
@@ -116,6 +142,7 @@
|
||||
|
||||
# LibreTranslate (Self-hosted Translation API)
|
||||
{$LT_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$LT_USERNAME} {$LT_PASSWORD_HASH}
|
||||
}
|
||||
@@ -124,21 +151,25 @@
|
||||
|
||||
# Neo4j
|
||||
{$NEO4J_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy neo4j:7474
|
||||
}
|
||||
|
||||
# Neo4j Bolt Protocol (wss)
|
||||
https://{$NEO4J_HOSTNAME}:7687 {
|
||||
import service_tls
|
||||
reverse_proxy neo4j:7687
|
||||
}
|
||||
|
||||
# NocoDB
|
||||
{$NOCODB_HOSTNAME} {
|
||||
import service_tls
|
||||
reverse_proxy nocodb:8080
|
||||
}
|
||||
|
||||
# PaddleOCR (PaddleX Basic Serving)
|
||||
{$PADDLEOCR_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$PADDLEOCR_USERNAME} {$PADDLEOCR_PASSWORD_HASH}
|
||||
}
|
||||
@@ -147,6 +178,7 @@ https://{$NEO4J_HOSTNAME}:7687 {
|
||||
|
||||
# Docling (Document Conversion API)
|
||||
{$DOCLING_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$DOCLING_USERNAME} {$DOCLING_PASSWORD_HASH}
|
||||
}
|
||||
@@ -154,7 +186,8 @@ https://{$NEO4J_HOSTNAME}:7687 {
|
||||
}
|
||||
|
||||
# Welcome Page (Post-install dashboard)
|
||||
{$WELCOME_HOSTNAME} {
|
||||
# HTTP block for Cloudflare Tunnel access (prevents redirect loop)
|
||||
http://{$WELCOME_HOSTNAME} {
|
||||
basic_auth {
|
||||
{$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
|
||||
}
|
||||
@@ -163,10 +196,23 @@ https://{$NEO4J_HOSTNAME}:7687 {
|
||||
try_files {path} /index.html
|
||||
}
|
||||
|
||||
import /etc/caddy/addons/*.conf
|
||||
# HTTPS block for direct access
|
||||
{$WELCOME_HOSTNAME} {
|
||||
import service_tls
|
||||
basic_auth {
|
||||
{$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
|
||||
}
|
||||
root * /srv/welcome
|
||||
file_server
|
||||
try_files {path} /index.html
|
||||
}
|
||||
|
||||
# # SearXNG
|
||||
# Import custom site addons
|
||||
import /etc/caddy/addons/site-*.conf
|
||||
|
||||
# SearXNG
|
||||
{$SEARXNG_HOSTNAME} {
|
||||
import service_tls
|
||||
@protected not remote_ip 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10
|
||||
|
||||
basic_auth @protected {
|
||||
|
||||
10
Makefile
10
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: help install update update-preview git-pull clean clean-all logs status monitor restart show-restarts doctor switch-beta switch-stable import setup-tls
|
||||
.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
|
||||
|
||||
@@ -17,6 +17,8 @@ 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"
|
||||
@@ -63,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/'); \
|
||||
|
||||
21
README.md
21
README.md
@@ -137,9 +137,10 @@ Get started quickly with a vast library of pre-built automations (optional impor
|
||||
1. **Domain Name:** You need a registered domain name (e.g., `yourdomain.com`).
|
||||
2. **DNS Configuration:** Before running the installation script, you **must** configure DNS A-record for your domain, pointing to the public IP address of the server where you'll install this system. Replace `yourdomain.com` with your actual domain:
|
||||
- **Wildcard Record:** `A *.yourdomain.com` -> `YOUR_SERVER_IP`
|
||||
3. **Server:** Minimum server system requirements: Ubuntu 24.04 LTS, 64-bit.
|
||||
- For running **all available services**: at least **20 GB Memory / 4 CPU Cores / 60 GB Disk Space**.
|
||||
- For a minimal setup with **n8n, Monitoring, Databasus and Portainer**: **4 GB Memory / 2 CPU Cores / 40 GB Disk Space**.
|
||||
3. **VPS (Virtual Private Server):** A dedicated VPS with a public IP address is required. Home servers, shared hosting, or localhost setups are not supported.
|
||||
- **Operating System:** Ubuntu 24.04 LTS, 64-bit
|
||||
- For a minimal setup with **n8n, Monitoring, Databasus and Portainer**: **4 GB Memory / 2 CPU Cores / 40 GB Disk Space**
|
||||
- For running **all available services**: at least **20 GB Memory / 4 CPU Cores / 60 GB Disk Space**
|
||||
|
||||
### Running the Install
|
||||
|
||||
@@ -318,6 +319,8 @@ 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 |
|
||||
@@ -365,6 +368,18 @@ Here are solutions to common issues you might encounter:
|
||||
- **VPN Conflicts:** Using a VPN might interfere with downloading Docker images. If you encounter issues pulling images, try temporarily disabling your VPN.
|
||||
- **Server Requirements:** If you experience unexpected issues, ensure your server meets the minimum hardware and operating system requirements (including version) as specified in the "Prerequisites before Installation" section.
|
||||
|
||||
### Update Script Not Working
|
||||
|
||||
- **Symptom:** The `make update` command fails, shows errors, or doesn't apply the latest changes.
|
||||
- **Cause:** This can happen if your local repository has diverged from the upstream, has uncommitted changes, or is in an inconsistent state.
|
||||
- **Solution:** Run the following command to force-sync your local installation with the latest version:
|
||||
|
||||
```bash
|
||||
git config pull.rebase true && git fetch origin && git checkout main && git reset --hard "origin/main" && make update
|
||||
```
|
||||
|
||||
**Warning:** This will discard any local changes you've made to the installer files. If you've customized any scripts or configurations, back them up first.
|
||||
|
||||
## Recommended Reading
|
||||
|
||||
n8n offers excellent resources for getting started with its AI capabilities:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This directory allows you to extend or override Caddy configuration without modifying the main `Caddyfile`.
|
||||
|
||||
All `.conf` files in this directory are automatically imported via `import /etc/caddy/addons/*.conf` at the end of the main Caddyfile.
|
||||
Files matching `site-*.conf` in this directory are automatically imported via `import /etc/caddy/addons/site-*.conf` in the main Caddyfile.
|
||||
|
||||
## Use Cases
|
||||
|
||||
@@ -15,6 +15,23 @@ All `.conf` files in this directory are automatically imported via `import /etc/
|
||||
|
||||
For corporate/internal deployments where Let's Encrypt is not available, you can use your own certificates.
|
||||
|
||||
### How It Works
|
||||
|
||||
The main `Caddyfile` imports a TLS snippet that all service blocks use:
|
||||
|
||||
```caddy
|
||||
# In Caddyfile (top)
|
||||
import /etc/caddy/addons/tls-snippet.conf
|
||||
|
||||
# In each service block
|
||||
{$N8N_HOSTNAME} {
|
||||
import service_tls # <-- Uses the snippet
|
||||
reverse_proxy n8n:5678
|
||||
}
|
||||
```
|
||||
|
||||
By default, the snippet is empty (Let's Encrypt is used). When you run `make setup-tls`, the snippet is updated with your certificate paths.
|
||||
|
||||
### Quick Setup
|
||||
|
||||
1. Place your certificates in the `certs/` directory:
|
||||
@@ -28,42 +45,22 @@ For corporate/internal deployments where Let's Encrypt is not available, you can
|
||||
make setup-tls
|
||||
```
|
||||
|
||||
3. Restart Caddy:
|
||||
```bash
|
||||
docker compose -p localai restart caddy
|
||||
```
|
||||
3. The script will:
|
||||
- Update `caddy-addon/tls-snippet.conf` with your certificate paths
|
||||
- Optionally restart Caddy to apply changes
|
||||
|
||||
### Manual Setup
|
||||
### Reset to Let's Encrypt
|
||||
|
||||
1. Copy the example file:
|
||||
```bash
|
||||
cp caddy-addon/custom-tls.conf.example caddy-addon/custom-tls.conf
|
||||
```
|
||||
To switch back to automatic Let's Encrypt certificates:
|
||||
|
||||
2. Edit `custom-tls.conf` with your hostnames and certificate paths
|
||||
|
||||
3. Place certificates in `certs/` directory
|
||||
|
||||
4. Restart Caddy:
|
||||
```bash
|
||||
docker compose -p localai restart caddy
|
||||
```
|
||||
|
||||
## How Site Override Works
|
||||
|
||||
When you define a site block in an addon file with the same hostname as the main Caddyfile, Caddy will use **both** configurations. To completely override a site, use the exact same hostname.
|
||||
|
||||
Example: To override `n8n.yourdomain.com` with a custom certificate:
|
||||
|
||||
```
|
||||
# caddy-addon/custom-tls.conf
|
||||
n8n.internal.company.com {
|
||||
tls /etc/caddy/certs/wildcard.crt /etc/caddy/certs/wildcard.key
|
||||
reverse_proxy n8n:5678
|
||||
}
|
||||
```bash
|
||||
make setup-tls --remove
|
||||
```
|
||||
|
||||
Make sure your `.env` file has `N8N_HOSTNAME=n8n.internal.company.com`.
|
||||
Or run directly:
|
||||
```bash
|
||||
bash scripts/setup_custom_tls.sh --remove
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
@@ -71,8 +68,9 @@ Make sure your `.env` file has `N8N_HOSTNAME=n8n.internal.company.com`.
|
||||
caddy-addon/
|
||||
├── .gitkeep # Keeps directory in git
|
||||
├── README.md # This file
|
||||
├── custom-tls.conf.example # Example for custom certificates
|
||||
└── custom-tls.conf # Your custom config (gitignored)
|
||||
├── tls-snippet.conf.example # Template for TLS snippet (tracked in git)
|
||||
├── tls-snippet.conf # Your TLS config (gitignored, auto-created)
|
||||
└── site-*.conf # Your custom addons (gitignored, must start with "site-")
|
||||
|
||||
certs/
|
||||
├── .gitkeep # Keeps directory in git
|
||||
@@ -80,11 +78,26 @@ certs/
|
||||
└── wildcard.key # Your private key (gitignored)
|
||||
```
|
||||
|
||||
## Adding Custom Addons
|
||||
|
||||
You can create `site-*.conf` files for custom Caddy configurations. They will be automatically loaded by the main Caddyfile.
|
||||
|
||||
**Important:** Custom addon files MUST start with `site-` prefix to be loaded (e.g., `site-custom.conf`, `site-myapp.conf`).
|
||||
|
||||
Example: `caddy-addon/site-custom-headers.conf`
|
||||
```caddy
|
||||
# Add custom headers to all responses
|
||||
(custom_headers) {
|
||||
header X-Custom-Header "My Value"
|
||||
}
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Files in `caddy-addon/*.conf` are gitignored (preserved during updates)
|
||||
- `tls-snippet.conf.example` is tracked in git (template with default Let's Encrypt behavior)
|
||||
- `tls-snippet.conf` is gitignored and auto-created from template (preserved during updates)
|
||||
- `site-*.conf` files are gitignored (preserved during updates)
|
||||
- Files in `certs/` are gitignored (certificates are not committed)
|
||||
- Example files (`*.example`) are tracked in git
|
||||
- Caddy validates configuration on startup - check logs if it fails:
|
||||
```bash
|
||||
docker compose -p localai logs caddy
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
# Custom TLS Configuration for Corporate/Internal Certificates
|
||||
#
|
||||
# This file provides examples for using your own TLS certificates instead of Let's Encrypt.
|
||||
# Copy this file to custom-tls.conf and modify as needed.
|
||||
#
|
||||
# Prerequisites:
|
||||
# 1. Place your certificate files in the ./certs/ directory
|
||||
# 2. Update .env hostnames to match your internal domain
|
||||
# 3. Restart Caddy: docker compose -p localai restart caddy
|
||||
|
||||
# =============================================================================
|
||||
# Option 1: Reusable TLS snippet (recommended for wildcard certificates)
|
||||
# =============================================================================
|
||||
# Define once, import in each service block
|
||||
|
||||
(custom_tls) {
|
||||
tls /etc/caddy/certs/wildcard.crt /etc/caddy/certs/wildcard.key
|
||||
}
|
||||
|
||||
# Then for each service you want to override:
|
||||
#
|
||||
# n8n.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy n8n:5678
|
||||
# }
|
||||
#
|
||||
# flowise.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy flowise:3001
|
||||
# }
|
||||
|
||||
# =============================================================================
|
||||
# Option 2: Individual service configuration
|
||||
# =============================================================================
|
||||
# Use when you have different certificates for different services
|
||||
|
||||
# n8n.internal.company.com {
|
||||
# tls /etc/caddy/certs/n8n.crt /etc/caddy/certs/n8n.key
|
||||
# reverse_proxy n8n:5678
|
||||
# }
|
||||
|
||||
# =============================================================================
|
||||
# Option 3: Internal CA with auto-reload
|
||||
# =============================================================================
|
||||
# Caddy can auto-reload certificates when they change
|
||||
|
||||
# n8n.internal.company.com {
|
||||
# tls /etc/caddy/certs/cert.pem /etc/caddy/certs/key.pem {
|
||||
# # Optional: specify CA certificate for client verification
|
||||
# # client_auth {
|
||||
# # mode require_and_verify
|
||||
# # trusted_ca_cert_file /etc/caddy/certs/ca.pem
|
||||
# # }
|
||||
# }
|
||||
# reverse_proxy n8n:5678
|
||||
# }
|
||||
|
||||
# =============================================================================
|
||||
# Full Example: All common services with wildcard certificate
|
||||
# =============================================================================
|
||||
# Uncomment and modify the hostnames to match your .env configuration
|
||||
|
||||
# # N8N
|
||||
# n8n.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy n8n:5678
|
||||
# }
|
||||
|
||||
# # Flowise
|
||||
# flowise.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy flowise:3001
|
||||
# }
|
||||
|
||||
# # Open WebUI
|
||||
# webui.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy open-webui:8080
|
||||
# }
|
||||
|
||||
# # Grafana
|
||||
# grafana.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy grafana:3000
|
||||
# }
|
||||
|
||||
# # Portainer
|
||||
# portainer.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy portainer:9000
|
||||
# }
|
||||
|
||||
# # Langfuse
|
||||
# langfuse.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy langfuse-web:3000
|
||||
# }
|
||||
|
||||
# # Supabase
|
||||
# supabase.internal.company.com {
|
||||
# import custom_tls
|
||||
# reverse_proxy kong:8000
|
||||
# }
|
||||
|
||||
# # Welcome Page (with basic auth preserved)
|
||||
# welcome.internal.company.com {
|
||||
# import custom_tls
|
||||
# basic_auth {
|
||||
# {$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
|
||||
# }
|
||||
# root * /srv/welcome
|
||||
# file_server
|
||||
# try_files {path} /index.html
|
||||
# }
|
||||
10
caddy-addon/tls-snippet.conf.example
Normal file
10
caddy-addon/tls-snippet.conf.example
Normal file
@@ -0,0 +1,10 @@
|
||||
# TLS Configuration Snippet
|
||||
# Imported by all service blocks in the main Caddyfile.
|
||||
#
|
||||
# Default: Empty (uses Let's Encrypt automatic certificates)
|
||||
# Custom: Overwritten by 'make setup-tls' with your certificate paths
|
||||
# Reset: Run 'make setup-tls --remove' to restore Let's Encrypt
|
||||
|
||||
(service_tls) {
|
||||
# Default: Let's Encrypt automatic certificates (empty = no override)
|
||||
}
|
||||
@@ -22,8 +22,8 @@ Cloudflare Tunnel **bypasses Caddy** and connects directly to your services. Thi
|
||||
|
||||
1. Go to [Cloudflare One Dashboard](https://one.dash.cloudflare.com/)
|
||||
2. Navigate to **Networks** → **Connectors** → **Cloudflare Tunnels**
|
||||
3. Click **Create new cloudflared Tunnel**
|
||||
4. Choose **Cloudflared** connector and click **Next**
|
||||
3. Click **Create a tunnel**
|
||||
4. Select **Cloudflared** as the connector type and click **Next**
|
||||
5. Name your tunnel (e.g., "n8n-install") and click **Save tunnel**
|
||||
6. Copy the installation command shown - it contains your tunnel token
|
||||
|
||||
@@ -106,7 +106,7 @@ dig NS yourdomain.com +short
|
||||
|
||||
#### 3. Configure Public Hostnames
|
||||
|
||||
After DNS is configured, go to **Cloudflare Zero Trust** → **Networks** → **Tunnels** → your tunnel → **Public Hostname** tab. For each service you want to expose, click **Add a public hostname** and configure:
|
||||
After DNS is configured, go to **Cloudflare One Dashboard** → **Networks** → **Connectors** → **Cloudflare Tunnels** → your tunnel → **Public Hostname** tab. For each service you want to expose, click **Add a public hostname** and configure:
|
||||
|
||||
| Service | Public Hostname | Service URL | Auth Notes |
|
||||
| ------------------ | ----------------------------- | ---------------------------- | ------------------- |
|
||||
@@ -122,6 +122,7 @@ After DNS is configured, go to **Cloudflare Zero Trust** → **Networks** → **
|
||||
| **LibreTranslate** | libretranslate.yourdomain.com | `http://libretranslate:5000` | ⚠️ Loses Caddy auth |
|
||||
| **LightRAG** | lightrag.yourdomain.com | `http://lightrag:9621` | No auth |
|
||||
| **Neo4j** | neo4j.yourdomain.com | `http://neo4j:7474` | Built-in login |
|
||||
| **NocoDB** | nocodb.yourdomain.com | `http://nocodb:8080` | Built-in login |
|
||||
| **Open WebUI** | webui.yourdomain.com | `http://open-webui:8080` | Built-in login |
|
||||
| **PaddleOCR** | paddleocr.yourdomain.com | `http://paddleocr:8080` | ⚠️ Loses Caddy auth |
|
||||
| **Portainer** | portainer.yourdomain.com | `http://portainer:9000` | Built-in login |
|
||||
@@ -134,6 +135,11 @@ After DNS is configured, go to **Cloudflare Zero Trust** → **Networks** → **
|
||||
| **Supabase** ¹ | supabase.yourdomain.com | `http://kong:8000` | Built-in login |
|
||||
| **WAHA** | waha.yourdomain.com | `http://waha:3000` | API key recommended |
|
||||
| **Weaviate** | weaviate.yourdomain.com | `http://weaviate:8080` | API key recommended |
|
||||
| **Welcome Page** ² | welcome.yourdomain.com | `http://caddy:80` | ⚠️ Loses Caddy auth |
|
||||
|
||||
**Notes:**
|
||||
- ¹ Dify and Supabase use external compose files from adjacent directories
|
||||
- ² Welcome Page is served by Caddy as static content; tunnel proxies through Caddy
|
||||
|
||||
**⚠️ Security Warning:**
|
||||
- Services marked **"Loses Caddy auth"** have basic authentication via Caddy that is bypassed by the tunnel. Use [Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/applications/) or keep them internal.
|
||||
@@ -181,7 +187,7 @@ You have two options for accessing your services:
|
||||
|
||||
For services that lose Caddy's basic auth protection, you can add Cloudflare Access:
|
||||
|
||||
1. In **Cloudflare One Dashboard** → **Access controls** → **Applications**
|
||||
1. In **Cloudflare One Dashboard** → **Access** → **Applications** (or **Access controls** → **Applications** depending on your dashboard version)
|
||||
2. Click **Add an application** → **Self-hosted**
|
||||
3. Configure:
|
||||
- **Application name**: e.g., "Prometheus"
|
||||
|
||||
@@ -33,9 +33,17 @@ volumes:
|
||||
ragflow_minio_data:
|
||||
ragflow_mysql_data:
|
||||
ragflow_redis_data:
|
||||
temporal_elasticsearch_data:
|
||||
valkey-data:
|
||||
weaviate_data:
|
||||
|
||||
# Shared logging configuration for services
|
||||
x-logging: &default-logging
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
max-file: "1"
|
||||
|
||||
# Shared proxy configuration for services that need outbound proxy support
|
||||
x-proxy-env: &proxy-env
|
||||
HTTP_PROXY: ${GOST_PROXY_URL:-}
|
||||
@@ -274,11 +282,7 @@ services:
|
||||
container_name: nocodb
|
||||
profiles: ["nocodb"]
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
max-file: "1"
|
||||
logging: *default-logging
|
||||
environment:
|
||||
NC_AUTH_JWT_SECRET: ${NOCODB_JWT_SECRET}
|
||||
NC_DB: pg://postgres:5432?u=postgres&p=${POSTGRES_PASSWORD}&d=nocodb
|
||||
@@ -339,6 +343,9 @@ services:
|
||||
PORTAINER_HOSTNAME: ${PORTAINER_HOSTNAME}
|
||||
DATABASUS_HOSTNAME: ${DATABASUS_HOSTNAME}
|
||||
POSTIZ_HOSTNAME: ${POSTIZ_HOSTNAME}
|
||||
TEMPORAL_UI_HOSTNAME: ${TEMPORAL_UI_HOSTNAME}
|
||||
TEMPORAL_UI_USERNAME: ${TEMPORAL_UI_USERNAME}
|
||||
TEMPORAL_UI_PASSWORD_HASH: ${TEMPORAL_UI_PASSWORD_HASH}
|
||||
PROMETHEUS_HOSTNAME: ${PROMETHEUS_HOSTNAME}
|
||||
PROMETHEUS_PASSWORD_HASH: ${PROMETHEUS_PASSWORD_HASH}
|
||||
PROMETHEUS_USERNAME: ${PROMETHEUS_USERNAME}
|
||||
@@ -361,11 +368,7 @@ services:
|
||||
- ALL
|
||||
cap_add:
|
||||
- NET_BIND_SERVICE
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
max-file: "1"
|
||||
logging: *default-logging
|
||||
|
||||
cloudflared:
|
||||
image: cloudflare/cloudflared:latest
|
||||
@@ -375,11 +378,7 @@ services:
|
||||
command: tunnel --no-autoupdate run
|
||||
environment:
|
||||
TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN}
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
max-file: "1"
|
||||
logging: *default-logging
|
||||
|
||||
gost:
|
||||
image: gogost/gost:latest
|
||||
@@ -397,11 +396,7 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
max-file: "1"
|
||||
logging: *default-logging
|
||||
|
||||
langfuse-worker:
|
||||
image: langfuse/langfuse-worker:3
|
||||
@@ -553,11 +548,7 @@ services:
|
||||
- SETGID
|
||||
- SETUID
|
||||
- DAC_OVERRIDE
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
max-file: "1"
|
||||
logging: *default-logging
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 3s
|
||||
@@ -580,11 +571,7 @@ services:
|
||||
- CHOWN
|
||||
- SETGID
|
||||
- SETUID
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
max-file: "1"
|
||||
logging: *default-logging
|
||||
|
||||
ollama-cpu:
|
||||
profiles: ["cpu"]
|
||||
@@ -778,6 +765,70 @@ services:
|
||||
- portainer_data:/data
|
||||
- ${DOCKER_SOCKET_LOCATION:-/var/run/docker.sock}:/var/run/docker.sock
|
||||
|
||||
temporal-elasticsearch:
|
||||
image: elasticsearch:7.17.27
|
||||
container_name: temporal-elasticsearch
|
||||
profiles: ["postiz"]
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
environment:
|
||||
cluster.routing.allocation.disk.threshold_enabled: "true"
|
||||
cluster.routing.allocation.disk.watermark.low: 512mb
|
||||
cluster.routing.allocation.disk.watermark.high: 256mb
|
||||
cluster.routing.allocation.disk.watermark.flood_stage: 128mb
|
||||
discovery.type: single-node
|
||||
ES_JAVA_OPTS: -Xms512m -Xmx512m
|
||||
xpack.security.enabled: "false"
|
||||
volumes:
|
||||
- temporal_elasticsearch_data:/usr/share/elasticsearch/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -qE '\"status\":\"(green|yellow)\"'"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
|
||||
temporal:
|
||||
image: temporalio/auto-setup:latest
|
||||
container_name: temporal
|
||||
profiles: ["postiz"]
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
environment:
|
||||
DB: postgres12
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PWD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_SEEDS: postgres
|
||||
DB_PORT: 5432
|
||||
TEMPORAL_NAMESPACE: default
|
||||
ENABLE_ES: "true"
|
||||
ES_SEEDS: temporal-elasticsearch
|
||||
ES_VERSION: v7
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
temporal-elasticsearch:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "temporal operator cluster health --address $(hostname -i):7233 | grep -q SERVING || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
|
||||
temporal-ui:
|
||||
image: temporalio/ui:latest
|
||||
container_name: temporal-ui
|
||||
profiles: ["postiz"]
|
||||
restart: unless-stopped
|
||||
logging: *default-logging
|
||||
environment:
|
||||
TEMPORAL_ADDRESS: temporal:7233
|
||||
TEMPORAL_CORS_ORIGINS: http://localhost:3000
|
||||
depends_on:
|
||||
temporal:
|
||||
condition: service_healthy
|
||||
|
||||
postiz:
|
||||
image: ghcr.io/gitroomhq/postiz-app:latest
|
||||
container_name: postiz
|
||||
@@ -796,6 +847,7 @@ services:
|
||||
NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads"
|
||||
REDIS_URL: "redis://redis:6379"
|
||||
STORAGE_PROVIDER: "local"
|
||||
TEMPORAL_ADDRESS: temporal:7233
|
||||
UPLOAD_DIRECTORY: "/uploads"
|
||||
# Social Media API Settings
|
||||
X_API_KEY: ${X_API_KEY}
|
||||
@@ -837,17 +889,15 @@ services:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
temporal:
|
||||
condition: service_healthy
|
||||
|
||||
databasus:
|
||||
image: databasus/databasus:latest
|
||||
container_name: databasus
|
||||
profiles: ["databasus"]
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
max-file: "1"
|
||||
logging: *default-logging
|
||||
volumes:
|
||||
- databasus_data:/databasus-data
|
||||
healthcheck:
|
||||
@@ -1044,11 +1094,7 @@ services:
|
||||
- SETGID
|
||||
- SETUID
|
||||
- DAC_OVERRIDE
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
max-file: "1"
|
||||
logging: *default-logging
|
||||
healthcheck:
|
||||
test: ["CMD", "valkey-cli", "-a", "${RAGFLOW_REDIS_PASSWORD}", "ping"]
|
||||
interval: 3s
|
||||
|
||||
@@ -55,6 +55,7 @@ EMAIL_VARS=(
|
||||
"PROMETHEUS_USERNAME"
|
||||
"RAGAPP_USERNAME"
|
||||
"SEARXNG_USERNAME"
|
||||
"TEMPORAL_UI_USERNAME"
|
||||
"WAHA_DASHBOARD_USERNAME"
|
||||
"WEAVIATE_USERNAME"
|
||||
"WELCOME_USERNAME"
|
||||
@@ -114,6 +115,7 @@ declare -A VARS_TO_GENERATE=(
|
||||
["RAGFLOW_REDIS_PASSWORD"]="password:32"
|
||||
["SEARXNG_PASSWORD"]="password:32" # Added SearXNG admin password
|
||||
["SECRET_KEY_BASE"]="base64:64" # 48 bytes -> 64 chars
|
||||
["TEMPORAL_UI_PASSWORD"]="password:32" # Temporal UI basic auth password
|
||||
["VAULT_ENC_KEY"]="alphanum:32"
|
||||
["WAHA_DASHBOARD_PASSWORD"]="password:32"
|
||||
["WEAVIATE_API_KEY"]="secret:48" # API Key for Weaviate service (36 bytes -> 48 chars base64)
|
||||
@@ -564,7 +566,7 @@ if [[ -n "$template_no_proxy" ]]; then
|
||||
fi
|
||||
|
||||
# Hash passwords using caddy with bcrypt (consolidated loop)
|
||||
SERVICES_NEEDING_HASH=("PROMETHEUS" "SEARXNG" "COMFYUI" "PADDLEOCR" "RAGAPP" "LT" "DOCLING" "WELCOME")
|
||||
SERVICES_NEEDING_HASH=("PROMETHEUS" "SEARXNG" "COMFYUI" "PADDLEOCR" "RAGAPP" "LT" "DOCLING" "TEMPORAL_UI" "WELCOME")
|
||||
|
||||
for service in "${SERVICES_NEEDING_HASH[@]}"; do
|
||||
password_var="${service}_PASSWORD"
|
||||
|
||||
@@ -32,6 +32,23 @@ require_file "$PROJECT_ROOT/docker-compose.yml" "docker-compose.yml file not fou
|
||||
require_file "$PROJECT_ROOT/Caddyfile" "Caddyfile not found in project root. Reverse proxy might not work."
|
||||
require_file "$PROJECT_ROOT/start_services.py" "start_services.py file not found in project root."
|
||||
|
||||
# Remove legacy custom-tls.conf that causes duplicate host errors
|
||||
# This is needed for users upgrading from older versions
|
||||
# TODO: Remove this cleanup block after v3.0 release (all users migrated)
|
||||
OLD_TLS_CONFIG="$PROJECT_ROOT/caddy-addon/custom-tls.conf"
|
||||
if [[ -f "$OLD_TLS_CONFIG" ]]; then
|
||||
log_warning "Removing obsolete custom-tls.conf (causes duplicate host errors)"
|
||||
rm -f "$OLD_TLS_CONFIG"
|
||||
fi
|
||||
|
||||
# Ensure TLS snippet exists (auto-create from template if missing)
|
||||
TLS_SNIPPET="$PROJECT_ROOT/caddy-addon/tls-snippet.conf"
|
||||
TLS_TEMPLATE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf.example"
|
||||
if [[ ! -f "$TLS_SNIPPET" ]] && [[ -f "$TLS_TEMPLATE" ]]; then
|
||||
cp "$TLS_TEMPLATE" "$TLS_SNIPPET"
|
||||
log_info "Created tls-snippet.conf from template (Let's Encrypt mode)"
|
||||
fi
|
||||
|
||||
# Check if Docker daemon is running
|
||||
if ! docker info > /dev/null 2>&1; then
|
||||
log_error "Docker daemon is not running. Please start Docker and try again."
|
||||
|
||||
@@ -97,6 +97,9 @@ fi
|
||||
if is_profile_active "nocodb"; then
|
||||
echo -e " ${GREEN}*${NC} ${WHITE}NocoDB${NC}: Create your account on first login"
|
||||
fi
|
||||
if is_profile_active "postiz"; then
|
||||
echo -e " ${GREEN}*${NC} ${WHITE}Postiz${NC}: Create your account on first login"
|
||||
fi
|
||||
if is_profile_active "gost"; then
|
||||
echo -e " ${GREEN}*${NC} ${WHITE}Gost Proxy${NC}: Routing AI traffic through external proxy"
|
||||
fi
|
||||
|
||||
@@ -30,6 +30,8 @@ INIT_DB_DATABASES=(
|
||||
"lightrag"
|
||||
"nocodb"
|
||||
"postiz"
|
||||
"temporal"
|
||||
"temporal_visibility"
|
||||
"waha"
|
||||
)
|
||||
|
||||
|
||||
@@ -327,6 +327,20 @@ if is_profile_active "postiz"; then
|
||||
}")
|
||||
fi
|
||||
|
||||
# Temporal UI
|
||||
if is_profile_active "postiz"; then
|
||||
SERVICES_ARRAY+=(" \"temporal-ui\": {
|
||||
\"hostname\": \"$(json_escape "$TEMPORAL_UI_HOSTNAME")\",
|
||||
\"credentials\": {
|
||||
\"username\": \"$(json_escape "$TEMPORAL_UI_USERNAME")\",
|
||||
\"password\": \"$(json_escape "$TEMPORAL_UI_PASSWORD")\"
|
||||
},
|
||||
\"extra\": {
|
||||
\"note\": \"Workflow orchestration admin for Postiz\"
|
||||
}
|
||||
}")
|
||||
fi
|
||||
|
||||
# WAHA
|
||||
if is_profile_active "waha"; then
|
||||
SERVICES_ARRAY+=(" \"waha\": {
|
||||
@@ -541,3 +555,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
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
# =============================================================================
|
||||
# setup_custom_tls.sh - Configure custom TLS certificates for Caddy
|
||||
# =============================================================================
|
||||
# Generates caddy-addon/custom-tls.conf for using corporate/internal certificates
|
||||
# Updates caddy-addon/tls-snippet.conf to use corporate/internal certificates
|
||||
# instead of Let's Encrypt.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/setup_custom_tls.sh # Interactive mode
|
||||
# bash scripts/setup_custom_tls.sh cert.crt key.key # Non-interactive mode
|
||||
# bash scripts/setup_custom_tls.sh --remove # Reset to Let's Encrypt
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Place certificate files in ./certs/ directory
|
||||
@@ -18,13 +19,27 @@ set -euo pipefail
|
||||
|
||||
source "$(dirname "$0")/utils.sh" && init_paths
|
||||
|
||||
ADDON_FILE="$PROJECT_ROOT/caddy-addon/custom-tls.conf"
|
||||
SNIPPET_FILE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf"
|
||||
SNIPPET_EXAMPLE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf.example"
|
||||
CERTS_DIR="$PROJECT_ROOT/certs"
|
||||
|
||||
# Legacy file that causes duplicate host errors (must be cleaned up on migration)
|
||||
# TODO: Remove OLD_CONFIG and cleanup_legacy_config() after v3.0 release (all users migrated)
|
||||
OLD_CONFIG="$PROJECT_ROOT/caddy-addon/custom-tls.conf"
|
||||
|
||||
# =============================================================================
|
||||
# FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
cleanup_legacy_config() {
|
||||
# Remove old custom-tls.conf that causes duplicate host errors
|
||||
# This is needed for users upgrading from older versions
|
||||
if [[ -f "$OLD_CONFIG" ]]; then
|
||||
log_warning "Removing obsolete custom-tls.conf (causes duplicate host errors)"
|
||||
rm -f "$OLD_CONFIG"
|
||||
fi
|
||||
}
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Setup Custom TLS Certificates for Caddy
|
||||
@@ -33,7 +48,7 @@ Usage: $(basename "$0") [OPTIONS] [CERT_FILE] [KEY_FILE]
|
||||
|
||||
Options:
|
||||
-h, --help Show this help message
|
||||
--remove Remove custom TLS configuration
|
||||
--remove Reset to Let's Encrypt automatic certificates
|
||||
|
||||
Arguments:
|
||||
CERT_FILE Path to certificate file (relative to ./certs/)
|
||||
@@ -42,13 +57,12 @@ Arguments:
|
||||
Examples:
|
||||
$(basename "$0") # Interactive mode
|
||||
$(basename "$0") wildcard.crt wildcard.key # Use specific files
|
||||
$(basename "$0") --remove # Remove custom TLS config
|
||||
$(basename "$0") --remove # Reset to Let's Encrypt
|
||||
|
||||
The script will:
|
||||
1. Detect certificate files in ./certs/
|
||||
2. Read active services from .env
|
||||
3. Generate caddy-addon/custom-tls.conf
|
||||
4. Optionally restart Caddy
|
||||
2. Update caddy-addon/tls-snippet.conf with your certificate paths
|
||||
3. Optionally restart Caddy
|
||||
|
||||
EOF
|
||||
}
|
||||
@@ -75,157 +89,53 @@ find_keys() {
|
||||
echo "${keys[*]:-}"
|
||||
}
|
||||
|
||||
get_active_services() {
|
||||
# Get list of services with their hostnames from .env
|
||||
load_env
|
||||
local services=()
|
||||
|
||||
# Map of service names to their hostname variables
|
||||
declare -A service_map=(
|
||||
["n8n"]="N8N_HOSTNAME"
|
||||
["flowise"]="FLOWISE_HOSTNAME"
|
||||
["webui"]="WEBUI_HOSTNAME"
|
||||
["grafana"]="GRAFANA_HOSTNAME"
|
||||
["prometheus"]="PROMETHEUS_HOSTNAME"
|
||||
["portainer"]="PORTAINER_HOSTNAME"
|
||||
["langfuse"]="LANGFUSE_HOSTNAME"
|
||||
["supabase"]="SUPABASE_HOSTNAME"
|
||||
["dify"]="DIFY_HOSTNAME"
|
||||
["nocodb"]="NOCODB_HOSTNAME"
|
||||
["ragapp"]="RAGAPP_HOSTNAME"
|
||||
["ragflow"]="RAGFLOW_HOSTNAME"
|
||||
["waha"]="WAHA_HOSTNAME"
|
||||
["searxng"]="SEARXNG_HOSTNAME"
|
||||
["comfyui"]="COMFYUI_HOSTNAME"
|
||||
["welcome"]="WELCOME_HOSTNAME"
|
||||
["databasus"]="DATABASUS_HOSTNAME"
|
||||
["letta"]="LETTA_HOSTNAME"
|
||||
["lightrag"]="LIGHTRAG_HOSTNAME"
|
||||
["weaviate"]="WEAVIATE_HOSTNAME"
|
||||
["qdrant"]="QDRANT_HOSTNAME"
|
||||
["neo4j"]="NEO4J_HOSTNAME"
|
||||
["postiz"]="POSTIZ_HOSTNAME"
|
||||
["libretranslate"]="LT_HOSTNAME"
|
||||
["paddleocr"]="PADDLEOCR_HOSTNAME"
|
||||
["docling"]="DOCLING_HOSTNAME"
|
||||
)
|
||||
|
||||
for service in "${!service_map[@]}"; do
|
||||
local hostname_var="${service_map[$service]}"
|
||||
local hostname="${!hostname_var:-}"
|
||||
if [[ -n "$hostname" && "$hostname" != *"yourdomain.com" ]]; then
|
||||
services+=("$service:$hostname")
|
||||
ensure_snippet_exists() {
|
||||
# Create tls-snippet.conf from example if it doesn't exist
|
||||
# This ensures the file survives git updates (it's gitignored)
|
||||
if [[ ! -f "$SNIPPET_FILE" ]]; then
|
||||
if [[ -f "$SNIPPET_EXAMPLE" ]]; then
|
||||
cp "$SNIPPET_EXAMPLE" "$SNIPPET_FILE"
|
||||
log_info "Created tls-snippet.conf from template"
|
||||
else
|
||||
# Fallback: create default content directly
|
||||
remove_config
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${services[*]:-}"
|
||||
fi
|
||||
}
|
||||
|
||||
generate_config() {
|
||||
local cert_file="$1"
|
||||
local key_file="$2"
|
||||
local services=("${@:3}")
|
||||
|
||||
cat > "$ADDON_FILE" << 'HEADER'
|
||||
# Custom TLS Configuration
|
||||
# Generated by setup_custom_tls.sh
|
||||
#
|
||||
# This file overrides default Let's Encrypt certificates with custom ones.
|
||||
# Regenerate with: make setup-tls
|
||||
cat > "$SNIPPET_FILE" << EOF
|
||||
# TLS Configuration Snippet
|
||||
# Generated by setup_custom_tls.sh on $(date -Iseconds)
|
||||
# Using custom certificates instead of Let's Encrypt.
|
||||
# Reset to Let's Encrypt: make setup-tls --remove
|
||||
|
||||
# Reusable TLS snippet
|
||||
(custom_tls) {
|
||||
HEADER
|
||||
(service_tls) {
|
||||
tls /etc/caddy/certs/$cert_file /etc/caddy/certs/$key_file
|
||||
}
|
||||
EOF
|
||||
|
||||
echo " tls /etc/caddy/certs/$cert_file /etc/caddy/certs/$key_file" >> "$ADDON_FILE"
|
||||
echo "}" >> "$ADDON_FILE"
|
||||
echo "" >> "$ADDON_FILE"
|
||||
|
||||
# Service-specific reverse proxy mappings
|
||||
declare -A proxy_map=(
|
||||
["n8n"]="n8n:5678"
|
||||
["flowise"]="flowise:3001"
|
||||
["webui"]="open-webui:8080"
|
||||
["grafana"]="grafana:3000"
|
||||
["prometheus"]="prometheus:9090"
|
||||
["portainer"]="portainer:9000"
|
||||
["langfuse"]="langfuse-web:3000"
|
||||
["supabase"]="kong:8000"
|
||||
["dify"]="nginx:80"
|
||||
["nocodb"]="nocodb:8080"
|
||||
["ragapp"]="ragapp:8000"
|
||||
["ragflow"]="ragflow:80"
|
||||
["waha"]="waha:3000"
|
||||
["searxng"]="searxng:8080"
|
||||
["comfyui"]="comfyui:8188"
|
||||
["welcome"]="file_server"
|
||||
["databasus"]="databasus:4005"
|
||||
["letta"]="letta:8283"
|
||||
["lightrag"]="lightrag:9621"
|
||||
["weaviate"]="weaviate:8080"
|
||||
["qdrant"]="qdrant:6333"
|
||||
["neo4j"]="neo4j:7474"
|
||||
["postiz"]="postiz:5000"
|
||||
["libretranslate"]="libretranslate:5000"
|
||||
["paddleocr"]="paddleocr:8080"
|
||||
["docling"]="docling:5001"
|
||||
)
|
||||
|
||||
# Services that need basic auth (format: USERNAME_VAR:PASSWORD_HASH_VAR)
|
||||
declare -A auth_services=(
|
||||
["prometheus"]="PROMETHEUS_USERNAME:PROMETHEUS_PASSWORD_HASH"
|
||||
["ragapp"]="RAGAPP_USERNAME:RAGAPP_PASSWORD_HASH"
|
||||
["comfyui"]="COMFYUI_USERNAME:COMFYUI_PASSWORD_HASH"
|
||||
["welcome"]="WELCOME_USERNAME:WELCOME_PASSWORD_HASH"
|
||||
["libretranslate"]="LT_USERNAME:LT_PASSWORD_HASH"
|
||||
["paddleocr"]="PADDLEOCR_USERNAME:PADDLEOCR_PASSWORD_HASH"
|
||||
["docling"]="DOCLING_USERNAME:DOCLING_PASSWORD_HASH"
|
||||
)
|
||||
|
||||
for service_entry in "${services[@]}"; do
|
||||
local service="${service_entry%%:*}"
|
||||
local hostname="${service_entry#*:}"
|
||||
local proxy="${proxy_map[$service]:-}"
|
||||
|
||||
[[ -z "$proxy" ]] && continue
|
||||
|
||||
echo "# $service" >> "$ADDON_FILE"
|
||||
echo "$hostname {" >> "$ADDON_FILE"
|
||||
echo " import custom_tls" >> "$ADDON_FILE"
|
||||
|
||||
# Add basic auth if needed
|
||||
if [[ -n "${auth_services[$service]:-}" ]]; then
|
||||
local auth_config="${auth_services[$service]}"
|
||||
local username_var="${auth_config%%:*}"
|
||||
local password_hash_var="${auth_config#*:}"
|
||||
echo " basic_auth {" >> "$ADDON_FILE"
|
||||
echo " {\$${username_var}} {\$${password_hash_var}}" >> "$ADDON_FILE"
|
||||
echo " }" >> "$ADDON_FILE"
|
||||
fi
|
||||
|
||||
# Add reverse proxy or file server
|
||||
if [[ "$proxy" == "file_server" ]]; then
|
||||
echo " root * /srv/welcome" >> "$ADDON_FILE"
|
||||
echo " file_server" >> "$ADDON_FILE"
|
||||
echo " try_files {path} /index.html" >> "$ADDON_FILE"
|
||||
else
|
||||
echo " reverse_proxy $proxy" >> "$ADDON_FILE"
|
||||
fi
|
||||
|
||||
echo "}" >> "$ADDON_FILE"
|
||||
echo "" >> "$ADDON_FILE"
|
||||
done
|
||||
|
||||
log_success "Generated $ADDON_FILE"
|
||||
log_success "Generated $SNIPPET_FILE"
|
||||
}
|
||||
|
||||
remove_config() {
|
||||
if [[ -f "$ADDON_FILE" ]]; then
|
||||
rm -f "$ADDON_FILE"
|
||||
log_success "Removed custom TLS configuration"
|
||||
else
|
||||
log_info "No custom TLS configuration found"
|
||||
fi
|
||||
cat > "$SNIPPET_FILE" << 'EOF'
|
||||
# TLS Configuration Snippet
|
||||
# Imported by all service blocks in the main Caddyfile.
|
||||
#
|
||||
# Default: Empty (uses Let's Encrypt automatic certificates)
|
||||
# Custom: Overwritten by 'make setup-tls' with your certificate paths
|
||||
# Reset: Run 'make setup-tls --remove' to restore Let's Encrypt
|
||||
|
||||
(service_tls) {
|
||||
# Default: Let's Encrypt automatic certificates (empty = no override)
|
||||
}
|
||||
EOF
|
||||
|
||||
log_success "Reset to Let's Encrypt (automatic certificates)"
|
||||
}
|
||||
|
||||
restart_caddy() {
|
||||
@@ -250,12 +160,19 @@ main() {
|
||||
exit 0
|
||||
;;
|
||||
--remove)
|
||||
cleanup_legacy_config
|
||||
remove_config
|
||||
restart_caddy
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Clean up legacy config that causes duplicate hosts
|
||||
cleanup_legacy_config
|
||||
|
||||
# Ensure snippet file exists (survives git updates)
|
||||
ensure_snippet_exists
|
||||
|
||||
# Ensure certs directory exists
|
||||
mkdir -p "$CERTS_DIR"
|
||||
|
||||
@@ -319,29 +236,16 @@ main() {
|
||||
log_info "Using certificate: $cert_file"
|
||||
log_info "Using key: $key_file"
|
||||
|
||||
# Get active services
|
||||
local services_arr
|
||||
IFS=' ' read -ra services_arr <<< "$(get_active_services)"
|
||||
|
||||
if [[ ${#services_arr[@]} -eq 0 ]]; then
|
||||
log_warning "No services with configured hostnames found in .env"
|
||||
log_info "Make sure to update *_HOSTNAME variables in .env with your domain"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Found ${#services_arr[@]} services with configured hostnames"
|
||||
# Ensure certificate files are readable by Caddy container
|
||||
# (Docker volume mounts preserve host permissions, Caddy may run as different UID)
|
||||
chmod 644 "$CERTS_DIR/$cert_file" "$CERTS_DIR/$key_file"
|
||||
|
||||
# Generate configuration
|
||||
generate_config "$cert_file" "$key_file" "${services_arr[@]}"
|
||||
generate_config "$cert_file" "$key_file"
|
||||
|
||||
# Show summary
|
||||
echo ""
|
||||
log_info "Configuration generated for the following services:"
|
||||
for service_entry in "${services_arr[@]}"; do
|
||||
local service="${service_entry%%:*}"
|
||||
local hostname="${service_entry#*:}"
|
||||
echo " - $service: $hostname"
|
||||
done
|
||||
log_info "Custom TLS configured successfully!"
|
||||
log_info "All services will use: /etc/caddy/certs/$cert_file"
|
||||
echo ""
|
||||
|
||||
# Restart Caddy
|
||||
|
||||
@@ -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>`
|
||||
};
|
||||
|
||||
@@ -335,6 +340,14 @@
|
||||
category: 'tools',
|
||||
docsUrl: 'https://docs.postiz.com'
|
||||
},
|
||||
'temporal-ui': {
|
||||
name: 'Temporal UI',
|
||||
description: 'Postiz Workflow Orchestration',
|
||||
icon: 'TM',
|
||||
color: 'bg-violet-500',
|
||||
category: 'tools',
|
||||
docsUrl: 'https://docs.temporal.io/'
|
||||
},
|
||||
'waha': {
|
||||
name: 'WAHA',
|
||||
description: 'WhatsApp HTTP API',
|
||||
@@ -415,6 +428,8 @@
|
||||
{ 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' },
|
||||
@@ -842,6 +857,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');
|
||||
|
||||
/**
|
||||
@@ -957,6 +973,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
|
||||
*/
|
||||
@@ -982,14 +1018,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) {
|
||||
@@ -1007,9 +1055,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