prevent re-auto-activation after manual station release

Introduce a manual-release reservation hold on station state so auto slot activation is suppressed for the current reserved user until the reservation window ends or is removed. Clear the hold on activation/reservation updates and backfill missing hold fields for existing station state files.
This commit is contained in:
2026-03-16 12:08:38 +01:00
parent 7189a98bab
commit ebeb18bb99

View File

@@ -2465,8 +2465,32 @@ async function autoActivateReservationSlotIfNeededUnlocked(options = {}) {
await persistStationReservationsIfChanged(reservations, { lastAction: "reserve-sync" }); await persistStationReservationsIfChanged(reservations, { lastAction: "reserve-sync" });
const activeEntry = findActiveReservationEntry(reservations, nowMs); const activeEntry = findActiveReservationEntry(reservations, nowMs);
if (!activeEntry) { if (!activeEntry) {
if (runtime.station.manualReleaseReservationUserId || runtime.station.manualReleaseReservationUntil) {
runtime.station.manualReleaseReservationUserId = null;
runtime.station.manualReleaseReservationUntil = null;
runtime.station.updatedAt = new Date().toISOString();
await writeJson(files.station, runtime.station);
}
return false; return false;
} }
const holdUserId = String(runtime.station.manualReleaseReservationUserId || "");
const holdUntilMs = parseIsoMs(runtime.station.manualReleaseReservationUntil);
if (
holdUserId
&& Number.isFinite(holdUntilMs)
&& holdUntilMs > nowMs
&& String(activeEntry.userId || "") === holdUserId
) {
return false;
}
if (holdUserId || runtime.station.manualReleaseReservationUntil) {
runtime.station.manualReleaseReservationUserId = null;
runtime.station.manualReleaseReservationUntil = null;
runtime.station.updatedAt = new Date().toISOString();
await writeJson(files.station, runtime.station);
}
if (excludeUserId && String(activeEntry.userId || "") === excludeUserId) { if (excludeUserId && String(activeEntry.userId || "") === excludeUserId) {
return false; return false;
} }
@@ -2491,6 +2515,8 @@ async function autoActivateReservationSlotIfNeededUnlocked(options = {}) {
startedAt: activeEntry.from, startedAt: activeEntry.from,
endsAt: activeEntry.to, endsAt: activeEntry.to,
reservations, reservations,
manualReleaseReservationUserId: null,
manualReleaseReservationUntil: null,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
lastAction: "activate-auto-slot" lastAction: "activate-auto-slot"
}; };
@@ -2520,6 +2546,14 @@ async function reconcileStationLeaseOnStartup() {
runtime.station.endsAt = null; runtime.station.endsAt = null;
changed = true; changed = true;
} }
if (!Object.prototype.hasOwnProperty.call(runtime.station, "manualReleaseReservationUserId")) {
runtime.station.manualReleaseReservationUserId = null;
changed = true;
}
if (!Object.prototype.hasOwnProperty.call(runtime.station, "manualReleaseReservationUntil")) {
runtime.station.manualReleaseReservationUntil = null;
changed = true;
}
if (runtime.station.isInUse && runtime.station.startedAt && !runtime.station.endsAt) { if (runtime.station.isInUse && runtime.station.startedAt && !runtime.station.endsAt) {
runtime.station.endsAt = computeLeaseEndIso(runtime.station.startedAt); runtime.station.endsAt = computeLeaseEndIso(runtime.station.startedAt);
changed = true; changed = true;
@@ -2766,6 +2800,8 @@ async function runActivationJob(job, user) {
startedAt, startedAt,
endsAt, endsAt,
reservations: reservationsAfterActivate, reservations: reservationsAfterActivate,
manualReleaseReservationUserId: null,
manualReleaseReservationUntil: null,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
lastAction: "activate" lastAction: "activate"
}; };
@@ -2870,6 +2906,12 @@ async function handleStationRelease(res, user) {
startedAt: null, startedAt: null,
endsAt: null, endsAt: null,
reservations: lockInfo.reservations, reservations: lockInfo.reservations,
manualReleaseReservationUserId: lockInfo.activeEntry && String(lockInfo.activeEntry.userId || "") === String(user && user.id ? user.id : "")
? String(user.id || "")
: null,
manualReleaseReservationUntil: lockInfo.activeEntry && String(lockInfo.activeEntry.userId || "") === String(user && user.id ? user.id : "")
? String(lockInfo.activeEntry.to || "")
: null,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
lastAction: "deactivate" lastAction: "deactivate"
}; };
@@ -2961,6 +3003,12 @@ async function handleDeleteOwnReservation(res, user) {
startedAt: null, startedAt: null,
endsAt: null, endsAt: null,
reservations: removed.reservations, reservations: removed.reservations,
manualReleaseReservationUserId: String(runtime.station.manualReleaseReservationUserId || "") === String(user && user.id ? user.id : "")
? null
: (runtime.station.manualReleaseReservationUserId || null),
manualReleaseReservationUntil: String(runtime.station.manualReleaseReservationUserId || "") === String(user && user.id ? user.id : "")
? null
: (runtime.station.manualReleaseReservationUntil || null),
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
lastAction: "reserve-remove-current" lastAction: "reserve-remove-current"
}; };
@@ -2978,6 +3026,12 @@ async function handleDeleteOwnReservation(res, user) {
runtime.station = { runtime.station = {
...runtime.station, ...runtime.station,
reservations: removed.reservations, reservations: removed.reservations,
manualReleaseReservationUserId: String(runtime.station.manualReleaseReservationUserId || "") === String(user && user.id ? user.id : "")
? null
: (runtime.station.manualReleaseReservationUserId || null),
manualReleaseReservationUntil: String(runtime.station.manualReleaseReservationUserId || "") === String(user && user.id ? user.id : "")
? null
: (runtime.station.manualReleaseReservationUntil || null),
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
lastAction: "reserve-remove" lastAction: "reserve-remove"
}; };
@@ -6957,6 +7011,8 @@ function buildDefaultStationState() {
startedAt: null, startedAt: null,
endsAt: null, endsAt: null,
reservations: [], reservations: [],
manualReleaseReservationUserId: null,
manualReleaseReservationUntil: null,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
lastAction: "init" lastAction: "init"
}; };