From 0f60aa9046d6ec51f9f65f5e4b5a14b3769d457f Mon Sep 17 00:00:00 2001 From: protokey Date: Tue, 8 Sep 2026 17:39:57 +0400 Subject: [PATCH] =?UTF-8?q?chore:=20=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE=20?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D1=80=D1=8B=D1=82=D1=8C=20=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=86=D1=83=20=D0=B8=20=D0=BF=D0=BE=D0=B4?= =?UTF-8?q?=D0=BE=D0=B6=D0=B4=D0=B0=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.py | 71 ++++++++++++++++++++++++++++++++++++++++++-- tests/test_main.http | 15 ++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index e77f49f..be15578 100644 --- a/src/main.py +++ b/src/main.py @@ -1,4 +1,9 @@ +from datetime import datetime + +from camoufox.async_api import AsyncCamoufox from fastapi import FastAPI +from fastapi import Request +from playwright.async_api import Page from pydantic import BaseModel @@ -27,6 +32,68 @@ async def root(): return {"message": "Hello World"} +async def _fill(page: "Page", act: ActionRequest) -> str | None: + ... + + +async def _mouse_move(page: "Page", act: ActionRequest) -> str | None: + ... + + +async def _click(page: "Page", act: ActionRequest) -> str | None: + ... + + +async def _wait_for(page: Page, act: ActionRequest) -> str | None: + try: + assert act.selector + await page.wait_for_selector(act.selector, timeout=act.timeout * 1000) + except Exception as e: + return str(e) + else: + return None + + @app.post("/v1/solve") -async def solve(payload: SolveRequest): - return {"message": payload.model_dump(exclude_none=True)} +async def solve(request: Request, payload: SolveRequest): + api_key = request.headers.get('X-API-Key') + async with AsyncCamoufox() as camoufo: + page = await camoufo.new_page() + page.set_default_timeout(30_000) + await page.goto(url=payload.url) + actions_result = [] + status = True + error = None + + for act in payload.actions: + result = await { + "fill": _fill, + "mouse_move": _mouse_move, + "click": _click, + "wait_for": _wait_for, + }[act.type](page, act) + if( ok := result ) is None: + act_log = {"type": act.type, "ok": ok} + else: + act_log = {"type": act.type, "ok": ok, "error": result} + actions_result.append(act_log) + if "error" in act_log: + status = False + error = act_log["error"] + break + + cookies = await camoufo.cookies() + html = await page.inner_html('html') + await page.screenshot( + path=( + f"/home/sokol/PycharmProjects/WebRoboApi/extra/screenshots/" + f"{datetime.timestamp(datetime.now())}.png" + ) + ) + return { + "status": ["err", "ok"][status], + "html": html, + "cookies": cookies, + "actions": actions_result, + "error": error, + } diff --git a/tests/test_main.http b/tests/test_main.http index 8566871..0646df4 100644 --- a/tests/test_main.http +++ b/tests/test_main.http @@ -33,3 +33,18 @@ X-API-Key: } ### +POST http://127.0.0.1:8000/v1/solve +Accept: application/json +Content-Type: application/json +X-API-Key: + + +{ + "url": "https://camoufox.com/python/usage/", + "proxy": "-", + "actions": [ + {"type": "wait_for", "selector": "span.dark\\:text-white", "timeout": 30} + ] +} + +###