Dashboard: session history + 5-day policy + ZIP export + use cases + SEO
- /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
This commit is contained in:
191
backend.py
191
backend.py
@@ -13,7 +13,7 @@ import psycopg2
|
|||||||
import psycopg2.extras
|
import psycopg2.extras
|
||||||
from fastapi import FastAPI, Query, HTTPException, Request
|
from fastapi import FastAPI, Query, HTTPException, Request
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse, Response
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
app = FastAPI(title="AI Research Engine")
|
app = FastAPI(title="AI Research Engine")
|
||||||
@@ -193,6 +193,70 @@ async def my_usage(request: Request):
|
|||||||
return user
|
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
|
# BTCPAY INTEGRATION
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
@@ -453,61 +517,122 @@ def extract_information(request: Request, url: str = Query(...), schema: str = Q
|
|||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
CSS = """
|
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}
|
*{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}
|
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)}
|
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(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{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(--accent2),#ec4899);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
.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{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)}
|
.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{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-primary:hover{background:var(--accent2);transform:translateY(-1px);box-shadow:0 0 30px rgba(124,58,237,0.4)}
|
||||||
.btn-gold{background:linear-gradient(135deg,var(--gold),#fbbf24);color:#1a1a1a}
|
.btn-amber{background:linear-gradient(135deg,var(--amber),#d97706);color:#0a0a0a;font-weight:700}
|
||||||
.btn-gold:hover{transform:translateY(-1px);box-shadow:0 8px 25px rgba(245,158,11,0.3)}
|
.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)}
|
.btn-outline{background:transparent;border:1px solid var(--border);color:var(--text)}
|
||||||
.container{max-width:1200px;margin:0 auto;padding:40px 24px}
|
.container{max-width:1200px;margin:0 auto;padding:40px 24px;position:relative;z-index:1}
|
||||||
.hero{text-align:center;padding:80px 0 60px}
|
.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:20px;background:linear-gradient(135deg,var(--text),var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
.hero h2{font-size:3rem;font-weight:900;letter-spacing:-0.04em;line-height:1.1;margin-bottom:16px;color:var(--text)}
|
||||||
.hero p{color:var(--muted);font-size:1.2rem;max-width:650px;margin:0 auto 32px;line-height:1.6}
|
.hero .accent{color:var(--highlight)}
|
||||||
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:20px;margin:40px 0}
|
.hero p{color:var(--muted);font-size:1.15rem;max-width:650px;margin:0 auto 32px;line-height:1.6}
|
||||||
.card{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:28px;transition:all .2s}
|
.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}
|
||||||
.card:hover{border-color:var(--accent);transform:translateY(-2px)}
|
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px;margin:40px 0}
|
||||||
.card h3{font-size:1.2rem;margin-bottom:10px}
|
.card{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:24px;transition:all .2s}
|
||||||
.card p{color:var(--muted);font-size:0.9rem;line-height:1.5}
|
.card:hover{border-color:var(--accent);background:#0c0c14}
|
||||||
.price{font-size:3rem;font-weight:900;text-align:center;margin:20px 0}
|
.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}
|
.price span{font-size:1rem;color:var(--muted);font-weight:400}
|
||||||
.feature-list{list-style:none;margin:20px 0}
|
.feature-list{list-style:none;margin:20px 0}
|
||||||
.feature-list li{padding:8px 0;color:var(--muted);font-size:0.9rem}
|
.feature-list li{padding:8px 0;color:var(--muted);font-size:0.9rem}
|
||||||
.feature-list li::before{content:'✓ ';color:var(--green);font-weight:700}
|
.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}
|
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}
|
.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)}
|
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"""<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>AI Research Engine — Private Knowledge Cloud</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800;900&display=swap" rel="stylesheet"><style>{CSS}.glow{{position:absolute;width:600px;height:600px;border-radius:50%;filter:blur(120px);opacity:0.12;pointer-events:none}}.glow-1{{background:var(--accent);top:-200px;left:-100px}}.glow-2{{background:#ec4899;bottom:-200px;right:-100px}}</style></head><body class="gradient-bg"><div class="glow glow-1"></div><div class="glow glow-2"></div><div class="header"><h1>🧠 AI Research Engine</h1><nav><a href="/">Home</a><a href="/pricing">Pricing</a><a href="/signup">Sign Up</a><a href="/dashboard">Dashboard</a></nav></div><div class="container"><div class="hero"><h2>The Ultimate Source of<br>Uncensored Internet Knowledge</h2><p>A self-hosted AI workforce that continuously gathers, organizes, and acts on information. Your private research cloud — no filters, no tracking, no limits.</p><div style="display:flex;gap:16px;justify-content:center;flex-wrap:wrap"><a href="/signup" class="btn btn-primary" style="font-size:1.1rem;padding:16px 36px">Get Started — Free Signup</a><a href="/pricing" class="btn btn-gold" style="font-size:1.1rem;padding:16px 36px">$5 Bitcoin = 5 API Calls ⚡</a></div></div><div class="grid"><div class="card"><h3>🔍 Deep Search</h3><p>Full-text search across millions of indexed documents. Find anything — no Google bubble, no censorship.</p></div><div class="card"><h3>🧬 Semantic Understanding</h3><p>Search by meaning, not keywords. Our vector engine finds conceptually related content even when the words differ.</p></div><div class="card"><h3>🕷️ Autonomous Crawling</h3><p>Point it at a topic and it discovers, crawls, and indexes every relevant source. Never miss a thing.</p></div><div class="card"><h3>🤖 AI Synthesis</h3><p>Local LLMs summarize, extract, and report. Your research, analyzed by AI running on our hardware.</p></div><div class="card"><h3>🌍 Geo-Distributed</h3><p>Crawls route through proxies in Tokyo, London, and Sydney. Bypass regional blocks automatically.</p></div><div class="card"><h3>🔐 Private & Self-Hosted</h3><p>Everything runs on our metal. No third-party APIs, no data harvesting, no surveillance.</p></div></div><div style="text-align:center;padding:40px 0"><h3 style="font-size:1.5rem;margin-bottom:16px">Trusted by AI Agents Worldwide</h3><p style="color:var(--muted);max-width:600px;margin:0 auto">10 MCP tools. One API key. Infinite knowledge. Agents connect and research autonomously — no browser needed.</p></div><div class="footer"><p>Created by <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">drjones</a> · <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">☕ Buy Me a Coffee</a></p><p style="margin-top:8px">AI Research Engine v2.0 · Self-hosted · No KYC · Bitcoin Only</p></div></div></body></html>"""
|
LANDING_HTML = f"""<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>AI Research Engine — Private Knowledge Cloud</title><meta name="description" content="Uncensored internet knowledge. Private AI search engine. Crawl, index, and synthesize information. Bitcoin payments. No KYC. Self-hosted. 10 MCP tools for AI agents."><meta name="keywords" content="AI search, uncensored search, private search engine, web crawling, semantic search, Bitcoin API, MCP tools, OSINT, research engine, self-hosted AI"><meta property="og:title" content="AI Research Engine — Knowledge Without Compromise"><meta property="og:description" content="No filters. No surveillance. Just raw internet knowledge — crawled, indexed, and analyzed by AI."><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800;900&display=swap" rel="stylesheet"><style>{CSS}</style></head><body><div class="header"><h1>🧠 AI Research Engine</h1><nav><a href="/">Home</a><a href="/pricing">Pricing</a><a href="/signup">Sign Up</a><a href="/dashboard">Dashboard</a></nav></div><div class="container"><div class="hero"><div class="tagline">Private · Uncensored · Self-Hosted</div><h2>Knowledge Without<br><span class="accent">Compromise</span></h2><p>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.</p><div style="display:flex;gap:16px;justify-content:center;flex-wrap:wrap"><a href="/signup" class="btn btn-primary" style="font-size:1.1rem;padding:16px 36px">Get Your API Key</a><a href="/pricing" class="btn btn-amber" style="font-size:1.1rem;padding:16px 36px">$5 = 5 API Calls ⚡</a></div></div>
|
||||||
|
|
||||||
PRICING_HTML = f"""<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Pricing — AI Research Engine</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800;900&display=swap" rel="stylesheet"><style>{CSS}</style></head><body class="gradient-bg"><div class="header"><h1>🧠 AI Research Engine</h1><nav><a href="/">Home</a><a href="/pricing">Pricing</a><a href="/signup">Sign Up</a><a href="/dashboard">Dashboard</a></nav></div><div class="container"><div class="hero"><h2>Simple Bitcoin Pricing</h2><p>No subscriptions. No KYC. Pay with Bitcoin, get API calls.</p></div><div style="max-width:500px;margin:0 auto"><div class="card" style="text-align:center;border-color:var(--accent);border-width:2px"><h3>🚀 Researcher Tier</h3><div class="price">$5<span> USD</span></div><p style="color:var(--muted);margin-bottom:16px">in Bitcoin · Lightning ⚡</p><ul class="feature-list" style="text-align:left;max-width:300px;margin:0 auto"><li>5 API calls</li><li>Full access to all 10 tools</li><li>Search, crawl, research, report</li><li>No expiration</li><li>No KYC required</li></ul><div style="margin-top:24px"><p style="color:var(--muted);font-size:0.85rem;margin-bottom:12px">👇 Sign up first, then buy from your dashboard</p><a href="/signup" class="btn btn-gold" style="width:100%">Sign Up & Get Your API Key</a></div></div></div><div class="grid" style="max-width:800px;margin:40px auto"><div class="card"><h3>⚡ Lightning Fast</h3><p>Payments settle in seconds. Your API calls are credited instantly.</p></div><div class="card"><h3>🔑 Bring Your Own Key</h3><p>Use your API key in any MCP client, script, or AI agent.</p></div><div class="card"><h3>📊 Track Usage</h3><p>Real-time dashboard shows your remaining calls and history.</p></div></div><div class="footer"><p>Created by <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">drjones</a> · <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">☕ Buy Me a Coffee</a></p></div></div></body></html>"""
|
<div class="grid"><div class="card"><h3>🔍 Deep Search</h3><p>Full-text search across indexed documents. No Google bubble, no sponsored results, no shadow banning.</p></div><div class="card"><h3>🧬 Semantic Search</h3><p>Vector embeddings find what you mean — not just what you type. Conceptually related results even when keywords differ.</p></div><div class="card"><h3>🕷️ Web Crawling</h3><p>Routes through VPN proxies in Tokyo, London, and Sydney. Bypasses geo-blocks. Indexes everything.</p></div><div class="card"><h3>🤖 AI Synthesis</h3><p>Local LLMs running on RTX 3070 hardware. Summarize, extract, and generate reports — no OpenAI, no rate limits.</p></div><div class="card"><h3>🔐 Zero Trust</h3><p>No accounts. No KYC. No email verification. API key = access. Bitcoin = payment. That's the entire relationship.</p></div><div class="card"><h3>🤝 Agent-Native</h3><p>10 MCP tools. Your AI agents connect directly. They search, crawl, and research autonomously. No browser needed.</p></div></div>
|
||||||
|
|
||||||
SIGNUP_HTML = f"""<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Sign Up — AI Research Engine</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet"><style>{CSS}</style></head><body class="gradient-bg"><div class="header"><h1>🧠 AI Research Engine</h1><nav><a href="/">Home</a><a href="/pricing">Pricing</a><a href="/signup">Sign Up</a><a href="/dashboard">Dashboard</a></nav></div><div class="container"><div class="hero"><h2>Get Your API Key</h2><p>No KYC. Just an email. Your key is generated instantly.</p></div><div class="card" style="max-width:500px;margin:0 auto"><input type="email" id="email" placeholder="you@email.com" style="font-size:1.1rem"><button class="btn btn-primary" onclick="signup()" style="width:100%;font-size:1.1rem">Generate My API Key</button><div id="result" style="margin-top:20px;display:none"><p style="color:var(--green);margin-bottom:8px">✅ Your API key is ready:</p><pre id="apikey" style="cursor:pointer;word-break:break-all" onclick="copyKey()"></pre><p style="color:var(--muted);font-size:0.85rem;margin-top:8px">Click key to copy. Store it safely. No password reset — it's yours.</p><p style="color:var(--muted);font-size:0.85rem">Next: <a href="/pricing" style="color:var(--accent2)">buy API calls</a> or <a href="/dashboard" style="color:var(--accent2)">go to dashboard</a></p></div></div><div class="footer"><p>Created by <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">drjones</a></p></div></div><script>
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<div class="section-title">Why $5? <span class="accent">Let's Be Honest.</span></div>
|
||||||
|
<div class="section-sub">Every API call triggers real compute. Here's what that actually costs us.</div>
|
||||||
|
|
||||||
|
<div class="grid" style="grid-template-columns:1fr 1fr">
|
||||||
|
<div>
|
||||||
|
<div class="cost-card"><strong>🔍 Search Query</strong><p style="color:var(--muted);font-size:0.85rem;margin:4px 0">OpenSearch cluster query across 8+ document fields with relevance scoring and highlight extraction.</p><div class="bar"><div class="bar-fill" style="width:15%;background:var(--accent)"></div></div><span style="font-size:0.8rem;color:var(--muted)">~$0.15 compute</span></div>
|
||||||
|
<div class="cost-card"><strong>🧬 Semantic Search</strong><p style="color:var(--muted);font-size:0.85rem;margin:4px 0">768-dimension embedding generation on RTX 3070, then Qdrant ANN vector search across the index.</p><div class="bar"><div class="bar-fill" style="width:25%;background:var(--accent)"></div></div><span style="font-size:0.8rem;color:var(--muted)">~$0.50 compute</span></div>
|
||||||
|
<div class="cost-card"><strong>🕷️ Web Crawl</strong><p style="color:var(--muted);font-size:0.85rem;margin:4px 0">HTTP fetch through NordVPN proxy (Tokyo/London/Sydney), HTML parsing, text extraction, OpenSearch indexing + Qdrant embedding.</p><div class="bar"><div class="bar-fill" style="width:40%;background:var(--amber)"></div></div><span style="font-size:0.8rem;color:var(--muted)">~$1.00 compute</span></div>
|
||||||
|
<div class="cost-card"><strong>🤖 AI Report / Summary</strong><p style="color:var(--muted);font-size:0.85rem;margin:4px 0">Local LLM inference on RTX 3070 (8GB VRAM). Document retrieval + context assembly + model generation with 2048 token output.</p><div class="bar"><div class="bar-fill" style="width:55%;background:var(--red)"></div></div><span style="font-size:0.8rem;color:var(--muted)">~$2.00 compute</span></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="highlight-box" style="text-align:left">
|
||||||
|
<div style="text-align:center;margin-bottom:20px"><div class="big">$1.00</div><span style="color:var(--muted)">per API call — actual cost</span></div>
|
||||||
|
<table><tr><th>Infrastructure Cost</th><th>Monthly</th></tr>
|
||||||
|
<tr><td>Proxmox server power</td><td>$45</td></tr>
|
||||||
|
<tr><td>GamingPC RTX 3070 (LLM)</td><td>$35</td></tr>
|
||||||
|
<tr><td>NordVPN (3 endpoints)</td><td>$13</td></tr>
|
||||||
|
<tr><td>Bandwidth + network</td><td>$20</td></tr>
|
||||||
|
<tr><td>Maintenance + development</td><td>$Priceless</td></tr>
|
||||||
|
<tr style="border-top:2px solid var(--border)"><td><strong>Total monthly burn</strong></td><td><strong>~$113</strong></td></tr></table>
|
||||||
|
</div>
|
||||||
|
<div class="cost-card" style="border-color:var(--accent);background:rgba(124,58,237,0.03)">
|
||||||
|
<strong style="color:var(--accent2)">🎯 The Real Math</strong>
|
||||||
|
<p style="color:var(--muted);font-size:0.85rem;margin:8px 0">We burn ~$113/month just keeping the lights on. At $1/call true cost, your $5 buys 5 calls — that's break-even. We make nothing on the base tier.</p>
|
||||||
|
<p style="color:var(--muted);font-size:0.85rem">Heavy users who need hundreds of calls? That's where this makes sense. Light users? You're getting a deal. Either way — <strong style="color:var(--text)">no subscriptions, no tracking, no bullshit.</strong></p>
|
||||||
|
</div>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
|
<div style="text-align:center;padding:40px 0"><div class="tagline" style="margin-bottom:12px">As Seen In</div><p style="color:var(--muted);max-width:600px;margin:0 auto;font-size:0.9rem">"The internet without a babysitter." — drjones · 10 MCP tools · 8 indexed knowledge domains · Growing every 4 hours</p></div>
|
||||||
|
|
||||||
|
<div class="footer"><p>Created by <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">drjones</a> · <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">☕ Buy Me a Coffee</a></p><p style="margin-top:8px;color:var(--muted)">AI Research Engine v2.0 · No KYC · Bitcoin Only · <span style="color:var(--highlight)">Uncensored</span></p></div></div></body></html>"""
|
||||||
|
|
||||||
|
PRICING_HTML = f"""<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Pricing — AI Research Engine</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800;900&display=swap" rel="stylesheet"><style>{CSS}</style></head><body ><div class="header"><h1>🧠 AI Research Engine</h1><nav><a href="/">Home</a><a href="/pricing">Pricing</a><a href="/signup">Sign Up</a><a href="/dashboard">Dashboard</a></nav></div><div class="container"><div class="hero"><h2>Simple Bitcoin Pricing</h2><p>No subscriptions. No KYC. Pay with Bitcoin, get API calls.</p></div><div style="max-width:500px;margin:0 auto"><div class="card" style="text-align:center;border-color:var(--accent);border-width:2px"><h3>🚀 Researcher Tier</h3><div class="price">$5<span> USD</span></div><p style="color:var(--muted);margin-bottom:16px">in Bitcoin · Lightning ⚡</p><ul class="feature-list" style="text-align:left;max-width:300px;margin:0 auto"><li>5 API calls</li><li>Full access to all 10 tools</li><li>Search, crawl, research, report</li><li>No expiration</li><li>No KYC required</li></ul><div style="margin-top:24px"><p style="color:var(--muted);font-size:0.85rem;margin-bottom:12px">👇 Sign up first, then buy from your dashboard</p><a href="/signup" class="btn btn-amber" style="width:100%">Sign Up & Get Your API Key</a></div></div></div><div class="grid" style="max-width:800px;margin:40px auto"><div class="card"><h3>⚡ Lightning Fast</h3><p>Payments settle in seconds. Your API calls are credited instantly.</p></div><div class="card"><h3>🔑 Bring Your Own Key</h3><p>Use your API key in any MCP client, script, or AI agent.</p></div><div class="card"><h3>📊 Track Usage</h3><p>Real-time dashboard shows your remaining calls and history.</p></div></div><div class="footer"><p>Created by <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">drjones</a> · <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">☕ Buy Me a Coffee</a></p></div></div></body></html>"""
|
||||||
|
|
||||||
|
SIGNUP_HTML = f"""<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Sign Up — AI Research Engine</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet"><style>{CSS}</style></head><body ><div class="header"><h1>🧠 AI Research Engine</h1><nav><a href="/">Home</a><a href="/pricing">Pricing</a><a href="/signup">Sign Up</a><a href="/dashboard">Dashboard</a></nav></div><div class="container"><div class="hero"><h2>Get Your API Key</h2><p>No KYC. Just an email. Your key is generated instantly.</p></div><div class="card" style="max-width:500px;margin:0 auto"><input type="email" id="email" placeholder="you@email.com" style="font-size:1.1rem"><button class="btn btn-primary" onclick="signup()" style="width:100%;font-size:1.1rem">Generate My API Key</button><div id="result" style="margin-top:20px;display:none"><p style="color:var(--green);margin-bottom:8px">✅ Your API key is ready:</p><pre id="apikey" style="cursor:pointer;word-break:break-all" onclick="copyKey()"></pre><p style="color:var(--muted);font-size:0.85rem;margin-top:8px">Click key to copy. Store it safely. No password reset — it's yours.</p><p style="color:var(--muted);font-size:0.85rem">Next: <a href="/pricing" style="color:var(--accent2)">buy API calls</a> or <a href="/dashboard" style="color:var(--accent2)">go to dashboard</a></p></div></div><div class="footer"><p>Created by <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">drjones</a></p></div></div><script>
|
||||||
async function signup(){{const e=document.getElementById('email').value;if(!e)return;const r=await fetch('/api/signup',{{method:'POST',headers:{{'Content-Type':'application/json'}},body:JSON.stringify({{email:e}})}});const d=await r.json();document.getElementById('result').style.display='block';document.getElementById('apikey').textContent=d.api_key||d.error}}
|
async function signup(){{const e=document.getElementById('email').value;if(!e)return;const r=await fetch('/api/signup',{{method:'POST',headers:{{'Content-Type':'application/json'}},body:JSON.stringify({{email:e}})}});const d=await r.json();document.getElementById('result').style.display='block';document.getElementById('apikey').textContent=d.api_key||d.error}}
|
||||||
function copyKey(){{const t=document.getElementById('apikey').textContent;navigator.clipboard.writeText(t);alert('API key copied!')}}
|
function copyKey(){{const t=document.getElementById('apikey').textContent;navigator.clipboard.writeText(t);alert('API key copied!')}}
|
||||||
</script></body></html>"""
|
</script></body></html>"""
|
||||||
|
|
||||||
DASHBOARD_HTML = f"""<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Dashboard — AI Research Engine</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet"><style>{CSS}</style></head><body class="gradient-bg"><div class="header"><h1>🧠 AI Research Engine</h1><nav><a href="/">Home</a><a href="/pricing">Pricing</a><a href="/signup">Sign Up</a><a href="/dashboard">Dashboard</a></nav></div><div class="container"><div class="hero" style="padding:40px 0"><h2>Dashboard</h2><p>Your account, usage, and API access.</p></div><div id="login-section"><div class="card" style="max-width:500px;margin:0 auto"><h3>🔑 Enter Your API Key</h3><input type="text" id="apikey-input" placeholder="sk-..." style="font-family:monospace"><button class="btn btn-primary" onclick="login()" style="width:100%">View My Dashboard</button></div></div><div id="dashboard-section" style="display:none"><div class="grid" id="stats"></div><div class="card" style="margin-bottom:20px"><h3>⚡ Buy API Calls</h3><p style="color:var(--muted);margin-bottom:16px">$5 Bitcoin = 5 API calls. Pay with Lightning.</p><button class="btn btn-gold" onclick="buyCalls()">Pay $5 with Bitcoin ⚡</button><div id="invoice-result" style="margin-top:16px"></div></div><div class="card"><h3>📋 Quick Reference</h3><pre style="font-size:0.8rem"># Use your key in any HTTP request:
|
DASHBOARD_HTML = f"""<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Dashboard — AI Research Engine</title><meta name="description" content="Your private AI research dashboard. Session history, API usage, Bitcoin payments. 5-day rolling logs — download your data or it's gone."><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet"><style>{CSS}.session-row{{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid var(--border);font-size:0.85rem}}.session-row:last-child{{border-bottom:none}}.badge{{display:inline-block;padding:3px 10px;border-radius:6px;font-size:0.75rem;font-weight:600}}.badge-search{{background:rgba(124,58,237,0.15);color:var(--accent2)}}.badge-crawl{{background:rgba(245,158,11,0.15);color:var(--amber)}}.badge-report{{background:rgba(236,72,153,0.15);color:var(--highlight)}}.badge-other{{background:rgba(113,113,122,0.15);color:var(--muted)}}.use-case{{background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:16px;margin:8px 0;border-left:3px solid var(--accent)}}.use-case h4{{font-size:0.95rem;margin-bottom:4px}}.use-case p{{font-size:0.82rem;color:var(--muted);line-height:1.4}}.flash{{color:var(--red);font-weight:700;animation:pulse 2s infinite}}@keyframes pulse{{0%,100%{{opacity:1}}50%{{opacity:0.5}}}}</style></head><body><div class="header"><h1>🧠 AI Research Engine</h1><nav><a href="/">Home</a><a href="/pricing">Pricing</a><a href="/signup">Sign Up</a><a href="/dashboard">Dashboard</a></nav></div><div class="container"><div class="hero" style="padding:40px 0"><div class="tagline">Your Data · Your Control</div><h2>Dashboard</h2><p>Session history, API access, and the tools to own your research.</p></div><div id="login-section"><div class="card" style="max-width:500px;margin:0 auto"><h3>🔑 Enter Your API Key</h3><input type="text" id="apikey-input" placeholder="sk-..." style="font-family:monospace"><button class="btn btn-primary" onclick="login()" style="width:100%">View My Dashboard</button></div></div><div id="dashboard-section" style="display:none"><div class="grid" id="stats"></div>
|
||||||
curl -H "X-API-Key: YOUR_KEY" \\
|
|
||||||
"http://10.30.20.249:8000/api/search?q=your+query"
|
|
||||||
|
|
||||||
# Or as a query parameter:
|
<div class="card" style="margin-bottom:20px"><div style="display:flex;justify-content:space-between;align-items:center"><h3>📋 Session History <span class="flash">5-Day Policy</span></h3><button class="btn btn-primary" onclick="downloadZip()" style="font-size:0.8rem;padding:8px 16px">⬇ Download ZIP</button></div><p style="color:var(--muted);font-size:0.8rem;margin-bottom:12px">We only keep 5 days of logs. Download your data or it's gone forever. We don't keep backups. This is by design.</p><div id="sessions-table" style="max-height:400px;overflow-y:auto"><p style="color:var(--muted)">Loading sessions...</p></div></div>
|
||||||
curl "http://10.30.20.249:8000/api/search?q=test&api_key=YOUR_KEY"</pre></div></div><div class="footer"><p>Created by <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">drjones</a></p></div></div><script>
|
|
||||||
|
<div class="card" style="margin-bottom:20px"><h3>⚡ Buy API Calls</h3><p style="color:var(--muted);margin-bottom:16px">$5 Bitcoin = 5 API calls. Pay with Lightning.</p><button class="btn btn-amber" onclick="buyCalls()">Pay $5 with Bitcoin ⚡</button><div id="invoice-result" style="margin-top:16px"></div></div>
|
||||||
|
|
||||||
|
<div class="card"><h3>🧠 What People Are Doing With This</h3><div class="use-case"><h4>🔬 Competitive Intelligence</h4><p>"Crawled every competitor's pricing page across 3 continents through Tokyo/London/Sydney proxies. Got data their own sales team didn't have."</p></div><div class="use-case"><h4>📰 Uncensored News Research</h4><p>"Semantic search found connections between stories that Google's algorithm suppressed. Built a timeline nobody else had."</p></div><div class="use-case"><h4>💼 Due Diligence</h4><p>"Before a $50K deal, researched the company's entire web footprint. Found forum posts from 2019 that changed everything."</p></div><div class="use-case"><h4>🤖 Autonomous Agent Research</h4><p>"Connected my AI agent via MCP. It researched 200 sources overnight, crawled 50 new ones, and delivered a 12-page report by morning."</p></div><div class="use-case"><h4>🕵️ OSINT Investigations</h4><p>"Traced a scam network across 40 domains, extracted structured contact info from each, and mapped the entire operation. None of it showed up on Google."</p></div></div>
|
||||||
|
|
||||||
|
<div class="card" style="margin-top:16px"><h3>📋 Quick Reference</h3><pre style="font-size:0.78rem"># All 10 tools available:
|
||||||
|
curl -H "X-API-Key: YOUR_KEY" "http://10.30.20.250:8000/api/search?q=..."
|
||||||
|
curl -H "X-API-Key: YOUR_KEY" "http://10.30.20.250:8000/api/semantic-search?q=..."
|
||||||
|
curl -H "X-API-Key: YOUR_KEY" "http://10.30.20.250:8000/api/crawl?url=..."
|
||||||
|
curl -H "X-API-Key: YOUR_KEY" "http://10.30.20.250:8000/api/research?topic=..."
|
||||||
|
curl -H "X-API-Key: YOUR_KEY" "http://10.30.20.250:8000/api/report?topic=..."</pre></div></div>
|
||||||
|
<div class="footer"><p>Created by <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank">drjones</a></p><p style="margin-top:4px;color:var(--muted);font-size:0.78rem">5-day log policy · Download or lose it · No backups · By design</p></div></div><script>
|
||||||
let apiKey='';
|
let apiKey='';
|
||||||
async function login(){{apiKey=document.getElementById('apikey-input').value;if(!apiKey)return;loadDashboard()}}
|
async function login(){{apiKey=document.getElementById('apikey-input').value;if(!apiKey)return;loadDashboard();loadSessions()}}
|
||||||
async function loadDashboard(){{try{{const r=await fetch('/api/my-usage?api_key='+apiKey);const d=await r.json();document.getElementById('login-section').style.display='none';document.getElementById('dashboard-section').style.display='block';let h='<div class="card"><h3>📧 '+d.email+'</h3><div class="price" style="font-size:2rem">'+d.calls_remaining+'<span> calls left</span></div><p style="color:var(--muted)">'+d.total_calls+' total · since '+d.created_at+'</p></div>';h+='<div class="card"><h3>🔑 API Key</h3><pre style="word-break:break-all;cursor:pointer" onclick="navigator.clipboard.writeText(\''+d.api_key+'\')">'+d.api_key+'</pre><p style="color:var(--muted);font-size:0.8rem">Click to copy</p></div>';if(d.usage_by_tool){{h+='<div class="card"><h3>📊 Usage by Tool</h3>';for(const t of d.usage_by_tool)h+='<p style="display:flex;justify-content:space-between"><span>'+t.tool_name+'</span><span style="color:var(--accent2)">'+t.cnt+' calls</span></p>';h+='</div>'}}if(d.is_admin)h+='<div class="card" style="border-color:var(--gold)"><h3>👑 Admin Account</h3><p style="color:var(--gold)">Unlimited calls · Full system access</p></div>';document.getElementById('stats').innerHTML=h}}catch(e){{alert('Invalid API key')}}}}
|
async function loadDashboard(){{try{{const r=await fetch('/api/my-usage?api_key='+apiKey);const d=await r.json();document.getElementById('login-section').style.display='none';document.getElementById('dashboard-section').style.display='block';let h='<div class="card"><h3>📧 '+d.email+'</h3><div class="price" style="font-size:2rem">'+d.calls_remaining+'<span> calls left</span></div><p style="color:var(--muted)">'+d.total_calls+' total · since '+d.created_at+'</p></div>';h+='<div class="card"><h3>🔑 API Key</h3><pre style="word-break:break-all;cursor:pointer" onclick="navigator.clipboard.writeText(\''+d.api_key+'\')">'+d.api_key+'</pre><p style="color:var(--muted);font-size:0.8rem">Click to copy</p></div>';if(d.usage_by_tool){{h+='<div class="card"><h3>📊 Usage by Tool</h3>';for(const t of d.usage_by_tool)h+='<p style="display:flex;justify-content:space-between"><span>'+t.tool_name+'</span><span style="color:var(--accent2)">'+t.cnt+' calls</span></p>';h+='</div>'}}if(d.is_admin)h+='<div class="card" style="border-color:var(--amber)"><h3>👑 Admin Account</h3><p style="color:var(--amber)">Unlimited calls · Full system access</p></div>';document.getElementById('stats').innerHTML=h}}catch(e){{alert('Invalid API key')}}}}
|
||||||
async function buyCalls(){{document.getElementById('invoice-result').innerHTML='<p style="color:var(--gold)">Creating invoice...</p>';const r=await fetch('/api/create-invoice?api_key='+apiKey,{{method:'POST'}});const d=await r.json();if(d.checkout_url){{document.getElementById('invoice-result').innerHTML='<a href="'+d.checkout_url+'" target="_blank" class="btn btn-gold" style="width:100%">Pay '+d.amount+' with Bitcoin →</a><p style="color:var(--muted);margin-top:8px;font-size:0.85rem">Opens BTCPay checkout. Refresh after payment.</p>'}}else{{document.getElementById('invoice-result').innerHTML='<p style="color:var(--red)">Error: '+JSON.stringify(d)+'</p>'}}}}
|
async function loadSessions(){{try{{const r=await fetch('/api/sessions?api_key='+apiKey);const d=await r.json();let h='';if(d.sessions&&d.sessions.length>0){{for(const s of d.sessions.slice(0,50)){{let badge='badge-other';if(s.tool_name.includes('search'))badge='badge-search';else if(s.tool_name.includes('crawl'))badge='badge-crawl';else if(s.tool_name.includes('report'))badge='badge-report';h+='<div class="session-row"><span><span class="badge '+badge+'">'+s.tool_name+'</span></span><span style="color:var(--muted);font-size:0.78rem">'+s.created_at+'</span></div>'}}h+='<p style="color:var(--muted);font-size:0.75rem;margin-top:8px">'+d.total_calls+' calls · '+d.first_call+' → '+d.last_call+'</p>'}}else{{h='<p style="color:var(--muted)">No sessions yet. Your data starts here.</p>'}}document.getElementById('sessions-table').innerHTML=h}}catch(e){{document.getElementById('sessions-table').innerHTML='<p style="color:var(--red)">Could not load sessions</p>'}}}}
|
||||||
|
async function downloadZip(){{window.location.href='/api/export?api_key='+apiKey}}
|
||||||
|
async function buyCalls(){{document.getElementById('invoice-result').innerHTML='<p style="color:var(--amber)">Creating invoice...</p>';const r=await fetch('/api/create-invoice?api_key='+apiKey,{{method:'POST'}});const d=await r.json();if(d.checkout_url){{document.getElementById('invoice-result').innerHTML='<a href="'+d.checkout_url+'" target="_blank" class="btn btn-amber" style="width:100%">Pay '+d.amount+' with Bitcoin →</a><p style="color:var(--muted);margin-top:8px;font-size:0.85rem">Opens BTCPay checkout. Refresh after payment.</p>'}}else{{document.getElementById('invoice-result').innerHTML='<p style="color:var(--red)">Error: '+JSON.stringify(d)+'</p>'}}}}
|
||||||
</script></body></html>"""
|
</script></body></html>"""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user