diff --git a/backend/main.py b/backend/main.py index b8a9884..5e9b7a7 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,4 +1,5 @@ -from fastapi import FastAPI, Request +from fastapi import FastAPI, Request, HTTPException, Security +from fastapi.security import APIKeyHeader from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import StreamingResponse, JSONResponse from pydantic import BaseModel @@ -27,6 +28,17 @@ app.add_middleware( allow_headers=["*"], ) +API_KEY = "your-secret-key-here" # 建议从环境变量读取 +api_key_header = APIKeyHeader(name="X-API-Key") + +async def get_api_key(api_key: str = Security(api_key_header)): + if api_key != API_KEY: + raise HTTPException( + status_code=403, + detail="Could not validate credentials" + ) + return api_key + from typing import Optional class UserPreferences(BaseModel): @@ -58,7 +70,7 @@ def get_client_ip(request: Request) -> str: return request.headers.get("X-Client-IP") or request.client.host if request.client else "unknown" @app.post("/v1/completions") -async def create_completion(request: Request, req: CompletionRequest): +async def create_completion(request: Request, req: CompletionRequest, api_key: str = Security(get_api_key)): request_id = str(uuid.uuid4())[:8] client_ip = "hidden" @@ -122,7 +134,7 @@ async def create_completion(request: Request, req: CompletionRequest): return JSONResponse(content={"error": str(e)}, status_code=500) @app.post("/v1/ocr") -async def ocr_image(request: OCRRequest): +async def ocr_image(request: OCRRequest, api_key: str = Security(get_api_key)): request_id = str(uuid.uuid4())[:8] try: logger.info( diff --git a/src/components/MilkdownEditor.vue b/src/components/MilkdownEditor.vue index cf8fa37..447e5ae 100644 --- a/src/components/MilkdownEditor.vue +++ b/src/components/MilkdownEditor.vue @@ -233,7 +233,10 @@ const performOCR = async (file, cacheKey, imageHash = '') => { try { const res = await fetch(OCR_URL, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': 'your-secret-key-here' + }, body: JSON.stringify({ image: base64, filename: file.name, diff --git a/src/utils/api.js b/src/utils/api.js index ecf62c6..60c3cbc 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -22,7 +22,10 @@ export async function fetchSuggestion(prefix, suffix, signal, apiUrl = API_URL) try { const settings = useSettingsStore() const clientIP = await getClientIP() - const headers = { 'Content-Type': 'application/json' } + const headers = { + 'Content-Type': 'application/json', + 'X-API-Key': 'your-secret-key-here' + } // Only send IP if privacy mode is OFF if (clientIP && !settings.privacyMode) { diff --git a/src/utils/config.js b/src/utils/config.js index 0e7612a..b3bb2aa 100644 --- a/src/utils/config.js +++ b/src/utils/config.js @@ -1,6 +1,6 @@ export const DEBUG = import.meta.env.DEV -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://149.104.29.239:8001' +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://api.learnteach.tech:8002' export const API_URL = import.meta.env.VITE_API_URL || `${API_BASE_URL}/v1/completions` export const OCR_URL = import.meta.env.VITE_OCR_URL || `${API_BASE_URL}/v1/ocr`