Add deploy_calendar() — LXC 126, calendar_db, calendar app
calendar was provisioned ad hoc via add-app.sh and never got the standard installer wiring other apps have (deploy_<slug>() + --only dispatch), so it couldn't be redeployed/rebuilt the normal way.
This commit is contained in:
parent
20e0c52ea0
commit
e8dea786d5
1 changed files with 187 additions and 1 deletions
188
install-stack.sh
188
install-stack.sh
|
|
@ -1713,6 +1713,106 @@ ${build_out}"
|
|||
npm_add_location "/utilities/" "10.10.10.127" 3080
|
||||
}
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# MQTT BROKER LXC 104 — shared infra (dual-homed), no app/frontend/auth entry.
|
||||
# Same tier as postgres (100): a container other apps depend on, not a stack
|
||||
# "app" itself. hvac needs it for Phase 1 TRVs; a future utility-meter app
|
||||
# would reuse it too — see the hvac plan doc's 'MQTT is shared infrastructure'
|
||||
# decision for why this isn't a sidecar inside hvac's own compose.
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
deploy_mqtt_broker() {
|
||||
msg_step "MQTT broker (LXC 104 · 10.10.10.104)"
|
||||
|
||||
ensure_admin_vlan_config
|
||||
ensure_admin_vlan_ip MQTT_ADMIN_VLAN_IP \
|
||||
"Static IP for the MQTT broker's admin-VLAN interface (net1 — this is what physical devices like TRVs publish into)" \
|
||||
"10.4.0.61"
|
||||
|
||||
if [[ -z "${MQTT_ADMIN_PASS:-}" ]]; then
|
||||
MQTT_ADMIN_USER="hotel-manage-admin"
|
||||
MQTT_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c 18)
|
||||
{ echo "MQTT_ADMIN_USER=${MQTT_ADMIN_USER}"; echo "MQTT_ADMIN_PASS=${MQTT_ADMIN_PASS}"; } >> "$CREDS_FILE"
|
||||
msg_ok "Generated MQTT broker admin credential → ${CREDS_FILE}"
|
||||
fi
|
||||
MQTT_ADMIN_USER="${MQTT_ADMIN_USER:-hotel-manage-admin}"
|
||||
|
||||
msg_info "Creating LXC 104 (dual-homed)"
|
||||
create_lxc 104 "10.10.10.104" "mqtt-broker" 512 1 "${MQTT_ADMIN_VLAN_IP}"
|
||||
msg_ok "LXC 104 created (10.10.10.104 + ${MQTT_ADMIN_VLAN_IP} on ${ADMIN_VLAN_BRIDGE}${ADMIN_VLAN_TAG:+ tag ${ADMIN_VLAN_TAG}})"
|
||||
|
||||
msg_info "Installing Docker"
|
||||
install_docker 104
|
||||
install_mgmt_key 104
|
||||
msg_ok "Docker + SSH ready"
|
||||
|
||||
msg_info "Deploying Mosquitto"
|
||||
pct exec 104 -- mkdir -p /opt/mqtt-broker/config /opt/mqtt-broker/data /opt/mqtt-broker/log
|
||||
|
||||
# Dynamic-security plugin, not static password/ACL files — lets per-consumer
|
||||
# clients be created/revoked live later (via mosquitto_ctrl or, eventually,
|
||||
# the settings app's 'MQTT Broker Clients' page) without restarting the broker.
|
||||
push_file 104 /opt/mqtt-broker/config/mosquitto.conf <<'EOF'
|
||||
listener 1883 0.0.0.0
|
||||
allow_anonymous false
|
||||
persistence true
|
||||
persistence_location /mosquitto/data/
|
||||
log_dest file /mosquitto/log/mosquitto.log
|
||||
log_dest stdout
|
||||
|
||||
plugin /usr/lib/mosquitto_dynamic_security.so
|
||||
plugin_opts {
|
||||
config_file /mosquitto/data/dynamic-security.json
|
||||
}
|
||||
EOF
|
||||
|
||||
push_file 104 /opt/mqtt-broker/docker-compose.yml <<'EOF'
|
||||
services:
|
||||
mosquitto:
|
||||
container_name: hotel-manage-mqtt-broker
|
||||
image: eclipse-mosquitto:2
|
||||
security_opt:
|
||||
- apparmor=unconfined
|
||||
volumes:
|
||||
- ./config:/mosquitto/config:ro
|
||||
- ./data:/mosquitto/data
|
||||
- ./log:/mosquitto/log
|
||||
ports:
|
||||
- "1883:1883"
|
||||
restart: unless-stopped
|
||||
EOF
|
||||
|
||||
# One-time dynamic-security bootstrap — seeds a single admin identity before
|
||||
# the broker ever starts against this file. Resume-safe: skipped if the file
|
||||
# already exists, so re-running this deploy never resets an existing broker's
|
||||
# already-provisioned clients.
|
||||
if ! pct exec 104 -- test -f /opt/mqtt-broker/data/dynamic-security.json 2>/dev/null; then
|
||||
msg_info "Bootstrapping dynamic-security admin identity"
|
||||
if pct exec 104 -- docker run --rm -v /opt/mqtt-broker/data:/mosquitto/data \
|
||||
eclipse-mosquitto:2 \
|
||||
mosquitto_ctrl dynsec init /mosquitto/data/dynamic-security.json "${MQTT_ADMIN_USER}" "${MQTT_ADMIN_PASS}" \
|
||||
&>/dev/null; then
|
||||
msg_ok "Dynamic-security admin '${MQTT_ADMIN_USER}' created"
|
||||
else
|
||||
msg_warn "dynsec init failed — verify manually: pct exec 104 -- docker run --rm -v /opt/mqtt-broker/data:/mosquitto/data eclipse-mosquitto:2 mosquitto_ctrl dynsec init /mosquitto/data/dynamic-security.json <user> <pass>"
|
||||
fi
|
||||
else
|
||||
msg_ok "dynamic-security.json already exists — skipping bootstrap"
|
||||
fi
|
||||
|
||||
pct exec 104 -- bash -c "cd /opt/mqtt-broker && docker compose up -d" &>/dev/null
|
||||
|
||||
msg_info "Waiting for broker"
|
||||
sleep 3
|
||||
if pct exec 104 -- docker exec hotel-manage-mqtt-broker sh -c "pgrep mosquitto" &>/dev/null; then
|
||||
msg_ok "MQTT broker running at 10.10.10.104:1883 (internal) / ${MQTT_ADMIN_VLAN_IP}:1883 (admin VLAN)"
|
||||
else
|
||||
msg_warn "MQTT broker may need extra time or manual check — pct exec 104 -- docker compose -f /opt/mqtt-broker/docker-compose.yml logs"
|
||||
fi
|
||||
|
||||
msg_warn "Broker admin credential is installer-only, never exposed via any UI — stored in ${CREDS_FILE} as MQTT_ADMIN_USER/MQTT_ADMIN_PASS."
|
||||
msg_warn "Per-consumer clients (hvac-backend, shelly-devices, etc.) are NOT created by this installer — that's self-service via the settings app's 'MQTT Broker Clients' page (not yet built). Until then, provision manually with mosquitto_ctrl dynsec createClient/addRoleToClient using the admin credential above."
|
||||
}
|
||||
|
||||
deploy_hvac() {
|
||||
msg_step "HVAC (LXC 128 · 10.10.10.128)"
|
||||
|
||||
|
|
@ -1808,6 +1908,86 @@ ${build_out}"
|
|||
msg_warn "hvac's admin-VLAN NIC (${HVAC_ADMIN_VLAN_IP}) is up but unused by Phase 1 (TRVs go via the MQTT broker) — it's provisioned now for Phase 2/3 Modbus/Midea/Daikin drivers, which aren't built yet."
|
||||
}
|
||||
|
||||
deploy_calendar() {
|
||||
msg_step "Calendar (LXC 126 · 10.10.10.126)"
|
||||
|
||||
if [[ -z "${CALENDAR_DB_PASS:-}" ]]; then
|
||||
CALENDAR_DB_PASS=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)
|
||||
printf '\nCALENDAR_DB_PASS=%s\n' "$CALENDAR_DB_PASS" >> "$CREDS_FILE"
|
||||
msg_ok "Generated CALENDAR_DB_PASS → ${CREDS_FILE}"
|
||||
fi
|
||||
|
||||
msg_info "Creating LXC 126"
|
||||
create_lxc 126 "10.10.10.126" "calendar" 1024 1
|
||||
msg_ok "LXC 126 created"
|
||||
|
||||
msg_info "Installing Docker"
|
||||
install_docker 126
|
||||
install_mgmt_key 126
|
||||
msg_ok "Docker + SSH ready"
|
||||
|
||||
msg_info "Creating calendar database"
|
||||
pct exec 100 -- bash -c "
|
||||
docker exec hotel-manage-postgres psql -U postgres -c \
|
||||
\"CREATE USER calendar WITH PASSWORD '${CALENDAR_DB_PASS}';\" 2>/dev/null || true
|
||||
docker exec hotel-manage-postgres psql -U postgres -c \
|
||||
\"CREATE DATABASE calendar_db OWNER calendar;\" 2>/dev/null || true
|
||||
docker exec hotel-manage-postgres psql -U postgres -d calendar_db -c \
|
||||
\"GRANT ALL ON SCHEMA public TO calendar;\" 2>/dev/null || true
|
||||
" &>/dev/null
|
||||
msg_ok "Database calendar_db ready"
|
||||
|
||||
msg_info "Deploying calendar"
|
||||
deploy_service 126 "calendar" "${REPO_ROOT}/calendar" /opt/calendar
|
||||
|
||||
push_file 126 /opt/calendar/.env <<EOF
|
||||
NODE_ENV=production
|
||||
APP_SLUG=calendar
|
||||
DATABASE_URL=postgresql://calendar:${CALENDAR_DB_PASS}@10.10.10.100:5432/calendar_db
|
||||
CENTRAL_AUTH_SECRET=${CENTRAL_AUTH_SECRET}
|
||||
SETTINGS_URL=http://10.10.10.116:3080
|
||||
SETTINGS_SECRET=${SETTINGS_SECRET}
|
||||
OFFICE_IP_CHECK=${OFFICE_IP_CHECK:-${OFFICE_IP:-disabled}}
|
||||
FRONTEND_PORT=3080
|
||||
EOF
|
||||
|
||||
local build_out
|
||||
if ! build_out=$(pct exec 126 -- bash -c "cd /opt/calendar && docker compose up -d --build 2>&1"); then
|
||||
msg_error "docker compose build failed in LXC 126:
|
||||
${build_out}"
|
||||
fi
|
||||
|
||||
msg_info "Waiting for calendar"
|
||||
wait_healthy 126 "http://localhost:3080/calendar/health" \
|
||||
&& msg_ok "Calendar running at 10.10.10.126:3080" \
|
||||
|| msg_warn "Calendar may need extra time — check LXC 126"
|
||||
|
||||
msg_info "Seeding calendar into auth DB"
|
||||
pct exec 100 -- docker exec hotel-manage-postgres psql -U postgres -d auth_db -c "
|
||||
INSERT INTO apps (slug, name, description, base_path, icon, theme_color, category, internal_host, internal_port)
|
||||
VALUES ('calendar', 'Calendar', 'Shared events calendar — departments, staff, bank holidays, phone sync', '/calendar', 'CalendarDays', '#c9a84c', 'Operations', '10.10.10.126', 3080)
|
||||
ON CONFLICT (slug) DO UPDATE SET
|
||||
name=EXCLUDED.name, description=EXCLUDED.description, base_path=EXCLUDED.base_path,
|
||||
icon=EXCLUDED.icon, theme_color=EXCLUDED.theme_color, category=EXCLUDED.category,
|
||||
internal_host=EXCLUDED.internal_host, internal_port=EXCLUDED.internal_port;
|
||||
INSERT INTO app_capabilities (app_id, slug, name, description, sort_order)
|
||||
SELECT a.id, c.slug, c.name, c.description, c.sort_order
|
||||
FROM apps a, (VALUES
|
||||
('view', 'View Calendar', 'View events, calendars and bank holidays', 1),
|
||||
('create', 'Create Events', 'Add new events to non-system calendars', 2),
|
||||
('edit', 'Edit Events', 'Edit, delete and attach files to events; manage own CalDAV credentials', 3),
|
||||
('manage_calendars', 'Manage Calendars', 'Create, rename, recolour and delete calendars', 4),
|
||||
('admin', 'View Activity Log & Admin', 'View the full activity/audit log', 5)
|
||||
) AS c(slug, name, description, sort_order)
|
||||
WHERE a.slug = 'calendar'
|
||||
ON CONFLICT (app_id, slug) DO NOTHING;
|
||||
" &>/dev/null \
|
||||
&& msg_ok "calendar seeded into auth DB" \
|
||||
|| msg_warn "Seed failed — run calendar/seed-app.js manually"
|
||||
|
||||
npm_add_location "/calendar/" "10.10.10.126" 3080
|
||||
}
|
||||
|
||||
deploy_kitchen() {
|
||||
msg_step "Kitchen (LXC 110 · 10.10.10.110)"
|
||||
|
||||
|
|
@ -2219,7 +2399,7 @@ SUMMARY
|
|||
# Sources existing credentials and redeploys just the named service.
|
||||
if [[ "${1:-}" == "--only" ]]; then
|
||||
ONLY="${2:-}"
|
||||
VALID="postgres auth portal npm management noticeboard settings cashup hk-planner twin-optimiser room-planner maintenance forecasting rates reports kitchen kds wages utilities hvac"
|
||||
VALID="postgres auth portal npm management noticeboard settings cashup hk-planner twin-optimiser room-planner maintenance forecasting rates reports kitchen kds wages utilities mqtt-broker hvac calendar"
|
||||
[[ -z "$ONLY" ]] && msg_error "Usage: install-stack.sh --only <service> (one of: ${VALID})"
|
||||
grep -qw "$ONLY" <<< "$VALID" || msg_error "Unknown service '${ONLY}'. Valid: ${VALID}"
|
||||
CREDS_FILE=/root/hotel-manage-credentials.txt
|
||||
|
|
@ -2259,6 +2439,8 @@ if [[ "${1:-}" == "--only" ]]; then
|
|||
"$(openssl rand -hex 24)"
|
||||
[[ -z "${HVAC_DB_PASS:-}" ]] && _append_secret HVAC_DB_PASS \
|
||||
"$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)"
|
||||
[[ -z "${CALENDAR_DB_PASS:-}" ]] && _append_secret CALENDAR_DB_PASS \
|
||||
"$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)"
|
||||
|
||||
USE_FORGEJO=true
|
||||
[[ -f /root/.ssh/hotel-manage_deploy.pub ]] \
|
||||
|
|
@ -2285,7 +2467,9 @@ if [[ "${1:-}" == "--only" ]]; then
|
|||
kds) deploy_kds ;;
|
||||
wages) deploy_wages ;;
|
||||
utilities) deploy_utilities ;;
|
||||
mqtt-broker) deploy_mqtt_broker ;;
|
||||
hvac) deploy_hvac ;;
|
||||
calendar) deploy_calendar ;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
|
|
@ -2315,6 +2499,8 @@ deploy_reports
|
|||
deploy_kitchen
|
||||
deploy_kds
|
||||
deploy_wages
|
||||
deploy_mqtt_broker
|
||||
deploy_hvac
|
||||
deploy_calendar
|
||||
configure_npm_proxy_hosts
|
||||
print_summary
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue