Spaces:
Running
Running
github-actions[bot] commited on
Commit ·
34d56f8
0
Parent(s):
Deploy HF adapter from 81228e7b28a9d511f79b2f59b1c00ef60bcc9374
Browse files- .env.example +17 -0
- Dockerfile +70 -0
- README.md +59 -0
- start.sh +101 -0
.env.example
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hugging Face Space runtime example for official Go Grok2API
|
| 2 |
+
#
|
| 3 |
+
# 用法:
|
| 4 |
+
# 1. 写入 HF Storage:/data/config.yaml(推荐)
|
| 5 |
+
# 2. 或用下面环境变量在首次启动时注入密钥
|
| 6 |
+
# 3. 不要把真实密钥提交到 Git
|
| 7 |
+
|
| 8 |
+
DATA_DIR=/data
|
| 9 |
+
SERVER_PORT=7860
|
| 10 |
+
GROK2API_CONFIG_SOURCE=/data/config.yaml
|
| 11 |
+
|
| 12 |
+
# 首次 seed 配置时可选注入(之后以 /data/config.yaml 为准)
|
| 13 |
+
# GROK2API_JWT_SECRET= # openssl rand -hex 32
|
| 14 |
+
# GROK2API_CREDENTIAL_ENCRYPTION_KEY= # openssl rand -base64 32
|
| 15 |
+
# GROK2API_ADMIN_USERNAME=admin
|
| 16 |
+
# GROK2API_ADMIN_PASSWORD=replace_me
|
| 17 |
+
# GROK2API_SECURE_COOKIES=true
|
Dockerfile
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hugging Face Space adapter for official chenyme/grok2api (Go + React rewrite).
|
| 2 |
+
# Build always clones upstream; this Space repo only ships the adapter layer.
|
| 3 |
+
|
| 4 |
+
ARG NODE_VERSION=22
|
| 5 |
+
ARG GO_VERSION=1.26
|
| 6 |
+
ARG ALPINE_VERSION=3.23
|
| 7 |
+
ARG GROK2API_REPO=https://github.com/chenyme/grok2api.git
|
| 8 |
+
ARG GROK2API_REF=main
|
| 9 |
+
|
| 10 |
+
FROM alpine:${ALPINE_VERSION} AS src
|
| 11 |
+
ARG GROK2API_REPO
|
| 12 |
+
ARG GROK2API_REF
|
| 13 |
+
RUN apk add --no-cache git ca-certificates \
|
| 14 |
+
&& git clone --depth 1 --branch "${GROK2API_REF}" "${GROK2API_REPO}" /src \
|
| 15 |
+
&& test -f /src/backend/go.mod \
|
| 16 |
+
&& test -f /src/frontend/package.json \
|
| 17 |
+
&& test -f /src/config.example.yaml \
|
| 18 |
+
&& test -f /src/docker/entrypoint.sh
|
| 19 |
+
|
| 20 |
+
FROM --platform=$BUILDPLATFORM node:${NODE_VERSION}-alpine AS frontend-builder
|
| 21 |
+
WORKDIR /src/frontend
|
| 22 |
+
RUN corepack enable
|
| 23 |
+
COPY --from=src /src/frontend/package.json /src/frontend/pnpm-lock.yaml ./
|
| 24 |
+
RUN pnpm config set store-dir /pnpm/store \
|
| 25 |
+
&& pnpm fetch --frozen-lockfile \
|
| 26 |
+
&& pnpm install --offline --frozen-lockfile
|
| 27 |
+
COPY --from=src /src/frontend/index.html /src/frontend/vite.config.ts /src/frontend/tsconfig.json /src/frontend/tsconfig.app.json /src/frontend/tsconfig.node.json ./
|
| 28 |
+
COPY --from=src /src/frontend/public ./public
|
| 29 |
+
COPY --from=src /src/frontend/src ./src
|
| 30 |
+
RUN pnpm build
|
| 31 |
+
|
| 32 |
+
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS backend-builder
|
| 33 |
+
ARG TARGETOS
|
| 34 |
+
ARG TARGETARCH
|
| 35 |
+
WORKDIR /src/backend
|
| 36 |
+
RUN apk add --no-cache ca-certificates git
|
| 37 |
+
COPY --from=src /src/backend/go.mod /src/backend/go.sum ./
|
| 38 |
+
RUN go mod download
|
| 39 |
+
COPY --from=src /src/backend/cmd ./cmd
|
| 40 |
+
COPY --from=src /src/backend/internal ./internal
|
| 41 |
+
COPY --from=src /src/backend/docs/docs.go ./docs/docs.go
|
| 42 |
+
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \
|
| 43 |
+
go build -buildvcs=false -trimpath -ldflags="-s -w" -o /out/grok2api ./cmd/grok2api
|
| 44 |
+
|
| 45 |
+
FROM alpine:${ALPINE_VERSION}
|
| 46 |
+
|
| 47 |
+
ENV TZ=Asia/Shanghai \
|
| 48 |
+
GROK2API_CONFIG_SOURCE=/data/config.yaml \
|
| 49 |
+
SERVER_PORT=7860
|
| 50 |
+
|
| 51 |
+
RUN apk add --no-cache ca-certificates su-exec tzdata wget \
|
| 52 |
+
&& addgroup -S -g 10001 grok2api \
|
| 53 |
+
&& adduser -S -D -H -u 10001 -G grok2api grok2api \
|
| 54 |
+
&& mkdir -p /app/data /app/frontend/dist /data /data/media /run/grok2api \
|
| 55 |
+
&& chown -R grok2api:grok2api /app/data /data /run/grok2api
|
| 56 |
+
|
| 57 |
+
WORKDIR /app
|
| 58 |
+
|
| 59 |
+
COPY --from=backend-builder --chmod=0755 /out/grok2api /app/grok2api
|
| 60 |
+
COPY --from=frontend-builder /src/frontend/dist /app/frontend/dist
|
| 61 |
+
COPY --from=src /src/config.example.yaml /app/config.example.yaml
|
| 62 |
+
COPY --chmod=0755 start.sh /usr/local/bin/hf-start.sh
|
| 63 |
+
|
| 64 |
+
EXPOSE 7860
|
| 65 |
+
|
| 66 |
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
| 67 |
+
CMD wget -qO- "http://127.0.0.1:${SERVER_PORT}/healthz" >/dev/null || exit 1
|
| 68 |
+
|
| 69 |
+
ENTRYPOINT ["/usr/local/bin/hf-start.sh"]
|
| 70 |
+
CMD ["/app/grok2api", "--config", "/app/config.yaml", "--listen", "0.0.0.0:7860"]
|
README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Grok2Api
|
| 3 |
+
emoji: 🚀
|
| 4 |
+
colorFrom: indigo
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
pinned: false
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
# Grok2Api on Hugging Face Spaces
|
| 12 |
+
|
| 13 |
+
Runs the official [chenyme/grok2api](https://github.com/chenyme/grok2api) **Go + React** gateway.
|
| 14 |
+
|
| 15 |
+
## Build model
|
| 16 |
+
|
| 17 |
+
- This Space repository only contains the HF adapter (`Dockerfile`, `start.sh`, this README, `.env.example`)
|
| 18 |
+
- Docker build **clones upstream** `https://github.com/chenyme/grok2api` (`main`) and builds frontend + backend from that source
|
| 19 |
+
- Local Python-era fork code is **not** used
|
| 20 |
+
|
| 21 |
+
## Persistent storage
|
| 22 |
+
|
| 23 |
+
Mount HF Storage (e.g. `DanielleNguyen/Grok2Api-storage`) to **`/data`**.
|
| 24 |
+
|
| 25 |
+
| Path | Purpose |
|
| 26 |
+
| --- | --- |
|
| 27 |
+
| `/data/config.yaml` | Runtime config (seeded from upstream `config.example.yaml` on first boot) |
|
| 28 |
+
| `/data/backend.db` | SQLite database |
|
| 29 |
+
| `/data/media` | Local media files |
|
| 30 |
+
| `/data/.env` | Optional env file loaded at start |
|
| 31 |
+
|
| 32 |
+
## First boot secrets
|
| 33 |
+
|
| 34 |
+
Edit `/data/config.yaml` (or set env vars **before** first seed):
|
| 35 |
+
|
| 36 |
+
| Field / env | Notes |
|
| 37 |
+
| --- | --- |
|
| 38 |
+
| `secrets.jwtSecret` / `GROK2API_JWT_SECRET` | `openssl rand -hex 32` |
|
| 39 |
+
| `secrets.credentialEncryptionKey` / `GROK2API_CREDENTIAL_ENCRYPTION_KEY` | `openssl rand -base64 32` (keep forever) |
|
| 40 |
+
| `bootstrapAdmin.password` / `GROK2API_ADMIN_PASSWORD` | Strong admin password |
|
| 41 |
+
| `auth.secureCookies` / `GROK2API_SECURE_COOKIES` | Prefer `true` on HTTPS Space |
|
| 42 |
+
|
| 43 |
+
Service refuses insecure defaults if secrets are not replaced.
|
| 44 |
+
|
| 45 |
+
## Service
|
| 46 |
+
|
| 47 |
+
- Port: `7860` (Spaces requirement)
|
| 48 |
+
- Health: `/healthz`
|
| 49 |
+
- Admin UI: `/` (same origin as API)
|
| 50 |
+
- API: `/v1/*` with `Authorization: Bearer g2a_...`
|
| 51 |
+
|
| 52 |
+
## Deploy from GitHub
|
| 53 |
+
|
| 54 |
+
Workflow `.github/workflows/deploy-hf.yml` syncs only `huggingface/*` to this Space.
|
| 55 |
+
|
| 56 |
+
GitHub secrets:
|
| 57 |
+
|
| 58 |
+
- `HF_TOKEN`
|
| 59 |
+
- `HF_SPACE_ID` (e.g. `DanielleNguyen/Grok2Api`)
|
start.sh
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/sh
|
| 2 |
+
set -eu
|
| 3 |
+
|
| 4 |
+
umask 077
|
| 5 |
+
|
| 6 |
+
DATA_DIR="${DATA_DIR:-/data}"
|
| 7 |
+
CONFIG_SOURCE="${GROK2API_CONFIG_SOURCE:-$DATA_DIR/config.yaml}"
|
| 8 |
+
EXAMPLE_CONFIG="/app/config.example.yaml"
|
| 9 |
+
APP_CONFIG="/app/config.yaml"
|
| 10 |
+
LISTEN_ADDR="0.0.0.0:${SERVER_PORT:-7860}"
|
| 11 |
+
|
| 12 |
+
mkdir -p "$DATA_DIR" "$DATA_DIR/media" /run/grok2api
|
| 13 |
+
|
| 14 |
+
# Load optional env file first so secrets are available for seed/override.
|
| 15 |
+
if [ -f "$DATA_DIR/.env" ]; then
|
| 16 |
+
set -a
|
| 17 |
+
# shellcheck disable=SC1090
|
| 18 |
+
. "$DATA_DIR/.env"
|
| 19 |
+
set +a
|
| 20 |
+
fi
|
| 21 |
+
|
| 22 |
+
apply_secret_overrides() {
|
| 23 |
+
target="$1"
|
| 24 |
+
[ -f "$target" ] || return 0
|
| 25 |
+
|
| 26 |
+
if [ -n "${GROK2API_JWT_SECRET:-}" ]; then
|
| 27 |
+
sed -i "s|jwtSecret: \".*\"|jwtSecret: \"${GROK2API_JWT_SECRET}\"|" "$target"
|
| 28 |
+
fi
|
| 29 |
+
if [ -n "${GROK2API_CREDENTIAL_ENCRYPTION_KEY:-}" ]; then
|
| 30 |
+
sed -i "s|credentialEncryptionKey: \".*\"|credentialEncryptionKey: \"${GROK2API_CREDENTIAL_ENCRYPTION_KEY}\"|" "$target"
|
| 31 |
+
fi
|
| 32 |
+
if [ -n "${GROK2API_ADMIN_USERNAME:-}" ]; then
|
| 33 |
+
sed -i "s|username: \".*\"|username: \"${GROK2API_ADMIN_USERNAME}\"|" "$target"
|
| 34 |
+
fi
|
| 35 |
+
if [ -n "${GROK2API_ADMIN_PASSWORD:-}" ]; then
|
| 36 |
+
sed -i "s|password: \".*\"|password: \"${GROK2API_ADMIN_PASSWORD}\"|" "$target"
|
| 37 |
+
fi
|
| 38 |
+
if [ -n "${GROK2API_SECURE_COOKIES:-}" ]; then
|
| 39 |
+
sed -i "s|secureCookies: .*|secureCookies: ${GROK2API_SECURE_COOKIES}|" "$target"
|
| 40 |
+
fi
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
seed_config() {
|
| 44 |
+
cp "$EXAMPLE_CONFIG" "$CONFIG_SOURCE"
|
| 45 |
+
|
| 46 |
+
sed -i \
|
| 47 |
+
-e 's|listen: "127.0.0.1:8000"|listen: "0.0.0.0:7860"|' \
|
| 48 |
+
-e 's|staticPath: "./frontend/dist"|staticPath: "/app/frontend/dist"|' \
|
| 49 |
+
-e 's|path: "./data/backend.db"|path: "'"$DATA_DIR"'/backend.db"|' \
|
| 50 |
+
-e 's|path: "./data/media"|path: "'"$DATA_DIR"'/media"|' \
|
| 51 |
+
"$CONFIG_SOURCE"
|
| 52 |
+
|
| 53 |
+
apply_secret_overrides "$CONFIG_SOURCE"
|
| 54 |
+
echo "Initialized HF runtime config at $CONFIG_SOURCE"
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
if [ ! -f "$CONFIG_SOURCE" ]; then
|
| 58 |
+
if [ ! -f "$EXAMPLE_CONFIG" ]; then
|
| 59 |
+
echo "missing example config: $EXAMPLE_CONFIG" >&2
|
| 60 |
+
exit 1
|
| 61 |
+
fi
|
| 62 |
+
seed_config
|
| 63 |
+
else
|
| 64 |
+
# Re-apply Space secrets every boot so placeholder configs can be fixed without rebuild.
|
| 65 |
+
apply_secret_overrides "$CONFIG_SOURCE"
|
| 66 |
+
fi
|
| 67 |
+
|
| 68 |
+
# Fail fast with clear guidance if placeholders remain.
|
| 69 |
+
if grep -q 'replace-with-at-least-32-characters' "$CONFIG_SOURCE" \
|
| 70 |
+
|| grep -q 'replace-with-base64-key' "$CONFIG_SOURCE" \
|
| 71 |
+
|| grep -q 'replace-with-a-strong-password' "$CONFIG_SOURCE"; then
|
| 72 |
+
cat >&2 <<'EOF'
|
| 73 |
+
ERROR: /data/config.yaml still contains example secrets.
|
| 74 |
+
|
| 75 |
+
Fix one of:
|
| 76 |
+
1) Edit HF Storage file: /data/config.yaml
|
| 77 |
+
- secrets.jwtSecret
|
| 78 |
+
- secrets.credentialEncryptionKey
|
| 79 |
+
- bootstrapAdmin.password
|
| 80 |
+
2) Or set Space Secrets, then restart:
|
| 81 |
+
- GROK2API_JWT_SECRET (openssl rand -hex 32)
|
| 82 |
+
- GROK2API_CREDENTIAL_ENCRYPTION_KEY (openssl rand -base64 32)
|
| 83 |
+
- GROK2API_ADMIN_PASSWORD
|
| 84 |
+
EOF
|
| 85 |
+
exit 1
|
| 86 |
+
fi
|
| 87 |
+
|
| 88 |
+
cp "$CONFIG_SOURCE" "$APP_CONFIG"
|
| 89 |
+
chown grok2api:grok2api "$APP_CONFIG" 2>/dev/null || true
|
| 90 |
+
chmod 0600 "$APP_CONFIG" || true
|
| 91 |
+
chown -R grok2api:grok2api "$DATA_DIR" 2>/dev/null || true
|
| 92 |
+
|
| 93 |
+
if ! printf '%s' "$*" | grep -q -- '--listen'; then
|
| 94 |
+
set -- "$@" --listen "$LISTEN_ADDR"
|
| 95 |
+
fi
|
| 96 |
+
|
| 97 |
+
if command -v su-exec >/dev/null 2>&1 && id grok2api >/dev/null 2>&1; then
|
| 98 |
+
exec su-exec grok2api:grok2api "$@"
|
| 99 |
+
fi
|
| 100 |
+
|
| 101 |
+
exec "$@"
|