from pathlib import Path from fastapi import APIRouter, Request from fastapi.templating import Jinja2Templates from app.db import fetch_all, fetch_one router = APIRouter(tags=["browser"]) BASE_DIR = Path(__file__).resolve().parent.parent.parent templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) def _iso_rows(rows: list) -> list: for row in rows: for key, val in list(row.items()): if hasattr(val, "isoformat"): row[key] = val.isoformat() return rows def _monitor_context() -> dict: sites, changes, logs = [], [], [] stats = {"active_sites": 0, "changes_24h": 0} try: sites = _iso_rows(fetch_all( """SELECT id, url, name, last_hash, last_crawled, is_active FROM monitored_sites ORDER BY last_crawled DESC NULLS LAST LIMIT 100""" )) row = fetch_one("SELECT COUNT(*) AS c FROM monitored_sites WHERE is_active = TRUE") stats["active_sites"] = int(row["c"]) if row else 0 row = fetch_one( "SELECT COUNT(*) AS c FROM page_changes WHERE changed_at > NOW() - interval '24 hours'" ) stats["changes_24h"] = int(row["c"]) if row else 0 except Exception: sites = [] try: changes = _iso_rows(fetch_all( """SELECT pc.id, pc.site_id, pc.old_hash, pc.new_hash, pc.changed_at, ms.url, ms.name FROM page_changes pc JOIN monitored_sites ms ON pc.site_id = ms.id ORDER BY pc.changed_at DESC LIMIT 50""" )) except Exception: changes = [] try: logs = _iso_rows(fetch_all( """SELECT cl.id, cl.site_id, cl.status, cl.message, cl.logged_at, ms.url, ms.name FROM crawl_logs cl LEFT JOIN monitored_sites ms ON cl.site_id = ms.id ORDER BY cl.logged_at DESC LIMIT 50""" )) except Exception: logs = [] return {"sites": sites, "page_changes": changes, "crawl_logs": logs, "stats": stats} @router.get("/browser") async def browser_page(request: Request): ctx = _monitor_context() return templates.TemplateResponse( "browser.html", { "request": request, "page_title": "Browser & Monitor", "default_url": "https://www.bidfood.nl/webshop/assortiment/mekkafood/_/N-1z10pje/", "browser_agent_url": "http://10.4.7.18:7790", "novnc_url": "http://10.4.7.18:6080/vnc.html?autoconnect=true&resize=scale&path=websockify&password=Foodlinkk2026", "gradio_url": "http://10.4.7.18:7788", **ctx, }, )