button zum prüfen
This commit is contained in:
40
server.js
40
server.js
@@ -13,7 +13,11 @@ const { readNotificationSettings, writeNotificationSettings } = require('./servi
|
||||
const notificationService = require('./services/notificationService');
|
||||
const { readStoreWatch, writeStoreWatch, listWatcherProfiles } = require('./services/storeWatchStore');
|
||||
const { readPreferences, writePreferences, sanitizeLocation } = require('./services/userPreferencesStore');
|
||||
const { readStoreStatus, writeStoreStatus } = require('./services/storeStatusStore');
|
||||
const {
|
||||
getStoreStatus: getCachedStoreStatusEntry,
|
||||
setStoreStatus: setCachedStoreStatusEntry,
|
||||
persistStoreStatusCache
|
||||
} = require('./services/storeStatusCache');
|
||||
|
||||
const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1000;
|
||||
const adminEmail = (process.env.ADMIN_EMAIL || '').toLowerCase();
|
||||
@@ -26,7 +30,6 @@ const cachedStoreSnapshots = new Map();
|
||||
const regionStoreCache = new Map();
|
||||
const REGION_STORE_CACHE_MS = 15 * 60 * 1000;
|
||||
const STORE_STATUS_MAX_AGE_MS = 60 * 24 * 60 * 60 * 1000;
|
||||
const storeStatusCache = new Map();
|
||||
const storeLocationIndex = new Map();
|
||||
let storeLocationIndexUpdatedAt = 0;
|
||||
const STORE_LOCATION_INDEX_TTL_MS = 12 * 60 * 60 * 1000;
|
||||
@@ -56,33 +59,6 @@ function haversineDistanceKm(lat1, lon1, lat2, lon2) {
|
||||
return R * c;
|
||||
}
|
||||
|
||||
(function bootstrapStoreStatusCache() {
|
||||
try {
|
||||
const cached = readStoreStatus();
|
||||
Object.entries(cached || {}).forEach(([storeId, entry]) => {
|
||||
if (entry && typeof entry === 'object') {
|
||||
storeStatusCache.set(String(storeId), {
|
||||
teamSearchStatus:
|
||||
entry.teamSearchStatus === null || entry.teamSearchStatus === undefined
|
||||
? null
|
||||
: Number(entry.teamSearchStatus),
|
||||
fetchedAt: Number(entry.fetchedAt) || 0
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[STORE-STATUS] Bootstrap fehlgeschlagen:', error.message);
|
||||
}
|
||||
})();
|
||||
|
||||
function persistStoreStatusCache() {
|
||||
const payload = {};
|
||||
storeStatusCache.forEach((value, key) => {
|
||||
payload[key] = value;
|
||||
});
|
||||
writeStoreStatus(payload);
|
||||
}
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json({ limit: '1mb' }));
|
||||
app.use(express.static(path.join(__dirname, 'build')));
|
||||
@@ -170,7 +146,7 @@ function setCachedRegionStores(regionId, payload) {
|
||||
}
|
||||
|
||||
function getCachedStoreStatus(storeId) {
|
||||
return storeStatusCache.get(String(storeId)) || null;
|
||||
return getCachedStoreStatusEntry(storeId);
|
||||
}
|
||||
|
||||
function ingestStoreLocations(stores = []) {
|
||||
@@ -318,7 +294,7 @@ async function refreshStoreStatus(
|
||||
const changes = [];
|
||||
for (const id of storeIds) {
|
||||
const storeId = String(id);
|
||||
const entry = storeStatusCache.get(storeId);
|
||||
const entry = getCachedStoreStatus(storeId);
|
||||
const ageOk = entry && now - (entry.fetchedAt || 0) <= STORE_STATUS_MAX_AGE_MS;
|
||||
if (!force && ageOk) {
|
||||
continue;
|
||||
@@ -328,7 +304,7 @@ async function refreshStoreStatus(
|
||||
const status = Number(details?.teamSearchStatus);
|
||||
const normalized = Number.isFinite(status) ? status : null;
|
||||
const previous = entry ? entry.teamSearchStatus : null;
|
||||
storeStatusCache.set(storeId, {
|
||||
setCachedStoreStatusEntry(storeId, {
|
||||
teamSearchStatus: normalized,
|
||||
fetchedAt: now
|
||||
});
|
||||
|
||||
@@ -137,10 +137,10 @@ async function sendStoreWatchSummaryNotification({ profileId, entries = [], trig
|
||||
.join('\n');
|
||||
const prefix =
|
||||
triggeredBy === 'manual'
|
||||
? 'Manuell angestoßene Store-Watch-Prüfung abgeschlossen:'
|
||||
? 'Manuell angestoßene Store-Watch-Aktualisierung abgeschlossen:'
|
||||
: 'Store-Watch-Prüfung abgeschlossen:';
|
||||
const title =
|
||||
triggeredBy === 'manual' ? 'Ad-hoc Store-Watch-Prüfung' : 'Store-Watch-Prüfung';
|
||||
triggeredBy === 'manual' ? 'Store-Watch-Aktualisierung' : 'Store-Watch-Prüfung';
|
||||
const message = `${prefix}\n${lines}`;
|
||||
await notifyChannels(profileId, {
|
||||
title,
|
||||
|
||||
@@ -5,6 +5,7 @@ const { DEFAULT_SETTINGS } = require('./adminConfig');
|
||||
const notificationService = require('./notificationService');
|
||||
const { readConfig, writeConfig } = require('./configStore');
|
||||
const { readStoreWatch, writeStoreWatch } = require('./storeWatchStore');
|
||||
const { setStoreStatus, persistStoreStatusCache } = require('./storeStatusCache');
|
||||
|
||||
function wait(ms) {
|
||||
if (!ms || ms <= 0) {
|
||||
@@ -428,6 +429,7 @@ async function checkWatchedStores(sessionId, settings = DEFAULT_SETTINGS, option
|
||||
|
||||
const perRequestDelay = Math.max(0, Number(settings?.storeWatchRequestDelayMs) || 0);
|
||||
let changed = false;
|
||||
let statusCacheUpdated = false;
|
||||
const summary = [];
|
||||
for (let index = 0; index < watchers.length; index += 1) {
|
||||
const watcher = watchers[index];
|
||||
@@ -449,6 +451,8 @@ async function checkWatchedStores(sessionId, settings = DEFAULT_SETTINGS, option
|
||||
}
|
||||
watcher.lastStatusCheckAt = checkedAt;
|
||||
changed = true;
|
||||
setStoreStatus(watcher.storeId, { teamSearchStatus: status, fetchedAt: checkedAt });
|
||||
statusCacheUpdated = true;
|
||||
summary.push({
|
||||
storeId: watcher.storeId,
|
||||
storeName: watcher.storeName,
|
||||
@@ -477,6 +481,9 @@ async function checkWatchedStores(sessionId, settings = DEFAULT_SETTINGS, option
|
||||
if (changed) {
|
||||
writeStoreWatch(session.profile.id, watchers);
|
||||
}
|
||||
if (statusCacheUpdated) {
|
||||
persistStoreStatusCache();
|
||||
}
|
||||
if (options.sendSummary && summary.length > 0) {
|
||||
try {
|
||||
await notificationService.sendStoreWatchSummaryNotification({
|
||||
|
||||
66
services/storeStatusCache.js
Normal file
66
services/storeStatusCache.js
Normal file
@@ -0,0 +1,66 @@
|
||||
const { readStoreStatus, writeStoreStatus } = require('./storeStatusStore');
|
||||
|
||||
const storeStatusCache = new Map();
|
||||
|
||||
function normalizeStatusEntry(entry = {}) {
|
||||
const status = Number(entry.teamSearchStatus);
|
||||
const fetchedAt = Number(entry.fetchedAt) || 0;
|
||||
return {
|
||||
teamSearchStatus: Number.isFinite(status) ? status : null,
|
||||
fetchedAt
|
||||
};
|
||||
}
|
||||
|
||||
(function bootstrapStoreStatusCache() {
|
||||
try {
|
||||
const cached = readStoreStatus();
|
||||
Object.entries(cached || {}).forEach(([storeId, entry]) => {
|
||||
if (!storeId) {
|
||||
return;
|
||||
}
|
||||
storeStatusCache.set(String(storeId), normalizeStatusEntry(entry));
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[STORE-STATUS] Bootstrap fehlgeschlagen:', error.message);
|
||||
}
|
||||
})();
|
||||
|
||||
function getStoreStatus(storeId) {
|
||||
if (!storeId) {
|
||||
return null;
|
||||
}
|
||||
return storeStatusCache.get(String(storeId)) || null;
|
||||
}
|
||||
|
||||
function setStoreStatus(storeId, data = {}) {
|
||||
if (!storeId) {
|
||||
return null;
|
||||
}
|
||||
const normalized = normalizeStatusEntry(data);
|
||||
storeStatusCache.set(String(storeId), normalized);
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function bulkSetStoreStatus(entries = []) {
|
||||
entries.forEach((entry) => {
|
||||
if (!entry || !entry.storeId) {
|
||||
return;
|
||||
}
|
||||
setStoreStatus(entry.storeId, entry);
|
||||
});
|
||||
}
|
||||
|
||||
function persistStoreStatusCache() {
|
||||
const payload = {};
|
||||
storeStatusCache.forEach((value, key) => {
|
||||
payload[key] = value;
|
||||
});
|
||||
writeStoreStatus(payload);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getStoreStatus,
|
||||
setStoreStatus,
|
||||
bulkSetStoreStatus,
|
||||
persistStoreStatusCache
|
||||
};
|
||||
@@ -986,11 +986,11 @@ const StoreWatchPage = ({
|
||||
checkedAt: Date.now(),
|
||||
stores: summary
|
||||
});
|
||||
setStatus('Ad-hoc-Prüfung abgeschlossen. Zusammenfassung versendet.');
|
||||
setStatus('Aktualisierung abgeschlossen. Zusammenfassung versendet.');
|
||||
setTimeout(() => setStatus(''), 4000);
|
||||
await loadSubscriptions();
|
||||
} catch (err) {
|
||||
setError(`Ad-hoc-Prüfung fehlgeschlagen: ${err.message}`);
|
||||
setError(`Aktualisierung fehlgeschlagen: ${err.message}`);
|
||||
} finally {
|
||||
setAdhocChecking(false);
|
||||
}
|
||||
@@ -1019,7 +1019,7 @@ const StoreWatchPage = ({
|
||||
onClick={handleAdhocWatchCheck}
|
||||
disabled={adhocChecking || subscriptionsLoading || watchList.length === 0}
|
||||
>
|
||||
{adhocChecking ? 'Prüfe...' : 'Ad-hoc prüfen'}
|
||||
{adhocChecking ? 'Aktualisiere...' : 'Aktualisieren'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -1133,7 +1133,7 @@ const StoreWatchPage = ({
|
||||
{lastAdhocCheck?.stores?.length > 0 && (
|
||||
<div className="mt-4 rounded-lg border border-blue-100 bg-blue-50 p-3 text-sm text-blue-900">
|
||||
<p className="text-xs font-semibold text-blue-800">
|
||||
Letzte Ad-hoc-Prüfung:{' '}
|
||||
Letzte Aktualisierung:{' '}
|
||||
{new Date(lastAdhocCheck.checkedAt).toLocaleString('de-DE')}
|
||||
</p>
|
||||
<ul className="mt-2 space-y-1 text-xs">
|
||||
|
||||
Reference in New Issue
Block a user