14 Commits

Author SHA1 Message Date
Yury Kossakovsky
a99676e3d5 fix(postiz): improve temporal integration
- increase elasticsearch memory to 512mb
- add temporal databases to initialization
- add postiz to final report
2026-01-17 19:56:29 -07:00
Yury Kossakovsky
bf7ce20f7b fix(caddy): add http block for welcome page to prevent redirect loop
when accessing welcome page through cloudflare tunnel, caddy was
redirecting http to https, causing an infinite redirect loop.
adding an explicit http block prevents automatic https redirect.
2026-01-17 19:42:50 -07:00
Yury Kossakovsky
36717a45c9 docs(readme): clarify vps requirement in prerequisites 2026-01-17 12:28:55 -07:00
Yury Kossakovsky
31b81b71a4 fix(postiz): add elasticsearch for temporal advanced visibility
temporal with sql visibility has a hard limit of 3 text search
attributes per namespace. postiz requires more, causing startup
failure. adding elasticsearch enables advanced visibility mode
which removes this limitation.
2026-01-17 12:26:40 -07:00
Yury Kossakovsky
a3e8f26925 fix(postiz): use correct temporal address env var 2026-01-16 20:27:06 -07:00
Yury Kossakovsky
917afe615c fix(temporal): use container ip for healthcheck connection 2026-01-16 20:15:03 -07:00
Yury Kossakovsky
641fd04290 fix(temporal): update healthcheck to use modern cli 2026-01-16 18:59:37 -07:00
Yury Kossakovsky
ca43e7ab12 docs(readme): add troubleshooting for update script issues 2026-01-16 18:48:31 -07:00
Yury Kossakovsky
e5db00098a refactor(docker-compose): extract logging config into yaml anchor 2026-01-16 18:45:30 -07:00
Yury Kossakovsky
4a6f1c0e01 feat(postiz): add temporal server for workflow orchestration
add temporal and temporal-ui services to the postiz profile for
workflow orchestration. includes caddy reverse proxy with basic
auth, secret generation, and welcome page integration.
2026-01-16 18:42:54 -07:00
Yury Kossakovsky
19cd6b6f91 docs(cloudflare): update tunnel instructions and add missing services
- update dashboard navigation to match current cloudflare ui
- add nocodb and welcome page to services table
- add notes explaining external compose files and caddy-served content
2026-01-13 08:40:36 -07:00
Yury Kossakovsky
b28093b5cd feat(welcome): add changelog section to dashboard 2026-01-12 10:03:46 -07:00
Yury Kossakovsky
361a726a07 docs(changelog): update v1.1.0 release date 2026-01-11 13:10:32 -07:00
Yury Kossakovsky
0b4c9d5dda feat(makefile): add stop and start commands for service control 2026-01-10 11:02:23 -07:00
16 changed files with 300 additions and 68 deletions

View File

@@ -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

View File

@@ -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
############
# 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
View File

@@ -12,6 +12,7 @@ volumes/
docker-compose.override.yml
docker-compose.n8n-workers.yml
welcome/data.json
welcome/changelog.json
# Custom TLS certificates
certs/*

View File

@@ -1,16 +1,22 @@
# 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.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 +224,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).

View File

@@ -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

View File

@@ -81,6 +81,14 @@
reverse_proxy postiz:5000
}
# Temporal UI (workflow orchestration for Postiz)
{$TEMPORAL_UI_HOSTNAME} {
basic_auth {
{$TEMPORAL_UI_USERNAME} {$TEMPORAL_UI_PASSWORD_HASH}
}
reverse_proxy temporal-ui:8080
}
# Databasus
{$DATABASUS_HOSTNAME} {
reverse_proxy databasus:4005
@@ -154,6 +162,17 @@ https://{$NEO4J_HOSTNAME}:7687 {
}
# Welcome Page (Post-install dashboard)
# HTTP block for Cloudflare Tunnel access (prevents redirect loop)
http://{$WELCOME_HOSTNAME} {
basic_auth {
{$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}
}
root * /srv/welcome
file_server
try_files {path} /index.html
}
# HTTPS block for direct access
{$WELCOME_HOSTNAME} {
basic_auth {
{$WELCOME_USERNAME} {$WELCOME_PASSWORD_HASH}

View File

@@ -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/'); \

View File

@@ -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:

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -30,6 +30,8 @@ INIT_DB_DATABASES=(
"lightrag"
"nocodb"
"postiz"
"temporal"
"temporal_visibility"
"waha"
)

View File

@@ -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

View File

@@ -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();

View File

@@ -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">