Interactive background (mouse parallax + cursor glow) + OpenAPI spec + agent structured data

This commit is contained in:
drjones
2026-09-19 21:17:13 -07:00
parent 8502e3471a
commit 19625811d6
3 changed files with 100 additions and 11 deletions

42
app.py
View File

@@ -530,6 +530,7 @@ Sells residential SOCKS5 and HTTP CONNECT proxies with global geo-targeting
- Signup: username + password only (no email, no KYC) - Signup: username + password only (no email, no KYC)
## API ## API
- OpenAPI spec: {SITE_HOST}/openapi.json
- GET /api/v1/info -> service info (JSON) - GET /api/v1/info -> service info (JSON)
- GET /api/v1/plans -> residential GB plans (JSON) - GET /api/v1/plans -> residential GB plans (JSON)
- POST /api/v1/proxy -> one-call purchase (JSON: plan_id, country, city, username, password) -> creds + BTCPay checkout URL - POST /api/v1/proxy -> one-call purchase (JSON: plan_id, country, city, username, password) -> creds + BTCPay checkout URL
@@ -652,6 +653,47 @@ def api_order_status(order_id):
return jsonify({"order_id": order_id, "status": o["status"], "amount_sats": o["amount_sats"]}) return jsonify({"order_id": order_id, "status": o["status"], "amount_sats": o["amount_sats"]})
@app.route("/openapi.json")
def openapi():
spec = {
"openapi": "3.0.3",
"info": {
"title": "Clean Proxys API",
"version": "1.0.0",
"description": "Programmatic residential SOCKS5/HTTP proxy purchase. No KYC. "
"One-call provisioning with Bitcoin (BTCPay) billing.",
},
"servers": [{"url": f"https://{SITE_HOST}"}],
"paths": {
"/api/v1/info": {"get": {"summary": "Service info", "responses": {"200": {"description": "ok"}}}},
"/api/v1/plans": {"get": {"summary": "List residential GB plans", "responses": {"200": {"description": "plans"}}}},
"/api/v1/proxy": {"post": {
"summary": "One-call purchase: create account + invoice, return proxy credentials",
"requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {
"plan_id": {"type": "integer", "description": "plan id from /api/v1/plans"},
"country": {"type": "string", "description": "ISO country code, e.g. US"},
"city": {"type": "string", "description": "optional city"},
"username": {"type": "string", "description": "optional; auto-generated if omitted"},
"password": {"type": "string", "description": "optional; auto-generated if omitted"},
}}}}},
"responses": {"200": {"description": "username, proxy_password, socks5/http strings, invoice checkout_url, status_url"}},
}},
"/api/v1/order/{order_id}": {"get": {
"summary": "Payment status (pending/paid)",
"parameters": [{"name": "order_id", "in": "path", "required": True, "schema": {"type": "string"}}],
"responses": {"200": {"description": "status"}},
}},
"/api/v1/dedicated/locations": {"get": {
"summary": "Dedicated IP location catalog",
"parameters": [{"name": "product", "in": "query", "schema": {"type": "integer", "enum": [3, 9]},
"description": "3=datacenter, 9=ISP"}],
"responses": {"200": {"description": "locations"}},
}},
},
}
return jsonify(spec)
@app.route("/sitemap.xml") @app.route("/sitemap.xml")
def sitemap(): def sitemap():
urls = [f"https://{SITE_HOST}/{p}" for p in ["", "plans", "signup", "login", "llms.txt"]] urls = [f"https://{SITE_HOST}/{p}" for p in ["", "plans", "signup", "login", "llms.txt"]]

View File

