Task 14 of portal-shell data abstraction plan (M3 security). router.yaml: - persisted_queries: enabled + require_manifest (env-controlled) - limits: max_depth=10, max_cost=1000, max_batch_size=5 - introspection: env-controlled (prod=false, dev=true) docker-compose.yml: - Mount portal-shell/public/pq-manifest.json as read-only volume - Add APOLLO_REQUIRE_PQ_MANIFEST + APOLLO_ROUTER_INTROSPECTION env vars - Dev defaults preserve current behavior (manifest optional) entrypoint.sh: - Pre-start check: if require_manifest=true, fail fast when manifest missing - Dev mode: warn but continue when manifest absent Production enables strict mode via env: APOLLO_REQUIRE_PQ_MANIFEST=true APOLLO_ROUTER_INTROSPECTION=false
98 lines
3.2 KiB
Bash
98 lines
3.2 KiB
Bash
#!/bin/bash
|
||
# Apollo Router 启动脚本
|
||
#
|
||
# 1. 等待所有子图 /graphql 端点就绪
|
||
# 2. 使用 rover 组合 supergraph SDL
|
||
# 3. 启动 apollo-router
|
||
|
||
set -e
|
||
|
||
SUBGRAPHS=(
|
||
"iam:3002"
|
||
"core-edu:3004"
|
||
"content:3005"
|
||
"msg:3007"
|
||
"ai:3008"
|
||
"data-ana:3006"
|
||
)
|
||
|
||
echo "[apollo-router] Waiting for subgraphs to be ready..."
|
||
|
||
wait_for_subgraph() {
|
||
local name="$1"
|
||
local host="$2"
|
||
local port="$3"
|
||
local max_retries=60
|
||
local retry=0
|
||
|
||
while [ $retry -lt $max_retries ]; do
|
||
if curl -sf -o /dev/null "http://${host}:${port}/healthz" 2>/dev/null; then
|
||
echo "[apollo-router] ${name} is ready (http://${host}:${port})"
|
||
return 0
|
||
fi
|
||
retry=$((retry + 1))
|
||
echo "[apollo-router] Waiting for ${name} at ${host}:${port} (attempt ${retry}/${max_retries})"
|
||
sleep 3
|
||
done
|
||
|
||
echo "[apollo-router] WARNING: ${name} not ready after ${max_retries} attempts, continuing anyway..."
|
||
return 0
|
||
}
|
||
|
||
for entry in "${SUBGRAPHS[@]}"; do
|
||
name="${entry%%:*}"
|
||
hostport="${entry#*:}"
|
||
host="${hostport%%:*}"
|
||
port="${hostport##*:}"
|
||
wait_for_subgraph "$name" "$host" "$port"
|
||
done
|
||
|
||
# 组合 supergraph SDL
|
||
echo "[apollo-router] Composing supergraph SDL..."
|
||
export APOLLO_ELV2_LICENSE=accept
|
||
|
||
max_compose_retries=5
|
||
compose_retry=0
|
||
while [ $compose_retry -lt $max_compose_retries ]; do
|
||
compose_retry=$((compose_retry + 1))
|
||
if rover supergraph compose --config /dist/supergraph.yaml --output /tmp/supergraph.graphql 2>&1; then
|
||
echo "[apollo-router] Supergraph composed successfully"
|
||
break
|
||
fi
|
||
echo "[apollo-router] Compose attempt ${compose_retry}/${max_compose_retries} failed, retrying in 5s..."
|
||
sleep 5
|
||
done
|
||
|
||
if [ ! -f /tmp/supergraph.graphql ]; then
|
||
echo "[apollo-router] ERROR: Failed to compose supergraph after ${max_compose_retries} attempts"
|
||
exit 1
|
||
fi
|
||
|
||
# PQ manifest 检查(v2.1 M3 安全加固)
|
||
# 关联:portal-shell spec §4.2、§4.3
|
||
# - 生产模式(APOLLO_REQUIRE_PQ_MANIFEST=true)必须存在 manifest,否则启动失败
|
||
# - 开发模式(APOLLO_REQUIRE_PQ_MANIFEST=false)manifest 不存在时仅警告
|
||
MANIFEST_PATH="/etc/apollo-router/pq-manifest.json"
|
||
REQUIRE_MANIFEST="${APOLLO_REQUIRE_PQ_MANIFEST:-false}"
|
||
|
||
if [ "${REQUIRE_MANIFEST}" = "true" ]; then
|
||
if [ ! -f "${MANIFEST_PATH}" ]; then
|
||
echo "[apollo-router] ERROR: APOLLO_REQUIRE_PQ_MANIFEST=true but manifest not found at ${MANIFEST_PATH}"
|
||
echo "[apollo-router] Run 'pnpm --filter @edu/portal-shell run generate-pq-manifest' to generate it."
|
||
exit 1
|
||
fi
|
||
QUERY_COUNT=$(grep -c '"' "${MANIFEST_PATH}" 2>/dev/null || echo "0")
|
||
echo "[apollo-router] PQ manifest loaded (require_manifest=true): ${MANIFEST_PATH}"
|
||
else
|
||
if [ ! -f "${MANIFEST_PATH}" ]; then
|
||
echo "[apollo-router] WARNING: PQ manifest not found at ${MANIFEST_PATH} (require_manifest=false, continuing)"
|
||
echo "[apollo-router] APQ will accept hash-only requests but cannot resolve unknown hashes."
|
||
else
|
||
echo "[apollo-router] PQ manifest loaded (require_manifest=false): ${MANIFEST_PATH}"
|
||
fi
|
||
fi
|
||
|
||
# 启动 router(router 二进制位于 /dist/router,由基础镜像 ghcr.io/apollographql/router 提供)
|
||
echo "[apollo-router] Starting Apollo Router on port 3000..."
|
||
exec /dist/router --config /dist/configuration.yaml --supergraph /tmp/supergraph.graphql --hot-reload
|