summaryrefslogtreecommitdiff
path: root/backend/app/main.py
diff options
context:
space:
mode:
authorYurenHao0426 <blackhao0426@gmail.com>2026-02-13 18:26:40 +0000
committerYurenHao0426 <blackhao0426@gmail.com>2026-02-13 18:26:40 +0000
commitfe5f0842bddb68ccb9862d86fd255af2694a8b52 (patch)
treed7e96773c5adf728cdced1a3e4ffb0ccd8fa20dc /backend/app/main.py
parentde9e6207c84ceb55ef4f377d68f93a117f1f1cc2 (diff)
Fix file upload/delete using user API keys instead of env vars only
The upload_file and delete_file endpoints were calling API clients without passing user-specific API keys, falling back only to environment variables. Now both endpoints resolve keys from the authenticated user's settings via get_user_api_key(), fixing "API key not found" errors for users who configured keys through the frontend. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'backend/app/main.py')
-rw-r--r--backend/app/main.py41
1 files changed, 28 insertions, 13 deletions
diff --git a/backend/app/main.py b/backend/app/main.py
index a6f4f10..2c625a9 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -639,12 +639,13 @@ async def ensure_user_vector_store(user: str, client=None) -> str:
json.dump({"id": vs_id}, f)
return vs_id
-async def ensure_openai_file_and_index(user: str, meta: FileMeta, path: str, llm_config: Optional[LLMConfig] = None) -> tuple[str, str]:
+async def ensure_openai_file_and_index(user: str, meta: FileMeta, path: str, llm_config: Optional[LLMConfig] = None, api_key: str = None) -> tuple[str, str]:
"""
Ensure the file is uploaded to OpenAI Files and added to the user's vector store.
Returns (openai_file_id, vector_store_id).
"""
- client = get_openai_client(llm_config.api_key if llm_config else None)
+ key = api_key or (llm_config.api_key if llm_config else None)
+ client = get_openai_client(key)
vs_id = await ensure_user_vector_store(user, client)
file_id = meta.openai_file_id or (meta.provider_file_id if meta.provider == "openai" else None)
@@ -1108,7 +1109,10 @@ async def upload_file(
file: UploadFile = File(...),
provider: str = Form("local"),
purpose: Optional[str] = Form(None),
+ current_user: User | None = Depends(get_current_user_optional),
):
+ if current_user:
+ user = current_user.username
migrate_legacy_layout(user)
items = load_files_index(user)
file_id = str(uuid4())
@@ -1131,9 +1135,14 @@ async def upload_file(
provider_file_id: Optional[str] = None
provider_created_at: Optional[float] = None
+ # Resolve user API keys for all providers
+ openai_key = get_user_api_key(current_user, "openai")
+ google_key = get_user_api_key(current_user, "google")
+ anthropic_key = get_user_api_key(current_user, "claude")
+
if provider_normalized == "openai":
try:
- client = get_openai_client()
+ client = get_openai_client(openai_key)
upload_purpose = purpose or OPENAI_DEFAULT_FILE_PURPOSE
resp = await client.files.create(
file=(file_name, content),
@@ -1145,10 +1154,10 @@ async def upload_file(
raise HTTPException(status_code=500, detail=f"OpenAI upload failed: {str(e)}")
elif provider_normalized == "google":
try:
- key = os.getenv("GOOGLE_API_KEY")
- if not key:
+ g_key = google_key
+ if not g_key:
raise HTTPException(status_code=500, detail="Google API Key not found")
- client = genai.Client(api_key=key)
+ client = genai.Client(api_key=g_key)
# The Google GenAI SDK upload is synchronous; run in thread to avoid blocking the event loop.
tmp_path = None
try:
@@ -1215,7 +1224,7 @@ async def upload_file(
logger.debug("Skipping OpenAI vector store indexing for image file: %s", file_name)
elif size <= OPENAI_MAX_FILE_SIZE:
try:
- openai_file_id, vs_id = await ensure_openai_file_and_index(user, meta, dest_path, None)
+ openai_file_id, vs_id = await ensure_openai_file_and_index(user, meta, dest_path, api_key=openai_key)
meta.openai_file_id = openai_file_id
meta.openai_vector_store_id = vs_id
if provider_normalized == "openai" and not meta.provider_file_id:
@@ -1226,12 +1235,12 @@ async def upload_file(
logger.warning("Skipping OpenAI indexing for %s: exceeds 50MB", file_name)
# Anthropic Files API upload (eager, like OpenAI)
- anthropic_id = await ensure_anthropic_file_upload(meta, dest_path)
+ anthropic_id = await ensure_anthropic_file_upload(meta, dest_path, api_key=anthropic_key)
if anthropic_id:
meta.anthropic_file_id = anthropic_id
# Google Files API upload (eager, like OpenAI)
- google_uri = await ensure_google_file_upload(meta, dest_path)
+ google_uri = await ensure_google_file_upload(meta, dest_path, api_key=google_key)
if google_uri:
meta.google_file_uri = google_uri
# Also backfill provider_file_id for legacy compat if provider was google
@@ -1257,7 +1266,13 @@ def download_file(user: str = DEFAULT_USER, file_id: str = ""):
@app.post("/api/files/delete")
-async def delete_file(user: str = DEFAULT_USER, file_id: str = ""):
+async def delete_file(
+ user: str = DEFAULT_USER,
+ file_id: str = "",
+ current_user: User | None = Depends(get_current_user_optional),
+):
+ if current_user:
+ user = current_user.username
migrate_legacy_layout(user)
items = load_files_index(user)
meta = next((i for i in items if i.id == file_id), None)
@@ -1269,7 +1284,7 @@ async def delete_file(user: str = DEFAULT_USER, file_id: str = ""):
await remove_file_from_vector_store(meta.openai_vector_store_id, meta.openai_file_id)
if meta.provider == "openai" and meta.provider_file_id:
try:
- client = get_openai_client()
+ client = get_openai_client(get_user_api_key(current_user, "openai"))
await client.files.delete(meta.provider_file_id)
except Exception as e:
raise HTTPException(status_code=500, detail=f"OpenAI delete failed: {str(e)}")
@@ -1281,7 +1296,7 @@ async def delete_file(user: str = DEFAULT_USER, file_id: str = ""):
google_refs.add(meta.google_file_uri)
for g_ref in google_refs:
try:
- key = os.getenv("GOOGLE_API_KEY")
+ key = get_user_api_key(current_user, "google")
if not key:
logger.warning("Skipping Google file deletion: no API key")
break
@@ -1294,7 +1309,7 @@ async def delete_file(user: str = DEFAULT_USER, file_id: str = ""):
# Delete from Anthropic Files API if present
if meta.anthropic_file_id:
try:
- client = get_anthropic_client()
+ client = get_anthropic_client(get_user_api_key(current_user, "claude"))
await asyncio.to_thread(
client.beta.files.delete, meta.anthropic_file_id
)