fix: resolve rate-limiting and Ombi requests caching bugs (fixes #42, fixes #43)
Build and Push Docker Image / build (push) Successful in 1m34s
Docs Check / Markdown lint (push) Successful in 2m14s
CI / Security audit (push) Successful in 2m30s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 2m40s
CI / Swagger Validation & Coverage (push) Successful in 3m22s
Docs Check / Mermaid diagram parse check (push) Successful in 3m43s
CI / Tests & coverage (push) Successful in 3m59s

This commit is contained in:
2026-05-23 18:55:03 +01:00
parent f8c7e35f31
commit 6ac0a8421e
10 changed files with 121 additions and 16 deletions
+1
View File
@@ -96,6 +96,7 @@ function createApp({ skipRateLimits = false } = {}) {
max: skipRateLimits ? Number.MAX_SAFE_INTEGER : 300,
standardHeaders: true,
legacyHeaders: false,
skip: (req) => req.originalUrl && req.originalUrl.startsWith('/api/dashboard/cover-art'),
message: { error: 'Too many requests, please try again later' }
});
+9 -6
View File
@@ -87,10 +87,11 @@ class OmbiRetriever extends ArrRetriever {
/**
* Refresh cached data from Ombi API
* @param {boolean} force - Whether to force a refresh regardless of TTL
* @returns {Promise<void>}
*/
async refreshCache() {
if (!this.isCacheExpired()) {
async refreshCache(force = false) {
if (!force && !this.isCacheExpired()) {
return;
}
@@ -141,19 +142,21 @@ class OmbiRetriever extends ArrRetriever {
/**
* Get all movie requests
* @param {boolean} force - Whether to force refresh from API
* @returns {Promise<Array>} Array of movie request objects
*/
async getMovieRequests() {
await this.refreshCache();
async getMovieRequests(force = false) {
await this.refreshCache(force);
return this.cache.movieRequests;
}
/**
* Get all TV requests
* @param {boolean} force - Whether to force refresh from API
* @returns {Promise<Array>} Array of TV request objects
*/
async getTvRequests() {
await this.refreshCache();
async getTvRequests(force = false) {
await this.refreshCache(force);
return this.cache.tvRequests;
}
+1
View File
@@ -206,6 +206,7 @@ const apiLimiter = rateLimit({
max: 300, // 300 requests per IP per window (generous for polling)
standardHeaders: true,
legacyHeaders: false,
skip: (req) => req.originalUrl && req.originalUrl.startsWith('/api/dashboard/cover-art'),
message: { error: 'Too many requests, please try again later' }
});
+1 -1
View File
@@ -114,7 +114,7 @@ router.get('/requests', requireAuth, async (req, res) => {
// initialize() is idempotent - cheap no-op if already initialized
await arrRetrieverRegistry.initialize();
const ombiRequests = await arrRetrieverRegistry.getOmbiRequests();
const ombiRequests = await arrRetrieverRegistry.getOmbiRequests(true);
// Filter by user if not admin or if showAll is false
const filteredMovieRequests = filterRequestsByUser(ombiRequests.movie || [], username, showAll);
+1 -1
View File
@@ -258,7 +258,7 @@ async function processWebhookEvent(serviceType, eventType) {
const ombiInstances = getOmbiInstances();
if (affectsOmbi) {
const ombiRequests = await arrRetrieverRegistry.getOmbiRequests();
const ombiRequests = await arrRetrieverRegistry.getOmbiRequests(true);
cache.set('poll:ombi-requests', ombiRequests, CACHE_TTL);
logToFile(`[Webhook] Refreshed poll:ombi-requests (${ombiRequests.movie?.length || 0} movies, ${ombiRequests.tv?.length || 0} TV shows)`);
}
+7 -5
View File
@@ -322,9 +322,10 @@ const arrRetrieverRegistry = {
/**
* Get all Ombi requests
* @param {boolean} force - Whether to force refresh from API
* @returns {Promise<Object>} Object with movie and TV request arrays
*/
async getOmbiRequests() {
async getOmbiRequests(force = false) {
const ombiRetrievers = this.getOmbiRetrievers();
if (ombiRetrievers.length === 0) {
return { movie: [], tv: [] };
@@ -333,8 +334,8 @@ const arrRetrieverRegistry = {
// Use the first Ombi retriever (single instance expected)
const retriever = ombiRetrievers[0];
try {
const movieRequests = await retriever.getMovieRequests();
const tvRequests = await retriever.getTvRequests();
const movieRequests = await retriever.getMovieRequests(force);
const tvRequests = await retriever.getTvRequests(false);
return { movie: movieRequests, tv: tvRequests };
} catch (error) {
logToFile(`[ArrRetrieverRegistry] Error fetching Ombi requests: ${error.message}`);
@@ -344,10 +345,11 @@ const arrRetrieverRegistry = {
/**
* Get Ombi requests grouped by type
* @param {boolean} force - Whether to force refresh from API
* @returns {Promise<Object>} Requests grouped by type (movie, tv)
*/
async getOmbiRequestsByType() {
return await this.getOmbiRequests();
async getOmbiRequestsByType(force = false) {
return await this.getOmbiRequests(force);
},
/**