feat(dash): connection testing — Discogs / Woo / DealGod
POST /settings/test/{service} validates stored creds against the live service
(Discogs identity, Woo orders, DealGod /api/me) so "connected" means connected.
Dash gets per-service Test buttons with green/amber status.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
834c549d5e
commit
0e8c84b0e7
@ -1,3 +1,6 @@
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
@ -46,3 +49,45 @@ async def save_secret(body: SecretIn, ident=Depends(require_token), db=Depends(g
|
||||
except vault.SecretsUnconfigured as e:
|
||||
raise HTTPException(503, str(e))
|
||||
return {"ok": True, "name": body.name, "set": True}
|
||||
|
||||
|
||||
@router.post("/test/{service}")
|
||||
async def test_connection(service: str, ident=Depends(require_token), db=Depends(get_db)):
|
||||
"""Validate stored credentials against the live service — so 'connected' means connected."""
|
||||
async with httpx.AsyncClient(timeout=15, follow_redirects=True) as c:
|
||||
if service == "discogs":
|
||||
tok = await vault.get_secret(db, "discogs_token")
|
||||
if not tok:
|
||||
raise HTTPException(400, "save a Discogs personal access token first")
|
||||
r = await c.get("https://api.discogs.com/oauth/identity",
|
||||
headers={"Authorization": f"Discogs token={tok}",
|
||||
"User-Agent": "RecordGod/0.1"})
|
||||
if r.status_code == 200:
|
||||
return {"ok": True, "connected": True, "as": r.json().get("username")}
|
||||
return {"ok": True, "connected": False, "detail": f"HTTP {r.status_code}"}
|
||||
|
||||
if service == "woo":
|
||||
base = await vault.get_secret(db, "woo_base_url")
|
||||
ck = await vault.get_secret(db, "woo_consumer_key")
|
||||
cs = await vault.get_secret(db, "woo_consumer_secret")
|
||||
if not (base and ck and cs):
|
||||
raise HTTPException(400, "save Woo store URL + consumer key + secret first")
|
||||
r = await c.get(base.rstrip("/") + "/wp-json/wc/v3/orders",
|
||||
params={"per_page": 1}, auth=(ck, cs))
|
||||
if r.status_code == 200:
|
||||
return {"ok": True, "connected": True, "as": urlparse(base).netloc}
|
||||
return {"ok": True, "connected": False, "detail": f"HTTP {r.status_code}"}
|
||||
|
||||
if service == "dealgod":
|
||||
key = await vault.get_secret(db, "dealgod_api_key")
|
||||
if not key:
|
||||
raise HTTPException(400, "save a DealGod API key first")
|
||||
r = await c.get("https://api.dealgod.pro/api/me", headers={"X-Api-Key": key})
|
||||
if r.status_code == 200:
|
||||
j = r.json()
|
||||
return {"ok": True, "connected": True, "as": f"{j.get('user')} · {j.get('plan')}"}
|
||||
if r.status_code == 404:
|
||||
return {"ok": True, "connected": False, "detail": "DealGod /api/me not deployed yet"}
|
||||
return {"ok": True, "connected": False, "detail": f"HTTP {r.status_code}"}
|
||||
|
||||
raise HTTPException(400, "unknown service")
|
||||
|
||||
@ -48,13 +48,17 @@
|
||||
<div id="secrets"></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Orders (WooCommerce)</h2>
|
||||
<p class="soon">Save your Woo store URL + consumer key/secret above, then live orders land here —
|
||||
managed without wp-admin. (Coming next.)</p>
|
||||
<h2>Connections <span class="soon">— save the keys above, then test</span></h2>
|
||||
<div style="display:flex;gap:10px;align-items:center;margin:9px 0">
|
||||
<button class="ghost" onclick="testConn('discogs')">Test Discogs</button><span id="c-discogs" class="soon"></span></div>
|
||||
<div style="display:flex;gap:10px;align-items:center;margin:9px 0">
|
||||
<button class="ghost" onclick="testConn('woo')">Test WooCommerce</button><span id="c-woo" class="soon"></span></div>
|
||||
<div style="display:flex;gap:10px;align-items:center;margin:9px 0">
|
||||
<button class="ghost" onclick="testConn('dealgod')">Test DealGod</button><span id="c-dealgod" class="soon"></span></div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Discogs</h2>
|
||||
<p class="soon">Save your Discogs OAuth/token above to enrich intake from the live API and your wantlist.</p>
|
||||
<h2>Orders (WooCommerce)</h2>
|
||||
<p class="soon">Once WooCommerce tests green, live orders land here — managed without wp-admin. (Next build.)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -100,6 +104,16 @@ async function save(name, btn){
|
||||
btn.previousElementSibling; setTimeout(()=>btn.textContent='Save', 1500); }
|
||||
}
|
||||
|
||||
async function testConn(svc){
|
||||
const el = document.getElementById('c-'+svc); el.textContent='testing…'; el.style.color='#9a9aa6';
|
||||
let r, d;
|
||||
try { r = await fetch('/settings/test/'+svc, {method:'POST', headers:hdr()}); d = await r.json(); }
|
||||
catch(e){ el.textContent='✕ network error'; el.style.color='#e66'; return; }
|
||||
if(!r.ok){ el.textContent='✕ '+(d.detail||r.status); el.style.color='#e6a046'; return; }
|
||||
if(d.connected){ el.textContent='✓ connected'+(d.as?(' as '+d.as):''); el.style.color='#46d18a'; }
|
||||
else { el.textContent='✕ '+(d.detail||('HTTP '+d.status)); el.style.color='#e6a046'; }
|
||||
}
|
||||
|
||||
// auto-resume if a token is already stored
|
||||
if(TOKEN){ $('#tok').value = TOKEN; signin(); }
|
||||
</script>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user