AI Gateway Go 0.10.0 源码快照 + 旗舰版需求规划报告

M0-M7 已完成:核心网关(身份/RBAC/TOTP/OIDC/SAML/Provider/配额/路由/内容策略/审计/定价)+ 资源市场(MCP/Skills/数字员工)。
含 22 个 PostgreSQL 迁移、管理端/门户端前端源码、OpenAPI 契约、部署 compose。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ben
2026-08-12 11:45:54 +08:00
commit 5759c1862e
807 changed files with 114727 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
FROM golang:1.26.5-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY cmd ./cmd
COPY internal ./internal
ARG VERSION=dev
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w -X main.version=${VERSION}" -o /out/gateway-api ./cmd/gateway-api
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/gateway-migrator ./cmd/gateway-migrator
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/gateway-bootstrap ./cmd/gateway-bootstrap
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/gateway-outbox-worker ./cmd/gateway-outbox-worker
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/gateway-maintenance ./cmd/gateway-maintenance
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/gateway-notification-worker ./cmd/gateway-notification-worker
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/gateway-loadtest ./cmd/gateway-loadtest
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/gateway-legacy-import ./cmd/gateway-legacy-import
FROM alpine:3.22
RUN apk add --no-cache ca-certificates tzdata \
&& addgroup -S gateway \
&& adduser -S -G gateway gateway
WORKDIR /app
COPY --from=builder /out/gateway-api /usr/local/bin/gateway-api
COPY --from=builder /out/gateway-migrator /usr/local/bin/gateway-migrator
COPY --from=builder /out/gateway-bootstrap /usr/local/bin/gateway-bootstrap
COPY --from=builder /out/gateway-outbox-worker /usr/local/bin/gateway-outbox-worker
COPY --from=builder /out/gateway-maintenance /usr/local/bin/gateway-maintenance
COPY --from=builder /out/gateway-notification-worker /usr/local/bin/gateway-notification-worker
COPY --from=builder /out/gateway-loadtest /usr/local/bin/gateway-loadtest
COPY --from=builder /out/gateway-legacy-import /usr/local/bin/gateway-legacy-import
COPY migrations ./migrations
USER gateway
EXPOSE 8080
ENTRYPOINT ["gateway-api"]
+16
View File
@@ -0,0 +1,16 @@
FROM node:24-alpine AS builder
ARG APP=admin
WORKDIR /src/web
RUN corepack enable
COPY web/package.json web/pnpm-lock.yaml web/pnpm-workspace.yaml ./
COPY web/apps ./apps
COPY web/packages ./packages
RUN CI=1 corepack pnpm@10.15.1 install --frozen-lockfile
RUN corepack pnpm@10.15.1 --filter @aigateway/${APP} build
FROM nginx:1.29-alpine
ARG APP=admin
COPY deploy/nginx-web.conf /etc/nginx/conf.d/default.conf
RUN sed -i "s/__APP__/${APP}/g" /etc/nginx/conf.d/default.conf
COPY --from=builder /src/web/apps/${APP}/dist /usr/share/nginx/html/${APP}
EXPOSE 80
+74
View File
@@ -0,0 +1,74 @@
# Production deployment
This bundle builds the Go services and both Art Design Pro applications from
source. PostgreSQL and two Redis roles are included; MinIO/S3 and ClickHouse are
not required.
## Prerequisites
- Docker Engine with Compose v2
- At least 4 CPU cores, 8 GiB RAM and 30 GiB free disk for an initial build
- An external TLS reverse proxy or load balancer
- A backup destination for the PostgreSQL volume
## First deployment
Run all commands from the repository root:
```bash
cp deploy/production.env.example deploy/production.env
chmod 600 deploy/production.env
# Edit deploy/production.env and replace every CHANGE_ME value.
docker compose \
--env-file deploy/production.env \
-f deploy/docker-compose.production.yml \
config --quiet
docker compose \
--env-file deploy/production.env \
-f deploy/docker-compose.production.yml \
up -d --build
```
Create the initial administrator once:
```bash
docker compose \
--env-file deploy/production.env \
-f deploy/docker-compose.production.yml \
--profile tools run --rm bootstrap-admin
```
Then remove `BOOTSTRAP_ADMIN_PASSWORD` from `deploy/production.env` and use the
admin UI to create database-backed gateway API keys.
## Endpoints
- API and OpenAI-compatible gateway: `127.0.0.1:8080`
- Admin UI: `http://127.0.0.1:8081/admin/`
- Portal UI: `http://127.0.0.1:8082/portal/`
- Liveness/readiness: `/healthz` and `/readyz`
Ports bind to loopback by default. Terminate TLS at a reverse proxy and forward
to these endpoints. Change `*_BIND_IP` only when the host firewall and network
policy are already in place.
## Operations
Check status and logs:
```bash
docker compose --env-file deploy/production.env -f deploy/docker-compose.production.yml ps
docker compose --env-file deploy/production.env -f deploy/docker-compose.production.yml logs --tail=200 gateway-api
curl --fail http://127.0.0.1:8080/readyz
```
For upgrades, back up PostgreSQL first, change `GATEWAY_VERSION`, then run the
same `up -d --build` command. The one-shot migrator applies forward migrations
before the API starts. Do not use `docker compose down -v` in production because
it removes persistent data.
The bundled database URLs use `sslmode=disable` only for the private Compose
network. When using an external PostgreSQL or Redis service, require TLS and use
`sslmode=verify-full` / `rediss://` as supported by that service.
+189
View File
@@ -0,0 +1,189 @@
name: ai-gateway
x-backend-build: &backend-build
context: ..
dockerfile: deploy/Dockerfile
args:
VERSION: ${GATEWAY_VERSION:-0.10.0}
x-gateway-environment: &gateway-environment
APP_ENV: production
HTTP_ADDR: :8080
DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required}
REDIS_CRITICAL_URL: ${REDIS_CRITICAL_URL:?REDIS_CRITICAL_URL is required}
REDIS_CACHE_URL: ${REDIS_CACHE_URL:-}
GATEWAY_BOOTSTRAP_API_KEY_ENABLED: ${GATEWAY_BOOTSTRAP_API_KEY_ENABLED:-false}
GATEWAY_BOOTSTRAP_API_KEY: ${GATEWAY_BOOTSTRAP_API_KEY:-}
CREDENTIAL_MASTER_KEY: ${CREDENTIAL_MASTER_KEY:?CREDENTIAL_MASTER_KEY is required}
CREDENTIAL_KEK_VERSION: ${CREDENTIAL_KEK_VERSION:-1}
CREDENTIAL_KEK_KEYRING: ${CREDENTIAL_KEK_KEYRING:-}
ALLOW_PRIVATE_PROVIDER_URLS: ${ALLOW_PRIVATE_PROVIDER_URLS:-false}
ALLOW_PRIVATE_TOOL_URLS: ${ALLOW_PRIVATE_TOOL_URLS:-false}
ALLOW_PRIVATE_WEBHOOK_URLS: ${ALLOW_PRIVATE_WEBHOOK_URLS:-false}
UPSTREAM_FALLBACK_ENABLED: ${UPSTREAM_FALLBACK_ENABLED:-false}
UPSTREAM_BASE_URL: ${UPSTREAM_BASE_URL:-https://api.openai.com}
UPSTREAM_API_KEY: ${UPSTREAM_API_KEY:-}
SHADOW_BASE_URL: ${SHADOW_BASE_URL:-}
SHADOW_API_KEY: ${SHADOW_API_KEY:-}
SHADOW_SAMPLE_RATE: ${SHADOW_SAMPLE_RATE:-0}
x-backend-service: &backend-service
image: ai-gateway-go:${GATEWAY_VERSION:-0.10.0}
build: *backend-build
environment: *gateway-environment
read_only: true
tmpfs:
- /tmp:size=64m,mode=1777
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB:-gateway}
POSTGRES_USER: ${POSTGRES_USER:-gateway}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 3s
retries: 30
restart: unless-stopped
redis-critical:
image: redis:8.2-alpine
environment:
REDIS_PASSWORD: ${REDIS_PASSWORD:?REDIS_PASSWORD is required}
command:
- sh
- -c
- exec redis-server --requirepass "$${REDIS_PASSWORD}" --appendonly yes --appendfsync everysec --maxmemory-policy noeviction
volumes:
- redis-critical-data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli --no-auth-warning -a \"$${REDIS_PASSWORD}\" ping | grep -q PONG"]
interval: 5s
timeout: 3s
retries: 30
restart: unless-stopped
redis-cache:
image: redis:8.2-alpine
environment:
REDIS_PASSWORD: ${REDIS_PASSWORD:?REDIS_PASSWORD is required}
REDIS_CACHE_MAXMEMORY: ${REDIS_CACHE_MAXMEMORY:-256mb}
command:
- sh
- -c
- exec redis-server --requirepass "$${REDIS_PASSWORD}" --maxmemory "$${REDIS_CACHE_MAXMEMORY}" --maxmemory-policy allkeys-lfu --save ""
healthcheck:
test: ["CMD-SHELL", "redis-cli --no-auth-warning -a \"$${REDIS_PASSWORD}\" ping | grep -q PONG"]
interval: 5s
timeout: 3s
retries: 30
restart: unless-stopped
migrator:
<<: *backend-service
entrypoint: ["gateway-migrator"]
depends_on:
postgres:
condition: service_healthy
restart: "no"
gateway-api:
<<: *backend-service
ports:
- "${GATEWAY_BIND_IP:-127.0.0.1}:${GATEWAY_PORT:-8080}:8080"
depends_on:
migrator:
condition: service_completed_successfully
redis-critical:
condition: service_healthy
redis-cache:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8080/readyz"]
interval: 10s
timeout: 3s
retries: 30
start_period: 10s
restart: unless-stopped
admin-web:
image: ai-gateway-admin:${GATEWAY_VERSION:-0.10.0}
build:
context: ..
dockerfile: deploy/Dockerfile.web
args:
APP: admin
ports:
- "${WEB_BIND_IP:-127.0.0.1}:${ADMIN_PORT:-8081}:80"
depends_on:
gateway-api:
condition: service_healthy
restart: unless-stopped
portal-web:
image: ai-gateway-portal:${GATEWAY_VERSION:-0.10.0}
build:
context: ..
dockerfile: deploy/Dockerfile.web
args:
APP: portal
ports:
- "${WEB_BIND_IP:-127.0.0.1}:${PORTAL_PORT:-8082}:80"
depends_on:
gateway-api:
condition: service_healthy
restart: unless-stopped
outbox-worker:
<<: *backend-service
entrypoint: ["gateway-outbox-worker"]
depends_on:
migrator:
condition: service_completed_successfully
redis-critical:
condition: service_healthy
restart: unless-stopped
maintenance-worker:
<<: *backend-service
entrypoint: ["gateway-maintenance"]
depends_on:
migrator:
condition: service_completed_successfully
restart: unless-stopped
notification-worker:
<<: *backend-service
entrypoint: ["gateway-notification-worker"]
depends_on:
migrator:
condition: service_completed_successfully
redis-critical:
condition: service_healthy
restart: unless-stopped
bootstrap-admin:
<<: *backend-service
profiles: ["tools"]
entrypoint: ["gateway-bootstrap"]
environment:
<<: *gateway-environment
BOOTSTRAP_ADMIN_USERNAME: ${BOOTSTRAP_ADMIN_USERNAME:-admin}
BOOTSTRAP_ADMIN_PASSWORD: ${BOOTSTRAP_ADMIN_PASSWORD:-}
depends_on:
migrator:
condition: service_completed_successfully
restart: "no"
volumes:
postgres-data:
redis-critical-data:
+154
View File
@@ -0,0 +1,154 @@
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: gateway
POSTGRES_USER: gateway
POSTGRES_PASSWORD: gateway
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gateway -d gateway"]
interval: 5s
timeout: 3s
retries: 20
redis-critical:
image: redis:8.2-alpine
command: ["redis-server", "--appendonly", "yes", "--appendfsync", "everysec", "--maxmemory-policy", "noeviction"]
volumes:
- redis-critical-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 20
redis-cache:
image: redis:8.2-alpine
command: ["redis-server", "--maxmemory", "256mb", "--maxmemory-policy", "allkeys-lfu", "--save", ""]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 20
migrator:
build:
context: ..
dockerfile: deploy/Dockerfile
args:
VERSION: ${GATEWAY_VERSION:-0.10.0}
entrypoint: ["gateway-migrator"]
environment: &gateway-environment
APP_ENV: local
HTTP_ADDR: :8080
DATABASE_URL: postgres://gateway:gateway@postgres:5432/gateway?sslmode=disable
REDIS_CRITICAL_URL: redis://redis-critical:6379/0
REDIS_CACHE_URL: redis://redis-cache:6379/0
GATEWAY_BOOTSTRAP_API_KEY: ${GATEWAY_BOOTSTRAP_API_KEY:-}
GATEWAY_BOOTSTRAP_API_KEY_ENABLED: ${GATEWAY_BOOTSTRAP_API_KEY_ENABLED:-false}
CREDENTIAL_MASTER_KEY: ${CREDENTIAL_MASTER_KEY:-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=}
CREDENTIAL_KEK_VERSION: 1
CREDENTIAL_KEK_KEYRING: ${CREDENTIAL_KEK_KEYRING:-}
ALLOW_PRIVATE_TOOL_URLS: ${ALLOW_PRIVATE_TOOL_URLS:-false}
ALLOW_PRIVATE_WEBHOOK_URLS: ${ALLOW_PRIVATE_WEBHOOK_URLS:-false}
UPSTREAM_BASE_URL: ${UPSTREAM_BASE_URL:-https://api.openai.com}
UPSTREAM_API_KEY: ${UPSTREAM_API_KEY:-}
SHADOW_BASE_URL: ${SHADOW_BASE_URL:-}
SHADOW_API_KEY: ${SHADOW_API_KEY:-}
SHADOW_SAMPLE_RATE: ${SHADOW_SAMPLE_RATE:-0}
depends_on:
postgres:
condition: service_healthy
gateway-api:
build:
context: ..
dockerfile: deploy/Dockerfile
args:
VERSION: ${GATEWAY_VERSION:-0.10.0}
environment: *gateway-environment
ports:
- "${GATEWAY_PORT:-8080}:8080"
depends_on:
migrator:
condition: service_completed_successfully
redis-critical:
condition: service_healthy
redis-cache:
condition: service_healthy
restart: unless-stopped
admin-web:
build:
context: ..
dockerfile: deploy/Dockerfile.web
args:
APP: admin
ports:
- "${ADMIN_PORT:-8081}:80"
depends_on:
gateway-api:
condition: service_started
restart: unless-stopped
portal-web:
build:
context: ..
dockerfile: deploy/Dockerfile.web
args:
APP: portal
ports:
- "${PORTAL_PORT:-8082}:80"
depends_on:
gateway-api:
condition: service_started
restart: unless-stopped
outbox-worker:
build:
context: ..
dockerfile: deploy/Dockerfile
args:
VERSION: ${GATEWAY_VERSION:-0.10.0}
entrypoint: ["gateway-outbox-worker"]
environment: *gateway-environment
depends_on:
migrator:
condition: service_completed_successfully
redis-critical:
condition: service_healthy
restart: unless-stopped
maintenance-worker:
build:
context: ..
dockerfile: deploy/Dockerfile
args:
VERSION: ${GATEWAY_VERSION:-0.10.0}
entrypoint: ["gateway-maintenance"]
environment: *gateway-environment
depends_on:
migrator:
condition: service_completed_successfully
restart: unless-stopped
notification-worker:
build:
context: ..
dockerfile: deploy/Dockerfile
args:
VERSION: ${GATEWAY_VERSION:-0.10.0}
entrypoint: ["gateway-notification-worker"]
environment: *gateway-environment
depends_on:
migrator:
condition: service_completed_successfully
redis-critical:
condition: service_healthy
restart: unless-stopped
volumes:
postgres-data:
redis-critical-data:
+71
View File
@@ -0,0 +1,71 @@
server {
listen 80;
server_name _;
# Keep Location headers relative (Location: /admin/) instead of letting
# nginx absolute_redirect rebuild them from $host + the listening port.
# The gateway is commonly published behind a non-standard port (e.g. 18081),
# and an absolute redirect would drop that port and send browsers to :80.
absolute_redirect off;
# Match the gateway's HTTP_MAX_BODY_BYTES (default 32 MiB). nginx's default
# of 1 MiB otherwise rejects large prompts and knowledge-base imports with
# 413 before they ever reach the gateway.
client_max_body_size 32m;
root /usr/share/nginx/html;
index index.html;
location = / {
return 302 /__APP__/;
}
location = /__APP__ {
return 301 /__APP__/;
}
location = /healthz {
proxy_pass http://gateway-api:8080/healthz;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location = /readyz {
proxy_pass http://gateway-api:8080/readyz;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /api/ {
proxy_pass http://gateway-api:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Request-ID $request_id;
proxy_set_header X-Forwarded-Proto $scheme;
# nginx 是控制台(/api/)的唯一入口:用 $remote_addr 覆盖 X-Forwarded-For
# 避免客户端自带 X-Forwarded-For 头伪造来源 IP、绕过登录限流。
proxy_set_header X-Forwarded-For $remote_addr;
}
location /v1/ {
proxy_pass http://gateway-api:8080;
proxy_http_version 1.1;
proxy_buffering off;
proxy_read_timeout 600s;
proxy_set_header Host $host;
proxy_set_header X-Request-ID $request_id;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Vite emits assets and client-side routes below /admin/ or /portal/.
location /__APP__/ {
try_files $uri $uri/ /__APP__/index.html;
}
location / {
return 404;
}
}
+42
View File
@@ -0,0 +1,42 @@
# Copy this file to deploy/production.env and replace every CHANGE_ME value.
# Keep production.env outside version control and restrict it to the deploy user.
GATEWAY_VERSION=0.10.0
POSTGRES_DB=gateway
POSTGRES_USER=gateway
POSTGRES_PASSWORD=CHANGE_ME_LONG_RANDOM_POSTGRES_PASSWORD
REDIS_PASSWORD=CHANGE_ME_LONG_RANDOM_REDIS_PASSWORD
# URL-encode special characters in passwords used in URLs.
DATABASE_URL=postgres://gateway:CHANGE_ME_URL_ENCODED_POSTGRES_PASSWORD@postgres:5432/gateway?sslmode=disable
REDIS_CRITICAL_URL=redis://:CHANGE_ME_URL_ENCODED_REDIS_PASSWORD@redis-critical:6379/0
REDIS_CACHE_URL=redis://:CHANGE_ME_URL_ENCODED_REDIS_PASSWORD@redis-cache:6379/0
# Generate once with: openssl rand -base64 32
# Never rotate this value without following a KEK rotation procedure.
CREDENTIAL_MASTER_KEY=CHANGE_ME_BASE64_32_BYTE_KEY
CREDENTIAL_KEK_VERSION=1
CREDENTIAL_KEK_KEYRING=
# Disabled by default. Create database-backed API keys after bootstrap.
GATEWAY_BOOTSTRAP_API_KEY_ENABLED=false
GATEWAY_BOOTSTRAP_API_KEY=
UPSTREAM_FALLBACK_ENABLED=false
UPSTREAM_BASE_URL=https://api.openai.com
UPSTREAM_API_KEY=
ALLOW_PRIVATE_PROVIDER_URLS=false
ALLOW_PRIVATE_TOOL_URLS=false
ALLOW_PRIVATE_WEBHOOK_URLS=false
GATEWAY_BIND_IP=127.0.0.1
GATEWAY_PORT=8080
WEB_BIND_IP=127.0.0.1
ADMIN_PORT=8081
PORTAL_PORT=8082
REDIS_CACHE_MAXMEMORY=256mb
# Used only for the one-time bootstrap-admin command; remove after use.
BOOTSTRAP_ADMIN_USERNAME=admin
BOOTSTRAP_ADMIN_PASSWORD=CHANGE_ME_AT_LEAST_12_CHARACTERS