diff --git a/new-api/init-channels.sh b/new-api/init-channels.sh index 8c2197d..06363e9 100755 --- a/new-api/init-channels.sh +++ b/new-api/init-channels.sh @@ -1,15 +1,18 @@ #!/usr/bin/env bash -# Configures new-api channels and token via the admin API. +# Configures new-api channels and API token via the admin API. # Run once after first boot: ./new-api/init-channels.sh # # Requires these env vars (or .env file in project root): -# NEW_API_USERNAME - admin username (default: root) # NEW_API_PASSWORD - admin password # DEEPINFRA_API_KEY # SILICONFLOW_API_KEY # OPENROUTER_API_KEY # GROQ_API_KEY # CEREBRAS_API_KEY +# +# Optional: +# NEW_API_USERNAME - admin username (default: root) +# NEW_API_BASE - API base URL (default: http://localhost:4000) set -euo pipefail @@ -28,9 +31,10 @@ API_BASE="${NEW_API_BASE:-http://localhost:4000}" USERNAME="${NEW_API_USERNAME:-root}" PASSWORD="${NEW_API_PASSWORD:?Set NEW_API_PASSWORD to the admin password}" COOKIE_JAR=$(mktemp) +USER_ID="" trap 'rm -f "$COOKIE_JAR"' EXIT -# ── Login to get session cookie ───────────────────────── +# ── Login and get user ID ─────────────────────────────── login() { echo "Logging in as ${USERNAME}..." local resp @@ -47,10 +51,22 @@ print(json.dumps({'username': sys.argv[1], 'password': sys.argv[2]})) echo "ERROR: Login failed: ${resp}" exit 1 fi - echo " Logged in." + + USER_ID=$(echo "$resp" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['id'])") + echo " Logged in (user ID: ${USER_ID})." } -# ── Helper ────────────────────────────────────────────── +# ── API call helper (cookie + New-Api-User header) ───── +api_call() { + local endpoint="$1" + shift + curl -s -b "$COOKIE_JAR" \ + -H "New-Api-User: ${USER_ID}" \ + -H "Content-Type: application/json" \ + "${API_BASE}${endpoint}" "$@" +} + +# ── Create channel ────────────────────────────────────── create_channel() { local name="$1" type="$2" key="$3" base_url="$4" priority="$5" models="$6" model_mapping="$7" @@ -75,10 +91,7 @@ print(json.dumps({ " "$type" "$name" "$key" "$base_url" "$models" "$model_mapping" "$priority") local resp success - resp=$(curl -s "${API_BASE}/api/channel/" \ - -b "$COOKIE_JAR" \ - -H "Content-Type: application/json" \ - -d "$payload") + resp=$(api_call "/api/channel/" -d "$payload") success=$(echo "$resp" | python3 -c "import sys,json; print(json.load(sys.stdin).get('success', False))") if [[ "$success" == "True" ]]; then @@ -89,7 +102,7 @@ print(json.dumps({ fi } -# Wait for new-api to be ready +# ── Wait for new-api ──────────────────────────────────── echo "Waiting for new-api at ${API_BASE}..." for i in $(seq 1 30); do if curl -sf "${API_BASE}/" > /dev/null 2>&1; then @@ -103,10 +116,32 @@ for i in $(seq 1 30); do sleep 2 done -# Login first +# ── Login ─────────────────────────────────────────────── login -# ── Channel: DeepInfra (priority 1) ──────────────────── +# ── Generate system access token for future use ───────── +echo "" +echo "Generating system access token..." +ACCESS_TOKEN_RESP=$(api_call "/api/user/token") +ACCESS_TOKEN=$(echo "$ACCESS_TOKEN_RESP" | python3 -c " +import sys, json +data = json.load(sys.stdin) +if data.get('success'): + print(data.get('data', '')) +else: + print('') +" 2>/dev/null || echo "") + +if [[ -n "$ACCESS_TOKEN" ]]; then + echo " Access token: ${ACCESS_TOKEN}" + echo " Save as NEW_API_ACCESS_TOKEN in .env for future API use." + echo " Usage: -H 'Authorization: Bearer ${ACCESS_TOKEN}' -H 'New-Api-User: ${USER_ID}'" +else + echo " Could not generate access token (non-critical, using session)." +fi + +# ── Channels ──────────────────────────────────────────── + create_channel "DeepInfra" 1 \ "${DEEPINFRA_API_KEY:?}" \ "https://api.deepinfra.com/v1/openai" \ @@ -114,7 +149,6 @@ create_channel "DeepInfra" 1 \ "deepseek-v3.2,deepseek-r1,gpt-oss,gpt-oss-20b,nemotron-super,nemotron-nano,devstral,glm-4.6,glm-4.7,glm-5,kimi-k2,kimi-k2.5" \ '{"deepseek-v3.2":"deepseek-ai/DeepSeek-V3.2","deepseek-r1":"deepseek-ai/DeepSeek-R1","gpt-oss":"openai/gpt-oss-120b","gpt-oss-20b":"openai/gpt-oss-20b","nemotron-super":"nvidia/Llama-3.3-Nemotron-Super-49B-v1.5","nemotron-nano":"nvidia/NVIDIA-Nemotron-Nano-9B-v2","devstral":"mistralai/Devstral-Small-2505","glm-4.6":"zai-org/GLM-4.6","glm-4.7":"zai-org/GLM-4.7","glm-5":"zai-org/GLM-5","kimi-k2":"moonshotai/Kimi-K2-Instruct-0905","kimi-k2.5":"moonshotai/Kimi-K2.5"}' -# ── Channel: SiliconFlow (priority 2) ────────────────── create_channel "SiliconFlow" 1 \ "${SILICONFLOW_API_KEY:?}" \ "https://api.siliconflow.com/v1" \ @@ -122,7 +156,6 @@ create_channel "SiliconFlow" 1 \ "deepseek-v3.2,glm-4.7,kimi-k2,qwen3-coder,qwen3-coder-30b" \ '{"deepseek-v3.2":"deepseek-ai/DeepSeek-V3.2","glm-4.7":"THUDM/GLM-4-32B-0414","kimi-k2":"moonshotai/Kimi-K2-Instruct-0905","qwen3-coder":"Qwen/Qwen3-Coder-480B-A35B-Instruct","qwen3-coder-30b":"Qwen/Qwen3-Coder-30B-A3B-Instruct"}' -# ── Channel: OpenRouter (priority 3) ─────────────────── create_channel "OpenRouter" 1 \ "${OPENROUTER_API_KEY:?}" \ "https://openrouter.ai/api/v1" \ @@ -130,7 +163,6 @@ create_channel "OpenRouter" 1 \ "deepseek-v3.2,deepseek-v3-free,kimi-k2.5,minimax-m2.5,gpt-4.1-mini,gpt-4.1,gemini-3-flash-preview,gemini-2.5-pro,claude-sonnet,trinity-large-preview" \ '{"deepseek-v3.2":"deepseek/deepseek-chat-v3-0324","deepseek-v3-free":"deepseek/deepseek-chat-v3-0324:free","kimi-k2.5":"moonshotai/kimi-k2.5","minimax-m2.5":"minimax/minimax-m2.5","gpt-4.1-mini":"openai/gpt-4.1-mini","gpt-4.1":"openai/gpt-4.1","gemini-3-flash-preview":"google/gemini-3-flash-preview","gemini-2.5-pro":"google/gemini-2.5-pro-preview","claude-sonnet":"anthropic/claude-sonnet-4","trinity-large-preview":"arcee-ai/trinity-large-preview"}' -# ── Channel: Groq (priority 1) ───────────────────────── create_channel "Groq" 1 \ "${GROQ_API_KEY:?}" \ "https://api.groq.com/openai/v1" \ @@ -138,7 +170,6 @@ create_channel "Groq" 1 \ "llama-3.3-70b" \ '{"llama-3.3-70b":"llama-3.3-70b-versatile"}' -# ── Channel: Cerebras (priority 1) ───────────────────── create_channel "Cerebras" 1 \ "${CEREBRAS_API_KEY:?}" \ "https://api.cerebras.ai/v1" \ @@ -146,13 +177,10 @@ create_channel "Cerebras" 1 \ "llama-3.3-70b-cerebras" \ '{"llama-3.3-70b-cerebras":"llama-3.3-70b"}' -# ── Create API token for Open WebUI ──────────────────── +# ── Create API token for Open WebUI ───────────────────── echo "" echo "Creating API token for Open WebUI..." -TOKEN_RESP=$(curl -s "${API_BASE}/api/token/" \ - -b "$COOKIE_JAR" \ - -H "Content-Type: application/json" \ - -d "$(python3 -c " +TOKEN_RESP=$(api_call "/api/token/" -d "$(python3 -c " import json print(json.dumps({ 'name': 'open-webui', @@ -170,23 +198,21 @@ else: print('FAILED: ' + data.get('message', 'unknown error')) " 2>/dev/null || echo "FAILED: could not parse response") -echo " Token: ${TOKEN_KEY}" - echo "" echo "══════════════════════════════════════" -echo "Channel setup complete!" +echo "Setup complete!" echo "" if [[ "$TOKEN_KEY" != FAILED* ]]; then echo "Open WebUI API key: ${TOKEN_KEY}" echo "Set OPENWEBUI_API_KEY=${TOKEN_KEY} in your .env" + echo "" + echo "Test:" + echo " curl ${API_BASE}/v1/chat/completions \\" + echo " -H 'Authorization: Bearer ${TOKEN_KEY}' \\" + echo " -H 'Content-Type: application/json' \\" + echo " -d '{\"model\":\"deepseek-v3.2\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}'" +else + echo "Token creation: ${TOKEN_KEY}" + echo "Create a token manually in the new-api UI." fi -echo "" -echo "Next steps:" -echo " 1. Verify channels at ${API_BASE}" -echo " 2. Test a model:" -echo " curl ${API_BASE}/v1/chat/completions \\" -echo " -H 'Authorization: Bearer ${TOKEN_KEY}' \\" -echo " -H 'Content-Type: application/json' \\" -echo " -d '{\"model\":\"deepseek-v3.2\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}'" -echo " 3. Restart Open WebUI with the new API key" echo "══════════════════════════════════════"