Spaces:
Sleeping
Sleeping
Nayan Ghosh commited on
Commit Β·
be5e8ad
1
Parent(s): 2bb63a3
new
Browse files- app.py +10 -5
- templates/dashboard.html +11 -4
app.py
CHANGED
|
@@ -633,12 +633,17 @@ def log_request(response):
|
|
| 633 |
return response
|
| 634 |
|
| 635 |
def _require_admin():
|
| 636 |
-
|
| 637 |
-
|
|
|
|
|
|
|
|
|
|
| 638 |
if not provided and request.is_json:
|
| 639 |
-
provided = (request.get_json() or {}).get("admin_key","")
|
| 640 |
-
|
| 641 |
-
|
|
|
|
|
|
|
| 642 |
return None
|
| 643 |
|
| 644 |
SKIP = ("chrome://","about:","chrome-extension://","moz-extension://",
|
|
|
|
| 633 |
return response
|
| 634 |
|
| 635 |
def _require_admin():
|
| 636 |
+
# If ADMIN_API_KEY is not configured, block ALL admin operations
|
| 637 |
+
if not _key_ok(ADMIN_API_KEY):
|
| 638 |
+
return jsonify({"error": "Admin key not configured on server β admin endpoints disabled"}), 403
|
| 639 |
+
# Get key from header or JSON body
|
| 640 |
+
provided = request.headers.get("X-Admin-Key", "")
|
| 641 |
if not provided and request.is_json:
|
| 642 |
+
provided = (request.get_json() or {}).get("admin_key", "")
|
| 643 |
+
# Constant-time comparison to prevent timing attacks
|
| 644 |
+
import hmac
|
| 645 |
+
if not provided or not hmac.compare_digest(provided, ADMIN_API_KEY):
|
| 646 |
+
return jsonify({"error": "Unauthorized β invalid or missing X-Admin-Key"}), 403
|
| 647 |
return None
|
| 648 |
|
| 649 |
SKIP = ("chrome://","about:","chrome-extension://","moz-extension://",
|
templates/dashboard.html
CHANGED
|
@@ -491,16 +491,23 @@ function sendFeedback(correctLabel){
|
|
| 491 |
|
| 492 |
document.getElementById('si').addEventListener('keydown',function(e){if(e.key==='Enter')doScan()});
|
| 493 |
|
| 494 |
-
// ββ Retrain Trigger ββββββββββββββββββββββββββββββββββββββ
|
| 495 |
function doRetrain(){
|
| 496 |
var b=document.querySelector('.rtbtn');
|
|
|
|
|
|
|
| 497 |
b.textContent='β³ RETRAININGβ¦';b.disabled=true;
|
| 498 |
-
fetch('/retrain/trigger',{
|
|
|
|
|
|
|
|
|
|
|
|
|
| 499 |
.then(function(r){return r.json()})
|
| 500 |
.then(function(d){
|
| 501 |
-
b.textContent=
|
|
|
|
| 502 |
setTimeout(function(){b.textContent='β‘ TRIGGER RETRAIN';b.disabled=false},3000);
|
| 503 |
-
}).catch(function(){b.textContent='β OFFLINE';setTimeout(function(){b.textContent='β‘ TRIGGER RETRAIN';b.disabled=false},3000)});
|
| 504 |
}
|
| 505 |
|
| 506 |
/* AUTO REFRESH β fetch stats every 60s instead of hard reload (FIX L3) */
|
|
|
|
| 491 |
|
| 492 |
document.getElementById('si').addEventListener('keydown',function(e){if(e.key==='Enter')doScan()});
|
| 493 |
|
| 494 |
+
// ββ Retrain Trigger (requires admin key) ββββββββββββββββββββββββββββββββββββββ
|
| 495 |
function doRetrain(){
|
| 496 |
var b=document.querySelector('.rtbtn');
|
| 497 |
+
var key=prompt('Enter Admin API Key to trigger retrain:');
|
| 498 |
+
if(!key||!key.trim()){b.textContent='β Cancelled';setTimeout(function(){b.textContent='β‘ TRIGGER RETRAIN'},2000);return;}
|
| 499 |
b.textContent='β³ RETRAININGβ¦';b.disabled=true;
|
| 500 |
+
fetch('/retrain/trigger',{
|
| 501 |
+
method:'POST',
|
| 502 |
+
headers:{'Content-Type':'application/json','X-Admin-Key':key.trim()},
|
| 503 |
+
body:JSON.stringify({})
|
| 504 |
+
})
|
| 505 |
.then(function(r){return r.json()})
|
| 506 |
.then(function(d){
|
| 507 |
+
if(d.error){b.textContent='π '+d.error;}
|
| 508 |
+
else{b.textContent=d.status==='triggered'?'β RETRAIN STARTED':'β '+(d.message||d.status);}
|
| 509 |
setTimeout(function(){b.textContent='β‘ TRIGGER RETRAIN';b.disabled=false},3000);
|
| 510 |
+
}).catch(function(){b.textContent='β OFFLINE';setTimeout(function(){b.textContent='β‘ TRIGGER RETRAIN';b.disabled=false},3000);});
|
| 511 |
}
|
| 512 |
|
| 513 |
/* AUTO REFRESH β fetch stats every 60s instead of hard reload (FIX L3) */
|