Harden install pipeline: health checks, log rotation, backup timer
- install.sh: Add Docker daemon check, 10GB disk space pre-flight, error handling on pull/up, post-startup health polling with crash detection, cleanup trap on failure - docker-compose: Fix nginx/listmonk depends_on to service_healthy, add x-logging anchor (10m/3 files) to all ~39 services - config.sh: Preserve existing secrets on re-run (reconfigure mode), add automated daily backup timer (systemd, 02:00, 30-day retention) - mirror-images.sh: Fix gotify source tag (2.9.0 not v2.9.0) - build-release.sh: Ensure mkdocs/docs and mkdocs/overrides dirs exist - .env.example: Add COMPOSE_PROFILES variable Bunker Admin
This commit is contained in:
parent
3262d92065
commit
7287328148
@ -179,6 +179,10 @@ VIDEO_PREVIEW_LINK_EXPIRY_HOURS=24
|
|||||||
# Leave IMAGE_TAG blank/unset (defaults to 'local') to build locally from source.
|
# Leave IMAGE_TAG blank/unset (defaults to 'local') to build locally from source.
|
||||||
GITEA_REGISTRY=gitea.bnkops.com/admin
|
GITEA_REGISTRY=gitea.bnkops.com/admin
|
||||||
IMAGE_TAG=
|
IMAGE_TAG=
|
||||||
|
|
||||||
|
# Docker Compose profiles — set to 'monitoring' to include Prometheus/Grafana/Alertmanager
|
||||||
|
# in every 'docker compose up -d'. Leave blank to start monitoring separately.
|
||||||
|
COMPOSE_PROFILES=
|
||||||
# Credentials used by the registry status API endpoint (GET /api/registry/status)
|
# Credentials used by the registry status API endpoint (GET /api/registry/status)
|
||||||
# For docker push/pull, run: docker login gitea.bnkops.com
|
# For docker push/pull, run: docker login gitea.bnkops.com
|
||||||
GITEA_REGISTRY_USER=admin
|
GITEA_REGISTRY_USER=admin
|
||||||
|
|||||||
262
config.sh
262
config.sh
@ -180,6 +180,8 @@ initialize_env() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
RECONFIGURE_MODE=false
|
||||||
|
|
||||||
if [[ -f "$ENV_FILE" ]]; then
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
warn "Existing .env file found at $ENV_FILE"
|
warn "Existing .env file found at $ENV_FILE"
|
||||||
if prompt_yes_no "Back up existing .env and create a fresh one?"; then
|
if prompt_yes_no "Back up existing .env and create a fresh one?"; then
|
||||||
@ -187,7 +189,8 @@ initialize_env() {
|
|||||||
cp "$ENV_EXAMPLE" "$ENV_FILE"
|
cp "$ENV_EXAMPLE" "$ENV_FILE"
|
||||||
success "Created fresh .env from .env.example"
|
success "Created fresh .env from .env.example"
|
||||||
else
|
else
|
||||||
info "Keeping existing .env. Will update values in place."
|
info "Keeping existing .env. Existing secrets will be preserved."
|
||||||
|
RECONFIGURE_MODE=true
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
cp "$ENV_EXAMPLE" "$ENV_FILE"
|
cp "$ENV_EXAMPLE" "$ENV_FILE"
|
||||||
@ -283,12 +286,37 @@ configure_admin() {
|
|||||||
success "Admin credentials configured"
|
success "Admin credentials configured"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Check if a key already has a real value in .env (non-empty, not a placeholder)
|
||||||
|
env_var_is_set() {
|
||||||
|
local key=$1
|
||||||
|
local val
|
||||||
|
val=$(grep "^${key}=" "$ENV_FILE" 2>/dev/null | head -1 | cut -d= -f2-)
|
||||||
|
[[ -n "$val" && "$val" != "changeme" && "$val" != *"example"* && "$val" != *"CHANGEME"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update an env var only if not already set (for reconfigure mode)
|
||||||
|
update_env_var_if_empty() {
|
||||||
|
local key=$1
|
||||||
|
local value=$2
|
||||||
|
if [[ "$RECONFIGURE_MODE" == "true" ]] && env_var_is_set "$key"; then
|
||||||
|
return 1 # signal: kept existing
|
||||||
|
fi
|
||||||
|
update_env_var "$key" "$value"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
generate_all_secrets() {
|
generate_all_secrets() {
|
||||||
header "Generating Secrets"
|
header "Generating Secrets"
|
||||||
|
|
||||||
info "Auto-generating 22 unique secrets and passwords..."
|
if [[ "$RECONFIGURE_MODE" == "true" ]]; then
|
||||||
|
info "Reconfigure mode: existing secrets will be preserved."
|
||||||
|
else
|
||||||
|
info "Auto-generating 22 unique secrets and passwords..."
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
local generated=0 kept=0
|
||||||
|
|
||||||
# JWT & Encryption (64-char hex)
|
# JWT & Encryption (64-char hex)
|
||||||
local jwt_access jwt_refresh jwt_invite enc_key
|
local jwt_access jwt_refresh jwt_invite enc_key
|
||||||
jwt_access=$(generate_secret)
|
jwt_access=$(generate_secret)
|
||||||
@ -296,22 +324,41 @@ generate_all_secrets() {
|
|||||||
jwt_invite=$(generate_secret)
|
jwt_invite=$(generate_secret)
|
||||||
enc_key=$(generate_secret)
|
enc_key=$(generate_secret)
|
||||||
|
|
||||||
update_env_var "JWT_ACCESS_SECRET" "$jwt_access"
|
local jwt_changed=false
|
||||||
update_env_var "JWT_REFRESH_SECRET" "$jwt_refresh"
|
update_env_var_if_empty "JWT_ACCESS_SECRET" "$jwt_access" && jwt_changed=true
|
||||||
update_env_var "JWT_INVITE_SECRET" "$jwt_invite"
|
update_env_var_if_empty "JWT_REFRESH_SECRET" "$jwt_refresh" && jwt_changed=true
|
||||||
update_env_var "ENCRYPTION_KEY" "$enc_key"
|
update_env_var_if_empty "JWT_INVITE_SECRET" "$jwt_invite" && jwt_changed=true
|
||||||
success "JWT secrets + encryption key"
|
update_env_var_if_empty "ENCRYPTION_KEY" "$enc_key" && jwt_changed=true
|
||||||
|
if [[ "$jwt_changed" == "true" ]]; then
|
||||||
|
success "JWT secrets + encryption key"
|
||||||
|
((generated+=4))
|
||||||
|
else
|
||||||
|
info "JWT secrets + encryption key (kept existing)"
|
||||||
|
((kept+=4))
|
||||||
|
fi
|
||||||
|
|
||||||
# Database passwords (24-char alphanum)
|
# Database passwords (24-char alphanum)
|
||||||
local pg_pass redis_pass
|
local pg_pass redis_pass
|
||||||
pg_pass=$(generate_password 24)
|
pg_pass=$(generate_password 24)
|
||||||
redis_pass=$(generate_password 24)
|
redis_pass=$(generate_password 24)
|
||||||
|
|
||||||
update_env_var "V2_POSTGRES_PASSWORD" "$pg_pass"
|
local db_changed=false
|
||||||
update_env_var "DATABASE_URL" "postgresql://changemaker:${pg_pass}@localhost:5433/changemaker_v2"
|
if update_env_var_if_empty "V2_POSTGRES_PASSWORD" "$pg_pass"; then
|
||||||
update_env_var "REDIS_PASSWORD" "$redis_pass"
|
update_env_var "DATABASE_URL" "postgresql://changemaker:${pg_pass}@localhost:5433/changemaker_v2"
|
||||||
update_env_var "REDIS_URL" "redis://:${redis_pass}@redis-changemaker:6379"
|
db_changed=true
|
||||||
success "PostgreSQL + Redis passwords"
|
fi
|
||||||
|
update_env_var_if_empty "REDIS_PASSWORD" "$redis_pass" && db_changed=true
|
||||||
|
if [[ "$db_changed" == "true" ]]; then
|
||||||
|
# Rebuild REDIS_URL if password changed
|
||||||
|
local current_redis_pass
|
||||||
|
current_redis_pass=$(grep "^REDIS_PASSWORD=" "$ENV_FILE" | cut -d= -f2-)
|
||||||
|
update_env_var "REDIS_URL" "redis://:${current_redis_pass}@redis-changemaker:6379"
|
||||||
|
success "PostgreSQL + Redis passwords"
|
||||||
|
((generated+=2))
|
||||||
|
else
|
||||||
|
info "PostgreSQL + Redis passwords (kept existing)"
|
||||||
|
((kept+=2))
|
||||||
|
fi
|
||||||
|
|
||||||
# Listmonk
|
# Listmonk
|
||||||
local lm_db_pass lm_web_pass lm_api_token
|
local lm_db_pass lm_web_pass lm_api_token
|
||||||
@ -319,72 +366,133 @@ generate_all_secrets() {
|
|||||||
lm_web_pass=$(generate_password 20)
|
lm_web_pass=$(generate_password 20)
|
||||||
lm_api_token=$(openssl rand -hex 16)
|
lm_api_token=$(openssl rand -hex 16)
|
||||||
|
|
||||||
update_env_var "LISTMONK_DB_PASSWORD" "$lm_db_pass"
|
local lm_changed=false
|
||||||
update_env_var "LISTMONK_WEB_ADMIN_PASSWORD" "$lm_web_pass"
|
update_env_var_if_empty "LISTMONK_DB_PASSWORD" "$lm_db_pass" && lm_changed=true
|
||||||
update_env_var "LISTMONK_API_TOKEN" "$lm_api_token"
|
update_env_var_if_empty "LISTMONK_WEB_ADMIN_PASSWORD" "$lm_web_pass" && lm_changed=true
|
||||||
update_env_var "LISTMONK_ADMIN_PASSWORD" "$lm_api_token"
|
if update_env_var_if_empty "LISTMONK_API_TOKEN" "$lm_api_token"; then
|
||||||
success "Listmonk passwords + API token"
|
update_env_var "LISTMONK_ADMIN_PASSWORD" "$lm_api_token"
|
||||||
|
lm_changed=true
|
||||||
|
fi
|
||||||
|
if [[ "$lm_changed" == "true" ]]; then
|
||||||
|
success "Listmonk passwords + API token"
|
||||||
|
((generated+=3))
|
||||||
|
else
|
||||||
|
info "Listmonk passwords + API token (kept existing)"
|
||||||
|
((kept+=3))
|
||||||
|
fi
|
||||||
|
|
||||||
# NocoDB
|
# NocoDB
|
||||||
local nc_pass
|
local nc_pass
|
||||||
nc_pass=$(generate_password 20)
|
nc_pass=$(generate_password 20)
|
||||||
update_env_var "NC_ADMIN_PASSWORD" "$nc_pass"
|
if update_env_var_if_empty "NC_ADMIN_PASSWORD" "$nc_pass"; then
|
||||||
success "NocoDB admin password"
|
success "NocoDB admin password"
|
||||||
|
((generated++))
|
||||||
|
else
|
||||||
|
info "NocoDB admin password (kept existing)"
|
||||||
|
((kept++))
|
||||||
|
fi
|
||||||
|
|
||||||
# Gitea
|
# Gitea
|
||||||
local gitea_db gitea_root
|
local gitea_db gitea_root
|
||||||
gitea_db=$(generate_password 20)
|
gitea_db=$(generate_password 20)
|
||||||
gitea_root=$(generate_password 20)
|
gitea_root=$(generate_password 20)
|
||||||
update_env_var "GITEA_DB_PASSWD" "$gitea_db"
|
local gitea_changed=false
|
||||||
update_env_var "GITEA_DB_ROOT_PASSWORD" "$gitea_root"
|
update_env_var_if_empty "GITEA_DB_PASSWD" "$gitea_db" && gitea_changed=true
|
||||||
success "Gitea database passwords"
|
update_env_var_if_empty "GITEA_DB_ROOT_PASSWORD" "$gitea_root" && gitea_changed=true
|
||||||
|
if [[ "$gitea_changed" == "true" ]]; then
|
||||||
|
success "Gitea database passwords"
|
||||||
|
((generated+=2))
|
||||||
|
else
|
||||||
|
info "Gitea database passwords (kept existing)"
|
||||||
|
((kept+=2))
|
||||||
|
fi
|
||||||
|
|
||||||
# n8n
|
# n8n
|
||||||
local n8n_enc n8n_pass
|
local n8n_enc n8n_pass
|
||||||
n8n_enc=$(generate_password 32)
|
n8n_enc=$(generate_password 32)
|
||||||
n8n_pass=$(generate_password 20)
|
n8n_pass=$(generate_password 20)
|
||||||
update_env_var "N8N_ENCRYPTION_KEY" "$n8n_enc"
|
local n8n_changed=false
|
||||||
update_env_var "N8N_USER_PASSWORD" "$n8n_pass"
|
update_env_var_if_empty "N8N_ENCRYPTION_KEY" "$n8n_enc" && n8n_changed=true
|
||||||
success "n8n encryption key + admin password"
|
update_env_var_if_empty "N8N_USER_PASSWORD" "$n8n_pass" && n8n_changed=true
|
||||||
|
if [[ "$n8n_changed" == "true" ]]; then
|
||||||
|
success "n8n encryption key + admin password"
|
||||||
|
((generated+=2))
|
||||||
|
else
|
||||||
|
info "n8n encryption key + admin password (kept existing)"
|
||||||
|
((kept+=2))
|
||||||
|
fi
|
||||||
|
|
||||||
# Monitoring
|
# Monitoring
|
||||||
local grafana_pass gotify_pass
|
local grafana_pass gotify_pass
|
||||||
grafana_pass=$(generate_password 20)
|
grafana_pass=$(generate_password 20)
|
||||||
gotify_pass=$(generate_password 20)
|
gotify_pass=$(generate_password 20)
|
||||||
update_env_var "GRAFANA_ADMIN_PASSWORD" "$grafana_pass"
|
local mon_changed=false
|
||||||
update_env_var "GOTIFY_ADMIN_PASSWORD" "$gotify_pass"
|
update_env_var_if_empty "GRAFANA_ADMIN_PASSWORD" "$grafana_pass" && mon_changed=true
|
||||||
success "Grafana + Gotify admin passwords"
|
update_env_var_if_empty "GOTIFY_ADMIN_PASSWORD" "$gotify_pass" && mon_changed=true
|
||||||
|
if [[ "$mon_changed" == "true" ]]; then
|
||||||
|
success "Grafana + Gotify admin passwords"
|
||||||
|
((generated+=2))
|
||||||
|
else
|
||||||
|
info "Grafana + Gotify admin passwords (kept existing)"
|
||||||
|
((kept+=2))
|
||||||
|
fi
|
||||||
|
|
||||||
# Vaultwarden
|
# Vaultwarden
|
||||||
local vw_admin_token
|
local vw_admin_token
|
||||||
vw_admin_token=$(generate_secret)
|
vw_admin_token=$(generate_secret)
|
||||||
update_env_var "VAULTWARDEN_ADMIN_TOKEN" "$vw_admin_token"
|
if update_env_var_if_empty "VAULTWARDEN_ADMIN_TOKEN" "$vw_admin_token"; then
|
||||||
success "Vaultwarden admin token"
|
success "Vaultwarden admin token"
|
||||||
|
((generated++))
|
||||||
|
else
|
||||||
|
info "Vaultwarden admin token (kept existing)"
|
||||||
|
((kept++))
|
||||||
|
fi
|
||||||
|
|
||||||
# Rocket.Chat
|
# Rocket.Chat
|
||||||
local rc_pass
|
local rc_pass
|
||||||
rc_pass=$(generate_password 20)
|
rc_pass=$(generate_password 20)
|
||||||
update_env_var "ROCKETCHAT_ADMIN_PASSWORD" "$rc_pass"
|
if update_env_var_if_empty "ROCKETCHAT_ADMIN_PASSWORD" "$rc_pass"; then
|
||||||
success "Rocket.Chat admin password"
|
success "Rocket.Chat admin password"
|
||||||
|
((generated++))
|
||||||
|
else
|
||||||
|
info "Rocket.Chat admin password (kept existing)"
|
||||||
|
((kept++))
|
||||||
|
fi
|
||||||
|
|
||||||
# Gancio
|
# Gancio
|
||||||
local gancio_pass
|
local gancio_pass
|
||||||
gancio_pass=$(generate_password 20)
|
gancio_pass=$(generate_password 20)
|
||||||
update_env_var "GANCIO_ADMIN_PASSWORD" "$gancio_pass"
|
if update_env_var_if_empty "GANCIO_ADMIN_PASSWORD" "$gancio_pass"; then
|
||||||
success "Gancio admin password"
|
success "Gancio admin password"
|
||||||
|
((generated++))
|
||||||
|
else
|
||||||
|
info "Gancio admin password (kept existing)"
|
||||||
|
((kept++))
|
||||||
|
fi
|
||||||
|
|
||||||
# Jitsi Meet
|
# Jitsi Meet
|
||||||
local jitsi_secret jitsi_jicofo jitsi_jvb
|
local jitsi_secret jitsi_jicofo jitsi_jvb
|
||||||
jitsi_secret=$(generate_secret)
|
jitsi_secret=$(generate_secret)
|
||||||
jitsi_jicofo=$(openssl rand -hex 16)
|
jitsi_jicofo=$(openssl rand -hex 16)
|
||||||
jitsi_jvb=$(openssl rand -hex 16)
|
jitsi_jvb=$(openssl rand -hex 16)
|
||||||
update_env_var "JITSI_APP_SECRET" "$jitsi_secret"
|
local jitsi_changed=false
|
||||||
update_env_var "JITSI_JICOFO_AUTH_PASSWORD" "$jitsi_jicofo"
|
update_env_var_if_empty "JITSI_APP_SECRET" "$jitsi_secret" && jitsi_changed=true
|
||||||
update_env_var "JITSI_JVB_AUTH_PASSWORD" "$jitsi_jvb"
|
update_env_var_if_empty "JITSI_JICOFO_AUTH_PASSWORD" "$jitsi_jicofo" && jitsi_changed=true
|
||||||
success "Jitsi Meet secrets (JWT + XMPP)"
|
update_env_var_if_empty "JITSI_JVB_AUTH_PASSWORD" "$jitsi_jvb" && jitsi_changed=true
|
||||||
|
if [[ "$jitsi_changed" == "true" ]]; then
|
||||||
|
success "Jitsi Meet secrets (JWT + XMPP)"
|
||||||
|
((generated+=3))
|
||||||
|
else
|
||||||
|
info "Jitsi Meet secrets (kept existing)"
|
||||||
|
((kept+=3))
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
success "All 21 secrets generated. No placeholder passwords remain."
|
if [[ $kept -gt 0 ]]; then
|
||||||
|
success "Secrets: ${generated} generated, ${kept} preserved from existing .env"
|
||||||
|
else
|
||||||
|
success "All 22 secrets generated. No placeholder passwords remain."
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
configure_smtp() {
|
configure_smtp() {
|
||||||
@ -519,6 +627,14 @@ configure_features() {
|
|||||||
DOCS_COMMENTS_ENABLED="no"
|
DOCS_COMMENTS_ENABLED="no"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if prompt_yes_no "Enable Monitoring stack (Prometheus, Grafana, Alertmanager, cAdvisor)?" "y"; then
|
||||||
|
update_env_var "COMPOSE_PROFILES" "monitoring"
|
||||||
|
success "Monitoring enabled (COMPOSE_PROFILES=monitoring)"
|
||||||
|
MONITORING_ENABLED="yes"
|
||||||
|
else
|
||||||
|
MONITORING_ENABLED="no"
|
||||||
|
fi
|
||||||
|
|
||||||
if prompt_yes_no "Enable Bunker Ops (fleet metrics push to central server)?"; then
|
if prompt_yes_no "Enable Bunker Ops (fleet metrics push to central server)?"; then
|
||||||
update_env_var "BUNKER_OPS_ENABLED" "true"
|
update_env_var "BUNKER_OPS_ENABLED" "true"
|
||||||
success "Bunker Ops enabled"
|
success "Bunker Ops enabled"
|
||||||
@ -994,16 +1110,21 @@ fix_container_permissions() {
|
|||||||
local -a dirs=(
|
local -a dirs=(
|
||||||
"configs/code-server/.config:Code Server config"
|
"configs/code-server/.config:Code Server config"
|
||||||
"configs/code-server/.local:Code Server local data"
|
"configs/code-server/.local:Code Server local data"
|
||||||
|
"mkdocs:MkDocs root"
|
||||||
|
"mkdocs/docs:MkDocs source docs"
|
||||||
|
"mkdocs/overrides:MkDocs template overrides"
|
||||||
"mkdocs/.cache:MkDocs cache"
|
"mkdocs/.cache:MkDocs cache"
|
||||||
"mkdocs/site:MkDocs built site"
|
"mkdocs/site:MkDocs built site"
|
||||||
"assets/uploads:Listmonk uploads"
|
"assets/uploads:Listmonk uploads"
|
||||||
"assets/images:Shared images"
|
"assets/images:Shared images"
|
||||||
"assets/icons:Homepage icons"
|
"assets/icons:Homepage icons"
|
||||||
"media/local/inbox:Media upload inbox"
|
"media/local/inbox:Media upload inbox"
|
||||||
|
"media/local/photos:Media photos"
|
||||||
"media/local/thumbnails:Video thumbnails"
|
"media/local/thumbnails:Video thumbnails"
|
||||||
"media/public:Public media files"
|
"media/public:Public media files"
|
||||||
"local-files:n8n local files"
|
"local-files:n8n local files"
|
||||||
"data:NAR import data"
|
"data:NAR import data"
|
||||||
|
"data/upgrade:Upgrade trigger directory"
|
||||||
)
|
)
|
||||||
|
|
||||||
local errors=0
|
local errors=0
|
||||||
@ -1085,6 +1206,59 @@ install_upgrade_watcher() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Automated Backups (systemd)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
install_backup_timer() {
|
||||||
|
header "Automated Backups"
|
||||||
|
|
||||||
|
info "Daily automated backups protect against data loss."
|
||||||
|
info "Backs up PostgreSQL databases + uploads to ./backups/ with 30-day retention."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local unit_src="$SCRIPT_DIR/scripts/systemd"
|
||||||
|
if [[ ! -f "$unit_src/changemaker-backup.timer" ]] || [[ ! -f "$unit_src/changemaker-backup.service" ]]; then
|
||||||
|
warn "Systemd backup unit templates not found in scripts/systemd/ — skipping"
|
||||||
|
BACKUP_TIMER="skipped"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v systemctl &>/dev/null; then
|
||||||
|
warn "systemctl not found — skipping (not a systemd host?)"
|
||||||
|
BACKUP_TIMER="skipped"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if prompt_yes_no "Install daily automated backups (requires sudo)?" "y"; then
|
||||||
|
local tmp_timer tmp_service
|
||||||
|
tmp_timer=$(mktemp)
|
||||||
|
tmp_service=$(mktemp)
|
||||||
|
|
||||||
|
sed -e "s|__PROJECT_DIR__|$SCRIPT_DIR|g" "$unit_src/changemaker-backup.timer" > "$tmp_timer"
|
||||||
|
sed -e "s|__PROJECT_DIR__|$SCRIPT_DIR|g" -e "s|__USER__|$(whoami)|g" "$unit_src/changemaker-backup.service" > "$tmp_service"
|
||||||
|
|
||||||
|
if sudo cp "$tmp_timer" /etc/systemd/system/changemaker-backup.timer \
|
||||||
|
&& sudo cp "$tmp_service" /etc/systemd/system/changemaker-backup.service \
|
||||||
|
&& sudo systemctl daemon-reload \
|
||||||
|
&& sudo systemctl enable --now changemaker-backup.timer; then
|
||||||
|
success "Daily backup timer installed and enabled (runs at 02:00)"
|
||||||
|
BACKUP_TIMER="yes"
|
||||||
|
else
|
||||||
|
warn "Failed to install systemd units (sudo may have failed)"
|
||||||
|
warn "Install manually later:"
|
||||||
|
echo -e " ${CYAN}sudo cp scripts/systemd/changemaker-backup.* /etc/systemd/system/${NC}"
|
||||||
|
echo -e " ${CYAN}sudo systemctl daemon-reload && sudo systemctl enable --now changemaker-backup.timer${NC}"
|
||||||
|
BACKUP_TIMER="manual"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$tmp_timer" "$tmp_service"
|
||||||
|
else
|
||||||
|
info "Skipped. Run backups manually: ./scripts/backup.sh"
|
||||||
|
BACKUP_TIMER="skipped"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Summary & Next Steps
|
# Summary & Next Steps
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@ -1103,10 +1277,12 @@ print_summary() {
|
|||||||
echo -e " ${BOLD}Gancio sync:${NC} ${GANCIO_SYNC:-no}"
|
echo -e " ${BOLD}Gancio sync:${NC} ${GANCIO_SYNC:-no}"
|
||||||
echo -e " ${BOLD}Jitsi Meet:${NC} ${MEET_ENABLED:-no}"
|
echo -e " ${BOLD}Jitsi Meet:${NC} ${MEET_ENABLED:-no}"
|
||||||
echo -e " ${BOLD}SMS Campaigns:${NC} ${SMS_ENABLED:-no}"
|
echo -e " ${BOLD}SMS Campaigns:${NC} ${SMS_ENABLED:-no}"
|
||||||
|
echo -e " ${BOLD}Monitoring:${NC} ${MONITORING_ENABLED:-no}"
|
||||||
echo -e " ${BOLD}Docs Comments:${NC} ${DOCS_COMMENTS_ENABLED:-no}"
|
echo -e " ${BOLD}Docs Comments:${NC} ${DOCS_COMMENTS_ENABLED:-no}"
|
||||||
echo -e " ${BOLD}Bunker Ops:${NC} ${BUNKER_OPS_ENABLED:-no}"
|
echo -e " ${BOLD}Bunker Ops:${NC} ${BUNKER_OPS_ENABLED:-no}"
|
||||||
echo -e " ${BOLD}Pangolin:${NC} ${PANGOLIN_CONFIGURED:-no}"
|
echo -e " ${BOLD}Pangolin:${NC} ${PANGOLIN_CONFIGURED:-no}"
|
||||||
echo -e " ${BOLD}Upgrade watcher:${NC} ${UPGRADE_WATCHER:-skipped}"
|
echo -e " ${BOLD}Upgrade watcher:${NC} ${UPGRADE_WATCHER:-skipped}"
|
||||||
|
echo -e " ${BOLD}Backup timer:${NC} ${BACKUP_TIMER:-skipped}"
|
||||||
echo -e " ${BOLD}Secrets:${NC} 22 auto-generated"
|
echo -e " ${BOLD}Secrets:${NC} 22 auto-generated"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${DIM}Config file: $ENV_FILE${NC}"
|
echo -e " ${DIM}Config file: $ENV_FILE${NC}"
|
||||||
@ -1155,9 +1331,8 @@ print_next_steps() {
|
|||||||
echo -e " ${CYAN}docker compose up -d rocketchat${NC} # Team chat"
|
echo -e " ${CYAN}docker compose up -d rocketchat${NC} # Team chat"
|
||||||
echo -e " ${CYAN}docker compose up -d jitsi-web jitsi-prosody jitsi-jicofo jitsi-jvb${NC} # Video calls"
|
echo -e " ${CYAN}docker compose up -d jitsi-web jitsi-prosody jitsi-jicofo jitsi-jvb${NC} # Video calls"
|
||||||
echo -e " ${CYAN}docker compose up -d homepage${NC} # Service dashboard"
|
echo -e " ${CYAN}docker compose up -d homepage${NC} # Service dashboard"
|
||||||
echo -e " ${CYAN}docker compose --profile monitoring up -d${NC} # Monitoring"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}5.${NC} Or start everything at once:"
|
echo -e " ${BOLD}5.${NC} Or start everything at once (monitoring included if enabled above):"
|
||||||
echo -e " ${CYAN}docker compose up -d${NC}"
|
echo -e " ${CYAN}docker compose up -d${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
@ -1187,12 +1362,17 @@ main() {
|
|||||||
generate_services_yaml
|
generate_services_yaml
|
||||||
fix_container_permissions
|
fix_container_permissions
|
||||||
install_upgrade_watcher
|
install_upgrade_watcher
|
||||||
|
install_backup_timer
|
||||||
|
|
||||||
# Release mode: auto-set production defaults
|
# Release mode: auto-set production defaults
|
||||||
if [[ "$INSTALL_MODE" == "release" ]]; then
|
if [[ "$INSTALL_MODE" == "release" ]]; then
|
||||||
header "Release Mode Settings"
|
header "Release Mode Settings"
|
||||||
update_env_var "IMAGE_TAG" "latest"
|
update_env_var "IMAGE_TAG" "latest"
|
||||||
update_env_var "NODE_ENV" "production"
|
update_env_var "NODE_ENV" "production"
|
||||||
|
# Ensure monitoring is included if user opted in
|
||||||
|
if [[ "${MONITORING_ENABLED:-no}" == "yes" ]]; then
|
||||||
|
update_env_var "COMPOSE_PROFILES" "monitoring"
|
||||||
|
fi
|
||||||
success "Set IMAGE_TAG=latest, NODE_ENV=production (pre-built images)"
|
success "Set IMAGE_TAG=latest, NODE_ENV=production (pre-built images)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,12 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
x-logging: &default-logging
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# V2 CORE SERVICES
|
# V2 CORE SERVICES
|
||||||
@ -129,6 +135,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -176,6 +183,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
v2-postgres:
|
v2-postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -202,6 +210,7 @@ services:
|
|||||||
- VITE_MKDOCS_SITE_PORT=${MKDOCS_SITE_SERVER_PORT:-4004}
|
- VITE_MKDOCS_SITE_PORT=${MKDOCS_SITE_SERVER_PORT:-4004}
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -225,6 +234,7 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -277,8 +287,11 @@ services:
|
|||||||
- ./public-web:/usr/share/nginx/public-web:ro
|
- ./public-web:/usr/share/nginx/public-web:ro
|
||||||
- ./configs/pangolin:/etc/pangolin:ro
|
- ./configs/pangolin:/etc/pangolin:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
api:
|
||||||
- admin
|
condition: service_healthy
|
||||||
|
admin:
|
||||||
|
condition: service_healthy
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -305,6 +318,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
v2-postgres:
|
v2-postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -330,6 +344,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./scripts/nocodb-init.sh:/init.sh:ro
|
- ./scripts/nocodb-init.sh:/init.sh:ro
|
||||||
entrypoint: ["/bin/sh", "/init.sh"]
|
entrypoint: ["/bin/sh", "/init.sh"]
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -382,7 +397,8 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
depends_on:
|
depends_on:
|
||||||
- listmonk-db
|
listmonk-db:
|
||||||
|
condition: service_healthy
|
||||||
command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"]
|
command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"]
|
||||||
environment:
|
environment:
|
||||||
LISTMONK_app__address: 0.0.0.0:9000
|
LISTMONK_app__address: 0.0.0.0:9000
|
||||||
@ -397,6 +413,7 @@ services:
|
|||||||
LISTMONK_ADMIN_PASSWORD: ${LISTMONK_WEB_ADMIN_PASSWORD:-}
|
LISTMONK_ADMIN_PASSWORD: ${LISTMONK_WEB_ADMIN_PASSWORD:-}
|
||||||
volumes:
|
volumes:
|
||||||
- ./assets/uploads:/listmonk/uploads:rw
|
- ./assets/uploads:/listmonk/uploads:rw
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -417,6 +434,7 @@ services:
|
|||||||
retries: 6
|
retries: 6
|
||||||
volumes:
|
volumes:
|
||||||
- listmonk-data:/var/lib/postgresql/data
|
- listmonk-data:/var/lib/postgresql/data
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -485,6 +503,7 @@ services:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[listmonk-init] Done"
|
echo "[listmonk-init] Done"
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -512,6 +531,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${CODE_SERVER_PORT:-8888}:8080"
|
- "127.0.0.1:${CODE_SERVER_PORT:-8888}:8080"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -540,6 +560,7 @@ services:
|
|||||||
- GANCIO_PORT=${GANCIO_PORT:-8092}
|
- GANCIO_PORT=${GANCIO_PORT:-8092}
|
||||||
entrypoint: ["/bin/sh", "/scripts/mkdocs-entrypoint.sh"]
|
entrypoint: ["/bin/sh", "/scripts/mkdocs-entrypoint.sh"]
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -557,6 +578,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${MKDOCS_SITE_SERVER_PORT:-4004}:80"
|
- "127.0.0.1:${MKDOCS_SITE_SERVER_PORT:-4004}:80"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -587,6 +609,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- n8n-data:/home/node/.n8n
|
- n8n-data:/home/node/.n8n
|
||||||
- ./local-files:/files
|
- ./local-files:/files
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -608,6 +631,7 @@ services:
|
|||||||
- HOMEPAGE_ALLOWED_HOSTS=*
|
- HOMEPAGE_ALLOWED_HOSTS=*
|
||||||
- HOMEPAGE_VAR_BASE_URL=${HOMEPAGE_VAR_BASE_URL:-http://localhost}
|
- HOMEPAGE_VAR_BASE_URL=${HOMEPAGE_VAR_BASE_URL:-http://localhost}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -649,6 +673,7 @@ services:
|
|||||||
- "127.0.0.1:${GITEA_SSH_PORT:-2222}:22"
|
- "127.0.0.1:${GITEA_SSH_PORT:-2222}:22"
|
||||||
depends_on:
|
depends_on:
|
||||||
- gitea-db
|
- gitea-db
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -668,6 +693,7 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -678,6 +704,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${MINI_QR_PORT:-8089}:8080"
|
- "127.0.0.1:${MINI_QR_PORT:-8089}:8080"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -696,6 +723,7 @@ services:
|
|||||||
start_period: 20s
|
start_period: 20s
|
||||||
environment:
|
environment:
|
||||||
- VITE_APP_COLLAB_SERVER_URL=${EXCALIDRAW_WS_URL:-wss://draw.cmlite.org}
|
- VITE_APP_COLLAB_SERVER_URL=${EXCALIDRAW_WS_URL:-wss://draw.cmlite.org}
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -728,6 +756,7 @@ services:
|
|||||||
- SMTP_PASSWORD=${SMTP_PASS:-}
|
- SMTP_PASSWORD=${SMTP_PASS:-}
|
||||||
volumes:
|
volumes:
|
||||||
- vaultwarden-data:/data
|
- vaultwarden-data:/data
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -794,6 +823,7 @@ services:
|
|||||||
|
|
||||||
rm -f "$$SESSION_COOKIE"
|
rm -f "$$SESSION_COOKIE"
|
||||||
echo "[vaultwarden-init] Done"
|
echo "[vaultwarden-init] Done"
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -834,6 +864,7 @@ services:
|
|||||||
- OVERWRITE_SETTING_VideoConf_Default_Provider=jitsi
|
- OVERWRITE_SETTING_VideoConf_Default_Provider=jitsi
|
||||||
volumes:
|
volumes:
|
||||||
- rocketchat-uploads:/app/uploads
|
- rocketchat-uploads:/app/uploads
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@ -849,6 +880,7 @@ services:
|
|||||||
container_name: nats-rocketchat
|
container_name: nats-rocketchat
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: --http_port 8222
|
command: --http_port 8222
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -860,6 +892,7 @@ services:
|
|||||||
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
|
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
|
||||||
volumes:
|
volumes:
|
||||||
- mongodb-rocketchat-data:/data/db
|
- mongodb-rocketchat-data:/data/db
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@ -897,6 +930,7 @@ services:
|
|||||||
- server__baseurl=${GANCIO_BASE_URL:-https://events.cmlite.org}
|
- server__baseurl=${GANCIO_BASE_URL:-https://events.cmlite.org}
|
||||||
volumes:
|
volumes:
|
||||||
- gancio-data:/home/node/data
|
- gancio-data:/home/node/data
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -938,6 +972,7 @@ services:
|
|||||||
ON CONFLICT (key) DO NOTHING;"
|
ON CONFLICT (key) DO NOTHING;"
|
||||||
echo "Gancio theme settings seeded."
|
echo "Gancio theme settings seeded."
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -976,6 +1011,7 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1009,6 +1045,7 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1032,6 +1069,7 @@ services:
|
|||||||
- TZ=UTC
|
- TZ=UTC
|
||||||
volumes:
|
volumes:
|
||||||
- jitsi-jicofo-config:/config
|
- jitsi-jicofo-config:/config
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1057,6 +1095,7 @@ services:
|
|||||||
- TZ=UTC
|
- TZ=UTC
|
||||||
volumes:
|
volumes:
|
||||||
- jitsi-jvb-config:/config
|
- jitsi-jvb-config:/config
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1091,6 +1130,7 @@ services:
|
|||||||
- NEWT_SECRET=${PANGOLIN_NEWT_SECRET}
|
- NEWT_SECRET=${PANGOLIN_NEWT_SECRET}
|
||||||
depends_on:
|
depends_on:
|
||||||
- nginx
|
- nginx
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1109,6 +1149,7 @@ services:
|
|||||||
- SWARM=0
|
- SWARM=0
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1129,6 +1170,7 @@ services:
|
|||||||
- ./configs/prometheus:/etc/prometheus
|
- ./configs/prometheus:/etc/prometheus
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
restart: always
|
restart: always
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1152,6 +1194,7 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- prometheus
|
- prometheus
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1173,6 +1216,7 @@ services:
|
|||||||
devices:
|
devices:
|
||||||
- /dev/kmsg
|
- /dev/kmsg
|
||||||
restart: always
|
restart: always
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1193,6 +1237,7 @@ services:
|
|||||||
- /sys:/host/sys:ro
|
- /sys:/host/sys:ro
|
||||||
- /:/rootfs:ro
|
- /:/rootfs:ro
|
||||||
restart: always
|
restart: always
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1209,6 +1254,7 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1226,6 +1272,7 @@ services:
|
|||||||
- '--config.file=/etc/alertmanager/alertmanager.yml'
|
- '--config.file=/etc/alertmanager/alertmanager.yml'
|
||||||
- '--storage.path=/alertmanager'
|
- '--storage.path=/alertmanager'
|
||||||
restart: always
|
restart: always
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1243,6 +1290,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- gotify-data:/app/data
|
- gotify-data:/app/data
|
||||||
restart: always
|
restart: always
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
|
|||||||
@ -2,6 +2,12 @@
|
|||||||
# Changemaker Lite v2 — Docker Compose
|
# Changemaker Lite v2 — Docker Compose
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
x-logging: &default-logging
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# V2 CORE SERVICES
|
# V2 CORE SERVICES
|
||||||
@ -130,6 +136,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -183,6 +190,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
v2-postgres:
|
v2-postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -215,6 +223,7 @@ services:
|
|||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -238,6 +247,7 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -292,8 +302,11 @@ services:
|
|||||||
- ./public-web:/usr/share/nginx/public-web:ro
|
- ./public-web:/usr/share/nginx/public-web:ro
|
||||||
- ./configs/pangolin:/etc/pangolin:ro
|
- ./configs/pangolin:/etc/pangolin:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
api:
|
||||||
- admin
|
condition: service_healthy
|
||||||
|
admin:
|
||||||
|
condition: service_healthy
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -320,6 +333,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
v2-postgres:
|
v2-postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -345,6 +359,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./scripts/nocodb-init.sh:/init.sh:ro
|
- ./scripts/nocodb-init.sh:/init.sh:ro
|
||||||
entrypoint: ["/bin/sh", "/init.sh"]
|
entrypoint: ["/bin/sh", "/init.sh"]
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -397,7 +412,8 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
depends_on:
|
depends_on:
|
||||||
- listmonk-db
|
listmonk-db:
|
||||||
|
condition: service_healthy
|
||||||
command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"]
|
command: [sh, -c, "./listmonk --install --idempotent --yes --config '' && ./listmonk --upgrade --yes --config '' && ./listmonk --config ''"]
|
||||||
environment:
|
environment:
|
||||||
LISTMONK_app__address: 0.0.0.0:9000
|
LISTMONK_app__address: 0.0.0.0:9000
|
||||||
@ -412,6 +428,7 @@ services:
|
|||||||
LISTMONK_ADMIN_PASSWORD: ${LISTMONK_WEB_ADMIN_PASSWORD:-}
|
LISTMONK_ADMIN_PASSWORD: ${LISTMONK_WEB_ADMIN_PASSWORD:-}
|
||||||
volumes:
|
volumes:
|
||||||
- ./assets/uploads:/listmonk/uploads:rw
|
- ./assets/uploads:/listmonk/uploads:rw
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -432,6 +449,7 @@ services:
|
|||||||
retries: 6
|
retries: 6
|
||||||
volumes:
|
volumes:
|
||||||
- listmonk-data:/var/lib/postgresql/data
|
- listmonk-data:/var/lib/postgresql/data
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -500,6 +518,7 @@ services:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[listmonk-init] Done"
|
echo "[listmonk-init] Done"
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -532,6 +551,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${CODE_SERVER_PORT:-8888}:8080"
|
- "127.0.0.1:${CODE_SERVER_PORT:-8888}:8080"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -560,6 +580,7 @@ services:
|
|||||||
- GANCIO_PORT=${GANCIO_PORT:-8092}
|
- GANCIO_PORT=${GANCIO_PORT:-8092}
|
||||||
entrypoint: ["/bin/sh", "/scripts/mkdocs-entrypoint.sh"]
|
entrypoint: ["/bin/sh", "/scripts/mkdocs-entrypoint.sh"]
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -577,6 +598,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${MKDOCS_SITE_SERVER_PORT:-4004}:80"
|
- "127.0.0.1:${MKDOCS_SITE_SERVER_PORT:-4004}:80"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -607,6 +629,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- n8n-data:/home/node/.n8n
|
- n8n-data:/home/node/.n8n
|
||||||
- ./local-files:/files
|
- ./local-files:/files
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -628,6 +651,7 @@ services:
|
|||||||
- HOMEPAGE_ALLOWED_HOSTS=*
|
- HOMEPAGE_ALLOWED_HOSTS=*
|
||||||
- HOMEPAGE_VAR_BASE_URL=${HOMEPAGE_VAR_BASE_URL:-http://localhost}
|
- HOMEPAGE_VAR_BASE_URL=${HOMEPAGE_VAR_BASE_URL:-http://localhost}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -669,6 +693,7 @@ services:
|
|||||||
- "127.0.0.1:${GITEA_SSH_PORT:-2222}:22"
|
- "127.0.0.1:${GITEA_SSH_PORT:-2222}:22"
|
||||||
depends_on:
|
depends_on:
|
||||||
- gitea-db
|
- gitea-db
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -688,6 +713,7 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -698,6 +724,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${MINI_QR_PORT:-8089}:8080"
|
- "127.0.0.1:${MINI_QR_PORT:-8089}:8080"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -716,6 +743,7 @@ services:
|
|||||||
start_period: 20s
|
start_period: 20s
|
||||||
environment:
|
environment:
|
||||||
- VITE_APP_COLLAB_SERVER_URL=${EXCALIDRAW_WS_URL:-wss://draw.cmlite.org}
|
- VITE_APP_COLLAB_SERVER_URL=${EXCALIDRAW_WS_URL:-wss://draw.cmlite.org}
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -748,6 +776,7 @@ services:
|
|||||||
- SMTP_PASSWORD=${SMTP_PASS:-}
|
- SMTP_PASSWORD=${SMTP_PASS:-}
|
||||||
volumes:
|
volumes:
|
||||||
- vaultwarden-data:/data
|
- vaultwarden-data:/data
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -814,6 +843,7 @@ services:
|
|||||||
|
|
||||||
rm -f "$$SESSION_COOKIE"
|
rm -f "$$SESSION_COOKIE"
|
||||||
echo "[vaultwarden-init] Done"
|
echo "[vaultwarden-init] Done"
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -854,6 +884,7 @@ services:
|
|||||||
- OVERWRITE_SETTING_VideoConf_Default_Provider=jitsi
|
- OVERWRITE_SETTING_VideoConf_Default_Provider=jitsi
|
||||||
volumes:
|
volumes:
|
||||||
- rocketchat-uploads:/app/uploads
|
- rocketchat-uploads:/app/uploads
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@ -869,6 +900,7 @@ services:
|
|||||||
container_name: nats-rocketchat
|
container_name: nats-rocketchat
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: --http_port 8222
|
command: --http_port 8222
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -880,6 +912,7 @@ services:
|
|||||||
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
|
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
|
||||||
volumes:
|
volumes:
|
||||||
- mongodb-rocketchat-data:/data/db
|
- mongodb-rocketchat-data:/data/db
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@ -917,6 +950,7 @@ services:
|
|||||||
- server__baseurl=${GANCIO_BASE_URL:-https://events.cmlite.org}
|
- server__baseurl=${GANCIO_BASE_URL:-https://events.cmlite.org}
|
||||||
volumes:
|
volumes:
|
||||||
- gancio-data:/home/node/data
|
- gancio-data:/home/node/data
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -958,6 +992,7 @@ services:
|
|||||||
ON CONFLICT (key) DO NOTHING;"
|
ON CONFLICT (key) DO NOTHING;"
|
||||||
echo "Gancio theme settings seeded."
|
echo "Gancio theme settings seeded."
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -996,6 +1031,7 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1029,6 +1065,7 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1052,6 +1089,7 @@ services:
|
|||||||
- TZ=UTC
|
- TZ=UTC
|
||||||
volumes:
|
volumes:
|
||||||
- jitsi-jicofo-config:/config
|
- jitsi-jicofo-config:/config
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1077,6 +1115,7 @@ services:
|
|||||||
- TZ=UTC
|
- TZ=UTC
|
||||||
volumes:
|
volumes:
|
||||||
- jitsi-jvb-config:/config
|
- jitsi-jvb-config:/config
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1111,6 +1150,7 @@ services:
|
|||||||
- NEWT_SECRET=${PANGOLIN_NEWT_SECRET}
|
- NEWT_SECRET=${PANGOLIN_NEWT_SECRET}
|
||||||
depends_on:
|
depends_on:
|
||||||
- nginx
|
- nginx
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1129,6 +1169,7 @@ services:
|
|||||||
- SWARM=0
|
- SWARM=0
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
|
|
||||||
@ -1149,6 +1190,7 @@ services:
|
|||||||
- ./configs/prometheus:/etc/prometheus
|
- ./configs/prometheus:/etc/prometheus
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
restart: always
|
restart: always
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1172,6 +1214,7 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- prometheus
|
- prometheus
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1193,6 +1236,7 @@ services:
|
|||||||
devices:
|
devices:
|
||||||
- /dev/kmsg
|
- /dev/kmsg
|
||||||
restart: always
|
restart: always
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1213,6 +1257,7 @@ services:
|
|||||||
- /sys:/host/sys:ro
|
- /sys:/host/sys:ro
|
||||||
- /:/rootfs:ro
|
- /:/rootfs:ro
|
||||||
restart: always
|
restart: always
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1229,6 +1274,7 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1246,6 +1292,7 @@ services:
|
|||||||
- '--config.file=/etc/alertmanager/alertmanager.yml'
|
- '--config.file=/etc/alertmanager/alertmanager.yml'
|
||||||
- '--storage.path=/alertmanager'
|
- '--storage.path=/alertmanager'
|
||||||
restart: always
|
restart: always
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
@ -1263,6 +1310,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- gotify-data:/app/data
|
- gotify-data:/app/data
|
||||||
restart: always
|
restart: always
|
||||||
|
logging: *default-logging
|
||||||
networks:
|
networks:
|
||||||
- changemaker-lite
|
- changemaker-lite
|
||||||
profiles:
|
profiles:
|
||||||
|
|||||||
@ -144,8 +144,8 @@ info "Nginx templates (for reference)"
|
|||||||
if [[ -d "$PROJECT_DIR/mkdocs" ]]; then
|
if [[ -d "$PROJECT_DIR/mkdocs" ]]; then
|
||||||
mkdir -p "$STAGE_DIR/mkdocs"
|
mkdir -p "$STAGE_DIR/mkdocs"
|
||||||
cp "$PROJECT_DIR/mkdocs/mkdocs.yml" "$STAGE_DIR/mkdocs/" 2>/dev/null || true
|
cp "$PROJECT_DIR/mkdocs/mkdocs.yml" "$STAGE_DIR/mkdocs/" 2>/dev/null || true
|
||||||
cp -r "$PROJECT_DIR/mkdocs/docs" "$STAGE_DIR/mkdocs/" 2>/dev/null || true
|
cp -r "$PROJECT_DIR/mkdocs/docs" "$STAGE_DIR/mkdocs/" 2>/dev/null || mkdir -p "$STAGE_DIR/mkdocs/docs"
|
||||||
cp -r "$PROJECT_DIR/mkdocs/overrides" "$STAGE_DIR/mkdocs/" 2>/dev/null || true
|
cp -r "$PROJECT_DIR/mkdocs/overrides" "$STAGE_DIR/mkdocs/" 2>/dev/null || mkdir -p "$STAGE_DIR/mkdocs/overrides"
|
||||||
mkdir -p "$STAGE_DIR/mkdocs/.cache"
|
mkdir -p "$STAGE_DIR/mkdocs/.cache"
|
||||||
mkdir -p "$STAGE_DIR/mkdocs/site"
|
mkdir -p "$STAGE_DIR/mkdocs/site"
|
||||||
info "MkDocs (starter documentation)"
|
info "MkDocs (starter documentation)"
|
||||||
|
|||||||
@ -21,6 +21,14 @@ REPO="admin/changemaker.lite"
|
|||||||
INSTALL_DIR="${HOME}/changemaker.lite"
|
INSTALL_DIR="${HOME}/changemaker.lite"
|
||||||
VERSION=""
|
VERSION=""
|
||||||
LOCAL_TARBALL=""
|
LOCAL_TARBALL=""
|
||||||
|
MIN_DISK_MB=10000
|
||||||
|
HEALTH_TIMEOUT=180
|
||||||
|
HEALTH_INTERVAL=5
|
||||||
|
|
||||||
|
# --- State flags for cleanup ---
|
||||||
|
EXTRACT_DIR=""
|
||||||
|
TARBALL_PATH=""
|
||||||
|
CONFIG_COMPLETE=false
|
||||||
|
|
||||||
# --- Colors ---
|
# --- Colors ---
|
||||||
if [[ -t 1 ]] && [[ -z "${NO_COLOR:-}" ]]; then
|
if [[ -t 1 ]] && [[ -z "${NO_COLOR:-}" ]]; then
|
||||||
@ -35,6 +43,28 @@ success() { echo -e "${GREEN}[OK]${NC} $*"; }
|
|||||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
|
||||||
|
# --- Cleanup on failure ---
|
||||||
|
cleanup() {
|
||||||
|
local exit_code=$?
|
||||||
|
if [[ $exit_code -ne 0 ]]; then
|
||||||
|
# Clean up temp extraction directory
|
||||||
|
if [[ -n "$EXTRACT_DIR" ]] && [[ -d "$EXTRACT_DIR" ]]; then
|
||||||
|
rm -rf "$EXTRACT_DIR"
|
||||||
|
fi
|
||||||
|
# Clean up downloaded tarball (but not user-provided ones)
|
||||||
|
if [[ -z "$LOCAL_TARBALL" ]] && [[ -n "$TARBALL_PATH" ]] && [[ -f "$TARBALL_PATH" ]]; then
|
||||||
|
rm -f "$TARBALL_PATH"
|
||||||
|
fi
|
||||||
|
# Remove install dir only if config wizard never ran (no user data to lose)
|
||||||
|
if [[ "$CONFIG_COMPLETE" == "false" ]] && [[ -d "$INSTALL_DIR" ]] && [[ ! -f "$INSTALL_DIR/.env" ]]; then
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
error "Installation failed. See errors above."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
# --- Arg parser ---
|
# --- Arg parser ---
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -51,7 +81,10 @@ done
|
|||||||
echo -e "${BOLD}Changemaker Lite — Installer${NC}"
|
echo -e "${BOLD}Changemaker Lite — Installer${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# --- Step 1: Check prerequisites ---
|
# =============================================================================
|
||||||
|
# Step 1: Check prerequisites
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
info "Checking prerequisites..."
|
info "Checking prerequisites..."
|
||||||
MISSING=()
|
MISSING=()
|
||||||
command -v docker >/dev/null 2>&1 || MISSING+=("docker")
|
command -v docker >/dev/null 2>&1 || MISSING+=("docker")
|
||||||
@ -68,7 +101,30 @@ if [[ ${#MISSING[@]} -gt 0 ]]; then
|
|||||||
fi
|
fi
|
||||||
success "Prerequisites OK (Docker $(docker --version | grep -oP '\d+\.\d+\.\d+'), OpenSSL available)"
|
success "Prerequisites OK (Docker $(docker --version | grep -oP '\d+\.\d+\.\d+'), OpenSSL available)"
|
||||||
|
|
||||||
# --- Step 2: Check install directory ---
|
# Docker daemon must be running (not just installed)
|
||||||
|
if ! docker info >/dev/null 2>&1; then
|
||||||
|
error "Docker daemon is not running."
|
||||||
|
echo ""
|
||||||
|
echo " Start it with: sudo systemctl start docker"
|
||||||
|
echo " Or: sudo service docker start"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
success "Docker daemon is running"
|
||||||
|
|
||||||
|
# Disk space check
|
||||||
|
AVAILABLE_MB=$(df -m "$(dirname "$INSTALL_DIR")" | awk 'NR==2 {print $4}')
|
||||||
|
if [[ "$AVAILABLE_MB" -lt "$MIN_DISK_MB" ]]; then
|
||||||
|
error "Insufficient disk space: ${AVAILABLE_MB}MB available, ${MIN_DISK_MB}MB required."
|
||||||
|
echo ""
|
||||||
|
echo " The full stack (images + volumes) needs ~10GB of free space."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
success "Disk space: ${AVAILABLE_MB}MB available (${MIN_DISK_MB}MB required)"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Step 2: Check install directory
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
if [[ -d "$INSTALL_DIR" ]]; then
|
if [[ -d "$INSTALL_DIR" ]]; then
|
||||||
if [[ -f "$INSTALL_DIR/docker-compose.yml" ]]; then
|
if [[ -f "$INSTALL_DIR/docker-compose.yml" ]]; then
|
||||||
error "Changemaker Lite is already installed at $INSTALL_DIR"
|
error "Changemaker Lite is already installed at $INSTALL_DIR"
|
||||||
@ -78,8 +134,10 @@ if [[ -d "$INSTALL_DIR" ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Step 3: Get tarball ---
|
# =============================================================================
|
||||||
TARBALL_PATH=""
|
# Step 3: Get tarball
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
if [[ -n "$LOCAL_TARBALL" ]]; then
|
if [[ -n "$LOCAL_TARBALL" ]]; then
|
||||||
if [[ ! -f "$LOCAL_TARBALL" ]]; then
|
if [[ ! -f "$LOCAL_TARBALL" ]]; then
|
||||||
error "Tarball not found: $LOCAL_TARBALL"
|
error "Tarball not found: $LOCAL_TARBALL"
|
||||||
@ -101,7 +159,7 @@ else
|
|||||||
if [[ -z "$RELEASE_JSON" ]]; then
|
if [[ -z "$RELEASE_JSON" ]]; then
|
||||||
error "Could not fetch release info from ${GITEA_URL}"
|
error "Could not fetch release info from ${GITEA_URL}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "If the registry requires authentication:"
|
echo "If the server is unreachable:"
|
||||||
echo " 1. Download the tarball manually from ${GITEA_URL}/${REPO}/releases"
|
echo " 1. Download the tarball manually from ${GITEA_URL}/${REPO}/releases"
|
||||||
echo " 2. Run: bash install.sh --tarball /path/to/changemaker-lite-*.tar.gz"
|
echo " 2. Run: bash install.sh --tarball /path/to/changemaker-lite-*.tar.gz"
|
||||||
exit 1
|
exit 1
|
||||||
@ -129,7 +187,10 @@ for a in assets:
|
|||||||
success "Downloaded $(du -h "$TARBALL_PATH" | cut -f1)"
|
success "Downloaded $(du -h "$TARBALL_PATH" | cut -f1)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Step 4: Extract ---
|
# =============================================================================
|
||||||
|
# Step 4: Extract
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
info "Extracting to ${INSTALL_DIR}..."
|
info "Extracting to ${INSTALL_DIR}..."
|
||||||
mkdir -p "$(dirname "$INSTALL_DIR")"
|
mkdir -p "$(dirname "$INSTALL_DIR")"
|
||||||
|
|
||||||
@ -141,12 +202,12 @@ tar xzf "$TARBALL_PATH" -C "$EXTRACT_DIR"
|
|||||||
EXTRACTED=$(find "$EXTRACT_DIR" -maxdepth 1 -mindepth 1 -type d | head -1)
|
EXTRACTED=$(find "$EXTRACT_DIR" -maxdepth 1 -mindepth 1 -type d | head -1)
|
||||||
if [[ -z "$EXTRACTED" ]]; then
|
if [[ -z "$EXTRACTED" ]]; then
|
||||||
error "Tarball extraction failed — no directory found"
|
error "Tarball extraction failed — no directory found"
|
||||||
rm -rf "$EXTRACT_DIR"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mv "$EXTRACTED" "$INSTALL_DIR"
|
mv "$EXTRACTED" "$INSTALL_DIR"
|
||||||
rm -rf "$EXTRACT_DIR"
|
rm -rf "$EXTRACT_DIR"
|
||||||
|
EXTRACT_DIR="" # Clear so cleanup doesn't try to remove it
|
||||||
|
|
||||||
# Clean up downloaded tarball
|
# Clean up downloaded tarball
|
||||||
if [[ -z "$LOCAL_TARBALL" ]] && [[ -f "$TARBALL_PATH" ]]; then
|
if [[ -z "$LOCAL_TARBALL" ]] && [[ -f "$TARBALL_PATH" ]]; then
|
||||||
@ -155,23 +216,148 @@ fi
|
|||||||
|
|
||||||
success "Extracted to ${INSTALL_DIR}"
|
success "Extracted to ${INSTALL_DIR}"
|
||||||
|
|
||||||
# --- Step 5: Run config wizard ---
|
# =============================================================================
|
||||||
|
# Step 5: Run config wizard
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${BOLD}Starting configuration wizard...${NC}"
|
echo -e "${BOLD}Starting configuration wizard...${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
cd "$INSTALL_DIR"
|
cd "$INSTALL_DIR"
|
||||||
bash config.sh
|
bash config.sh
|
||||||
|
CONFIG_COMPLETE=true
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Step 6: Start services
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Configuration complete!${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
START_SERVICES="y"
|
||||||
|
if [[ -t 0 ]]; then
|
||||||
|
read -rp "Start all services now? [Y/n]: " START_SERVICES
|
||||||
|
START_SERVICES=${START_SERVICES:-y}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$START_SERVICES" =~ ^[Yy]$ ]]; then
|
||||||
|
echo ""
|
||||||
|
info "Pulling images from registry (this may take a few minutes on first run)..."
|
||||||
|
echo ""
|
||||||
|
cd "$INSTALL_DIR"
|
||||||
|
|
||||||
|
if ! docker compose pull 2>&1; then
|
||||||
|
echo ""
|
||||||
|
error "Failed to pull images from the registry."
|
||||||
|
echo ""
|
||||||
|
echo " Check that the registry is reachable:"
|
||||||
|
echo " curl -sf https://gitea.bnkops.com/api/v1/repos/admin/changemaker.lite/releases/latest"
|
||||||
|
echo ""
|
||||||
|
echo " If pulling from a private registry, log in first:"
|
||||||
|
echo " docker login gitea.bnkops.com"
|
||||||
|
echo ""
|
||||||
|
echo " Then retry:"
|
||||||
|
echo " cd ${INSTALL_DIR} && docker compose pull && docker compose up -d"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
success "All images pulled"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
info "Starting services..."
|
||||||
|
if ! docker compose up -d 2>&1; then
|
||||||
|
echo ""
|
||||||
|
error "Failed to start services."
|
||||||
|
echo " Check logs: docker compose logs --tail 30"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Post-startup health verification ---
|
||||||
|
echo ""
|
||||||
|
info "Waiting for services to become healthy (up to ${HEALTH_TIMEOUT}s)..."
|
||||||
|
info " Database migrations and seeding run automatically on first boot."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
CORE_SERVICES=("v2-postgres" "redis" "api" "admin")
|
||||||
|
ELAPSED=0
|
||||||
|
ALL_HEALTHY=false
|
||||||
|
|
||||||
|
while [[ $ELAPSED -lt $HEALTH_TIMEOUT ]]; do
|
||||||
|
ALL_HEALTHY=true
|
||||||
|
|
||||||
|
for svc in "${CORE_SERVICES[@]}"; do
|
||||||
|
# Detect crashed containers early
|
||||||
|
state=$(docker compose ps "$svc" --format '{{.State}}' 2>/dev/null || echo "missing")
|
||||||
|
if [[ "$state" == "exited" || "$state" == "dead" ]]; then
|
||||||
|
echo ""
|
||||||
|
error "Service '${svc}' exited unexpectedly. Last logs:"
|
||||||
|
docker compose logs "$svc" --tail 20 2>/dev/null || true
|
||||||
|
echo ""
|
||||||
|
error "Fix the issue and retry: cd ${INSTALL_DIR} && docker compose up -d"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check health status
|
||||||
|
health=$(docker compose ps "$svc" --format '{{.Health}}' 2>/dev/null || echo "")
|
||||||
|
if [[ "$health" != "healthy" ]]; then
|
||||||
|
ALL_HEALTHY=false
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$ALL_HEALTHY" == "true" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep "$HEALTH_INTERVAL"
|
||||||
|
ELAPSED=$((ELAPSED + HEALTH_INTERVAL))
|
||||||
|
done
|
||||||
|
|
||||||
|
# Print status table
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD} Service Status:${NC}"
|
||||||
|
for svc in "${CORE_SERVICES[@]}"; do
|
||||||
|
health=$(docker compose ps "$svc" --format '{{.Health}}' 2>/dev/null || echo "unknown")
|
||||||
|
state=$(docker compose ps "$svc" --format '{{.State}}' 2>/dev/null || echo "unknown")
|
||||||
|
if [[ "$health" == "healthy" ]]; then
|
||||||
|
echo -e " ${GREEN}[healthy]${NC} $svc"
|
||||||
|
elif [[ "$state" == "running" ]]; then
|
||||||
|
echo -e " ${YELLOW}[starting]${NC} $svc"
|
||||||
|
else
|
||||||
|
echo -e " ${RED}[${state}]${NC} $svc"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ "$ALL_HEALTHY" == "true" ]]; then
|
||||||
|
success "All core services are healthy! (${ELAPSED}s)"
|
||||||
|
echo ""
|
||||||
|
echo " Admin GUI: http://localhost:3000"
|
||||||
|
echo " API: http://localhost:4000"
|
||||||
|
echo ""
|
||||||
|
echo " Check full stack: docker compose ps"
|
||||||
|
echo " View API logs: docker compose logs -f api --tail 20"
|
||||||
|
else
|
||||||
|
warn "Some services are still starting after ${HEALTH_TIMEOUT}s."
|
||||||
|
echo ""
|
||||||
|
echo " This may be normal on first boot (migrations + seeding can be slow)."
|
||||||
|
echo " Monitor progress with:"
|
||||||
|
echo " docker compose logs -f api --tail 30"
|
||||||
|
echo ""
|
||||||
|
echo " Check status with:"
|
||||||
|
echo " docker compose ps"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
info "Skipped. Start services manually when ready:"
|
||||||
|
echo ""
|
||||||
|
echo " cd ${INSTALL_DIR} && docker compose up -d"
|
||||||
|
echo ""
|
||||||
|
echo " Pre-built images will be pulled from the registry (~2 min first time)."
|
||||||
|
echo " Database migrations and seeding run automatically on startup."
|
||||||
|
fi
|
||||||
|
|
||||||
# --- Done ---
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${BOLD}${GREEN}Installation complete!${NC}"
|
echo -e "${BOLD}${GREEN}Installation complete!${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo " Start all services:"
|
echo -e " \033[0;33mIMPORTANT: Change your admin password after first login!\033[0m"
|
||||||
echo " cd ${INSTALL_DIR} && docker compose up -d"
|
|
||||||
echo ""
|
|
||||||
echo " Check status:"
|
|
||||||
echo " docker compose ps"
|
|
||||||
echo ""
|
|
||||||
echo " View API logs:"
|
|
||||||
echo " docker compose logs -f api --tail 20"
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@ -144,7 +144,7 @@ declare -A MONITORING_IMAGES=(
|
|||||||
["oliver006/redis_exporter:v1.81.0"]="redis_exporter:v1.81.0"
|
["oliver006/redis_exporter:v1.81.0"]="redis_exporter:v1.81.0"
|
||||||
["gcr.io/cadvisor/cadvisor:v0.55.1"]="cadvisor:v0.55.1"
|
["gcr.io/cadvisor/cadvisor:v0.55.1"]="cadvisor:v0.55.1"
|
||||||
["prom/node-exporter:v1.10.2"]="node-exporter:v1.10.2"
|
["prom/node-exporter:v1.10.2"]="node-exporter:v1.10.2"
|
||||||
["gotify/server:v2.9.0"]="gotify:v2.9.0"
|
["gotify/server:2.9.0"]="gotify:v2.9.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
13
scripts/systemd/changemaker-backup.service
Normal file
13
scripts/systemd/changemaker-backup.service
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Changemaker Lite database and uploads backup
|
||||||
|
Documentation=https://docs.cmlite.org/docs/admin/backups/
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
User=__USER__
|
||||||
|
Group=__USER__
|
||||||
|
WorkingDirectory=__PROJECT_DIR__
|
||||||
|
ExecStart=__PROJECT_DIR__/scripts/backup.sh --retention 30
|
||||||
|
TimeoutStartSec=600
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
10
scripts/systemd/changemaker-backup.timer
Normal file
10
scripts/systemd/changemaker-backup.timer
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Daily Changemaker Lite backup
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=*-*-* 02:00:00
|
||||||
|
Persistent=true
|
||||||
|
RandomizedDelaySec=300
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
Loading…
x
Reference in New Issue
Block a user