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/hubApi/liftOver.c src/hg/hubApi/liftOver.c index c39da3f64db..04edc9e3aad 100644 --- src/hg/hubApi/liftOver.c +++ src/hg/hubApi/liftOver.c @@ -71,35 +71,43 @@ static void listExisting() /* output the fromDb,toDb from liftOverChain.hgcentral SQL table */ { char *filter = cgiOptionalString(argFilter); char *fromDb = cgiOptionalString(argFromGenome); char *toDb = cgiOptionalString(argToGenome); struct sqlConnection *conn = hConnectCentral(); char *tableName = cloneString(liftOverChainTable()); struct dyString *query = newDyString(0); sqlDyStringPrintf(query, "SELECT count(*) FROM %s", tableName); long long totalRows = sqlQuickLongLong(conn, dyStringContents(query)); dyStringClear(query); -if (isNotEmpty(fromDb) || isNotEmpty(toDb)) +if (isNotEmpty(fromDb) && isNotEmpty(toDb)) + { + /* match a chain recorded in either direction */ + sqlDyStringPrintf(query, "SELECT * FROM %s WHERE " + "(LOWER(fromDb) = LOWER('%s') AND LOWER(toDb) = LOWER('%s')) " + "OR (LOWER(fromDb) = LOWER('%s') AND LOWER(toDb) = LOWER('%s'))", + tableName, fromDb, toDb, toDb, fromDb); + } +else if (isNotEmpty(fromDb) || isNotEmpty(toDb)) { sqlDyStringPrintf(query, "SELECT * FROM %s WHERE ", tableName); if (isNotEmpty(fromDb)) - sqlDyStringPrintf(query, "LOWER(fromDb) = LOWER('%s') %s ", fromDb, isNotEmpty(toDb) ? "AND" : ""); + sqlDyStringPrintf(query, "LOWER(fromDb) = LOWER('%s') ", fromDb); if (isNotEmpty(toDb)) sqlDyStringPrintf(query, "LOWER(toDb) = LOWER('%s') ", toDb); } else if (isNotEmpty(filter)) { sqlDyStringPrintf(query, "SELECT * FROM %s WHERE LOWER(fromDb) = LOWER('%s') OR LOWER(toDb) = LOWER('%s')", tableName, filter, filter); } else { sqlDyStringPrintf(query, "SELECT * FROM %s", tableName); } sqlDyStringPrintf(query, " LIMIT %d;", maxItemsOutput); char *dataTime = sqlTableUpdate(conn, tableName); time_t dataTimeStamp = sqlDateToUnixTime(dataTime); @@ -289,30 +297,60 @@ char *toGenome = cgiOptionalString(argToGenome); char *email = cgiOptionalString(argEmail); char *comment = cgiOptionalString(argComment); /* probably want a silent exit here */ if (isEmpty(fromGenome) || isEmpty(toGenome) || isEmpty(email) || isEmpty(comment)) apiErrAbort(err400, err400Msg, "must have all arguments: %s, %s, %s, %s for endpoint '/liftRequest", argFromGenome, argToGenome, argEmail, argComment); /* Require a session cookie. Robots that have not * passed the challenge will not have one. */ char *cookieName = hUserCookie(); char *userId = findCookieData(cookieName); if (isEmpty(userId)) apiErrAbort(err400, err400Msg, "can not find required inputs for endpoint '/liftRequest"); +/* per-email daily rate limit, per requestType, calendar-day server time */ +char *limitStr = cfgOption("liftDailyLimit"); +int dailyLimit = isNotEmpty(limitStr) ? atoi(limitStr) : 0; +if (dailyLimit > 0) + { + char *limitOttoTable = cfgOption("ottoTable"); + if (isNotEmpty(limitOttoTable)) + { + struct sqlConnection *conn = hConnectCentral(); + if (sqlTableExists(conn, limitOttoTable)) + { + struct dyString *q = newDyString(0); + sqlDyStringPrintf(q, + "SELECT COUNT(*) FROM %s " + "WHERE requestType='liftOver' AND email='%s' " + "AND DATE(requestTime) = CURDATE()", + limitOttoTable, email); + int todayCount = sqlQuickNum(conn, dyStringCannibalize(&q)); + hDisconnectCentral(&conn); + if (todayCount >= dailyLimit) + apiErrAbort(err429, err429Msg, + "Daily limit reached: %d liftOver requests per day. " + " Please try again tomorrow.", + dailyLimit); + } + else + hDisconnectCentral(&conn); + } + } + char *toAddr = cfgOption("chainFileRequestEmail"); char *fromAddr = cfgOption("apiFromEmail"); if (isNotEmpty(toAddr) && isNotEmpty(fromAddr)) { char nowTime[256]; time_t seconds = clock1(); struct tm *timeNow = localtime(&seconds); strftime(nowTime, sizeof nowTime, "%Y-%m-%d %H:%M:%S", timeNow); struct dyString *msg = newDyString(0); /* may need to encode these inputs to make them safe */ dyStringPrintf(msg, "%s\nLift over request\nfrom: %s\nto: %s\nemail '%s'\ncomment: '%s'", nowTime, fromGenome, toGenome, email, comment); /* Even if the mailViaPipe returned a relevant return code, and I'm not * sure it would, there isn't much we can do about it from here.