|
|
|
|
|
from fastapi import FastAPI, Depends, HTTPException, Security |
|
|
from fastapi.security import APIKeyHeader |
|
|
from fastapi.middleware.cors import CORSMiddleware |
|
|
from fastapi import Request, Query |
|
|
from fastapi.templating import Jinja2Templates |
|
|
from fastapi import File, UploadFile |
|
|
from fastapi.responses import FileResponse |
|
|
from fastapi.responses import Response |
|
|
|
|
|
from pydantic import BaseModel |
|
|
from serpapi import GoogleSearch |
|
|
import os |
|
|
|
|
|
app = FastAPI(title="SerpApi Research Backend") |
|
|
|
|
|
app.add_middleware( |
|
|
CORSMiddleware, |
|
|
allow_origins=["*"], |
|
|
allow_credentials=True, |
|
|
allow_methods=["*"], |
|
|
allow_headers=["*"], |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
api_key_header = APIKeyHeader(name="X-API-Key") |
|
|
SERPAPI_KEY = os.getenv("SERPAPI_SECRET") |
|
|
|
|
|
class SearchQuery(BaseModel): |
|
|
query: str |
|
|
engine: str = "google" |
|
|
num_results: int = 10 |
|
|
|
|
|
def get_serpapi_client(): |
|
|
if not SERPAPI_KEY: |
|
|
raise HTTPException( |
|
|
status_code=500, |
|
|
detail="SerpApi configuration missing" |
|
|
) |
|
|
return GoogleSearch({"api_key": SERPAPI_KEY}) |
|
|
|
|
|
def verify_api_key(api_key: str = Security(api_key_header)): |
|
|
print("api_key: " + api_key) |
|
|
if api_key != os.getenv("API_SECRET_KEY"): |
|
|
raise HTTPException( |
|
|
status_code=401, |
|
|
detail="Invalid API key" |
|
|
) |
|
|
return True |
|
|
|
|
|
templates = Jinja2Templates(directory=".") |
|
|
|
|
|
@app.get("/") |
|
|
def read_root(request: Request): |
|
|
return templates.TemplateResponse("index.html", {"request": request}) |
|
|
|
|
|
@app.post("/search") |
|
|
async def perform_search( |
|
|
search_query: SearchQuery, |
|
|
_: bool = Depends(verify_api_key) |
|
|
): |
|
|
try: |
|
|
|
|
|
params = { |
|
|
"q": search_query.query, |
|
|
"engine": search_query.engine, |
|
|
"num": search_query.num_results, |
|
|
"api_key": SERPAPI_KEY |
|
|
} |
|
|
|
|
|
|
|
|
search = GoogleSearch(params) |
|
|
|
|
|
|
|
|
results = search.get_dict() |
|
|
|
|
|
|
|
|
return { |
|
|
"organic_results": results.get("organic_results", []), |
|
|
"related_searches": results.get("related_searches", []) |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
raise HTTPException( |
|
|
status_code=500, |
|
|
detail=f"Search failed: {str(e)}" |
|
|
) |
|
|
|
|
|
@app.get("/health") |
|
|
async def health_check(): |
|
|
return {"status": "operational"} |
|
|
|