diff --git a/server/README.md b/server/README.md index 06a0d82a..de1a469b 100644 --- a/server/README.md +++ b/server/README.md @@ -149,7 +149,7 @@ Authentication is enforced only when `server.api_key` is set. If the value is em All API endpoints (except `/health`, `/docs`, `/redoc`) require authentication via the `OPEN-SANDBOX-API-KEY` header when authentication is enabled: ```bash -curl http://localhost:8080/sandboxes +curl http://localhost:8080/v1/sandboxes ``` ### Example usage @@ -157,7 +157,7 @@ curl http://localhost:8080/sandboxes **Create a Sandbox** ```bash -curl -X POST "http://localhost:8080/sandboxes" \ +curl -X POST "http://localhost:8080/v1/sandboxes" \ -H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \ -H "Content-Type: application/json" \ -d '{ @@ -209,18 +209,18 @@ Response: ```bash curl -H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \ - http://localhost:8080/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab + http://localhost:8080/v1/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab ``` **Get Service Endpoint** ```bash curl -H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \ - http://localhost:8080/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab/endpoints/8000 + http://localhost:8080/v1/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab/endpoints/8000 # execd (agent) endpoint curl -H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \ - http://localhost:8080/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab/endpoints/44772 + http://localhost:8080/v1/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab/endpoints/44772 ``` Response: @@ -233,7 +233,7 @@ Response: **Renew Expiration** ```bash -curl -X POST "http://localhost:8080/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab/renew-expiration" \ +curl -X POST "http://localhost:8080/v1/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab/renew-expiration" \ -H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \ -H "Content-Type: application/json" \ -d '{ @@ -246,7 +246,7 @@ curl -X POST "http://localhost:8080/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890 ```bash curl -X DELETE \ -H "OPEN-SANDBOX-API-KEY: your-secret-api-key" \ - http://localhost:8080/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab + http://localhost:8080/v1/sandboxes/a1b2c3d4-5678-90ab-cdef-1234567890ab ``` ## Architecture diff --git a/server/README_zh.md b/server/README_zh.md index c7f071c5..693fc875 100644 --- a/server/README_zh.md +++ b/server/README_zh.md @@ -152,7 +152,7 @@ curl http://localhost:8080/health 当鉴权开启时,除 `/health`、`/docs`、`/redoc` 外的 API 端点均需要通过 `OPEN-SANDBOX-API-KEY` 请求头进行认证: ```bash -curl http://localhost:8080/sandboxes +curl http://localhost:8080/v1/sandboxes ``` ### 使用示例 @@ -160,7 +160,7 @@ curl http://localhost:8080/sandboxes **创建沙箱** ```bash -curl -X POST "http://localhost:8080/sandboxes" \ +curl -X POST "http://localhost:8080/v1/sandboxes" \ -H "Content-Type: application/json" \ -d '{ "image": { @@ -210,17 +210,17 @@ curl -X POST "http://localhost:8080/sandboxes" \ **获取沙箱详情** ```bash -curl http://localhost:8080/sandboxes/ +curl http://localhost:8080/v1/sandboxes/ ``` **获取服务端点** ```bash # 获取自定义服务端点 -curl http://localhost:8080/sandboxes//endpoints/8000 +curl http://localhost:8080/v1/sandboxes//endpoints/8000 # 获取OpenSandbox守护进程(execd)端点 -curl http://localhost:8080/sandboxes//endpoints/44772 +curl http://localhost:8080/v1/sandboxes//endpoints/44772 ``` 响应: @@ -233,7 +233,7 @@ curl http://localhost:8080/sandboxes//endpoints/44772 **续期沙箱** ```bash -curl -X POST "http://localhost:8080/sandboxes//renew-expiration" \ +curl -X POST "http://localhost:8080/v1/sandboxes//renew-expiration" \ -H "Content-Type: application/json" \ -d '{ "expiresAt": "2024-01-15T12:30:00Z" @@ -243,7 +243,7 @@ curl -X POST "http://localhost:8080/sandboxes//renew-expiration" \ **删除沙箱** ```bash -curl -X DELETE http://localhost:8080/sandboxes/ +curl -X DELETE http://localhost:8080/v1/sandboxes/ ``` ## 系统架构 diff --git a/server/src/main.py b/server/src/main.py index 08fe8add..df060df6 100644 --- a/server/src/main.py +++ b/server/src/main.py @@ -88,8 +88,9 @@ # Add authentication middleware app.add_middleware(AuthMiddleware, config=app_config) -# Include API routes at root +# Include API routes at root and versioned prefix app.include_router(router) +app.include_router(router, prefix="/v1") DEFAULT_ERROR_CODE = "GENERAL::UNKNOWN_ERROR" DEFAULT_ERROR_MESSAGE = "An unexpected error occurred." diff --git a/server/tests/smoke.sh b/server/tests/smoke.sh index 5c08ae8f..7745b71c 100755 --- a/server/tests/smoke.sh +++ b/server/tests/smoke.sh @@ -50,6 +50,7 @@ error() { } BASE_URL="${BASE_URL:-http://localhost:32888}" +BASE_API_URL="${BASE_URL}/v1" API_KEY_HEADER=() if [[ -n "${OPEN_SANDBOX_API_KEY:-}" ]]; then API_KEY_HEADER=(-H "OPEN-SANDBOX-API-KEY: ${OPEN_SANDBOX_API_KEY}") @@ -68,7 +69,7 @@ wait_for_running() { local deadline=$((SECONDS + 10)) while true; do local resp - resp=$(curl_json "${BASE_URL}/sandboxes/${SANDBOX_ID}") + resp=$(curl_json "${BASE_API_URL}/sandboxes/${SANDBOX_ID}") local state state=$(python - <<'PY' "${resp}" import json,sys @@ -97,7 +98,7 @@ wait_for_expired() { local deadline=$((SECONDS + 90)) while true; do local resp body status - resp=$(curl_json_status "${BASE_URL}/sandboxes/${sandbox_id}") + resp=$(curl_json_status "${BASE_API_URL}/sandboxes/${sandbox_id}") status="${resp##*$'\n'}" body="${resp%$'\n'*}" if [[ "${status}" == "404" ]]; then @@ -128,7 +129,7 @@ step "Create sandbox (60s TTL)" create_resp=$(curl_json \ -H 'Content-Type: application/json' \ -d "${create_payload}" \ - "${BASE_URL}/sandboxes") + "${BASE_API_URL}/sandboxes") SANDBOX_ID=$(python - <<'PY' "${create_resp}" import json,sys @@ -166,7 +167,7 @@ list_resp=$(curl_json \ --data-urlencode "metadata=hello=world" \ --data-urlencode "page=1" \ --data-urlencode "pageSize=10" \ - "${BASE_URL}/sandboxes") + "${BASE_API_URL}/sandboxes") python - <<'PY' "${list_resp}" "${SANDBOX_ID}" import json,sys @@ -196,7 +197,7 @@ renew_resp=$(curl_json \ -X POST \ -H 'Content-Type: application/json' \ -d "${renew_payload}" \ - "${BASE_URL}/sandboxes/${SANDBOX_ID}/renew-expiration") + "${BASE_API_URL}/sandboxes/${SANDBOX_ID}/renew-expiration") renewed=$(python - <<'PY' "${renew_resp}" import json,sys body=json.loads(sys.argv[1]) @@ -206,7 +207,7 @@ PY echo "Expiration renewed to: ${renewed}" step "Request endpoint on port 8080" -endpoint_resp=$(curl_json "${BASE_URL}/sandboxes/${SANDBOX_ID}/endpoints/8080") +endpoint_resp=$(curl_json "${BASE_API_URL}/sandboxes/${SANDBOX_ID}/endpoints/8080") endpoint=$(python - <<'PY' "${endpoint_resp}" import json,sys body=json.loads(sys.argv[1]) @@ -216,7 +217,7 @@ PY echo "Endpoint: ${endpoint}" step "Delete sandbox" -curl_json -X DELETE "${BASE_URL}/sandboxes/${SANDBOX_ID}" +curl_json -X DELETE "${BASE_API_URL}/sandboxes/${SANDBOX_ID}" echo "Sandbox ${SANDBOX_ID} deleted." step "Create short-lived sandbox (60s TTL) for auto-expiration" @@ -232,7 +233,7 @@ create_payload_short='{ create_resp_short=$(curl_json \ -H 'Content-Type: application/json' \ -d "${create_payload_short}" \ - "${BASE_URL}/sandboxes") + "${BASE_API_URL}/sandboxes") SANDBOX_ID=$(python - <<'PY' "${create_resp_short}" import json,sys diff --git a/server/tests/test_routes.py b/server/tests/test_routes.py index 1e9cbc00..2b835993 100644 --- a/server/tests/test_routes.py +++ b/server/tests/test_routes.py @@ -50,6 +50,14 @@ def test_missing_api_key(self, client: TestClient): assert response.status_code == 401 assert "MISSING_API_KEY" in response.json()["code"] + def test_missing_api_key_v1_prefix(self, client: TestClient): + """ + Test request without API key on versioned route returns 401. + """ + response = client.get("/v1/sandboxes/123e4567-e89b-12d3-a456-426614174000") + assert response.status_code == 401 + assert "MISSING_API_KEY" in response.json()["code"] + def test_invalid_api_key(self, client: TestClient): """ Test request with invalid API key returns 401.