Add deploy_kitchen (LXC 110) + deploy_kds (LXC 125) functions

- deploy_kitchen: 2 GB RAM, 10 GB disk; creates kitchen_db (shared with kds);
  FastAPI backend with MSSQL ODBC drivers (~5 min build); seeds 10 caps into auth DB
- deploy_kds: 1 GB RAM; connects to kitchen_db (no separate DB — KDS shares schema);
  slim FastAPI build (httpx only, no MSSQL ODBC); seeds 3 caps + Staff role grants
- KITCHEN_DB_PASS added to _append_secret section for --only deploys
- Both added to case dispatch and full deploy sequence

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-11 11:26:13 +00:00
parent 5c345e47a9
commit 0275641243

View file

@ -1503,6 +1503,169 @@ ${build_out}"
npm_add_location "/reports/" "10.10.10.122" 3080
}
deploy_kitchen() {
msg_step "Kitchen (LXC 110 · 10.10.10.110)"
if [[ -z "${KITCHEN_DB_PASS:-}" ]]; then
KITCHEN_DB_PASS=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)
printf '\nKITCHEN_DB_PASS=%s\n' "$KITCHEN_DB_PASS" >> "$CREDS_FILE"
msg_ok "Generated KITCHEN_DB_PASS → ${CREDS_FILE}"
fi
# 2 GB RAM — FastAPI + MSSQL ODBC drivers + Azure DI OCR
msg_info "Creating LXC 110 (2 GB RAM)"
create_lxc 110 "10.10.10.110" "kitchen" 2048 2
pct resize 110 rootfs 10G &>/dev/null || true
msg_ok "LXC 110 created"
msg_info "Installing Docker"
install_docker 110
install_mgmt_key 110
msg_ok "Docker + SSH ready"
msg_info "Creating kitchen_db (shared by kitchen and kds)"
pct exec 100 -- bash -c "
docker exec hotel-manage-postgres psql -U postgres -c \
\"CREATE USER kitchen WITH PASSWORD '${KITCHEN_DB_PASS}';\" 2>/dev/null || true
docker exec hotel-manage-postgres psql -U postgres -c \
\"CREATE DATABASE kitchen_db OWNER kitchen;\" 2>/dev/null || true
docker exec hotel-manage-postgres psql -U postgres -d kitchen_db -c \
\"GRANT ALL ON SCHEMA public TO kitchen;\" 2>/dev/null || true
" &>/dev/null
msg_ok "Database kitchen_db ready (schema applied by Python backend on first start)"
msg_info "Deploying kitchen"
deploy_service 110 "kitchen" "${REPO_ROOT}/kitchen" /opt/kitchen
# AZURE_DI_ENDPOINT and AZURE_DI_KEY are for the OCR invoice upload pipeline.
# ANTHROPIC_API_KEY enables the LLM integration (allergen / flag analysis).
# Leave blank to disable optional integrations; they can be added post-deploy via .env.
push_file 110 /opt/kitchen/.env <<EOF
APP_SLUG=kitchen
DATABASE_URL=postgresql://kitchen:${KITCHEN_DB_PASS}@10.10.10.100:5432/kitchen_db
CENTRAL_AUTH_SECRET=${CENTRAL_AUTH_SECRET}
STACK_INTERNAL_SECRET=${STACK_INTERNAL_SECRET:-}
SETTINGS_URL=http://10.10.10.116:3080
SETTINGS_SECRET=${SETTINGS_SECRET}
AZURE_DI_ENDPOINT=${AZURE_DI_ENDPOINT:-}
AZURE_DI_KEY=${AZURE_DI_KEY:-}
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
OFFICE_IP_CHECK=${OFFICE_IP_CHECK:-${OFFICE_IP:-disabled}}
VITE_HOTEL_NAME=${SITE_NAME:-Hotel}
EOF
# MSSQL ODBC drivers add ~5 min to the build
msg_info "Building kitchen (MSSQL ODBC drivers — takes ~5 min)"
local build_out
if ! build_out=$(pct exec 110 -- bash -c "cd /opt/kitchen && docker compose up -d --build 2>&1"); then
msg_error "docker compose build failed in LXC 110:
${build_out}"
fi
msg_info "Waiting for kitchen"
wait_healthy 110 "http://localhost:3080/kitchen/health" 200 \
&& msg_ok "Kitchen running at 10.10.10.110:3080" \
|| msg_warn "Kitchen may need extra time — check LXC 110 logs"
msg_info "Seeding kitchen 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 ('kitchen', 'Kitchen', 'Invoice/GP, recipes, menus and kitchen management', '/kitchen', 'ChefHat', '#0d9488', 'Kitchen', '10.10.10.110', 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 app', 'Access the kitchen app and dashboard', 1),
('invoices', 'View invoices', 'View invoice list, details and search', 2),
('invoices_manage', 'Manage invoices', 'Upload, edit, approve and delete invoices', 3),
('disputes', 'Disputes', 'Open, manage and resolve invoice disputes', 4),
('logbook', 'Wastage logbook', 'Record and view wastage logbook entries', 5),
('orders', 'Purchase orders', 'Create and manage purchase orders', 6),
('recipes', 'Recipes', 'View and edit recipes, ingredients and allergens', 7),
('menus', 'Menus', 'Build, edit and publish menus and dishes', 8),
('manage_flags', 'Manage flags', 'Review and dismiss food compliance and allergen flags', 9),
('settings', 'Manage settings', 'App settings: integrations, API keys, SambaPOS config', 10)
) AS c(slug, name, description, sort_order)
WHERE a.slug = 'kitchen'
ON CONFLICT (app_id, slug) DO NOTHING;
" &>/dev/null \
&& msg_ok "kitchen seeded into auth DB" \
|| msg_warn "Seed failed — run auth db.js manually"
npm_add_location "/kitchen/" "10.10.10.110" 3080
}
deploy_kds() {
msg_step "KDS (LXC 125 · 10.10.10.125)"
# KDS uses kitchen_db (shared schema). KITCHEN_DB_PASS must exist (deploy_kitchen first).
if [[ -z "${KITCHEN_DB_PASS:-}" ]]; then
msg_error "KITCHEN_DB_PASS not set — deploy kitchen first, or add it to ${CREDS_FILE}"
fi
msg_info "Creating LXC 125"
create_lxc 125 "10.10.10.125" "kds" 1024 1
msg_ok "LXC 125 created"
msg_info "Installing Docker"
install_docker 125
install_mgmt_key 125
msg_ok "Docker + SSH ready"
msg_info "Deploying kds"
deploy_service 125 "kds" "${REPO_ROOT}/kds" /opt/kds
push_file 125 /opt/kds/.env <<EOF
APP_SLUG=kds
DATABASE_URL=postgresql://kitchen:${KITCHEN_DB_PASS}@10.10.10.100:5432/kitchen_db
CENTRAL_AUTH_SECRET=${CENTRAL_AUTH_SECRET}
VITE_HOTEL_NAME=${SITE_NAME:-Hotel}
EOF
local build_out
if ! build_out=$(pct exec 125 -- bash -c "cd /opt/kds && docker compose up -d --build 2>&1"); then
msg_error "docker compose build failed in LXC 125:
${build_out}"
fi
msg_info "Waiting for kds"
wait_healthy 125 "http://localhost:3080/kds/health" 200 \
&& msg_ok "KDS running at 10.10.10.125:3080" \
|| msg_warn "KDS may need extra time — check LXC 125 logs"
msg_info "Seeding kds 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 ('kds', 'Kitchen Display', 'SambaPOS ticket feed and course flow display', '/kds', 'Monitor', '#0d9488', 'Kitchen', '10.10.10.125', 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 board', 'View the KDS ticket board', 1),
('manage', 'Manage courses', 'Call away, mark sent and clear courses on live tickets', 2),
('settings', 'Manage settings', 'KDS timer thresholds, SambaPOS GraphQL config', 3)
) AS c(slug, name, description, sort_order)
WHERE a.slug = 'kds'
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 = 'kds')
WHERE r.slug = 'staff' AND ac.slug IN ('view', 'manage')
AND NOT EXISTS (SELECT 1 FROM role_capabilities rc WHERE rc.role_id = r.id AND rc.capability_id = ac.id)
ON CONFLICT DO NOTHING;
" &>/dev/null \
&& msg_ok "kds seeded into auth DB" \
|| msg_warn "Seed failed — run auth db.js manually"
npm_add_location "/kds/" "10.10.10.125" 3080
}
# ════════════════════════════════════════════════════════════════════════════
# NPM PROXY HOSTS (via API)
# ════════════════════════════════════════════════════════════════════════════
@ -1751,7 +1914,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"
VALID="postgres auth portal npm management noticeboard settings cashup hk-planner twin-optimiser room-planner maintenance forecasting rates reports kitchen kds"
[[ -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
@ -1781,6 +1944,8 @@ if [[ "${1:-}" == "--only" ]]; then
"$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)"
[[ -z "${REPORTS_DB_PASS:-}" ]] && _append_secret REPORTS_DB_PASS \
"$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 24)"
[[ -z "${KITCHEN_DB_PASS:-}" ]] && _append_secret KITCHEN_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 ]] \
@ -1803,6 +1968,8 @@ if [[ "${1:-}" == "--only" ]]; then
forecasting) deploy_forecasting ;;
rates) deploy_rates ;;
reports) deploy_reports ;;
kitchen) deploy_kitchen ;;
kds) deploy_kds ;;
esac
exit 0
fi
@ -1828,5 +1995,7 @@ deploy_maintenance
deploy_forecasting
deploy_rates
deploy_reports
deploy_kitchen
deploy_kds
configure_npm_proxy_hosts
print_summary