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"""
A self-hosted AI workforce that continuously gathers, organizes, and acts on information. Your private research cloud — no filters, no tracking, no limits.
Full-text search across millions of indexed documents. Find anything — no Google bubble, no censorship.
Search by meaning, not keywords. Our vector engine finds conceptually related content even when the words differ.
Point it at a topic and it discovers, crawls, and indexes every relevant source. Never miss a thing.
Local LLMs summarize, extract, and report. Your research, analyzed by AI running on our hardware.
Crawls route through proxies in Tokyo, London, and Sydney. Bypass regional blocks automatically.
Everything runs on our metal. No third-party APIs, no data harvesting, no surveillance.
10 MCP tools. One API key. Infinite knowledge. Agents connect and research autonomously — no browser needed.
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.
No subscriptions. No KYC. Pay with Bitcoin, get API calls.
in Bitcoin · Lightning ⚡
👇 Sign up first, then buy from your dashboard
Sign Up & Get Your API KeyPayments settle in seconds. Your API calls are credited instantly.
Use your API key in any MCP client, script, or AI agent.
Real-time dashboard shows your remaining calls and history.
Full-text search across indexed documents. No Google bubble, no sponsored results, no shadow banning.
Vector embeddings find what you mean — not just what you type. Conceptually related results even when keywords differ.
Routes through VPN proxies in Tokyo, London, and Sydney. Bypasses geo-blocks. Indexes everything.
Local LLMs running on RTX 3070 hardware. Summarize, extract, and generate reports — no OpenAI, no rate limits.
No accounts. No KYC. No email verification. API key = access. Bitcoin = payment. That's the entire relationship.
10 MCP tools. Your AI agents connect directly. They search, crawl, and research autonomously. No browser needed.
No KYC. Just an email. Your key is generated instantly.
Your account, usage, and API access.