feat: Add error handling for rate limit exceeded in form submission

This commit adds error handling for rate limit exceeded in the form submission process. If the server returns a 429 status code, the client will display an error message indicating the rate limit has been exceeded and provide information on when the user can try again. This improves the user experience by providing clear feedback and guidance when rate limits are reached.
This commit is contained in:
unclecode
2024-07-08 20:24:00 +08:00
parent 65ed1aeade
commit ca0336af9e
2 changed files with 47 additions and 8 deletions

View File

@@ -14,6 +14,7 @@
<div class="form-group"> <div class="form-group">
<button class="btn btn-default" type="submit">Submit</button> <button class="btn btn-default" type="submit">Submit</button>
</div> </div>
</fieldset> </fieldset>
</form> </form>
@@ -93,6 +94,10 @@
</div> </div>
</section> </section>
<div id="error" class="error-message" style="display: none; margin-top:1em;">
<div class="terminal-alert terminal-alert-error"></div>
</div>
<script> <script>
function showTab(tabId) { function showTab(tabId) {
const tabs = document.querySelectorAll('.tab-content'); const tabs = document.querySelectorAll('.tab-content');
@@ -162,7 +167,17 @@
}, },
body: JSON.stringify(data) body: JSON.stringify(data)
}) })
.then(response => response.json()) .then(response => {
if (!response.ok) {
if (response.status === 429) {
return response.json().then(err => {
throw Object.assign(new Error('Rate limit exceeded'), { status: 429, details: err });
});
}
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => { .then(data => {
data = data.results[0]; // Only one URL is requested data = data.results[0]; // Only one URL is requested
document.getElementById('loading').style.display = 'none'; document.getElementById('loading').style.display = 'none';
@@ -187,11 +202,29 @@ result = crawler.run(
print(result) print(result)
`; `;
redo(document.getElementById('pythonCode'), pythonCode); redo(document.getElementById('pythonCode'), pythonCode);
document.getElementById('error').style.display = 'none';
}) })
.catch(error => { .catch(error => {
document.getElementById('loading').style.display = 'none'; document.getElementById('loading').style.display = 'none';
document.getElementById('response').style.display = 'block'; document.getElementById('error').style.display = 'block';
document.getElementById('markdownContent').textContent = 'Error: ' + error; let errorMessage = 'An unexpected error occurred. Please try again later.';
if (error.status === 429) {
const details = error.details;
if (details.retry_after) {
errorMessage = `Rate limit exceeded. Please wait ${parseFloat(details.retry_after).toFixed(1)} seconds before trying again.`;
} else if (details.reset_at) {
const resetTime = new Date(details.reset_at);
const waitTime = Math.ceil((resetTime - new Date()) / 1000);
errorMessage = `Rate limit exceeded. Please try again after ${waitTime} seconds.`;
} else {
errorMessage = `Rate limit exceeded. Please try again later.`;
}
} else if (error.message) {
errorMessage = error.message;
}
document.querySelector('#error .terminal-alert').textContent = errorMessage;
}); });
}); });
</script> </script>

16
main.py
View File

@@ -51,6 +51,8 @@ app.state.limiter = limiter
# Dictionary to store last request times for each client # Dictionary to store last request times for each client
last_request_times = {} last_request_times = {}
last_rate_limit = {}
def get_rate_limit(): def get_rate_limit():
limit = os.environ.get('ACCESS_PER_MIN', "5") limit = os.environ.get('ACCESS_PER_MIN', "5")
@@ -58,15 +60,18 @@ def get_rate_limit():
# Custom rate limit exceeded handler # Custom rate limit exceeded handler
async def custom_rate_limit_exceeded_handler(request: Request, exc: RateLimitExceeded) -> JSONResponse: async def custom_rate_limit_exceeded_handler(request: Request, exc: RateLimitExceeded) -> JSONResponse:
try_after = last_request_times.get(request.client.host, 0) + 10 - time.time() if request.client.host not in last_rate_limit or time.time() - last_rate_limit[request.client.host] > 60:
reset_at = time.time() + try_after last_rate_limit[request.client.host] = time.time()
retry_after = 60 - (time.time() - last_rate_limit[request.client.host])
reset_at = time.time() + retry_after
return JSONResponse( return JSONResponse(
status_code=429, status_code=429,
content={ content={
"detail": "Rate limit exceeded", "detail": "Rate limit exceeded",
"limit": str(exc.limit.limit), "limit": str(exc.limit.limit),
"reset_at": reset_at, "retry_after": retry_after,
"message": f"You have exceeded the rate limit of {exc.limit.limit}. Please try again after {try_after} seconds." 'reset_at': reset_at,
"message": f"You have exceeded the rate limit of {exc.limit.limit}."
} }
) )
@@ -95,7 +100,8 @@ class RateLimitMiddleware(BaseHTTPMiddleware):
content={ content={
"detail": "Too many requests", "detail": "Too many requests",
"message": "Rate limit exceeded. Please wait 10 seconds between requests.", "message": "Rate limit exceeded. Please wait 10 seconds between requests.",
"retry_after": max(0, SPAN - time_since_last_request) "retry_after": max(0, SPAN - time_since_last_request),
"reset_at": current_time + max(0, SPAN - time_since_last_request),
} }
) )