watcher
This commit is contained in:
85
server.js
85
server.js
@@ -11,7 +11,7 @@ const { scheduleConfig } = require('./services/pickupScheduler');
|
|||||||
const adminConfig = require('./services/adminConfig');
|
const adminConfig = require('./services/adminConfig');
|
||||||
const { readNotificationSettings, writeNotificationSettings } = require('./services/userSettingsStore');
|
const { readNotificationSettings, writeNotificationSettings } = require('./services/userSettingsStore');
|
||||||
const notificationService = require('./services/notificationService');
|
const notificationService = require('./services/notificationService');
|
||||||
const { readStoreWatch, writeStoreWatch } = require('./services/storeWatchStore');
|
const { readStoreWatch, writeStoreWatch, listWatcherProfiles } = require('./services/storeWatchStore');
|
||||||
const { readPreferences, writePreferences } = require('./services/userPreferencesStore');
|
const { readPreferences, writePreferences } = require('./services/userPreferencesStore');
|
||||||
const { readStoreStatus, writeStoreStatus } = require('./services/storeStatusStore');
|
const { readStoreStatus, writeStoreStatus } = require('./services/storeStatusStore');
|
||||||
|
|
||||||
@@ -145,12 +145,63 @@ function getCachedStoreStatus(storeId) {
|
|||||||
return storeStatusCache.get(String(storeId)) || null;
|
return storeStatusCache.get(String(storeId)) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshStoreStatus(storeIds = [], cookieHeader, { force = false } = {}) {
|
async function notifyWatchersForStatusChanges(changes = [], storeInfoMap = new Map()) {
|
||||||
|
if (!Array.isArray(changes) || changes.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const changeMap = new Map();
|
||||||
|
changes.forEach((change) => {
|
||||||
|
if (!change) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
changeMap.set(String(change.storeId), change);
|
||||||
|
});
|
||||||
|
if (changeMap.size === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const profiles = listWatcherProfiles();
|
||||||
|
for (const profileId of profiles) {
|
||||||
|
const watchers = readStoreWatch(profileId);
|
||||||
|
if (!Array.isArray(watchers) || watchers.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let changed = false;
|
||||||
|
for (const watcher of watchers) {
|
||||||
|
const change = changeMap.get(String(watcher.storeId));
|
||||||
|
if (!change) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (watcher.lastTeamSearchStatus !== change.newStatus) {
|
||||||
|
if (change.newStatus === 1 && watcher.lastTeamSearchStatus !== 1) {
|
||||||
|
const details = storeInfoMap.get(String(watcher.storeId)) || {};
|
||||||
|
await notificationService.sendStoreWatchNotification({
|
||||||
|
profileId,
|
||||||
|
storeName: details.name || watcher.storeName,
|
||||||
|
storeId: watcher.storeId,
|
||||||
|
regionName: details.region?.name || watcher.regionName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
watcher.lastTeamSearchStatus = change.newStatus;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
writeStoreWatch(profileId, watchers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshStoreStatus(
|
||||||
|
storeIds = [],
|
||||||
|
cookieHeader,
|
||||||
|
{ force = false, storeInfoMap = new Map() } = {}
|
||||||
|
) {
|
||||||
if (!Array.isArray(storeIds) || storeIds.length === 0 || !cookieHeader) {
|
if (!Array.isArray(storeIds) || storeIds.length === 0 || !cookieHeader) {
|
||||||
return { refreshed: 0 };
|
return { refreshed: 0, changes: [] };
|
||||||
}
|
}
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
let refreshed = 0;
|
let refreshed = 0;
|
||||||
|
const changes = [];
|
||||||
for (const id of storeIds) {
|
for (const id of storeIds) {
|
||||||
const storeId = String(id);
|
const storeId = String(id);
|
||||||
const entry = storeStatusCache.get(storeId);
|
const entry = storeStatusCache.get(storeId);
|
||||||
@@ -162,10 +213,22 @@ async function refreshStoreStatus(storeIds = [], cookieHeader, { force = false }
|
|||||||
const details = await foodsharingClient.fetchStoreDetails(storeId, cookieHeader);
|
const details = await foodsharingClient.fetchStoreDetails(storeId, cookieHeader);
|
||||||
const status = Number(details?.teamSearchStatus);
|
const status = Number(details?.teamSearchStatus);
|
||||||
const normalized = Number.isFinite(status) ? status : null;
|
const normalized = Number.isFinite(status) ? status : null;
|
||||||
|
const previous = entry ? entry.teamSearchStatus : null;
|
||||||
storeStatusCache.set(storeId, {
|
storeStatusCache.set(storeId, {
|
||||||
teamSearchStatus: normalized,
|
teamSearchStatus: normalized,
|
||||||
fetchedAt: now
|
fetchedAt: now
|
||||||
});
|
});
|
||||||
|
if (previous !== normalized) {
|
||||||
|
const info = storeInfoMap.get(storeId) || {};
|
||||||
|
changes.push({
|
||||||
|
storeId,
|
||||||
|
previousStatus: previous,
|
||||||
|
newStatus: normalized,
|
||||||
|
fetchedAt: now,
|
||||||
|
storeName: info.name || null,
|
||||||
|
regionName: info.region?.name || info.regionName || null
|
||||||
|
});
|
||||||
|
}
|
||||||
refreshed += 1;
|
refreshed += 1;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[STORE-STATUS] Status für Store ${storeId} konnte nicht aktualisiert werden:`, error.message);
|
console.error(`[STORE-STATUS] Status für Store ${storeId} konnte nicht aktualisiert werden:`, error.message);
|
||||||
@@ -174,7 +237,7 @@ async function refreshStoreStatus(storeIds = [], cookieHeader, { force = false }
|
|||||||
if (refreshed > 0) {
|
if (refreshed > 0) {
|
||||||
persistStoreStatusCache();
|
persistStoreStatusCache();
|
||||||
}
|
}
|
||||||
return { refreshed };
|
return { refreshed, changes };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function enrichStoresWithTeamStatus(stores = [], cookieHeader, { forceRefresh = false } = {}) {
|
async function enrichStoresWithTeamStatus(stores = [], cookieHeader, { forceRefresh = false } = {}) {
|
||||||
@@ -184,7 +247,11 @@ async function enrichStoresWithTeamStatus(stores = [], cookieHeader, { forceRefr
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const staleIds = [];
|
const staleIds = [];
|
||||||
let cacheHits = 0;
|
let cacheHits = 0;
|
||||||
|
const storeInfoMap = new Map();
|
||||||
stores.forEach((store) => {
|
stores.forEach((store) => {
|
||||||
|
if (store?.id) {
|
||||||
|
storeInfoMap.set(String(store.id), store);
|
||||||
|
}
|
||||||
const entry = getCachedStoreStatus(store.id);
|
const entry = getCachedStoreStatus(store.id);
|
||||||
const fresh = entry && now - (entry.fetchedAt || 0) <= STORE_STATUS_MAX_AGE_MS;
|
const fresh = entry && now - (entry.fetchedAt || 0) <= STORE_STATUS_MAX_AGE_MS;
|
||||||
if (entry && fresh && !forceRefresh) {
|
if (entry && fresh && !forceRefresh) {
|
||||||
@@ -197,9 +264,14 @@ async function enrichStoresWithTeamStatus(stores = [], cookieHeader, { forceRefr
|
|||||||
});
|
});
|
||||||
|
|
||||||
let refreshed = 0;
|
let refreshed = 0;
|
||||||
|
let changes = [];
|
||||||
if (staleIds.length > 0) {
|
if (staleIds.length > 0) {
|
||||||
const result = await refreshStoreStatus(staleIds, cookieHeader, { force: forceRefresh });
|
const result = await refreshStoreStatus(staleIds, cookieHeader, {
|
||||||
|
force: forceRefresh,
|
||||||
|
storeInfoMap
|
||||||
|
});
|
||||||
refreshed = result.refreshed || 0;
|
refreshed = result.refreshed || 0;
|
||||||
|
changes = result.changes || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
stores.forEach((store) => {
|
stores.forEach((store) => {
|
||||||
@@ -222,6 +294,9 @@ async function enrichStoresWithTeamStatus(stores = [], cookieHeader, { forceRefr
|
|||||||
missing: stores.filter((store) => store.teamStatusUpdatedAt == null).length,
|
missing: stores.filter((store) => store.teamStatusUpdatedAt == null).length,
|
||||||
generatedAt: Date.now()
|
generatedAt: Date.now()
|
||||||
};
|
};
|
||||||
|
if (changes.length > 0) {
|
||||||
|
await notifyWatchersForStatusChanges(changes, storeInfoMap);
|
||||||
|
}
|
||||||
return { stores, statusMeta };
|
return { stores, statusMeta };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,16 @@ function writeStoreWatch(profileId, entries = []) {
|
|||||||
return sanitized;
|
return sanitized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function listWatcherProfiles() {
|
||||||
|
ensureDir();
|
||||||
|
return fs
|
||||||
|
.readdirSync(STORE_WATCH_DIR)
|
||||||
|
.filter((file) => file.endsWith('-store-watch.json'))
|
||||||
|
.map((file) => file.replace(/-store-watch\.json$/, ''));
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
readStoreWatch,
|
readStoreWatch,
|
||||||
writeStoreWatch
|
writeStoreWatch,
|
||||||
|
listWatcherProfiles
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user