Add weather app: deploy_weather(), --only dispatch, full install chain

LXC 131 / 10.10.10.131. Generates WEATHER_DB_PASS + WEATHER_SECRET (bearer
token for the app's own /api/internal/forecast endpoint). Follows the same
deploy_<slug>() shape as deploy_tasks().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-08-25 12:22:58 +00:00
parent 68e6c951fd
commit 49e82d553f

View file

@ -2328,6 +2328,174 @@ ${build_out}"
npm_add_location "/kds/" "10.10.10.125" 3080 npm_add_location "/kds/" "10.10.10.125" 3080
} }
deploy_tasks() {
msg_step "Tasks (LXC 130 · 10.10.10.130)"
if [[ -z "${TASKS_DB_PASS:-}" ]]; then
TASKS_DB_PASS=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)
printf '\nTASKS_DB_PASS=%s\n' "$TASKS_DB_PASS" >> "$CREDS_FILE"
msg_ok "Generated TASKS_DB_PASS → ${CREDS_FILE}"
fi
msg_info "Creating LXC 130"
create_lxc 130 "10.10.10.130" "tasks" 1024 1
msg_ok "LXC 130 created"
msg_info "Installing Docker"
install_docker 130
install_mgmt_key 130
msg_ok "Docker + SSH ready"
msg_info "Creating tasks database"
pct exec 100 -- bash -c "
docker exec hotel-manage-postgres psql -U postgres -c \
\"CREATE USER tasks WITH PASSWORD '${TASKS_DB_PASS}';\" 2>/dev/null || true
docker exec hotel-manage-postgres psql -U postgres -c \
\"CREATE DATABASE tasks_db OWNER tasks;\" 2>/dev/null || true
docker exec hotel-manage-postgres psql -U postgres -d tasks_db -c \
\"GRANT ALL ON SCHEMA public TO tasks;\" 2>/dev/null || true
" &>/dev/null
msg_ok "Database tasks_db ready"
msg_info "Deploying tasks"
deploy_service 130 "tasks" "${REPO_ROOT}/tasks" /opt/tasks
push_file 130 /opt/tasks/.env <<EOF
NODE_ENV=production
APP_SLUG=tasks
DATABASE_URL=postgresql://tasks:${TASKS_DB_PASS}@10.10.10.100:5432/tasks_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}}
VITE_HOTEL_NAME=${SITE_NAME:-Hotel}
FRONTEND_PORT=3080
EOF
local build_out
if ! build_out=$(pct exec 130 -- bash -c "cd /opt/tasks && docker compose up -d --build 2>&1"); then
msg_error "docker compose build failed in LXC 130:
${build_out}"
fi
msg_info "Waiting for tasks"
wait_healthy 130 "http://localhost:3080/tasks/health" \
&& msg_ok "Tasks running at 10.10.10.130:3080" \
|| msg_warn "Tasks may need extra time — check LXC 130"
msg_info "Seeding tasks 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 ('tasks', 'Tasks', 'Personal task planner — lists, reminders and recurring to-dos', '/tasks', 'ListChecks', '#0d9488', 'Operations', '10.10.10.130', 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
('use', 'Use Tasks', 'Create and manage your own lists and tasks', 1),
('assign', 'Assign Tasks', 'Assign tasks to other users (phase 2)', 2),
('admin', 'Manage All', 'View and manage every user''s tasks (phase 2)', 3),
('settings', 'Settings', 'Configure tasks app settings', 4)
) AS c(slug, name, description, sort_order)
WHERE a.slug = 'tasks'
ON CONFLICT (app_id, slug) DO NOTHING;
INSERT INTO role_capabilities (role_id, capability_id)
SELECT r.id, ac.id FROM roles r
JOIN app_capabilities ac ON ac.app_id = (SELECT id FROM apps WHERE slug = 'tasks')
WHERE r.slug = 'staff' AND ac.slug = 'use'
ON CONFLICT DO NOTHING;
" &>/dev/null \
&& msg_ok "tasks seeded into auth DB" \
|| msg_warn "Seed failed — run tasks/seed-app.js manually"
npm_add_location "/tasks/" "10.10.10.130" 3080
}
deploy_weather() {
msg_step "Weather (LXC 131 · 10.10.10.131)"
if [[ -z "${WEATHER_DB_PASS:-}" ]]; then
WEATHER_DB_PASS=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)
printf '\nWEATHER_DB_PASS=%s\n' "$WEATHER_DB_PASS" >> "$CREDS_FILE"
msg_ok "Generated WEATHER_DB_PASS → ${CREDS_FILE}"
fi
if [[ -z "${WEATHER_SECRET:-}" ]]; then
WEATHER_SECRET=$(openssl rand -hex 32)
printf '\nWEATHER_SECRET=%s\n' "$WEATHER_SECRET" >> "$CREDS_FILE"
msg_ok "Generated WEATHER_SECRET → ${CREDS_FILE}"
fi
msg_info "Creating LXC 131"
create_lxc 131 "10.10.10.131" "weather" 1024 1
msg_ok "LXC 131 created"
msg_info "Installing Docker"
install_docker 131
install_mgmt_key 131
msg_ok "Docker + SSH ready"
msg_info "Creating weather database"
pct exec 100 -- bash -c "
docker exec hotel-manage-postgres psql -U postgres -c \
\"CREATE USER weather WITH PASSWORD '${WEATHER_DB_PASS}';\" 2>/dev/null || true
docker exec hotel-manage-postgres psql -U postgres -c \
\"CREATE DATABASE weather_db OWNER weather;\" 2>/dev/null || true
docker exec hotel-manage-postgres psql -U postgres -d weather_db -c \
\"GRANT ALL ON SCHEMA public TO weather;\" 2>/dev/null || true
" &>/dev/null
msg_ok "Database weather_db ready"
msg_info "Deploying weather"
deploy_service 131 "weather" "${REPO_ROOT}/weather" /opt/weather
push_file 131 /opt/weather/.env <<EOF
NODE_ENV=production
APP_SLUG=weather
DATABASE_URL=postgresql://weather:${WEATHER_DB_PASS}@10.10.10.100:5432/weather_db
CENTRAL_AUTH_SECRET=${CENTRAL_AUTH_SECRET}
SETTINGS_URL=http://10.10.10.116:3080
SETTINGS_SECRET=${SETTINGS_SECRET}
WEATHER_SECRET=${WEATHER_SECRET}
OFFICE_IP_CHECK=${OFFICE_IP_CHECK:-${OFFICE_IP:-disabled}}
VITE_HOTEL_NAME=${SITE_NAME:-Hotel}
FRONTEND_PORT=3080
EOF
local build_out
if ! build_out=$(pct exec 131 -- bash -c "cd /opt/weather && docker compose up -d --build 2>&1"); then
msg_error "docker compose build failed in LXC 131:
${build_out}"
fi
msg_info "Waiting for weather"
wait_healthy 131 "http://localhost:3080/weather/health" \
&& msg_ok "Weather running at 10.10.10.131:3080" \
|| msg_warn "Weather may need extra time — check LXC 131"
msg_info "Seeding weather 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 ('weather', 'Weather', 'Reception forecast display — today hourly, next 2 days, 7-day outlook', '/weather/admin', 'CloudSun', '#0ea5e9', 'Hotel', '10.10.10.131', 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
('settings', 'Settings', 'View forecast fetch status and trigger a manual refresh', 1)
) AS c(slug, name, description, sort_order)
WHERE a.slug = 'weather'
ON CONFLICT (app_id, slug) DO NOTHING;
" &>/dev/null \
&& msg_ok "weather seeded into auth DB" \
|| msg_warn "Seed failed — run weather/seed-app.js manually"
npm_add_location "/weather/" "10.10.10.131" 3080
}
# ════════════════════════════════════════════════════════════════════════════ # ════════════════════════════════════════════════════════════════════════════
# NPM PROXY HOSTS (via API) # NPM PROXY HOSTS (via API)
# ════════════════════════════════════════════════════════════════════════════ # ════════════════════════════════════════════════════════════════════════════
@ -2576,7 +2744,7 @@ SUMMARY
# Sources existing credentials and redeploys just the named service. # Sources existing credentials and redeploys just the named service.
if [[ "${1:-}" == "--only" ]]; then if [[ "${1:-}" == "--only" ]]; then
ONLY="${2:-}" 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 mqtt-broker hvac calendar plant" 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 plant tasks weather"
[[ -z "$ONLY" ]] && msg_error "Usage: install-stack.sh --only <service> (one of: ${VALID})" [[ -z "$ONLY" ]] && msg_error "Usage: install-stack.sh --only <service> (one of: ${VALID})"
grep -qw "$ONLY" <<< "$VALID" || msg_error "Unknown service '${ONLY}'. Valid: ${VALID}" grep -qw "$ONLY" <<< "$VALID" || msg_error "Unknown service '${ONLY}'. Valid: ${VALID}"
CREDS_FILE=/root/hotel-manage-credentials.txt CREDS_FILE=/root/hotel-manage-credentials.txt
@ -2620,6 +2788,12 @@ if [[ "${1:-}" == "--only" ]]; then
"$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)" "$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)"
[[ -z "${CALENDAR_DB_PASS:-}" ]] && _append_secret CALENDAR_DB_PASS \ [[ -z "${CALENDAR_DB_PASS:-}" ]] && _append_secret CALENDAR_DB_PASS \
"$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)" "$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)"
[[ -z "${TASKS_DB_PASS:-}" ]] && _append_secret TASKS_DB_PASS \
"$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)"
[[ -z "${WEATHER_DB_PASS:-}" ]] && _append_secret WEATHER_DB_PASS \
"$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)"
[[ -z "${WEATHER_SECRET:-}" ]] && _append_secret WEATHER_SECRET \
"$(openssl rand -hex 32)"
USE_FORGEJO=true USE_FORGEJO=true
[[ -f /root/.ssh/hotel-manage_deploy.pub ]] \ [[ -f /root/.ssh/hotel-manage_deploy.pub ]] \
@ -2650,6 +2824,8 @@ if [[ "${1:-}" == "--only" ]]; then
hvac) deploy_hvac ;; hvac) deploy_hvac ;;
calendar) deploy_calendar ;; calendar) deploy_calendar ;;
plant) deploy_plant ;; plant) deploy_plant ;;
tasks) deploy_tasks ;;
weather) deploy_weather ;;
esac esac
exit 0 exit 0
fi fi
@ -2685,5 +2861,6 @@ deploy_wages
deploy_hvac deploy_hvac
deploy_calendar deploy_calendar
deploy_plant deploy_plant
deploy_weather
configure_npm_proxy_hosts configure_npm_proxy_hosts
print_summary print_summary