Download docker/entrypoint.sh from li2895/li: direct link, hf CLI and curl.
- Browser
- Download file 9.39 kB
-
https://huggingface.co/spaces/li2895/li/resolve/main/docker/entrypoint.sh
- Command line
-
hf download hf://spaces/li2895/li/docker/entrypoint.sh
-
curl -L -o entrypoint.sh https://huggingface.co/spaces/li2895/li/resolve/main/docker/entrypoint.sh
9.39 kB
| set -eu | |
| umask 077 | |
| CONFIG_SOURCE="${GROK2API_CONFIG_SOURCE:-/run/grok2api/config.yaml}" | |
| APP_CONFIG="/app/config.yaml" | |
| SECRETS_FILE_NAME=".grok2api-secrets.env" | |
| log() { | |
| printf '%s\n' "$*" >&2 | |
| } | |
| die() { | |
| log "error: $*" | |
| exit 1 | |
| } | |
| yaml_quote() { | |
| printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' | |
| } | |
| is_truthy() { | |
| case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in | |
| 1|true|yes|on) return 0 ;; | |
| *) return 1 ;; | |
| esac | |
| } | |
| has_writable_dir() { | |
| dir="$1" | |
| [ -d "$dir" ] || return 1 | |
| [ -w "$dir" ] || return 1 | |
| return 0 | |
| } | |
| resolve_data_root() { | |
| if [ -n "${GROK2API_DATA_DIR:-}" ]; then | |
| printf '%s' "$GROK2API_DATA_DIR" | |
| return | |
| fi | |
| if has_writable_dir /data; then | |
| printf '%s' /data | |
| return | |
| fi | |
| printf '%s' /app/data | |
| } | |
| random_hex() { | |
| dd if=/dev/urandom bs="$1" count=1 2>/dev/null | od -An -tx1 | tr -d ' \n' | |
| } | |
| random_base64_32() { | |
| dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d '\n' | |
| } | |
| shell_single_quote() { | |
| printf "%s" "$1" | sed "s/'/'\\\\''/g" | |
| } | |
| persist_secrets() { | |
| secrets_file="$1" | |
| jwt_secret="$2" | |
| credential_key="$3" | |
| tmp="${secrets_file}.tmp" | |
| { | |
| printf "export JWT_SECRET='%s'\n" "$(shell_single_quote "$jwt_secret")" | |
| printf "export CREDENTIAL_ENCRYPTION_KEY='%s'\n" "$(shell_single_quote "$credential_key")" | |
| } >"$tmp" | |
| mv "$tmp" "$secrets_file" | |
| chmod 0600 "$secrets_file" 2>/dev/null || true | |
| } | |
| resolve_listen() { | |
| if [ -n "${GROK2API_LISTEN:-}" ]; then | |
| printf '%s' "$GROK2API_LISTEN" | |
| return | |
| fi | |
| if [ -n "${PORT:-}" ]; then | |
| printf '0.0.0.0:%s' "$PORT" | |
| return | |
| fi | |
| if [ -n "${SPACE_ID:-}" ] || [ -n "${SPACE_HOST:-}" ] || is_truthy "${HF_SPACE:-}" || is_truthy "${GROK2API_HF:-}"; then | |
| printf '%s' "0.0.0.0:7860" | |
| return | |
| fi | |
| printf '%s' "0.0.0.0:8000" | |
| } | |
| resolve_public_api_base_url() { | |
| if [ -n "${PUBLIC_API_BASE_URL:-}" ]; then | |
| printf '%s' "${PUBLIC_API_BASE_URL%/}" | |
| return | |
| fi | |
| if [ -n "${GROK2API_PUBLIC_API_BASE_URL:-}" ]; then | |
| printf '%s' "${GROK2API_PUBLIC_API_BASE_URL%/}" | |
| return | |
| fi | |
| if [ -n "${SPACE_HOST:-}" ]; then | |
| printf 'https://%s' "$SPACE_HOST" | |
| return | |
| fi | |
| printf '%s' "" | |
| } | |
| resolve_secure_cookies() { | |
| if [ -n "${SECURE_COOKIES:-}" ]; then | |
| if is_truthy "$SECURE_COOKIES"; then | |
| printf '%s' "true" | |
| else | |
| printf '%s' "false" | |
| fi | |
| return | |
| fi | |
| if [ -n "${SPACE_ID:-}" ] || [ -n "${SPACE_HOST:-}" ] || is_truthy "${HF_SPACE:-}" || is_truthy "${GROK2API_HF:-}"; then | |
| printf '%s' "true" | |
| return | |
| fi | |
| printf '%s' "false" | |
| } | |
| prepare_data_dirs() { | |
| data_root="$1" | |
| mkdir -p "$data_root" "$data_root/media" | |
| if [ "$(id -u)" -eq 0 ]; then | |
| chown -R grok2api:grok2api "$data_root" 2>/dev/null || true | |
| fi | |
| } | |
| resolve_secret_value() { | |
| env_primary="$1" | |
| env_alt="$2" | |
| file_value="$3" | |
| if [ -n "$env_primary" ]; then | |
| printf '%s' "$env_primary" | |
| return | |
| fi | |
| if [ -n "$env_alt" ]; then | |
| printf '%s' "$env_alt" | |
| return | |
| fi | |
| printf '%s' "$file_value" | |
| } | |
| generate_config_from_env() { | |
| data_root="$1" | |
| secrets_file="$data_root/$SECRETS_FILE_NAME" | |
| env_jwt="${JWT_SECRET:-}" | |
| env_jwt_alt="${GROK2API_JWT_SECRET:-}" | |
| env_key="${CREDENTIAL_ENCRYPTION_KEY:-}" | |
| env_key_alt="${GROK2API_CREDENTIAL_ENCRYPTION_KEY:-}" | |
| file_jwt="" | |
| file_key="" | |
| if [ -f "$secrets_file" ]; then | |
| # shellcheck disable=SC1090 | |
| . "$secrets_file" | |
| file_jwt="${JWT_SECRET:-}" | |
| file_key="${CREDENTIAL_ENCRYPTION_KEY:-}" | |
| fi | |
| jwt_secret="$(resolve_secret_value "$env_jwt" "$env_jwt_alt" "$file_jwt")" | |
| credential_key="$(resolve_secret_value "$env_key" "$env_key_alt" "$file_key")" | |
| generated_secrets=0 | |
| if [ -z "$jwt_secret" ]; then | |
| jwt_secret="$(random_hex 32)" | |
| generated_secrets=1 | |
| fi | |
| if [ -z "$credential_key" ]; then | |
| credential_key="$(random_base64_32)" | |
| generated_secrets=1 | |
| fi | |
| if [ "${#jwt_secret}" -lt 32 ]; then | |
| die "JWT_SECRET must be at least 32 characters" | |
| fi | |
| admin_user="${BOOTSTRAP_ADMIN_USERNAME:-${GROK2API_BOOTSTRAP_ADMIN_USERNAME:-admin}}" | |
| admin_pass="${BOOTSTRAP_ADMIN_PASSWORD:-${GROK2API_BOOTSTRAP_ADMIN_PASSWORD:-}}" | |
| if [ -z "$admin_pass" ]; then | |
| die "BOOTSTRAP_ADMIN_PASSWORD is required when generating config from environment" | |
| fi | |
| case "$admin_pass" in | |
| replace-with-a-strong-password) | |
| die "BOOTSTRAP_ADMIN_PASSWORD must not use the example placeholder" | |
| ;; | |
| esac | |
| db_driver="${DATABASE_DRIVER:-${GROK2API_DATABASE_DRIVER:-sqlite}}" | |
| runtime_driver="${RUNTIME_STORE_DRIVER:-${GROK2API_RUNTIME_STORE_DRIVER:-memory}}" | |
| sqlite_path="${SQLITE_PATH:-${GROK2API_SQLITE_PATH:-$data_root/backend.db}}" | |
| media_path="${MEDIA_PATH:-${GROK2API_MEDIA_PATH:-$data_root/media}}" | |
| static_path="${FRONTEND_STATIC_PATH:-/app/frontend/dist}" | |
| public_api_base_url="$(resolve_public_api_base_url)" | |
| secure_cookies="$(resolve_secure_cookies)" | |
| swagger_enabled="false" | |
| if is_truthy "${SWAGGER_ENABLED:-}"; then | |
| swagger_enabled="true" | |
| fi | |
| postgres_dsn="${POSTGRES_DSN:-${GROK2API_POSTGRES_DSN:-}}" | |
| redis_address="${REDIS_ADDRESS:-${GROK2API_REDIS_ADDRESS:-127.0.0.1:6379}}" | |
| redis_username="${REDIS_USERNAME:-${GROK2API_REDIS_USERNAME:-}}" | |
| redis_password="${REDIS_PASSWORD:-${GROK2API_REDIS_PASSWORD:-}}" | |
| redis_database="${REDIS_DATABASE:-${GROK2API_REDIS_DATABASE:-0}}" | |
| redis_prefix="${REDIS_KEY_PREFIX:-${GROK2API_REDIS_KEY_PREFIX:-grok2api:}}" | |
| redis_tls="false" | |
| if is_truthy "${REDIS_TLS:-${GROK2API_REDIS_TLS:-}}"; then | |
| redis_tls="true" | |
| fi | |
| if [ "$db_driver" = "postgres" ] && [ -z "$postgres_dsn" ]; then | |
| die "POSTGRES_DSN is required when DATABASE_DRIVER=postgres" | |
| fi | |
| jwt_q="$(yaml_quote "$jwt_secret")" | |
| key_q="$(yaml_quote "$credential_key")" | |
| admin_user_q="$(yaml_quote "$admin_user")" | |
| admin_pass_q="$(yaml_quote "$admin_pass")" | |
| sqlite_path_q="$(yaml_quote "$sqlite_path")" | |
| media_path_q="$(yaml_quote "$media_path")" | |
| static_path_q="$(yaml_quote "$static_path")" | |
| public_api_q="$(yaml_quote "$public_api_base_url")" | |
| postgres_dsn_q="$(yaml_quote "$postgres_dsn")" | |
| redis_address_q="$(yaml_quote "$redis_address")" | |
| redis_username_q="$(yaml_quote "$redis_username")" | |
| redis_password_q="$(yaml_quote "$redis_password")" | |
| redis_prefix_q="$(yaml_quote "$redis_prefix")" | |
| cat >"$APP_CONFIG" <<EOF | |
| server: | |
| listen: "0.0.0.0:8000" | |
| maxBodyBytes: 33554432 | |
| readTimeout: 15m | |
| requestTimeout: 2h | |
| swaggerEnabled: ${swagger_enabled} | |
| auth: | |
| accessTokenTTL: 15m | |
| refreshTokenTTL: 720h | |
| secureCookies: ${secure_cookies} | |
| secrets: | |
| jwtSecret: "${jwt_q}" | |
| credentialEncryptionKey: "${key_q}" | |
| bootstrapAdmin: | |
| username: "${admin_user_q}" | |
| password: "${admin_pass_q}" | |
| frontend: | |
| publicApiBaseURL: "${public_api_q}" | |
| staticPath: "${static_path_q}" | |
| database: | |
| driver: ${db_driver} | |
| sqlite: | |
| path: "${sqlite_path_q}" | |
| postgres: | |
| dsn: "${postgres_dsn_q}" | |
| maxOpenConns: 50 | |
| maxIdleConns: 10 | |
| runtimeStore: | |
| driver: ${runtime_driver} | |
| redis: | |
| address: "${redis_address_q}" | |
| username: "${redis_username_q}" | |
| password: "${redis_password_q}" | |
| database: ${redis_database} | |
| keyPrefix: "${redis_prefix_q}" | |
| tls: ${redis_tls} | |
| media: | |
| driver: local | |
| local: | |
| path: "${media_path_q}" | |
| routing: | |
| reasoningReplayEnabled: true | |
| reasoningReplayTTL: 1h | |
| reasoningReplayMaxEntries: 10240 | |
| EOF | |
| if [ "$(id -u)" -eq 0 ]; then | |
| chown grok2api:grok2api "$APP_CONFIG" | |
| fi | |
| chmod 0600 "$APP_CONFIG" | |
| persist_secrets "$secrets_file" "$jwt_secret" "$credential_key" | |
| if [ "$(id -u)" -eq 0 ]; then | |
| chown grok2api:grok2api "$secrets_file" 2>/dev/null || true | |
| fi | |
| if [ "$generated_secrets" -eq 1 ]; then | |
| log "generated JWT/credential secrets and saved them under ${secrets_file}" | |
| log "set JWT_SECRET and CREDENTIAL_ENCRYPTION_KEY as Space Secrets for stable personal use" | |
| fi | |
| if [ -z "$public_api_base_url" ]; then | |
| log "PUBLIC_API_BASE_URL is empty; docs may show http://127.0.0.1:8000" | |
| log "on Hugging Face, SPACE_HOST is usually injected; or set PUBLIC_API_BASE_URL" | |
| fi | |
| } | |
| install_config_from_source() { | |
| src="$1" | |
| [ -f "$src" ] || die "missing config: ${src}" | |
| cp "$src" "$APP_CONFIG" | |
| if [ "$(id -u)" -eq 0 ]; then | |
| chown grok2api:grok2api "$APP_CONFIG" | |
| fi | |
| chmod 0600 "$APP_CONFIG" | |
| } | |
| run_as_app() { | |
| if [ "$(id -u)" -eq 0 ]; then | |
| exec su-exec grok2api:grok2api "$@" | |
| fi | |
| exec "$@" | |
| } | |
| # ---- main ---- | |
| DATA_ROOT="$(resolve_data_root)" | |
| prepare_data_dirs "$DATA_ROOT" | |
| LISTEN="$(resolve_listen)" | |
| if [ -f "$CONFIG_SOURCE" ]; then | |
| log "using mounted config: $CONFIG_SOURCE" | |
| install_config_from_source "$CONFIG_SOURCE" | |
| elif [ -n "${BOOTSTRAP_ADMIN_PASSWORD:-}" ] || [ -n "${GROK2API_BOOTSTRAP_ADMIN_PASSWORD:-}" ] || [ -n "${JWT_SECRET:-}" ] || [ -n "${GROK2API_JWT_SECRET:-}" ] || [ -n "${CREDENTIAL_ENCRYPTION_KEY:-}" ] || [ -n "${GROK2API_CREDENTIAL_ENCRYPTION_KEY:-}" ]; then | |
| log "generating config from environment (data root: $DATA_ROOT, listen: $LISTEN)" | |
| generate_config_from_env "$DATA_ROOT" | |
| else | |
| die "missing config: mount ${CONFIG_SOURCE} or set BOOTSTRAP_ADMIN_PASSWORD (and optional JWT_SECRET / CREDENTIAL_ENCRYPTION_KEY) for Hugging Face / env deploy" | |
| fi | |
| # CLI --listen overrides YAML. Append resolved listen last so HF PORT/7860 wins over image CMD. | |
| if [ "$#" -eq 0 ]; then | |
| set -- /app/grok2api --config "$APP_CONFIG" --listen "$LISTEN" | |
| else | |
| set -- "$@" --listen "$LISTEN" | |
| fi | |
| log "starting grok2api on $LISTEN" | |
| run_as_app "$@" |