@@ -45,6 +45,19 @@ body {
opacity: 0.6; opacity: 0.6;
} }
/* ---- cursor glow (follows mouse, subtle) ---- */
#glow {
position: fixed;
left: 0; top: 0;
width: 520px; height: 520px;
margin-left: -260px; margin-top: -260px;
border-radius: 50%;
pointer-events: none;
z-index: 0;
background: radial-gradient(circle, rgba(0, 229, 255, 0.07), rgba(0, 255, 156, 0.03) 45%, transparent 70%);
will-change: transform;
}
/* ---- grid overlay ---- */ /* ---- grid overlay ---- */
.grid-overlay { .grid-overlay {
position: fixed; position: fixed;

View File

@@ -6,9 +6,21 @@
<title>{% block title %}Clean Proxys{% endblock %}</title> <title>{% block title %}Clean Proxys{% endblock %}</title>
<meta name="description" content="Clean residential SOCKS5 & HTTP proxies. Global geo-targeting. No KYC. Bitcoin checkout. Encrypted backend. Agent-ready."> <meta name="description" content="Clean residential SOCKS5 & HTTP proxies. Global geo-targeting. No KYC. Bitcoin checkout. Encrypted backend. Agent-ready.">
<meta name="robots" content="index,follow"> <meta name="robots" content="index,follow">
<meta name="ai-agent" content="api: /api/v1/info, /openapi.json; purchase: POST /api/v1/proxy; auth: username+password; billing: bitcoin">
<meta property="og:title" content="Clean Proxys — residential SOCKS5 proxies"> <meta property="og:title" content="Clean Proxys — residential SOCKS5 proxies">
<meta property="og:description" content="Clean, fast, geo-targeted residential proxies. No KYC. Pay with Bitcoin. Built for agents and humans alike."> <meta property="og:description" content="Clean, fast, geo-targeted residential proxies. No KYC. Pay with Bitcoin. Built for agents and humans alike.">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebAPI",
"name": "Clean Proxys API",
"description": "Programmatic residential SOCKS5/HTTP proxy purchase. One-call provisioning, Bitcoin billing.",
"documentation": "https://clean-proxys.thetempleofdoom.com/llms.txt",
"termsOfService": "https://clean-proxys.thetempleofdoom.com/",
"provider": {"@type": "Organization", "name": "Clean Proxys"}
}
</script>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
@@ -18,6 +30,7 @@
<body> <body>
<canvas id="stars"></canvas> <canvas id="stars"></canvas>
<div class="grid-overlay"></div> <div class="grid-overlay"></div>
<div id="glow"></div>
<nav> <nav>
<div class="wrap inner"> <div class="wrap inner">
@@ -43,44 +56,65 @@
<div class="wrap"> <div class="wrap">
Clean Proxys — residential SOCKS5 &amp; HTTP proxies. &nbsp;·&nbsp; Clean Proxys — residential SOCKS5 &amp; HTTP proxies. &nbsp;·&nbsp;
<a href="https://buymeacoffee.com/r26xrthzttg" target="_blank" rel="noopener">Support the project ☕</a> <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank" rel="noopener">Support the project ☕</a>
&nbsp;·&nbsp; <a href="/llms.txt">llms.txt</a> &nbsp;·&nbsp; <a href="/llms.txt">llms.txt</a> &nbsp;·&nbsp; <a href="/openapi.json">OpenAPI</a>
</div> </div>
</footer> </footer>
<script> <script>
// slow, calm starfield — no fast motion // interactive background: slow starfield + mouse parallax + cursor glow (all smooth, no fast motion)
(function(){ (function(){
const c = document.getElementById('stars'), x = c.getContext('2d'); const c = document.getElementById('stars'), x = c.getContext('2d');
const glow = document.getElementById('glow');
let W, H, pts = []; let W, H, pts = [];
const raw = { x: innerWidth/2, y: innerHeight/2 }; // actual cursor
const sm = { x: innerWidth/2, y: innerHeight/2 }; // smoothed (lerp)
function resize(){ function resize(){
W = c.width = innerWidth; H = c.height = innerHeight; W = c.width = innerWidth; H = c.height = innerHeight;
const n = Math.min(140, Math.floor(W * H / 9000)); const n = Math.min(150, Math.floor(W * H / 8500));
pts = Array.from({length: n}, () => ({ pts = Array.from({length: n}, () => ({
x: Math.random()*W, y: Math.random()*H, x: Math.random()*W, y: Math.random()*H,
r: Math.random()*1.3 + .3, r: Math.random()*1.4 + .3,
vx: (Math.random()-.5)*.12, vy: (Math.random()-.5)*.12, vx: (Math.random()-.5)*.1, vy: (Math.random()-.5)*.1,
d: Math.random()*2 + .3, // parallax depth
a: Math.random()*.5 + .2 a: Math.random()*.5 + .2
})); }));
} }
resize(); addEventListener('resize', resize); resize(); addEventListener('resize', resize);
addEventListener('mousemove', e => { raw.x = e.clientX; raw.y = e.clientY; });
function tick(){ function tick(){
sm.x += (raw.x - sm.x) * .08; // smooth follow
sm.y += (raw.y - sm.y) * .08;
// parallax: normalized cursor -> star offset (opposite, subtle)
const nx = (sm.x / W - .5) * 2, ny = (sm.y / H - .5) * 2;
const px = nx * 28, py = ny * 28;
x.clearRect(0,0,W,H); x.clearRect(0,0,W,H);
for (const p of pts){ for (const p of pts){
p.x += p.vx; p.y += p.vy; p.x += p.vx; p.y += p.vy;
if (p.x<0)p.x=W; if(p.x>W)p.x=0; if(p.y<0)p.y=H; if(p.y>H)p.y=0; if (p.x<0)p.x=W; if(p.x>W)p.x=0; if(p.y<0)p.y=H; if(p.y>H)p.y=0;
x.beginPath(); x.arc(p.x,p.y,p.r,0,7); const sx = p.x - px*p.d, sy = p.y - py*p.d;
x.beginPath(); x.arc(sx,sy,p.r,0,7);
x.fillStyle = 'rgba(180,230,255,'+p.a+')'; x.fill(); x.fillStyle = 'rgba(180,230,255,'+p.a+')'; x.fill();
} }
// faint constellation lines between near stars
for (let i=0;i<pts.length;i++){ for (let i=0;i<pts.length;i++){
for (let j=i+1;j<pts.length;j++){ for (let j=i+1;j<pts.length;j++){
const dx=pts[i].x-pts[j].x, dy=pts[i].y-pts[j].y, d=dx*dx+dy*dy; const dx=(pts[i].x-px*pts[i].d)-(pts[j].x-px*pts[j].d);
if (d < 120*120){ const dy=(pts[i].y-py*pts[i].d)-(pts[j].y-py*pts[j].d);
x.beginPath(); x.moveTo(pts[i].x,pts[i].y); x.lineTo(pts[j].x,pts[j].y); const d=dx*dx+dy*dy;
x.strokeStyle = 'rgba(0,229,255,'+(0.05*(1-d/(120*120)))+')'; x.stroke(); if (d < 130*130){
x.beginPath();
x.moveTo(pts[i].x-px*pts[i].d, pts[i].y-py*pts[i].d);
x.lineTo(pts[j].x-px*pts[j].d, pts[j].y-py*pts[j].d);
x.strokeStyle='rgba(0,229,255,'+(0.06*(1-d/(130*130)))+')'; x.stroke();
} }
} }
} }
// cursor glow follows (smoothed)
glow.style.transform = 'translate(' + (sm.x) + 'px,' + (sm.y) + 'px)';
requestAnimationFrame(tick); requestAnimationFrame(tick);
} }
tick(); tick();