diff --git a/install-stack.sh b/install-stack.sh index 889728a..42155e4 100755 --- a/install-stack.sh +++ b/install-stack.sh @@ -1247,6 +1247,97 @@ ${build_out}" npm_add_location "/maintenance/" "10.10.10.121" 3080 } +deploy_forecasting() { + msg_step "Forecasting (LXC 113 · 10.10.10.113)" + + if [[ -z "${FORECASTING_DB_PASS:-}" ]]; then + FORECASTING_DB_PASS=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24) + printf '\nFORECASTING_DB_PASS=%s\n' "$FORECASTING_DB_PASS" >> "$CREDS_FILE" + msg_ok "Generated FORECASTING_DB_PASS → ${CREDS_FILE}" + fi + + # Forecasting needs more RAM (ML models) and more disk (Chromium, Prophet/CmdStan) + msg_info "Creating LXC 113 (4 GB RAM, 2 cores)" + create_lxc 113 "10.10.10.113" "forecasting" 4096 2 + pct resize 113 rootfs 20G &>/dev/null || true + msg_ok "LXC 113 created" + + msg_info "Installing Docker" + install_docker 113 + install_mgmt_key 113 + msg_ok "Docker + SSH ready" + + msg_info "Creating forecasting database" + pct exec 100 -- bash -c " + docker exec hotel-manage-postgres psql -U postgres -c \ + \"CREATE USER forecasting WITH PASSWORD '${FORECASTING_DB_PASS}';\" 2>/dev/null || true + docker exec hotel-manage-postgres psql -U postgres -c \ + \"CREATE DATABASE forecasting_db OWNER forecasting;\" 2>/dev/null || true + docker exec hotel-manage-postgres psql -U postgres -d forecasting_db -c \ + \"GRANT ALL ON SCHEMA public TO forecasting;\" 2>/dev/null || true + " &>/dev/null + + msg_ok "Database forecasting_db ready (schema applied by Python backend on first start)" + + msg_info "Deploying forecasting" + deploy_service 113 "forecasting" "${REPO_ROOT}/forecasting" /opt/forecasting + + # Note: NEWBOOK_* and RESOS_API_KEY are configured via the Settings page after first deploy. + # ANTHROPIC_API_KEY is optional — enables the AI Dashboard feature. + push_file 113 /opt/forecasting/.env <&1"); then + msg_error "docker compose build failed in LXC 113: +${build_out}" + fi + + msg_info "Waiting for forecasting (up to 10 min)" + wait_healthy 113 "http://localhost:3080/forecasting/health" 200 \ + && msg_ok "Forecasting running at 10.10.10.113:3080" \ + || msg_warn "Forecasting may need extra time — check LXC 113 logs" + + # Seed app + capabilities into auth DB + msg_info "Seeding forecasting 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 ('forecasting', 'Forecasting', 'Revenue forecasting and reporting', '/forecasting', 'TrendingUp', '#0077b6', 'Finance', '10.10.10.113', 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 Forecasts','View dashboard, forecasts, and history',1), + ('view_bookability','View Bookability','View the rate / bookability matrix',2), + ('view_competitor_rates','View Competitors','View competitor rate scraping and matrix',3), + ('view_accuracy','View Accuracy','View model accuracy metrics and backtesting',4), + ('manage_sync','Manage Data Sync','Trigger Newbook / Resos data syncs manually',5), + ('settings','Manage Settings','Configure Newbook credentials and sync schedules',6) + ) AS c(slug, name, description, sort_order) + WHERE a.slug = 'forecasting' + ON CONFLICT (app_id, slug) DO NOTHING; + " &>/dev/null \ + && msg_ok "forecasting seeded into auth DB" \ + || msg_warn "Seed failed — run forecasting/seed-app.js manually" + + npm_add_location "/forecasting/" "10.10.10.113" 3080 +} + # ════════════════════════════════════════════════════════════════════════════ # NPM PROXY HOSTS (via API) # ════════════════════════════════════════════════════════════════════════════ @@ -1521,6 +1612,8 @@ if [[ "${1:-}" == "--only" ]]; then "$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)" [[ -z "${TWIN_OPT_DB_PASS:-}" ]] && _append_secret TWIN_OPT_DB_PASS \ "$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)" + [[ -z "${FORECASTING_DB_PASS:-}" ]] && _append_secret FORECASTING_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 ]] \ @@ -1540,6 +1633,7 @@ if [[ "${1:-}" == "--only" ]]; then twin-optimiser) deploy_twin_optimiser ;; room-planner) deploy_room_planner ;; maintenance) deploy_maintenance ;; + forecasting) deploy_forecasting ;; esac exit 0 fi @@ -1562,5 +1656,6 @@ deploy_hk_planner deploy_twin_optimiser deploy_room_planner deploy_maintenance +deploy_forecasting configure_npm_proxy_hosts print_summary