# syntax=docker/dockerfile:1.7
#
# sap-mcp-bridge
# -------------------------------------------------------------------
# One container, five SAP MCP servers, all reachable through a single
# external HTTP port via an internal nginx reverse proxy. This makes
# the container deployable behind Traefik/Runtipi where only one port
# per app can be exposed.
#
#   External (single port — Runtipi binds it):
#     /              landing page
#     /healthz       aggregate health probe
#     /cap/mcp       sap-cap          (cap-js-mcp-server)
#     /cap/healthz
#     /abap/mcp      sap-abap-adt     (mcp-abap-adt)
#     /abap/healthz
#     /odata/mcp     sap-odata        (btp-sap-odata-to-mcp-server)
#     /odata/healthz
#     /ui5/mcp       ui5-mcp-server   (ui5mcp)
#     /ui5/healthz
#     /fiori/mcp     fiori-mcp-server (fiori-mcp)
#     /fiori/healthz
#
# Internally:
#   127.0.0.1:9001..9005  individual supergateway HTTP endpoints
#   127.0.0.1:8080        nginx (the only port EXPOSEd)
# -------------------------------------------------------------------

FROM node:24-bookworm-slim

ENV NODE_ENV=production \
    NPM_CONFIG_FUND=false \
    NPM_CONFIG_AUDIT=false \
    NPM_CONFIG_UPDATE_NOTIFIER=false

# tini  : PID-1 init that reaps zombies and forwards SIGTERM cleanly
# nginx : in-container reverse proxy (single externally-visible port)
# gosu  : drop privileges from root → mcp after we've chowned /workspace
# curl  : healthcheck client
RUN apt-get update \
 && apt-get install -y --no-install-recommends \
      tini ca-certificates curl nginx gosu \
 && rm -rf /var/lib/apt/lists/* /var/log/nginx/*

# Install all MCP servers + the stdio<->HTTP bridge globally.
# All versions pinned for reproducibility — a broken supergateway or
# MCP-server release would silently break every endpoint on the next
# rebuild. Bump deliberately, not by accident.
RUN npm install -g --omit=dev --no-audit --no-fund \
      supergateway@3.4.3 \
      @iflow-mcp/cap-js-mcp-server@0.0.3 \
      @iflow-mcp/mcp-abap-adt@1.1.0 \
      @iflow-mcp/btp-sap-odata-to-mcp-server@1.0.0 \
      @ui5/mcp-server@0.2.11 \
      @sap-ux/fiori-mcp-server@0.6.49

# Non-root runtime user. Container still starts as root so the
# entrypoint can chown /workspace; it then drops to this user via gosu.
# (No -r flag: Debian's SYS_UID_MAX is 999, and we want UID 10001 — the
# warning useradd -r produces is harmless but ugly in build logs.)
RUN useradd -u 10001 -g nogroup -d /home/mcp -m mcp

COPY nginx.conf /etc/nginx/nginx.conf
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

# /workspace is the project root the cap / ui5 / fiori servers operate on.
# It's the volume mount target — host-side it's bound to a user-config path
# from the Runtipi compose. Pre-creating it here makes "no volume bound"
# (e.g. local docker run) still work.
RUN mkdir -p /workspace && chown -R mcp:nogroup /workspace
WORKDIR /workspace

EXPOSE 8080

# Cheap liveness: nginx aggregates /healthz; if it answers 200, the
# container is "alive enough" from Traefik's perspective.
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
  CMD curl -fsS http://127.0.0.1:8080/healthz || exit 1

ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["/usr/local/bin/entrypoint.sh"]
