Spaces:
Running
Running
| """ | |
| backend/routes/internet_routes.py | |
| Β§2.4 β Internet Layer: live OSINT, web search, URL fetch, scrape | |
| """ | |
| from fastapi import HTTPException, APIRouter, Depends | |
| from pydantic import BaseModel | |
| from typing import Optional | |
| from backend.billing.gating import require_feature | |
| from backend.billing.plans import Feature | |
| router = APIRouter() | |
| # v22: OSINT is a paid feature in the plan catalog (plus/pro), WEB_SEARCH is not (free | |
| # grants it). So the gate goes on /osint specifically, NOT on the router β gating the | |
| # whole router would take /search away from the free tier that is entitled to it. | |
| _OSINT_GATE = Depends(require_feature(Feature.OSINT)) | |
| class SearchRequest(BaseModel): | |
| query: str | |
| num_results: int = 5 | |
| persona: Optional[str] = "jarvis" | |
| class FetchRequest(BaseModel): | |
| url: str | |
| summarise: bool = True | |
| class OsintRequest(BaseModel): | |
| target: str # domain, IP, username, email, etc. | |
| recon_type: str = "full" # full | whois | social | dns | email | |
| async def web_search(req: SearchRequest): | |
| """Live OSINT web search via the backend tool registry.""" | |
| try: | |
| from backend.tools.web_search_tools import search_web | |
| results = await search_web(req.query, num_results=req.num_results) | |
| return {"status": "ok", "query": req.query, "results": results} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def fetch_url(req: FetchRequest): | |
| """Fetch and optionally summarise a URL using the browser tool.""" | |
| try: | |
| # browser_tools has never exported `fetch_url` (it exposes navigate / | |
| # get_ephemeral_context), so this route was a guaranteed 500: | |
| # "cannot import name 'fetch_url' from 'backend.tools.browser_tools'". | |
| # The function that actually does this job β Playwright fetch, CAPTCHA | |
| # handling, text extraction β is web_search_tools.fetch_page. | |
| from backend.tools.web_search_tools import fetch_page | |
| content = await fetch_page(req.url) | |
| return {"status": "ok", "url": req.url, "content": content} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def osint_recon(req: OsintRequest, _user: str = _OSINT_GATE): | |
| """ | |
| Elite Defensive OSINT recon endpoint. | |
| Performs web reconnaissance, digital footprint analysis, | |
| and threat modelling on a given target. | |
| """ | |
| if not req.target: | |
| raise HTTPException(status_code=400, detail="target is required") | |
| try: | |
| from backend.tools.web_search_tools import search_web | |
| queries = { | |
| "full": [f'site:shodan.io "{req.target}"', f'"{req.target}" breach OR leak OR hack', f'"{req.target}" whois DNS records'], | |
| "whois": [f'whois "{req.target}"'], | |
| "social": [f'"{req.target}" site:twitter.com OR site:linkedin.com OR site:github.com'], | |
| "dns": [f'DNS records "{req.target}"'], | |
| "email": [f'"{req.target}" email breach haveibeenpwned'], | |
| }.get(req.recon_type, [f'"{req.target}"']) | |
| all_results = [] | |
| for q in queries: | |
| results = await search_web(q, num_results=3) | |
| # search_web returns {"results": [...]} (or {"error": ...}). | |
| # extend()ing the dict itself appended its KEYS β so findings came | |
| # back as ["results", "results", ...] instead of the actual hits. | |
| if isinstance(results, dict): | |
| all_results.extend(results.get("results", [])) | |
| elif isinstance(results, list): | |
| all_results.extend(results) | |
| return { | |
| "status": "ok", | |
| "target": req.target, | |
| "recon_type": req.recon_type, | |
| "findings": all_results, | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def internet_status(): | |
| """Health check: verify the internet layer is reachable.""" | |
| import httpx | |
| try: | |
| async with httpx.AsyncClient(timeout=5) as client: | |
| r = await client.get("https://www.google.com") | |
| return {"status": "online", "latency_ms": r.elapsed.total_seconds() * 1000} | |
| except Exception as e: | |
| raise HTTPException(status_code=503, detail=f"Internet layer offline: {e}") | |