aktueller stand

This commit is contained in:
2026-01-29 17:00:24 +01:00
parent 132b571798
commit 916ca1dbc2
10 changed files with 256 additions and 55 deletions

View File

@@ -6,7 +6,7 @@ const notificationService = require('./notificationService');
const { readConfig, writeConfig } = require('./configStore');
const { readStoreWatch, writeStoreWatch } = require('./storeWatchStore');
const { readJournal, writeJournal } = require('./journalStore');
const { setStoreStatus, persistStoreStatusCache } = require('./storeStatusCache');
const { getStoreStatus, setStoreStatus, persistStoreStatusCache } = require('./storeStatusCache');
const { sendDormantPickupWarning, sendJournalReminderNotification } = require('./notificationService');
const { ensureSession, withSessionRetry } = require('./sessionRefresh');
@@ -17,6 +17,53 @@ function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
const DEFAULT_STORE_WATCH_STATUS_MAX_AGE_MINUTES = 120;
const storeWatchInFlight = new Map();
async function fetchSharedStoreStatus(session, storeId, { forceRefresh = false, maxAgeMs } = {}) {
if (!storeId) {
return { status: null, fetchedAt: null, fromCache: false };
}
const cacheEntry = getStoreStatus(storeId);
const cachedStatus = cacheEntry?.teamSearchStatus;
const hasCachedStatus = cachedStatus === 0 || cachedStatus === 1;
const cachedAt = Number(cacheEntry?.fetchedAt) || 0;
const effectiveMaxAge =
Number.isFinite(maxAgeMs) && maxAgeMs >= 0
? maxAgeMs
: DEFAULT_STORE_WATCH_STATUS_MAX_AGE_MINUTES * 60 * 1000;
const cacheFresh = hasCachedStatus && Date.now() - cachedAt <= effectiveMaxAge;
if (cacheFresh && !forceRefresh) {
return { status: cachedStatus, fetchedAt: cachedAt, fromCache: true };
}
const key = String(storeId);
if (!forceRefresh && storeWatchInFlight.has(key)) {
return storeWatchInFlight.get(key);
}
const fetchPromise = (async () => {
const details = await withSessionRetry(
session,
() => foodsharingClient.fetchStoreDetails(storeId, session.cookieHeader, session),
{ label: 'fetchStoreDetails' }
);
const status = details?.teamSearchStatus === 1 ? 1 : 0;
const fetchedAt = Date.now();
setStoreStatus(storeId, { teamSearchStatus: status, fetchedAt });
return { status, fetchedAt, fromCache: false };
})();
storeWatchInFlight.set(key, fetchPromise);
try {
return await fetchPromise;
} finally {
if (storeWatchInFlight.get(key) === fetchPromise) {
storeWatchInFlight.delete(key);
}
}
}
const weekdayMap = {
Montag: 'Monday',
Dienstag: 'Tuesday',
@@ -103,6 +150,9 @@ function resolveSettings(settings) {
storeWatchRequestDelayMs: Number.isFinite(settings.storeWatchRequestDelayMs)
? settings.storeWatchRequestDelayMs
: DEFAULT_SETTINGS.storeWatchRequestDelayMs,
storeWatchStatusCacheMaxAgeMinutes: Number.isFinite(settings.storeWatchStatusCacheMaxAgeMinutes)
? settings.storeWatchStatusCacheMaxAgeMinutes
: DEFAULT_SETTINGS.storeWatchStatusCacheMaxAgeMinutes,
ignoredSlots: Array.isArray(settings.ignoredSlots) ? settings.ignoredSlots : DEFAULT_SETTINGS.ignoredSlots,
notifications: {
ntfy: {
@@ -305,10 +355,8 @@ function shouldIgnoreSlot(entry, pickup, settings) {
if (String(rule.storeId) !== entry.id) {
return false;
}
if (rule.description) {
return pickup.description === rule.description;
}
return true;
const slotName = rule.slotName || rule.description;
return slotName ? pickup.description === slotName : true;
});
}
@@ -469,12 +517,13 @@ async function checkWatchedStores(sessionId, settings = DEFAULT_SETTINGS, option
for (let index = 0; index < watchers.length; index += 1) {
const watcher = watchers[index];
try {
const details = await withSessionRetry(
session,
() => foodsharingClient.fetchStoreDetails(watcher.storeId, session.cookieHeader, session),
{ label: 'fetchStoreDetails' }
);
const status = details?.teamSearchStatus === 1 ? 1 : 0;
const cacheMaxAgeMs = Math.max(
0,
Number(settings?.storeWatchStatusCacheMaxAgeMinutes ?? DEFAULT_STORE_WATCH_STATUS_MAX_AGE_MINUTES)
) * 60 * 1000;
const { status, fromCache } = await fetchSharedStoreStatus(session, watcher.storeId, {
maxAgeMs: cacheMaxAgeMs
});
const checkedAt = Date.now();
if (status === 1 && watcher.lastTeamSearchStatus !== 1) {
await notificationService.sendStoreWatchNotification({
@@ -490,8 +539,9 @@ async function checkWatchedStores(sessionId, settings = DEFAULT_SETTINGS, option
}
watcher.lastStatusCheckAt = checkedAt;
changed = true;
setStoreStatus(watcher.storeId, { teamSearchStatus: status, fetchedAt: checkedAt });
statusCacheUpdated = true;
if (!fromCache) {
statusCacheUpdated = true;
}
summary.push({
storeId: watcher.storeId,
storeName: watcher.storeName,