Files
Pickup-Config/services/sessionRefresh.js
Meik bd41e8c079 chore: sync running docker state
Commit the app code currently running in Docker, including scheduler, store status, journal reminder, maintenance mode, and Foodsharing API check updates. Remove generated runtime logs from version control and ignore local runtime captures.
2026-06-14 21:21:07 +02:00

142 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const foodsharingClient = require('./foodsharingClient');
const sessionStore = require('./sessionStore');
const notificationService = require('./notificationService');
const ADMIN_SESSION_ERROR_COOLDOWN_MS = 6 * 60 * 60 * 1000;
const adminSessionErrorCooldowns = new Map();
function shouldNotifyAdminSessionError(key) {
if (!key) {
return false;
}
const now = Date.now();
const lastNotified = adminSessionErrorCooldowns.get(key) || 0;
if (now - lastNotified < ADMIN_SESSION_ERROR_COOLDOWN_MS) {
return false;
}
adminSessionErrorCooldowns.set(key, now);
return true;
}
function isUnauthorizedError(error) {
const status = error?.response?.status;
if (status === 401) {
return true;
}
if (status !== 403) {
return false;
}
const data = error?.response?.data;
const message =
typeof data === 'string'
? data
: typeof data?.message === 'string'
? data.message
: '';
return message.toLowerCase().includes('not logged in');
}
function isCsrfError(error) {
const status = error?.response?.status;
if (status !== 400) {
return false;
}
const data = error?.response?.data;
const message =
typeof data === 'string'
? data
: typeof data?.message === 'string'
? data.message
: '';
return message.toLowerCase().includes('csrf');
}
async function refreshSession(session, { label } = {}) {
if (!session?.credentials?.email || !session?.credentials?.password) {
console.warn(
`[SESSION] Session ${session?.id || 'unbekannt'} kann nicht erneuert werden keine Zugangsdaten gespeichert.`
);
return false;
}
try {
const refreshed = await foodsharingClient.login(
session.credentials.email,
session.credentials.password
);
sessionStore.update(session.id, {
cookieHeader: refreshed.cookieHeader,
csrfToken: refreshed.csrfToken,
profile: {
...session.profile,
...refreshed.profile
}
});
console.log(
`[SESSION] Session ${session.id} wurde erfolgreich erneuert${label ? ` (${label})` : ''}.`
);
return true;
} catch (error) {
console.error(
`[SESSION] Session ${session?.id || 'unbekannt'} konnte nicht erneuert werden${label ? ` (${label})` : ''}:`,
error.message
);
const profileId = session?.profile?.id ? String(session.profile.id) : null;
const notifyKey = profileId || session?.id || null;
if (shouldNotifyAdminSessionError(notifyKey)) {
try {
await notificationService.sendAdminSessionErrorNotification({
profileId,
profileEmail: session?.credentials?.email || session?.profile?.email,
profileName: session?.profile?.name,
sessionId: session?.id,
error: error?.message,
label
});
} catch (notifyError) {
console.error('[NOTIFY] Admin-Session-Fehler konnte nicht gemeldet werden:', notifyError.message);
}
}
return false;
}
}
async function ensureSession(session) {
if (!session?.profile?.id) {
return false;
}
if (!session.cookieHeader) {
return refreshSession(session, { label: 'missing-cookie' });
}
return true;
}
async function withSessionRetry(session, action, { label } = {}) {
if (!session) {
throw new Error('Session fehlt');
}
if (!session.cookieHeader && session.credentials) {
const refreshed = await refreshSession(session, { label });
if (!refreshed) {
throw new Error('Session konnte nicht erneuert werden');
}
}
try {
return await action();
} catch (error) {
if (!isUnauthorizedError(error) && !isCsrfError(error)) {
throw error;
}
const refreshed = await refreshSession(session, { label });
if (!refreshed) {
throw error;
}
return action();
}
}
module.exports = {
ensureSession,
refreshSession,
withSessionRetry
};