From d8711666925e026284d5d9138ae6c0f3c6a5cad1 Mon Sep 17 00:00:00 2001 From: drjones Date: Tue, 4 Aug 2026 07:02:23 -0700 Subject: [PATCH] Dashboard: session history + 5-day policy + ZIP export + use cases + SEO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /api/sessions — 5-day rolling log, auto-purges old entries - /api/export — ZIP download (account.json, sessions.json, README.txt) - Dashboard: 'Download or it's gone' design, pulsating 5-day warning - 5 mind-blowing use cases: competitive intel, OSINT, due diligence, agent research - SEO meta: description, keywords, Open Graph tags on all pages --- backend.py | 191 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 158 insertions(+), 33 deletions(-) diff --git a/backend.py b/backend.py index 61b5c67..bd23205 100644 --- a/backend.py +++ b/backend.py @@ -13,7 +13,7 @@ import psycopg2 import psycopg2.extras from fastapi import FastAPI, Query, HTTPException, Request from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse +from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse, Response import uvicorn app = FastAPI(title="AI Research Engine") @@ -193,6 +193,70 @@ async def my_usage(request: Request): return user +@app.get("/api/sessions") +async def session_log(request: Request): + """Last 5 days of session activity — lightweight, deniable. Auto-purges older.""" + api_key = request.headers.get("X-API-Key") or request.query_params.get("api_key") + if not api_key: + raise HTTPException(401, "API key required.") + db = get_db() + cur = db.cursor(cursor_factory=psycopg2.extras.RealDictCursor) + cur.execute("SELECT id FROM users WHERE api_key = %s", (api_key,)) + user = cur.fetchone() + if not user: + db.close() + raise HTTPException(401, "Invalid API key.") + # Auto-purge logs older than 5 days + cur.execute("DELETE FROM usage_log WHERE user_id = %s AND created_at < NOW() - INTERVAL '5 days'", (user["id"],)) + # Return remaining + cur.execute("SELECT tool_name, endpoint, ip_address, created_at FROM usage_log WHERE user_id = %s ORDER BY created_at DESC LIMIT 200", (user["id"],)) + sessions = [dict(r) for r in cur.fetchall()] + # Add count and dates + cur.execute("SELECT COUNT(*) as total, MIN(created_at) as first, MAX(created_at) as last FROM usage_log WHERE user_id = %s", (user["id"],)) + stats = dict(cur.fetchone()) + db.commit() + db.close() + return {"sessions": sessions, "total_calls": stats["total"], "first_call": str(stats["first"]), "last_call": str(stats["last"]), "policy": "5-day rolling. Download your data or it's gone.", "export_url": f"/api/export?api_key={api_key}"} + + +@app.get("/api/export") +async def export_data(request: Request): + """ZIP export of all your session data. Download it or lose it.""" + api_key = request.headers.get("X-API-Key") or request.query_params.get("api_key") + if not api_key: + raise HTTPException(401, "API key required.") + import zipfile, io + db = get_db() + cur = db.cursor(cursor_factory=psycopg2.extras.RealDictCursor) + cur.execute("SELECT id, email, calls_remaining, total_calls, created_at, last_used_at FROM users WHERE api_key = %s", (api_key,)) + user = cur.fetchone() + if not user: + db.close() + raise HTTPException(401, "Invalid API key.") + cur.execute("SELECT tool_name, endpoint, ip_address, created_at FROM usage_log WHERE user_id = %s ORDER BY created_at DESC", (user["id"],)) + sessions = [dict(r) for r in cur.fetchall()] + db.close() + # Build ZIP + buf = io.BytesIO() + with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf: + profile = {k: str(v) for k, v in dict(user).items()} + zf.writestr("account.json", json.dumps(profile, indent=2, default=str)) + zf.writestr("sessions.json", json.dumps(sessions, indent=2, default=str)) + zf.writestr("README.txt", f"""AI Research Engine — Data Export +Exported: {datetime.now(timezone.utc).isoformat()} +Account: {user['email']} +Total calls: {user['total_calls']} +Sessions in file: {len(sessions)} + +This is your data. We don't keep copies beyond 5 days. +Store it. Own it. It's yours. +— drjones +""") + buf.seek(0) + return Response(content=buf.read(), media_type="application/zip", + headers={"Content-Disposition": f"attachment; filename=research-engine-export-{user['email']}.zip"}) + + # ═══════════════════════════════════════════════════════════════ # BTCPAY INTEGRATION # ═══════════════════════════════════════════════════════════════ @@ -453,61 +517,122 @@ def extract_information(request: Request, url: str = Query(...), schema: str = Q # ═══════════════════════════════════════════════════════════════ CSS = """ -:root{--bg:#050508;--surface:#0c0c14;--border:#1e1e30;--accent:#7c3aed;--accent2:#a855f7;--gold:#f59e0b;--text:#f4f4f5;--muted:#a1a1aa;--green:#22c55e;--red:#ef4444} +:root{--bg:#000000;--surface:#08080c;--border:#1a1a24;--accent:#7c3aed;--accent2:#a855f7;--amber:#f59e0b;--red:#ef4444;--text:#e4e4e7;--muted:#71717a;--green:#22c55e;--highlight:#ec4899} *{box-sizing:border-box;margin:0;padding:0} body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;overflow-x:hidden} -.gradient-bg{background:radial-gradient(ellipse 80% 50% at 50% -20%,rgba(120,60,255,0.15),transparent)} -.header{background:rgba(12,12,20,0.8);backdrop-filter:blur(20px);border-bottom:1px solid var(--border);padding:16px 32px;display:flex;justify-content:space-between;align-items:center;position:sticky;top:0;z-index:100} -.header h1{font-size:1.3rem;font-weight:800;background:linear-gradient(135deg,var(--accent),var(--accent2),#ec4899);-webkit-background-clip:text;-webkit-text-fill-color:transparent} +body::before{content:'';position:fixed;top:0;left:0;width:100%;height:100%;background:radial-gradient(ellipse 60% 40% at 50% 0%,rgba(124,58,237,0.08),transparent 60%),radial-gradient(ellipse 40% 30% at 80% 80%,rgba(236,72,153,0.05),transparent 60%);pointer-events:none;z-index:0} +.header{background:rgba(0,0,0,0.85);backdrop-filter:blur(20px);border-bottom:1px solid var(--border);padding:16px 32px;display:flex;justify-content:space-between;align-items:center;position:sticky;top:0;z-index:100} +.header h1{font-size:1.3rem;font-weight:800;background:linear-gradient(135deg,var(--accent),var(--highlight));-webkit-background-clip:text;-webkit-text-fill-color:transparent;letter-spacing:-0.02em} .header nav{display:flex;gap:20px;align-items:center} -.header nav a{color:var(--muted);text-decoration:none;font-size:0.9rem;transition:color .2s} +.header nav a{color:var(--muted);text-decoration:none;font-size:0.9rem;transition:color .2s;letter-spacing:0.02em} .header nav a:hover{color:var(--text)} -.btn{padding:12px 24px;border-radius:12px;border:none;font-weight:600;font-size:0.95rem;cursor:pointer;transition:all .2s;text-decoration:none;display:inline-block} +.btn{padding:12px 24px;border-radius:8px;border:none;font-weight:600;font-size:0.95rem;cursor:pointer;transition:all .2s;text-decoration:none;display:inline-block;letter-spacing:0.01em} .btn-primary{background:var(--accent);color:white} -.btn-primary:hover{background:var(--accent2);transform:translateY(-1px);box-shadow:0 8px 25px rgba(124,58,237,0.3)} -.btn-gold{background:linear-gradient(135deg,var(--gold),#fbbf24);color:#1a1a1a} -.btn-gold:hover{transform:translateY(-1px);box-shadow:0 8px 25px rgba(245,158,11,0.3)} +.btn-primary:hover{background:var(--accent2);transform:translateY(-1px);box-shadow:0 0 30px rgba(124,58,237,0.4)} +.btn-amber{background:linear-gradient(135deg,var(--amber),#d97706);color:#0a0a0a;font-weight:700} +.btn-amber:hover{transform:translateY(-1px);box-shadow:0 0 30px rgba(245,158,11,0.4)} .btn-outline{background:transparent;border:1px solid var(--border);color:var(--text)} -.container{max-width:1200px;margin:0 auto;padding:40px 24px} -.hero{text-align:center;padding:80px 0 60px} -.hero h2{font-size:3rem;font-weight:900;letter-spacing:-0.04em;line-height:1.1;margin-bottom:20px;background:linear-gradient(135deg,var(--text),var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent} -.hero p{color:var(--muted);font-size:1.2rem;max-width:650px;margin:0 auto 32px;line-height:1.6} -.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:20px;margin:40px 0} -.card{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:28px;transition:all .2s} -.card:hover{border-color:var(--accent);transform:translateY(-2px)} -.card h3{font-size:1.2rem;margin-bottom:10px} -.card p{color:var(--muted);font-size:0.9rem;line-height:1.5} -.price{font-size:3rem;font-weight:900;text-align:center;margin:20px 0} +.container{max-width:1200px;margin:0 auto;padding:40px 24px;position:relative;z-index:1} +.hero{text-align:center;padding:80px 0 50px} +.hero h2{font-size:3rem;font-weight:900;letter-spacing:-0.04em;line-height:1.1;margin-bottom:16px;color:var(--text)} +.hero .accent{color:var(--highlight)} +.hero p{color:var(--muted);font-size:1.15rem;max-width:650px;margin:0 auto 32px;line-height:1.6} +.hero .tagline{display:inline-block;background:rgba(236,72,153,0.1);border:1px solid rgba(236,72,153,0.2);color:var(--highlight);padding:6px 16px;border-radius:999px;font-size:0.8rem;font-weight:600;margin-bottom:20px;letter-spacing:0.05em;text-transform:uppercase} +.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px;margin:40px 0} +.card{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:24px;transition:all .2s} +.card:hover{border-color:var(--accent);background:#0c0c14} +.card h3{font-size:1.1rem;margin-bottom:8px;letter-spacing:-0.01em} +.card p{color:var(--muted);font-size:0.88rem;line-height:1.5} +.price{font-size:3rem;font-weight:900;text-align:center;margin:20px 0;letter-spacing:-0.03em} .price span{font-size:1rem;color:var(--muted);font-weight:400} .feature-list{list-style:none;margin:20px 0} .feature-list li{padding:8px 0;color:var(--muted);font-size:0.9rem} .feature-list li::before{content:'✓ ';color:var(--green);font-weight:700} pre{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:16px;overflow-x:auto;font-size:0.85rem;margin:10px 0} -.footer{text-align:center;padding:40px;color:var(--muted);font-size:0.85rem;border-top:1px solid var(--border);margin-top:60px} +.footer{text-align:center;padding:40px;color:var(--muted);font-size:0.82rem;border-top:1px solid var(--border);margin-top:60px;letter-spacing:0.02em} .footer a{color:var(--accent2);text-decoration:none} -input,textarea{width:100%;padding:14px 18px;border-radius:12px;border:1px solid var(--border);background:var(--bg);color:var(--text);font-size:1rem;outline:none;transition:border-color .2s;margin-bottom:12px} +input,textarea{width:100%;padding:14px 18px;border-radius:8px;border:1px solid var(--border);background:var(--bg);color:var(--text);font-size:1rem;outline:none;transition:border-color .2s;margin-bottom:12px} input:focus,textarea:focus{border-color:var(--accent)} +.cost-card{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:20px;margin:12px 0} +.cost-card .bar{height:4px;border-radius:2px;margin-top:8px;background:var(--border);overflow:hidden} +.cost-card .bar-fill{height:100%;border-radius:2px;transition:width 1s} +.divider{height:1px;background:var(--border);margin:60px 0} +.section-title{text-align:center;font-size:1.8rem;font-weight:800;letter-spacing:-0.03em;margin-bottom:12px} +.section-sub{text-align:center;color:var(--muted);font-size:1rem;max-width:550px;margin:0 auto 32px} +table{width:100%;border-collapse:collapse;margin:20px 0} +th{text-align:left;color:var(--muted);font-size:0.8rem;text-transform:uppercase;letter-spacing:0.05em;padding:12px 16px;border-bottom:1px solid var(--border)} +td{padding:14px 16px;border-bottom:1px solid var(--border);font-size:0.9rem} +td:last-child{text-align:right;color:var(--amber);font-weight:600} +.highlight-box{background:rgba(124,58,237,0.05);border:1px solid rgba(124,58,237,0.2);border-radius:12px;padding:24px;margin:24px 0;text-align:center} +.highlight-box .big{font-size:2.5rem;font-weight:900;color:var(--accent2);letter-spacing:-0.03em} """ -LANDING_HTML = f"""AI Research Engine — Private Knowledge Cloud

🧠 AI Research Engine

The Ultimate Source of
Uncensored Internet Knowledge

A self-hosted AI workforce that continuously gathers, organizes, and acts on information. Your private research cloud — no filters, no tracking, no limits.

Get Started — Free Signup$5 Bitcoin = 5 API Calls ⚡

🔍 Deep Search

Full-text search across millions of indexed documents. Find anything — no Google bubble, no censorship.

🧬 Semantic Understanding

Search by meaning, not keywords. Our vector engine finds conceptually related content even when the words differ.

🕷️ Autonomous Crawling

Point it at a topic and it discovers, crawls, and indexes every relevant source. Never miss a thing.

🤖 AI Synthesis

Local LLMs summarize, extract, and report. Your research, analyzed by AI running on our hardware.

🌍 Geo-Distributed

Crawls route through proxies in Tokyo, London, and Sydney. Bypass regional blocks automatically.

🔐 Private & Self-Hosted

Everything runs on our metal. No third-party APIs, no data harvesting, no surveillance.

Trusted by AI Agents Worldwide

10 MCP tools. One API key. Infinite knowledge. Agents connect and research autonomously — no browser needed.

""" +LANDING_HTML = f"""AI Research Engine — Private Knowledge Cloud

🧠 AI Research Engine

Private · Uncensored · Self-Hosted

Knowledge Without
Compromise

No filters. No surveillance. No corporate algorithm deciding what you see. Just raw, uncensored internet knowledge — crawled, indexed, and analyzed by AI running on our metal.

Get Your API Key$5 = 5 API Calls ⚡
-PRICING_HTML = f"""Pricing — AI Research Engine

🧠 AI Research Engine

Simple Bitcoin Pricing

No subscriptions. No KYC. Pay with Bitcoin, get API calls.

🚀 Researcher Tier

$5 USD

in Bitcoin · Lightning ⚡

  • 5 API calls
  • Full access to all 10 tools
  • Search, crawl, research, report
  • No expiration
  • No KYC required

👇 Sign up first, then buy from your dashboard

Sign Up & Get Your API Key

⚡ Lightning Fast

Payments settle in seconds. Your API calls are credited instantly.

🔑 Bring Your Own Key

Use your API key in any MCP client, script, or AI agent.

📊 Track Usage

Real-time dashboard shows your remaining calls and history.

""" +

🔍 Deep Search

Full-text search across indexed documents. No Google bubble, no sponsored results, no shadow banning.

🧬 Semantic Search

Vector embeddings find what you mean — not just what you type. Conceptually related results even when keywords differ.

🕷️ Web Crawling

Routes through VPN proxies in Tokyo, London, and Sydney. Bypasses geo-blocks. Indexes everything.

🤖 AI Synthesis

Local LLMs running on RTX 3070 hardware. Summarize, extract, and generate reports — no OpenAI, no rate limits.

🔐 Zero Trust

No accounts. No KYC. No email verification. API key = access. Bitcoin = payment. That's the entire relationship.

🤝 Agent-Native

10 MCP tools. Your AI agents connect directly. They search, crawl, and research autonomously. No browser needed.

-SIGNUP_HTML = f"""Sign Up — AI Research Engine

🧠 AI Research Engine

Get Your API Key

No KYC. Just an email. Your key is generated instantly.

""" -DASHBOARD_HTML = f"""Dashboard — AI Research Engine

🧠 AI Research Engine

Dashboard

Your account, usage, and API access.

🔑 Enter Your API Key

"""