diff --git a/services/adminConfig.js b/services/adminConfig.js
index 5a6a964..39d547e 100644
--- a/services/adminConfig.js
+++ b/services/adminConfig.js
@@ -13,6 +13,7 @@ const DEFAULT_SETTINGS = {
storeWatchCron: '*/30 * * * *',
storeWatchInitialDelayMinSeconds: 10,
storeWatchInitialDelayMaxSeconds: 60,
+ storeWatchRequestDelayMs: 1000,
storePickupCheckDelayMs: 400,
ignoredSlots: [
{
@@ -113,6 +114,10 @@ function readSettings() {
parsed.storeWatchInitialDelayMaxSeconds,
DEFAULT_SETTINGS.storeWatchInitialDelayMaxSeconds
),
+ storeWatchRequestDelayMs: sanitizeNumber(
+ parsed.storeWatchRequestDelayMs,
+ DEFAULT_SETTINGS.storeWatchRequestDelayMs
+ ),
storePickupCheckDelayMs: sanitizeNumber(
parsed.storePickupCheckDelayMs,
DEFAULT_SETTINGS.storePickupCheckDelayMs
@@ -143,6 +148,10 @@ function writeSettings(patch = {}) {
patch.storeWatchInitialDelayMaxSeconds,
current.storeWatchInitialDelayMaxSeconds
),
+ storeWatchRequestDelayMs: sanitizeNumber(
+ patch.storeWatchRequestDelayMs,
+ current.storeWatchRequestDelayMs
+ ),
storePickupCheckDelayMs: sanitizeNumber(
patch.storePickupCheckDelayMs,
current.storePickupCheckDelayMs
diff --git a/services/pickupScheduler.js b/services/pickupScheduler.js
index 6d0696b..d76b43d 100644
--- a/services/pickupScheduler.js
+++ b/services/pickupScheduler.js
@@ -6,6 +6,13 @@ const notificationService = require('./notificationService');
const { readConfig, writeConfig } = require('./configStore');
const { readStoreWatch, writeStoreWatch } = require('./storeWatchStore');
+function wait(ms) {
+ if (!ms || ms <= 0) {
+ return Promise.resolve();
+ }
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
const weekdayMap = {
Montag: 'Monday',
Dienstag: 'Tuesday',
@@ -47,6 +54,9 @@ function resolveSettings(settings) {
storeWatchInitialDelayMaxSeconds: Number.isFinite(settings.storeWatchInitialDelayMaxSeconds)
? settings.storeWatchInitialDelayMaxSeconds
: DEFAULT_SETTINGS.storeWatchInitialDelayMaxSeconds,
+ storeWatchRequestDelayMs: Number.isFinite(settings.storeWatchRequestDelayMs)
+ ? settings.storeWatchRequestDelayMs
+ : DEFAULT_SETTINGS.storeWatchRequestDelayMs,
ignoredSlots: Array.isArray(settings.ignoredSlots) ? settings.ignoredSlots : DEFAULT_SETTINGS.ignoredSlots,
notifications: {
ntfy: {
@@ -295,7 +305,7 @@ async function checkEntry(sessionId, entry, settings) {
}
}
-async function checkWatchedStores(sessionId) {
+async function checkWatchedStores(sessionId, settings = DEFAULT_SETTINGS) {
const session = sessionStore.get(sessionId);
if (!session?.profile?.id) {
return;
@@ -310,8 +320,10 @@ async function checkWatchedStores(sessionId) {
return;
}
+ const perRequestDelay = Math.max(0, Number(settings?.storeWatchRequestDelayMs) || 0);
let changed = false;
- for (const watcher of watchers) {
+ for (let index = 0; index < watchers.length; index += 1) {
+ const watcher = watchers[index];
try {
const details = await foodsharingClient.fetchStoreDetails(watcher.storeId, session.cookieHeader);
const status = details?.teamSearchStatus === 1 ? 1 : 0;
@@ -329,6 +341,11 @@ async function checkWatchedStores(sessionId) {
}
} catch (error) {
console.error(`[WATCH] Prüfung für Store ${watcher.storeId} fehlgeschlagen:`, error.message);
+ } finally {
+ const hasNext = index < watchers.length - 1;
+ if (hasNext && perRequestDelay > 0) {
+ await wait(perRequestDelay);
+ }
}
}
@@ -338,6 +355,7 @@ async function checkWatchedStores(sessionId) {
}
function scheduleStoreWatchers(sessionId, settings) {
+ const effectiveSettings = settings || DEFAULT_SETTINGS;
const session = sessionStore.get(sessionId);
if (!session?.profile?.id) {
return false;
@@ -346,11 +364,11 @@ function scheduleStoreWatchers(sessionId, settings) {
if (!Array.isArray(watchers) || watchers.length === 0) {
return false;
}
- const cronExpression = settings.storeWatchCron || DEFAULT_SETTINGS.storeWatchCron;
+ const cronExpression = effectiveSettings.storeWatchCron || DEFAULT_SETTINGS.storeWatchCron;
const job = cron.schedule(
cronExpression,
() => {
- checkWatchedStores(sessionId).catch((error) => {
+ checkWatchedStores(sessionId, effectiveSettings).catch((error) => {
console.error('[WATCH] Regelmäßige Prüfung fehlgeschlagen:', error.message);
});
},
@@ -358,8 +376,11 @@ function scheduleStoreWatchers(sessionId, settings) {
);
sessionStore.attachJob(sessionId, job);
setTimeout(
- () => checkWatchedStores(sessionId),
- randomDelayMs(settings.storeWatchInitialDelayMinSeconds, settings.storeWatchInitialDelayMaxSeconds)
+ () => checkWatchedStores(sessionId, effectiveSettings),
+ randomDelayMs(
+ effectiveSettings.storeWatchInitialDelayMinSeconds,
+ effectiveSettings.storeWatchInitialDelayMaxSeconds
+ )
);
console.log(
`[WATCH] Überwache ${watchers.length} Betriebe für Session ${sessionId} (Cron: ${cronExpression}).`
diff --git a/src/components/AdminSettingsPanel.js b/src/components/AdminSettingsPanel.js
index 39d75ba..6902cc0 100644
--- a/src/components/AdminSettingsPanel.js
+++ b/src/components/AdminSettingsPanel.js
@@ -210,6 +210,20 @@ const AdminSettingsPanel = ({
/>
+
+
+ onSettingChange('storeWatchRequestDelayMs', event.target.value, true)}
+ className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
+ placeholder="z. B. 1000"
+ />
+
{
storeWatchCron: raw.storeWatchCron || '',
storeWatchInitialDelayMinSeconds: raw.storeWatchInitialDelayMinSeconds ?? '',
storeWatchInitialDelayMaxSeconds: raw.storeWatchInitialDelayMaxSeconds ?? '',
+ storeWatchRequestDelayMs: raw.storeWatchRequestDelayMs ?? '',
ignoredSlots: Array.isArray(raw.ignoredSlots)
? raw.ignoredSlots.map((slot) => ({
storeId: slot?.storeId ? String(slot.storeId) : '',
@@ -56,6 +57,7 @@ export const serializeAdminSettings = (adminSettings) => {
storeWatchCron: adminSettings.storeWatchCron,
storeWatchInitialDelayMinSeconds: toNumberOrUndefined(adminSettings.storeWatchInitialDelayMinSeconds),
storeWatchInitialDelayMaxSeconds: toNumberOrUndefined(adminSettings.storeWatchInitialDelayMaxSeconds),
+ storeWatchRequestDelayMs: toNumberOrUndefined(adminSettings.storeWatchRequestDelayMs),
ignoredSlots: (adminSettings.ignoredSlots || []).map((slot) => ({
storeId: slot.storeId || '',
description: slot.description || ''