19e534539738dc80225bff27bda1f1835dc5b3d3
hiram
  Mon May 11 15:09:10 2026 -0700
rate limit the lifrt requests to "liftDailyLimi" setting from hg.conf refs #31811

diff --git src/hg/js/liftRequest.js src/hg/js/liftRequest.js
index bcdfe68ce52..3154c58c304 100644
--- src/hg/js/liftRequest.js
+++ src/hg/js/liftRequest.js
@@ -35,31 +35,31 @@
     }
     liftPath += "/liftOver/" + asm1 + "To";
     liftPath += asm2.charAt(0).toUpperCase() + asm2.slice(1);
     liftPath += ".over.chain.gz";
     return(liftPath);
 }
 
 function checkAssemblyCompatibility(asm1, asm2) {
     const url = "/cgi-bin/hubApi/liftOver/listExisting?fromGenome=" + encodeURIComponent(asm1) +
                 ";" + "toGenome=" + encodeURIComponent(asm2);
 
     fetch(url)
       .then(response => response.json())
       .then(response => {
 //      console.log(JSON.stringify(response, null, 2));
-        if (response.itemsReturned === 1) {
+        if (response.itemsReturned >= 1) {
           const liftPath1 = liftOverPath(asm1, asm2);
           const liftPath2 = liftOverPath(asm2, asm1);
           const browser1 = "/cgi-bin/hgTracks?db=" + asm1;
           const browser2 = "/cgi-bin/hgTracks?db=" + asm2;
 
           fileExists(liftPath1, function(exists) {
             if (exists) {
               document.getElementById("genome1Link").href = browser1;
               document.getElementById("genome1Link").textContent = assembly1Value;
 
               document.getElementById("genome1LiftOver").href = liftPath1;
               document.getElementById("genome1LiftOver").textContent = asm1 + " to " + asm2;
 
               document.getElementById("liftExists").style.display = "block";
               document.getElementById("emailForm").style.display = "none";
@@ -85,35 +85,45 @@
         }
       })
       .catch(error => {
         console.error("Error fetching liftOver list:", error);
       });
 }	// end of function checkAssemblyCompatibility(asm1, asm2)
 
 function checkBothAssembliesSelected() {
     if (genome1 && genome2) { // Both assemblies are now selected
         checkAssemblyCompatibility(genome1, genome2);
     }
 }
 
 function resetFormVisibility() {
     document.getElementById("liftExists").style.display = "none";
+    document.getElementById("errorMessage").style.display = "none";
     document.getElementById("emailForm").style.display = "block";
     document.getElementById("commentsForm").style.display = "block";
     document.getElementById("submitButton").style.display = "block";
 }
 
+function showError(heading, errorMsg) {
+    document.getElementById("errorHeading").textContent = heading;
+    document.getElementById("errorText").textContent = errorMsg;
+    document.getElementById("errorMessage").style.display = "block";
+    document.getElementById("emailForm").style.display = "none";
+    document.getElementById("commentsForm").style.display = "none";
+    document.getElementById("submitButton").style.display = "none";
+}
+
 function assembly1Select(selectEle, item) {
     selectEle.innerHTML = item.label;
     assembly1Value = item.value || item.label;
     genome1 = item.genome;
 //  console.log("asm1:", JSON.stringify(item, null, 2));
     resetFormVisibility();
     checkBothAssembliesSelected();
 }
 
 function assembly2Select(selectEle, item) {
     selectEle.innerHTML = item.label;
     assembly2Value = item.value || item.label;
     genome2 = item.genome;
 //  console.log("asm2:", JSON.stringify(item, null, 2));
     resetFormVisibility();
@@ -125,30 +135,32 @@
     var validEmail = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$/;
 
     if (!validEmail.test(checkEmail)) {
         alert("You have entered an invalid email address !");
         return false;
     }
     return true;
 }
 
 function submitForm() {
     var email = document.getElementById("emailInput").value;
     var comments = document.getElementById("commentsInput").value;
 
     // Hide any previous error message
     document.getElementById("errorMessage").style.display = "none";
+    document.getElementById("errorHeading").textContent = "Error";
+    document.getElementById("errorText").textContent = "";
 
     if (!assembly1Value) {
         alert("Please select Assembly 1");
         return;
     }
     if (!assembly2Value) {
         alert("Please select Assembly 2");
         return;
     }
     if (!email) {
         alert("Please enter an email address");
         return;
     }
     if (! validateEmail(email)) {
         return;
@@ -161,48 +173,53 @@
         "fromGenome=" + encodeURIComponent(genome1) + ";" +
         "toGenome=" + encodeURIComponent(genome2) + ";" +
         "email=" + encodeURIComponent(email) + ";" +
         "comment=" + encodeURIComponent(comment);
 
     fetch(apiUrl, { method: 'GET' })
       .then((response) => {
         return response.text().then((text) => {
           if (response.ok) {
             localStorage.setItem('liftRequestEmail', email);
             document.getElementById("formContainer").style.display = "none";
             document.getElementById("successMessage").style.display = "block";
           } else {
             // Try to extract error message from JSON or text
             var errorMsg = "Error submitting request";
+            var heading = "Error";
             try {
               var parsed = JSON.parse(text);
               if (parsed.error) {
                 errorMsg = parsed.error;
               }
             } catch(e) {
               errorMsg = text || response.statusText || errorMsg;
             }
-            document.getElementById("errorText").textContent = errorMsg;
-            document.getElementById("errorMessage").style.display = "block";
+            if (response.status === 429) {
+              heading = "Daily limit reached";
+              errorMsg = "Thank you for your interest. " + errorMsg +
+                  " If you have an urgent need, please contact us at" +
+                  " genome-www@soe.ucsc.edu.";
+            }
+            showError(heading, errorMsg);
           }
         });
       })
       .catch((error) => {
         // Network or other fetch errors
         var errorMsg = error.message || "Unknown error occurred";
-        document.getElementById("errorText").textContent = errorMsg;
-        document.getElementById("errorMessage").style.display = "block";
+        showError("Error", errorMsg);
       });
 }	// end of function submitForm()
 
 function dismissLiftExists() {
     resetFormVisibility();
     document.getElementById("genomeSearch1").value = "";
     document.getElementById("genomeSearch2").value = "";
     assembly1Value = "";
     assembly2Value = "";
     genome1 = "";
     genome2 = "";
 }
 
 function onSearchError(jqXHR, textStatus, errorThrown, term) {
     return [{label: 'No genomes found', value: '', genome: '', disabled: true}];
@@ -227,16 +244,17 @@
     initSpeciesAutoCompleteDropdown('genomeSearch2', boundSelect2,
         "/cgi-bin/hubApi/findGenome?browser=mustExist;q=", null, null, onSearchError);
 
     let btn2 = document.getElementById("genomeSearchButton2");
     btn2.addEventListener("click", () => {
         let val = document.getElementById("genomeSearch2").value;
         $("[id='genomeSearch2']").autocompleteCat("search", val);
     });
     // restore saved email if it exists
     var savedEmail = localStorage.getItem('liftRequestEmail');
     if (savedEmail) {
         document.getElementById("emailInput").value = savedEmail;
     }
 
     document.getElementById("dismissLiftExists").addEventListener("click", dismissLiftExists);
+    document.getElementById("dismissError").addEventListener("click", resetFormVisibility);
 });