142 lines
6.4 KiB
HTML
142 lines
6.4 KiB
HTML
{% extends "base.html" %}
|
|
{% block body %}
|
|
<h2>Dashboard</h2>
|
|
<div class="stats-row">
|
|
<div class="card stat"><span class="lbl">Balance</span><span class="val">{{ '%.2f'|format(user.balance_gb) }} GB</span></div>
|
|
<div class="card stat"><span class="lbl">Used</span><span class="val">{{ '%.3f'|format(used_gb) }} GB</span></div>
|
|
<div class="card stat"><span class="lbl">Status</span>
|
|
<span class="val" style="color:{{ 'var(--ok)' if user.active else 'var(--danger)' }}">{{ 'ACTIVE' if user.active else 'INACTIVE' }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{% if not user.active %}
|
|
<div class="flash warn">Account inactive — <a href="/plans">top up</a> to activate your proxies.</div>
|
|
{% endif %}
|
|
|
|
<div class="dash-grid">
|
|
<div>
|
|
<div class="card">
|
|
<h3>Connection strings</h3>
|
|
<label>SOCKS5</label>
|
|
<div class="copy">
|
|
<input type="text" readonly value="{{ socks }}" onclick="this.select()">
|
|
<button onclick="copy(this.previousElementSibling)">Copy</button>
|
|
</div>
|
|
<label>HTTP (HTTPS tunneling)</label>
|
|
<div class="copy">
|
|
<input type="text" readonly value="{{ http }}" onclick="this.select()">
|
|
<button onclick="copy(this.previousElementSibling)">Copy</button>
|
|
</div>
|
|
<p class="muted" style="font-size:12px;margin-bottom:0">Proxy password is unique to you. Use it for proxy auth, not your web password.</p>
|
|
<button class="btn btn-ghost" id="testbtn" onclick="testProxy()" style="margin-top:12px">⚡ Test my proxy</button>
|
|
<pre class="code" id="testout" style="display:none;margin-top:10px"></pre>
|
|
<form method="post" action="/dashboard/credentials" style="margin-top:14px">
|
|
<label>Proxy username</label>
|
|
<input type="text" name="username" value="{{ user.username }}" autocomplete="off">
|
|
<label>Proxy password</label>
|
|
<div class="copy" style="margin:0">
|
|
<input type="text" name="proxy_password" id="pwfield" placeholder="leave blank to keep" autocomplete="off">
|
|
<button type="button" onclick="randompw()">⚡ random</button>
|
|
</div>
|
|
<input type="submit" value="Update proxy credentials">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Location & session</h3>
|
|
<form method="post" action="/dashboard/location">
|
|
<label>Country (ISO code, e.g. US, GB, JP, DE)</label>
|
|
<input type="text" name="country" value="{{ user.country }}" placeholder="US">
|
|
<label>City (optional)</label>
|
|
<input type="text" name="city" value="{{ user.city }}" placeholder="New York">
|
|
<label>Region (optional, e.g. europe)</label>
|
|
<input type="text" name="region" value="{{ user.region }}" placeholder="">
|
|
<div class="switch" style="margin-top:14px">
|
|
<input type="checkbox" name="sticky" id="sticky" {% if user.sticky %}checked{% endif %} style="width:auto">
|
|
<label for="sticky" style="margin:0">Sticky session (hold IP 30 min)</label>
|
|
</div>
|
|
<input type="submit" value="Save location">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{% if dedicated %}
|
|
<div class="card" style="margin-top:18px">
|
|
<h3>Dedicated IPs</h3>
|
|
{% for d in dedicated %}
|
|
<div style="border:1px solid var(--border);border-radius:8px;padding:12px;margin:10px 0">
|
|
<div style="display:flex;justify-content:space-between">
|
|
<strong style="color:var(--text)">{{ d.product_name }} · {{ d.location_name }}</strong>
|
|
<span style="color:{{ {'active':'var(--ok)','provisioning':'var(--warn)','pending_payment':'var(--warn)','failed':'var(--danger)','expired':'var(--muted)'}.get(d.status,'var(--muted)') }}">{{ d.status }}</span>
|
|
</div>
|
|
{% if d.status == 'active' %}
|
|
<label style="font-size:11px;color:var(--muted)">SOCKS5</label>
|
|
<div class="copy">
|
|
<input type="text" readonly value="socks5://{{ d.proxy_user }}:{{ d.proxy_pass }}@{{ d.proxy_host }}:{{ d.proxy_port }}" onclick="this.select()">
|
|
<button onclick="copy(this.previousElementSibling)">Copy</button>
|
|
</div>
|
|
<label style="font-size:11px;color:var(--muted)">HTTP</label>
|
|
<div class="copy" style="margin-top:6px">
|
|
<input type="text" readonly value="http://{{ d.proxy_user }}:{{ d.proxy_pass }}@{{ d.proxy_host }}:{{ d.http_port }}" onclick="this.select()">
|
|
<button onclick="copy(this.previousElementSibling)">Copy</button>
|
|
</div>
|
|
<p class="muted" style="font-size:11px;margin:6px 0 0">IP {{ d.proxy_host }} · expires in {{ d.plan_days }}d</p>
|
|
{% elif d.status in ('provisioning','pending_payment') %}
|
|
<p class="muted" style="font-size:12px;margin:6px 0 0">Provisioning — refresh shortly for your IP.</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
<p class="muted" style="font-size:12px;margin:0"><a href="/dedicated">Order another dedicated IP →</a></p>
|
|
</div>
|
|
{% else %}
|
|
<p class="muted" style="margin-top:18px">No dedicated IPs yet — <a href="/dedicated">order one</a>.</p>
|
|
{% endif %}
|
|
|
|
{% if orders %}
|
|
<div class="card" style="margin-top:18px">
|
|
<h3>Order history</h3>
|
|
<table style="width:100%;font-size:13px;border-collapse:collapse">
|
|
<tr style="color:var(--muted);text-align:left"><th>Plan</th><th>GB</th><th>sats</th><th>Status</th></tr>
|
|
{% for o in orders %}
|
|
<tr style="border-top:1px solid var(--border)">
|
|
<td style="padding:4px">{{ o.plan_name or '—' }}</td>
|
|
<td>{{ o.plan_gb or '—' }}</td>
|
|
<td>{{ o.amount_sats }}</td>
|
|
<td style="color:{{ 'var(--ok)' if o.status=='paid' else 'var(--warn)' }}">{{ o.status }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</table>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<script>
|
|
function copy(el){ el.select(); document.execCommand('copy'); }
|
|
function randompw(){
|
|
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789';
|
|
let s = '';
|
|
for (let i=0;i<18;i++) s += chars[Math.floor(Math.random()*chars.length)];
|
|
document.getElementById('pwfield').value = s;
|
|
}
|
|
async function testProxy(){
|
|
const btn = document.getElementById('testbtn');
|
|
const out = document.getElementById('testout');
|
|
btn.textContent = 'Testing…';
|
|
out.style.display = 'block';
|
|
out.textContent = '…';
|
|
try {
|
|
const r = await fetch('/dashboard/test');
|
|
const j = await r.json();
|
|
if (j.status === 'success') {
|
|
out.textContent = 'egress ' + j.query + '\n' + j.country + ' · ' + j.city + ' · ' + j.isp;
|
|
} else {
|
|
out.textContent = 'proxy error: ' + (j.message || j.status);
|
|
}
|
|
} catch(e) {
|
|
out.textContent = 'error: ' + e;
|
|
}
|
|
btn.textContent = '⚡ Test my proxy';
|
|
}
|
|
</script>
|
|
{% endblock %}
|