@zeniai/client-epic-state 5.0.12 → 5.0.13-beta1ND
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/coreEpics.js +2 -1
- package/lib/entity/tenant/SessionManager.d.ts +56 -0
- package/lib/entity/tenant/SessionManager.js +308 -0
- package/lib/entity/tenant/clearAllEpic.d.ts +2 -1
- package/lib/entity/tenant/clearAllEpic.js +2 -0
- package/lib/entity/tenant/epic/sessionHeartbeatEpic.d.ts +16 -0
- package/lib/entity/tenant/epic/sessionHeartbeatEpic.js +25 -0
- package/lib/entity/tenant/sessionTypes.d.ts +26 -0
- package/lib/entity/tenant/sessionTypes.js +12 -0
- package/lib/entity/tenant/tenantReducer.d.ts +5 -1
- package/lib/entity/tenant/tenantReducer.js +23 -2
- package/lib/entity/tenant/tenantState.d.ts +1 -0
- package/lib/epic.d.ts +7 -6
- package/lib/epic.js +7 -6
- package/lib/esm/coreEpics.js +2 -1
- package/lib/esm/entity/tenant/SessionManager.js +304 -0
- package/lib/esm/entity/tenant/clearAllEpic.js +2 -0
- package/lib/esm/entity/tenant/epic/sessionHeartbeatEpic.js +21 -0
- package/lib/esm/entity/tenant/sessionTypes.js +9 -0
- package/lib/esm/entity/tenant/tenantReducer.js +21 -1
- package/lib/esm/epic.js +7 -6
- package/lib/esm/index.js +5 -2
- package/lib/esm/view/spendManagement/zeniAccounts/zeniAccountList/zeniAccountListSelector.js +10 -3
- package/lib/index.d.ts +7 -4
- package/lib/index.js +36 -30
- package/lib/tsconfig.typecheck.tsbuildinfo +1 -1
- package/lib/view/spendManagement/zeniAccounts/zeniAccountList/zeniAccountListSelector.d.ts +9 -1
- package/lib/view/spendManagement/zeniAccounts/zeniAccountList/zeniAccountListSelector.js +10 -3
- package/package.json +1 -1
package/lib/coreEpics.js
CHANGED
|
@@ -24,6 +24,7 @@ const resendVerifyDeviceOTPEpic_1 = require("./entity/tenant/epic/resendVerifyDe
|
|
|
24
24
|
const saveExternalConnectionEpic_1 = require("./entity/tenant/epic/saveExternalConnectionEpic");
|
|
25
25
|
const sendEmailMagicLinkToUserEpic_1 = require("./entity/tenant/epic/sendEmailMagicLinkToUserEpic");
|
|
26
26
|
const signInUserEpic_1 = require("./entity/tenant/epic/signInUserEpic");
|
|
27
|
+
const sessionHeartbeatEpic_1 = require("./entity/tenant/epic/sessionHeartbeatEpic");
|
|
27
28
|
const signOutUserEpic_1 = require("./entity/tenant/epic/signOutUserEpic");
|
|
28
29
|
const verifyDeviceWithTwoFAEpic_1 = require("./entity/tenant/epic/verifyDeviceWithTwoFAEpic");
|
|
29
30
|
// ── Toast Notification Epic ──────────────────────────────────────────
|
|
@@ -48,7 +49,7 @@ const verifyOtpEpic_1 = require("./view/twoFactorAuthentication/verifyOtpEpic");
|
|
|
48
49
|
// RootActionType is only available in the dynamically imported epic.ts.
|
|
49
50
|
const coreRootEpic = (0, redux_observable_1.combineEpics)(
|
|
50
51
|
// Auth & tenant
|
|
51
|
-
clearAllEpic_1.clearAllEpic, doMagicLinkSignInEpic_1.doMagicLinkSignInEpic, signInUserEpic_1.doSignInEpic, signOutUserEpic_1.doSignOutEpic, fetchActiveTenantEpic_1.fetchActiveTenantEpic, fetchAllTenantsEpic_1.fetchAllTenantsEpic, fetchExcludedResourcesEpic_1.fetchExcludedResourcesEpic, fetchExternalConnectionsEpic_1.fetchExternalConnectionsEpic, fetchSubscriptionSummaryForTenantEpic_1.fetchSubscriptionSummaryForTenantEpic, resendVerifyDeviceOTPEpic_1.resendVerifyDeviceOTPEpic, saveExternalConnectionEpic_1.saveExternalConnectionEpic, sendEmailMagicLinkToUserEpic_1.sendEmailMagicLinkToUserEpic, verifyDeviceWithTwoFAEpic_1.verifyDeviceWithTwoFAEpic,
|
|
52
|
+
clearAllEpic_1.clearAllEpic, doMagicLinkSignInEpic_1.doMagicLinkSignInEpic, signInUserEpic_1.doSignInEpic, signOutUserEpic_1.doSignOutEpic, sessionHeartbeatEpic_1.sessionHeartbeatEpic, fetchActiveTenantEpic_1.fetchActiveTenantEpic, fetchAllTenantsEpic_1.fetchAllTenantsEpic, fetchExcludedResourcesEpic_1.fetchExcludedResourcesEpic, fetchExternalConnectionsEpic_1.fetchExternalConnectionsEpic, fetchSubscriptionSummaryForTenantEpic_1.fetchSubscriptionSummaryForTenantEpic, resendVerifyDeviceOTPEpic_1.resendVerifyDeviceOTPEpic, saveExternalConnectionEpic_1.saveExternalConnectionEpic, sendEmailMagicLinkToUserEpic_1.sendEmailMagicLinkToUserEpic, verifyDeviceWithTwoFAEpic_1.verifyDeviceWithTwoFAEpic,
|
|
52
53
|
// Toast
|
|
53
54
|
pushToastNotificationEpic_1.pushToastNotificationEpic,
|
|
54
55
|
// Collaboration
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionManager — Central session lifecycle manager.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* 1. Track user activity (mousemove, keydown, click, scroll, touch)
|
|
6
|
+
* 2. Send heartbeat at configurable intervals — only when user has been active
|
|
7
|
+
* 3. Show warning popup after idle timeout
|
|
8
|
+
* 4. Auto-logout after warning countdown expires
|
|
9
|
+
* 5. Sync activity across same-origin tabs via BroadcastChannel so one
|
|
10
|
+
* active tab keeps other tabs' sessions alive (no cascading logout).
|
|
11
|
+
*/
|
|
12
|
+
import { SessionCallbacks, SessionConfig } from './sessionTypes';
|
|
13
|
+
export declare class SessionManager {
|
|
14
|
+
private config;
|
|
15
|
+
private callbacks;
|
|
16
|
+
/** Timestamps and timers */
|
|
17
|
+
private lastActivityTime;
|
|
18
|
+
/** Timestamp of the last heartbeat fire (initial/periodic/continue/activity). */
|
|
19
|
+
private lastHeartbeatTime;
|
|
20
|
+
/** Set to true whenever user (or any same-origin tab) shows activity. */
|
|
21
|
+
private hadActivitySinceLastHeartbeat;
|
|
22
|
+
private idleCheckInterval;
|
|
23
|
+
private heartbeatInterval;
|
|
24
|
+
private countdownInterval;
|
|
25
|
+
private activityDebounceTimer;
|
|
26
|
+
/** State */
|
|
27
|
+
private running;
|
|
28
|
+
private warningActive;
|
|
29
|
+
private secondsRemaining;
|
|
30
|
+
/** Wall-clock timestamp when the warning countdown began (for background-tab accuracy). */
|
|
31
|
+
private warningStartTime;
|
|
32
|
+
/** Cross-tab activity sync */
|
|
33
|
+
private broadcastChannel;
|
|
34
|
+
/** Bound handlers for event listener cleanup. */
|
|
35
|
+
private boundOnActivity;
|
|
36
|
+
private boundOnVisibilityChange;
|
|
37
|
+
private boundOnBroadcastMessage;
|
|
38
|
+
start(config: Partial<SessionConfig> | undefined, callbacks: SessionCallbacks): void;
|
|
39
|
+
stop(): void;
|
|
40
|
+
continueSession(): void;
|
|
41
|
+
isWarningActive(): boolean;
|
|
42
|
+
getSecondsRemaining(): number;
|
|
43
|
+
private onActivity;
|
|
44
|
+
private onVisibilityChange;
|
|
45
|
+
/** Single source of truth for marking activity locally. */
|
|
46
|
+
private registerActivity;
|
|
47
|
+
private checkIdle;
|
|
48
|
+
private startWarning;
|
|
49
|
+
private startHeartbeat;
|
|
50
|
+
private fireHeartbeat;
|
|
51
|
+
private setupBroadcastChannel;
|
|
52
|
+
private teardownBroadcastChannel;
|
|
53
|
+
private broadcast;
|
|
54
|
+
private onBroadcastMessage;
|
|
55
|
+
private clearTimer;
|
|
56
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SessionManager — Central session lifecycle manager.
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* 1. Track user activity (mousemove, keydown, click, scroll, touch)
|
|
7
|
+
* 2. Send heartbeat at configurable intervals — only when user has been active
|
|
8
|
+
* 3. Show warning popup after idle timeout
|
|
9
|
+
* 4. Auto-logout after warning countdown expires
|
|
10
|
+
* 5. Sync activity across same-origin tabs via BroadcastChannel so one
|
|
11
|
+
* active tab keeps other tabs' sessions alive (no cascading logout).
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.SessionManager = void 0;
|
|
15
|
+
const sessionTypes_1 = require("./sessionTypes");
|
|
16
|
+
/** Events that indicate user activity. */
|
|
17
|
+
const ACTIVITY_EVENTS = [
|
|
18
|
+
'mousemove',
|
|
19
|
+
'mousedown',
|
|
20
|
+
'keydown',
|
|
21
|
+
'scroll',
|
|
22
|
+
'touchstart',
|
|
23
|
+
'click',
|
|
24
|
+
];
|
|
25
|
+
/** Debounce interval for activity events (ms). */
|
|
26
|
+
const ACTIVITY_DEBOUNCE_MS = 1000;
|
|
27
|
+
/** Name of the BroadcastChannel used for cross-tab activity sync. */
|
|
28
|
+
const BROADCAST_CHANNEL_NAME = 'zeni-session-activity';
|
|
29
|
+
class SessionManager {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.config = sessionTypes_1.DEFAULT_SESSION_CONFIG;
|
|
32
|
+
this.callbacks = null;
|
|
33
|
+
/** Timestamps and timers */
|
|
34
|
+
this.lastActivityTime = 0;
|
|
35
|
+
/** Timestamp of the last heartbeat fire (initial/periodic/continue/activity). */
|
|
36
|
+
this.lastHeartbeatTime = 0;
|
|
37
|
+
/** Set to true whenever user (or any same-origin tab) shows activity. */
|
|
38
|
+
this.hadActivitySinceLastHeartbeat = false;
|
|
39
|
+
this.idleCheckInterval = null;
|
|
40
|
+
this.heartbeatInterval = null;
|
|
41
|
+
this.countdownInterval = null;
|
|
42
|
+
this.activityDebounceTimer = null;
|
|
43
|
+
/** State */
|
|
44
|
+
this.running = false;
|
|
45
|
+
this.warningActive = false;
|
|
46
|
+
this.secondsRemaining = 0;
|
|
47
|
+
/** Wall-clock timestamp when the warning countdown began (for background-tab accuracy). */
|
|
48
|
+
this.warningStartTime = 0;
|
|
49
|
+
/** Cross-tab activity sync */
|
|
50
|
+
this.broadcastChannel = null;
|
|
51
|
+
/** Bound handlers for event listener cleanup. */
|
|
52
|
+
this.boundOnActivity = this.onActivity.bind(this);
|
|
53
|
+
this.boundOnVisibilityChange = this.onVisibilityChange.bind(this);
|
|
54
|
+
this.boundOnBroadcastMessage = this.onBroadcastMessage.bind(this);
|
|
55
|
+
}
|
|
56
|
+
// ─── Public API ────────────────────────────────────────────
|
|
57
|
+
start(config = {}, callbacks) {
|
|
58
|
+
if (this.running) {
|
|
59
|
+
this.stop();
|
|
60
|
+
}
|
|
61
|
+
this.config = { ...sessionTypes_1.DEFAULT_SESSION_CONFIG, ...config };
|
|
62
|
+
this.callbacks = callbacks;
|
|
63
|
+
this.running = true;
|
|
64
|
+
this.warningActive = false;
|
|
65
|
+
this.lastActivityTime = Date.now();
|
|
66
|
+
this.lastHeartbeatTime = 0;
|
|
67
|
+
this.hadActivitySinceLastHeartbeat = true; // initial heartbeat is expected
|
|
68
|
+
const heartbeatEnabled = this.config.heartbeatIntervalMinutes > 0;
|
|
69
|
+
const idleTrackingEnabled = this.config.isAutoLogoutEnabled;
|
|
70
|
+
if (!heartbeatEnabled && !idleTrackingEnabled) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Activity listeners — needed for both heartbeat activity-gate and idle
|
|
74
|
+
// detection. BroadcastChannel keeps multiple tabs in sync.
|
|
75
|
+
ACTIVITY_EVENTS.forEach((event) => {
|
|
76
|
+
document.addEventListener(event, this.boundOnActivity, {
|
|
77
|
+
capture: true,
|
|
78
|
+
passive: true,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
this.setupBroadcastChannel();
|
|
82
|
+
if (heartbeatEnabled) {
|
|
83
|
+
this.startHeartbeat();
|
|
84
|
+
// Initial heartbeat on sign-in
|
|
85
|
+
this.fireHeartbeat();
|
|
86
|
+
}
|
|
87
|
+
if (idleTrackingEnabled) {
|
|
88
|
+
document.addEventListener('visibilitychange', this.boundOnVisibilityChange);
|
|
89
|
+
this.idleCheckInterval = setInterval(() => this.checkIdle(), 1000);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
stop() {
|
|
93
|
+
this.running = false;
|
|
94
|
+
this.warningActive = false;
|
|
95
|
+
ACTIVITY_EVENTS.forEach((event) => {
|
|
96
|
+
document.removeEventListener(event, this.boundOnActivity, {
|
|
97
|
+
capture: true,
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
document.removeEventListener('visibilitychange', this.boundOnVisibilityChange);
|
|
101
|
+
this.teardownBroadcastChannel();
|
|
102
|
+
this.clearTimer('idleCheckInterval');
|
|
103
|
+
this.clearTimer('heartbeatInterval');
|
|
104
|
+
this.clearTimer('countdownInterval');
|
|
105
|
+
this.clearTimer('activityDebounceTimer');
|
|
106
|
+
}
|
|
107
|
+
continueSession() {
|
|
108
|
+
if (!this.running) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
this.warningActive = false;
|
|
112
|
+
this.secondsRemaining = 0;
|
|
113
|
+
this.lastActivityTime = Date.now();
|
|
114
|
+
this.hadActivitySinceLastHeartbeat = true;
|
|
115
|
+
this.clearTimer('countdownInterval');
|
|
116
|
+
// Fire heartbeat immediately — user explicitly extended the session
|
|
117
|
+
this.fireHeartbeat();
|
|
118
|
+
this.callbacks?.onSessionExtended();
|
|
119
|
+
this.broadcast({ type: 'activity', timestamp: Date.now() });
|
|
120
|
+
this.startHeartbeat();
|
|
121
|
+
}
|
|
122
|
+
isWarningActive() {
|
|
123
|
+
return this.warningActive;
|
|
124
|
+
}
|
|
125
|
+
getSecondsRemaining() {
|
|
126
|
+
return this.secondsRemaining;
|
|
127
|
+
}
|
|
128
|
+
// ─── Private ───────────────────────────────────────────────
|
|
129
|
+
onActivity() {
|
|
130
|
+
if (!this.running || this.warningActive) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// Debounce: only update lastActivityTime at most once per second
|
|
134
|
+
if (this.activityDebounceTimer != null) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const now = Date.now();
|
|
138
|
+
this.registerActivity(now);
|
|
139
|
+
this.broadcast({ type: 'activity', timestamp: now });
|
|
140
|
+
// If heartbeat is enabled and the interval has elapsed since the last
|
|
141
|
+
// heartbeat, fire one immediately and restart the timer. This covers
|
|
142
|
+
// the "user active again after >interval idle" case without waiting
|
|
143
|
+
// for the next scheduled tick.
|
|
144
|
+
if (this.config.heartbeatIntervalMinutes > 0) {
|
|
145
|
+
const intervalMs = this.config.heartbeatIntervalMinutes * 60 * 1000;
|
|
146
|
+
const timeSinceLastHeartbeat = now - this.lastHeartbeatTime;
|
|
147
|
+
if (timeSinceLastHeartbeat >= intervalMs) {
|
|
148
|
+
this.fireHeartbeat();
|
|
149
|
+
this.startHeartbeat();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
this.activityDebounceTimer = setTimeout(() => {
|
|
153
|
+
this.activityDebounceTimer = null;
|
|
154
|
+
}, ACTIVITY_DEBOUNCE_MS);
|
|
155
|
+
}
|
|
156
|
+
onVisibilityChange() {
|
|
157
|
+
if (!this.running) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (document.visibilityState === 'visible') {
|
|
161
|
+
if (this.warningActive) {
|
|
162
|
+
// Tab was hidden while countdown was running — check if it already expired.
|
|
163
|
+
const elapsed = Math.floor((Date.now() - this.warningStartTime) / 1000);
|
|
164
|
+
if (elapsed >= this.config.warningDurationSeconds) {
|
|
165
|
+
this.secondsRemaining = 0;
|
|
166
|
+
this.stop();
|
|
167
|
+
this.callbacks?.onAutoLogout();
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
// Industry-standard idle detection: resetting on tab focus is expected.
|
|
172
|
+
this.registerActivity(Date.now());
|
|
173
|
+
this.checkIdle();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/** Single source of truth for marking activity locally. */
|
|
177
|
+
registerActivity(timestamp) {
|
|
178
|
+
this.lastActivityTime = Math.max(this.lastActivityTime, timestamp);
|
|
179
|
+
this.hadActivitySinceLastHeartbeat = true;
|
|
180
|
+
}
|
|
181
|
+
checkIdle() {
|
|
182
|
+
if (!this.running || this.warningActive) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const idleMs = Date.now() - this.lastActivityTime;
|
|
186
|
+
const idleTimeoutMs = this.config.idleTimeoutMinutes * 60 * 1000;
|
|
187
|
+
if (idleMs >= idleTimeoutMs) {
|
|
188
|
+
this.startWarning();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
startWarning() {
|
|
192
|
+
this.warningActive = true;
|
|
193
|
+
this.warningStartTime = Date.now();
|
|
194
|
+
this.secondsRemaining = this.config.warningDurationSeconds;
|
|
195
|
+
// Don't extend session while warning is showing.
|
|
196
|
+
this.clearTimer('heartbeatInterval');
|
|
197
|
+
this.callbacks?.onWarningStart(this.secondsRemaining);
|
|
198
|
+
// Use wall-clock elapsed time so countdown stays accurate in background tabs.
|
|
199
|
+
// Browsers throttle setInterval when the tab is hidden, but each tick
|
|
200
|
+
// recomputes from the real start time so no seconds are "lost".
|
|
201
|
+
this.countdownInterval = setInterval(() => {
|
|
202
|
+
const elapsed = Math.floor((Date.now() - this.warningStartTime) / 1000);
|
|
203
|
+
const remaining = this.config.warningDurationSeconds - elapsed;
|
|
204
|
+
if (remaining <= 0) {
|
|
205
|
+
this.secondsRemaining = 0;
|
|
206
|
+
// stop() clears all timers and listeners before the callback fires so
|
|
207
|
+
// no late tick can re-trigger checkIdle → startWarning if the consumer
|
|
208
|
+
// delays or skips the redirect in onAutoLogout.
|
|
209
|
+
this.stop();
|
|
210
|
+
this.callbacks?.onAutoLogout();
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
this.secondsRemaining = remaining;
|
|
214
|
+
this.callbacks?.onWarningTick(this.secondsRemaining);
|
|
215
|
+
}
|
|
216
|
+
}, 1000);
|
|
217
|
+
}
|
|
218
|
+
startHeartbeat() {
|
|
219
|
+
this.clearTimer('heartbeatInterval');
|
|
220
|
+
const intervalMs = this.config.heartbeatIntervalMinutes * 60 * 1000;
|
|
221
|
+
this.heartbeatInterval = setInterval(() => {
|
|
222
|
+
if (!this.running || this.warningActive) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (!this.hadActivitySinceLastHeartbeat) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
this.fireHeartbeat();
|
|
229
|
+
}, intervalMs);
|
|
230
|
+
}
|
|
231
|
+
fireHeartbeat() {
|
|
232
|
+
this.lastHeartbeatTime = Date.now();
|
|
233
|
+
this.hadActivitySinceLastHeartbeat = false;
|
|
234
|
+
this.callbacks?.onHeartbeat();
|
|
235
|
+
// Let other tabs know we just fired a heartbeat — they can skip theirs
|
|
236
|
+
// this cycle to avoid N tabs all firing heartbeats.
|
|
237
|
+
this.broadcast({ type: 'heartbeat-fired', timestamp: this.lastHeartbeatTime });
|
|
238
|
+
}
|
|
239
|
+
// ─── BroadcastChannel (cross-tab activity sync) ───────────
|
|
240
|
+
setupBroadcastChannel() {
|
|
241
|
+
if (typeof BroadcastChannel === 'undefined') {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
this.broadcastChannel = new BroadcastChannel(BROADCAST_CHANNEL_NAME);
|
|
246
|
+
this.broadcastChannel.addEventListener('message', this.boundOnBroadcastMessage);
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
console.warn('[SessionManager] BroadcastChannel setup failed:', err);
|
|
250
|
+
this.broadcastChannel = null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
teardownBroadcastChannel() {
|
|
254
|
+
if (this.broadcastChannel == null) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
this.broadcastChannel.removeEventListener('message', this.boundOnBroadcastMessage);
|
|
258
|
+
this.broadcastChannel.close();
|
|
259
|
+
this.broadcastChannel = null;
|
|
260
|
+
}
|
|
261
|
+
broadcast(message) {
|
|
262
|
+
this.broadcastChannel?.postMessage(message);
|
|
263
|
+
}
|
|
264
|
+
onBroadcastMessage(event) {
|
|
265
|
+
if (!this.running) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const msg = event.data;
|
|
269
|
+
if (msg == null) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (msg.type === 'activity') {
|
|
273
|
+
// Another tab saw user activity — treat as activity here too.
|
|
274
|
+
this.registerActivity(msg.timestamp);
|
|
275
|
+
}
|
|
276
|
+
else if (msg.type === 'heartbeat-fired') {
|
|
277
|
+
// Another tab already fired a heartbeat in this interval — backend
|
|
278
|
+
// session is already extended. Reset our local tracking so we don't
|
|
279
|
+
// also fire one this cycle.
|
|
280
|
+
this.lastHeartbeatTime = Math.max(this.lastHeartbeatTime, msg.timestamp);
|
|
281
|
+
this.hadActivitySinceLastHeartbeat = false;
|
|
282
|
+
}
|
|
283
|
+
// If this tab is showing the warning but another tab is active, dismiss it —
|
|
284
|
+
// the user is clearly still working. Backend session is already being
|
|
285
|
+
// extended by the active tab, so we just reset local state and resume
|
|
286
|
+
// the heartbeat timer that was paused when the warning started.
|
|
287
|
+
if (this.warningActive) {
|
|
288
|
+
this.warningActive = false;
|
|
289
|
+
this.secondsRemaining = 0;
|
|
290
|
+
this.clearTimer('countdownInterval');
|
|
291
|
+
this.callbacks?.onSessionExtended();
|
|
292
|
+
this.startHeartbeat();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
clearTimer(name) {
|
|
296
|
+
const timer = this[name];
|
|
297
|
+
if (timer != null) {
|
|
298
|
+
if (name === 'activityDebounceTimer') {
|
|
299
|
+
clearTimeout(timer);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
clearInterval(timer);
|
|
303
|
+
}
|
|
304
|
+
this[name] = null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
exports.SessionManager = SessionManager;
|
|
@@ -78,6 +78,7 @@ import { clearScheduleAccruedDetailView } from '../../view/scheduleView/schedule
|
|
|
78
78
|
import { clearScheduleDetailView } from '../../view/scheduleView/scheduleDetailView/scheduleDetailReducer';
|
|
79
79
|
import { clearScheduleList } from '../../view/scheduleView/scheduleListView/scheduleListReducer';
|
|
80
80
|
import { clearSettingsView } from '../../view/settingsView/settingsViewReducer';
|
|
81
|
+
import { clearAutoTransferRules } from '../../view/spendManagement/autotransferRules/autoTransferRulesReducer';
|
|
81
82
|
import { clearBillDetailView } from '../../view/spendManagement/billPay/billDetailView/billDetailViewReducer';
|
|
82
83
|
import { clearBillList } from '../../view/spendManagement/billPay/billList/billListReducer';
|
|
83
84
|
import { clearBillPayConfig } from '../../view/spendManagement/billPay/billPayConfig/billPayConfigReducer';
|
|
@@ -202,7 +203,7 @@ import { clearAllUserRoles } from '../userRole/userRoleReducer';
|
|
|
202
203
|
import { clearAllVendors } from '../vendor/vendorReducer';
|
|
203
204
|
import { clearAllVendorExpenseTrends } from '../vendorExpense/vendorExpenseReducer';
|
|
204
205
|
import { clearAll } from './tenantReducer';
|
|
205
|
-
type clearActionTypeForViews = ReturnType<typeof clearAccountList> | ReturnType<typeof clearAccountMappingView> | ReturnType<typeof clearActivityHistory> | ReturnType<typeof clearActivityRealTimeApproval> | ReturnType<typeof clearAddressView> | ReturnType<typeof clearAiAccountantView> | ReturnType<typeof clearAllAiAccountantCustomers> | ReturnType<typeof clearAiCfoView> | ReturnType<typeof clearAllClasses> | ReturnType<typeof clearAllProjects> | ReturnType<typeof clearAllMonthEndCloseChecksView> | ReturnType<typeof clearApAging> | ReturnType<typeof clearApAgingDetail> | ReturnType<typeof clearArAging> | ReturnType<typeof clearArAgingDetail> | ReturnType<typeof clearAuthenticationView> | ReturnType<typeof clearBalanceSheet> | ReturnType<typeof clearBankAccountView> | ReturnType<typeof clearBillDetailView> | ReturnType<typeof clearBillList> | ReturnType<typeof clearBillPayBulkActionView> | ReturnType<typeof clearBillPayCard> | ReturnType<typeof clearBillPayConfig> | ReturnType<typeof clearBillPayReview> | ReturnType<typeof clearBillPaySetupApproverView> | ReturnType<typeof clearBillPaySetupView> | ReturnType<typeof clearCardBalance> | ReturnType<typeof clearCardPaymentView> | ReturnType<typeof clearCardUserOnboarding> | ReturnType<typeof clearCashbackDetail> | ReturnType<typeof clearCashBalance> | ReturnType<typeof clearCashFlow> | ReturnType<typeof clearCashInCashOut> | ReturnType<typeof clearCashPosition> | ReturnType<typeof clearChargeCardConfig> | ReturnType<typeof clearChargeCardDetail> | ReturnType<typeof clearChargeCardList> | ReturnType<typeof clearChargeCardRepaymentDetail> | ReturnType<typeof clearChargeCardSetupView> | ReturnType<typeof clearChargeCardStatementList> | ReturnType<typeof clearCheckDeposit> | ReturnType<typeof clearCompanyConfigView> | ReturnType<typeof clearCompanyHealthMetric> | ReturnType<typeof clearCompanyHealthMetricView> | ReturnType<typeof clearCompanyMonthEndReportView> | ReturnType<typeof clearCompanyTaskManagerView> | ReturnType<typeof clearCompanyView> | ReturnType<typeof clearDashboard> | ReturnType<typeof clearDashboardLayout> | ReturnType<typeof clearDepositAccountDetail> | ReturnType<typeof clearDepositAccountList> | ReturnType<typeof clearDepositAccountTransactionList> | ReturnType<typeof clearEditBillViewDetail> | ReturnType<typeof clearEditRemiViewDetail> | ReturnType<typeof clearAllEntityAutoCompleteResults> | ReturnType<typeof clearExpenseAutomationFluxAnalysisView> | ReturnType<typeof clearExpenseAutomationJESchedulesView> | ReturnType<typeof clearExpenseAutomationMissingReceiptsView> | ReturnType<typeof clearExpenseAutomationReconciliationView> | ReturnType<typeof clearExpenseAutomationTransactionsView> | ReturnType<typeof clearExpenseAutomationView> | ReturnType<typeof clearExpressPayView> | ReturnType<typeof clearFileViewList> | ReturnType<typeof clearFinanceStatement> | ReturnType<typeof clearForecastList> | ReturnType<typeof clearGlobalMerchantAutoCompleteResults> | ReturnType<typeof clearGlobalMerchantView> | ReturnType<typeof clearInsightsCard> | ReturnType<typeof clearInternationalWire> | ReturnType<typeof clearInternationalVerificationView> | ReturnType<typeof clearIssueChargeCard> | ReturnType<typeof clearMerchantList> | ReturnType<typeof clearNetBurnOrIncome> | ReturnType<typeof clearNetBurnOrIncomeClassesView> | ReturnType<typeof clearNetBurnOrIncomeStoryCard> | ReturnType<typeof clearNotificationView> | ReturnType<typeof clearOnboardingCockpitView> | ReturnType<typeof clearOnboardingCustomerView> | ReturnType<typeof clearOpEx> | ReturnType<typeof clearOpExByVendor> | ReturnType<typeof clearOpExClassesView> | ReturnType<typeof clearOwnerList> | ReturnType<typeof clearPaymentAccountList> | ReturnType<typeof clearPeople> | ReturnType<typeof clearAggregatedReport> | ReturnType<typeof clearPreviousBills> | ReturnType<typeof clearProfitAndLoss> | ReturnType<typeof clearProfitAndLossClassesView> | ReturnType<typeof clearProfitAndLossProjectView> | ReturnType<typeof clearRecommendation> | ReturnType<typeof clearReferrals> | ReturnType<typeof clearReimbursementCard> | ReturnType<typeof clearReimbursementConfig> | ReturnType<typeof clearRemiBulkActionView> | ReturnType<typeof clearRemiDetailView> | ReturnType<typeof clearRemiList> | ReturnType<typeof clearRemiSetupApproverView> | ReturnType<typeof clearRemiSetupView> | ReturnType<typeof clearReportUIOption> | ReturnType<typeof clearRevenue> | ReturnType<typeof clearRevenueClassesView> | ReturnType<typeof clearReviewCompany> | ReturnType<typeof clearScheduleDetailView> | ReturnType<typeof clearScheduleAccruedDetailView> | ReturnType<typeof clearScheduleList> | ReturnType<typeof clearSettingsView> | ReturnType<typeof clearSetupView> | ReturnType<typeof clearPlaidAccountView> | ReturnType<typeof clearSubscriptionView> | ReturnType<typeof clearTagView> | ReturnType<typeof clearTaskDetail> | ReturnType<typeof clearTaskGroupTemplatesView> | ReturnType<typeof clearTaskGroupView> | ReturnType<typeof clearTaskList> | ReturnType<typeof clearTasksCard> | ReturnType<typeof clearTopEx> | ReturnType<typeof clearTransactionDetail> | ReturnType<typeof clearTransactionList> | ReturnType<typeof clearTransferDetail> | ReturnType<typeof clearTreasuryOverviewDetail> | ReturnType<typeof clearTreasurySetupView> | ReturnType<typeof clearTreasuryStatementList> | ReturnType<typeof clearTreasuryTaxLetterList> | ReturnType<typeof clearTreasuryTransferMoney> | ReturnType<typeof clearTrendData> | ReturnType<typeof clearTrendWithTransactions> | ReturnType<typeof clearTwoFactorAuthentication> | ReturnType<typeof clearUserFinancialAccount> | ReturnType<typeof clearUserListView> | ReturnType<typeof clearUserRoleConfigView> | ReturnType<typeof clearVendorGlobalReviewView> | ReturnType<typeof clearVendorReviewView> | ReturnType<typeof clearVendorTypeList> | ReturnType<typeof clearVendor1099TypeList> | ReturnType<typeof clearVendorView> | ReturnType<typeof clearVendorsFiling1099List> | ReturnType<typeof clearVendorsList> | ReturnType<typeof clearVendorsTabVendorView> | ReturnType<typeof clearWiseRedirectStatus> | ReturnType<typeof clearZeniAccStatementList> | ReturnType<typeof clearZeniAccountList> | ReturnType<typeof clearZeniAccountSetupView> | ReturnType<typeof clearZeniAccountsConfig> | ReturnType<typeof clearZeniAccountsPromoCard>;
|
|
206
|
+
type clearActionTypeForViews = ReturnType<typeof clearAccountList> | ReturnType<typeof clearAccountMappingView> | ReturnType<typeof clearActivityHistory> | ReturnType<typeof clearActivityRealTimeApproval> | ReturnType<typeof clearAddressView> | ReturnType<typeof clearAiAccountantView> | ReturnType<typeof clearAllAiAccountantCustomers> | ReturnType<typeof clearAiCfoView> | ReturnType<typeof clearAllClasses> | ReturnType<typeof clearAllProjects> | ReturnType<typeof clearAllMonthEndCloseChecksView> | ReturnType<typeof clearApAging> | ReturnType<typeof clearApAgingDetail> | ReturnType<typeof clearArAging> | ReturnType<typeof clearArAgingDetail> | ReturnType<typeof clearAuthenticationView> | ReturnType<typeof clearAutoTransferRules> | ReturnType<typeof clearBalanceSheet> | ReturnType<typeof clearBankAccountView> | ReturnType<typeof clearBillDetailView> | ReturnType<typeof clearBillList> | ReturnType<typeof clearBillPayBulkActionView> | ReturnType<typeof clearBillPayCard> | ReturnType<typeof clearBillPayConfig> | ReturnType<typeof clearBillPayReview> | ReturnType<typeof clearBillPaySetupApproverView> | ReturnType<typeof clearBillPaySetupView> | ReturnType<typeof clearCardBalance> | ReturnType<typeof clearCardPaymentView> | ReturnType<typeof clearCardUserOnboarding> | ReturnType<typeof clearCashbackDetail> | ReturnType<typeof clearCashBalance> | ReturnType<typeof clearCashFlow> | ReturnType<typeof clearCashInCashOut> | ReturnType<typeof clearCashPosition> | ReturnType<typeof clearChargeCardConfig> | ReturnType<typeof clearChargeCardDetail> | ReturnType<typeof clearChargeCardList> | ReturnType<typeof clearChargeCardRepaymentDetail> | ReturnType<typeof clearChargeCardSetupView> | ReturnType<typeof clearChargeCardStatementList> | ReturnType<typeof clearCheckDeposit> | ReturnType<typeof clearCompanyConfigView> | ReturnType<typeof clearCompanyHealthMetric> | ReturnType<typeof clearCompanyHealthMetricView> | ReturnType<typeof clearCompanyMonthEndReportView> | ReturnType<typeof clearCompanyTaskManagerView> | ReturnType<typeof clearCompanyView> | ReturnType<typeof clearDashboard> | ReturnType<typeof clearDashboardLayout> | ReturnType<typeof clearDepositAccountDetail> | ReturnType<typeof clearDepositAccountList> | ReturnType<typeof clearDepositAccountTransactionList> | ReturnType<typeof clearEditBillViewDetail> | ReturnType<typeof clearEditRemiViewDetail> | ReturnType<typeof clearAllEntityAutoCompleteResults> | ReturnType<typeof clearExpenseAutomationFluxAnalysisView> | ReturnType<typeof clearExpenseAutomationJESchedulesView> | ReturnType<typeof clearExpenseAutomationMissingReceiptsView> | ReturnType<typeof clearExpenseAutomationReconciliationView> | ReturnType<typeof clearExpenseAutomationTransactionsView> | ReturnType<typeof clearExpenseAutomationView> | ReturnType<typeof clearExpressPayView> | ReturnType<typeof clearFileViewList> | ReturnType<typeof clearFinanceStatement> | ReturnType<typeof clearForecastList> | ReturnType<typeof clearGlobalMerchantAutoCompleteResults> | ReturnType<typeof clearGlobalMerchantView> | ReturnType<typeof clearInsightsCard> | ReturnType<typeof clearInternationalWire> | ReturnType<typeof clearInternationalVerificationView> | ReturnType<typeof clearIssueChargeCard> | ReturnType<typeof clearMerchantList> | ReturnType<typeof clearNetBurnOrIncome> | ReturnType<typeof clearNetBurnOrIncomeClassesView> | ReturnType<typeof clearNetBurnOrIncomeStoryCard> | ReturnType<typeof clearNotificationView> | ReturnType<typeof clearOnboardingCockpitView> | ReturnType<typeof clearOnboardingCustomerView> | ReturnType<typeof clearOpEx> | ReturnType<typeof clearOpExByVendor> | ReturnType<typeof clearOpExClassesView> | ReturnType<typeof clearOwnerList> | ReturnType<typeof clearPaymentAccountList> | ReturnType<typeof clearPeople> | ReturnType<typeof clearAggregatedReport> | ReturnType<typeof clearPreviousBills> | ReturnType<typeof clearProfitAndLoss> | ReturnType<typeof clearProfitAndLossClassesView> | ReturnType<typeof clearProfitAndLossProjectView> | ReturnType<typeof clearRecommendation> | ReturnType<typeof clearReferrals> | ReturnType<typeof clearReimbursementCard> | ReturnType<typeof clearReimbursementConfig> | ReturnType<typeof clearRemiBulkActionView> | ReturnType<typeof clearRemiDetailView> | ReturnType<typeof clearRemiList> | ReturnType<typeof clearRemiSetupApproverView> | ReturnType<typeof clearRemiSetupView> | ReturnType<typeof clearReportUIOption> | ReturnType<typeof clearRevenue> | ReturnType<typeof clearRevenueClassesView> | ReturnType<typeof clearReviewCompany> | ReturnType<typeof clearScheduleDetailView> | ReturnType<typeof clearScheduleAccruedDetailView> | ReturnType<typeof clearScheduleList> | ReturnType<typeof clearSettingsView> | ReturnType<typeof clearSetupView> | ReturnType<typeof clearPlaidAccountView> | ReturnType<typeof clearSubscriptionView> | ReturnType<typeof clearTagView> | ReturnType<typeof clearTaskDetail> | ReturnType<typeof clearTaskGroupTemplatesView> | ReturnType<typeof clearTaskGroupView> | ReturnType<typeof clearTaskList> | ReturnType<typeof clearTasksCard> | ReturnType<typeof clearTopEx> | ReturnType<typeof clearTransactionDetail> | ReturnType<typeof clearTransactionList> | ReturnType<typeof clearTransferDetail> | ReturnType<typeof clearTreasuryOverviewDetail> | ReturnType<typeof clearTreasurySetupView> | ReturnType<typeof clearTreasuryStatementList> | ReturnType<typeof clearTreasuryTaxLetterList> | ReturnType<typeof clearTreasuryTransferMoney> | ReturnType<typeof clearTrendData> | ReturnType<typeof clearTrendWithTransactions> | ReturnType<typeof clearTwoFactorAuthentication> | ReturnType<typeof clearUserFinancialAccount> | ReturnType<typeof clearUserListView> | ReturnType<typeof clearUserRoleConfigView> | ReturnType<typeof clearVendorGlobalReviewView> | ReturnType<typeof clearVendorReviewView> | ReturnType<typeof clearVendorTypeList> | ReturnType<typeof clearVendor1099TypeList> | ReturnType<typeof clearVendorView> | ReturnType<typeof clearVendorsFiling1099List> | ReturnType<typeof clearVendorsList> | ReturnType<typeof clearVendorsTabVendorView> | ReturnType<typeof clearWiseRedirectStatus> | ReturnType<typeof clearZeniAccStatementList> | ReturnType<typeof clearZeniAccountList> | ReturnType<typeof clearZeniAccountSetupView> | ReturnType<typeof clearZeniAccountsConfig> | ReturnType<typeof clearZeniAccountsPromoCard>;
|
|
206
207
|
type clearActionTypeForEntities = ReturnType<typeof clearAiCfo> | ReturnType<typeof clearAllAccountGroups> | ReturnType<typeof clearAllAccountRecon> | ReturnType<typeof clearAllAccountingSummary> | ReturnType<typeof clearAllAccounts> | ReturnType<typeof clearAllAddresses> | ReturnType<typeof clearAllApprovalRules> | ReturnType<typeof clearAllBankAccounts> | ReturnType<typeof clearAllBillTransactions> | ReturnType<typeof clearAllCardPayments> | ReturnType<typeof clearAllChargeCardRepayments> | ReturnType<typeof clearAllChargeCardTransactions> | ReturnType<typeof clearAllChargeCards> | ReturnType<typeof clearAllCompanies> | ReturnType<typeof clearAllConnectedAccounts> | ReturnType<typeof clearAllCurrencies> | ReturnType<typeof clearAllCustomerIncomeTrend> | ReturnType<typeof clearAllCustomers> | ReturnType<typeof clearAllDepositAccounts> | ReturnType<typeof clearAllEntities> | ReturnType<typeof clearAllEntityApprovalStatus> | ReturnType<typeof clearAllFiles> | ReturnType<typeof clearAllForecasts> | ReturnType<typeof clearAllGlobalMerchants> | ReturnType<typeof clearAllInsights> | ReturnType<typeof clearAllAccruedJESchedules> | ReturnType<typeof clearAllJEScheduleTransactions> | ReturnType<typeof clearAllMerchants> | ReturnType<typeof clearAllMonthEndCloseChecks> | ReturnType<typeof clearAllNotifications> | ReturnType<typeof clearAllOnboardingCustomersInfo> | ReturnType<typeof clearAllPaymentAccounts> | ReturnType<typeof clearAllPaymentInstruments> | ReturnType<typeof clearAllRecurringBills> | ReturnType<typeof clearAllReimbursements> | ReturnType<typeof clearAllSectionsAccountsView> | ReturnType<typeof clearAllSectionsClassesViewV2> | ReturnType<typeof clearAllSectionsProjectView> | ReturnType<typeof clearAllSubscriptionAddOns> | ReturnType<typeof clearAllSubscriptionCoupons> | ReturnType<typeof clearAllSubscriptionPlans> | ReturnType<typeof clearAllSubscriptionSummary> | ReturnType<typeof clearAllSubscriptions> | ReturnType<typeof clearAllTags> | ReturnType<typeof clearAllTaskGroups> | ReturnType<typeof clearAllTaskGroupTemplates> | ReturnType<typeof clearAllTaskSummary> | ReturnType<typeof clearAllTasks> | ReturnType<typeof clearAllToastNotifications> | ReturnType<typeof clearAllTransactions> | ReturnType<typeof clearAllUserRoles> | ReturnType<typeof clearAllUsers> | ReturnType<typeof clearAllVendorExpenseTrends> | ReturnType<typeof clearAllVendors> | ReturnType<typeof clearClassList> | ReturnType<typeof clearContacts> | ReturnType<typeof clearCountryList> | ReturnType<typeof clearDepositAccountTransaction> | ReturnType<typeof clearSnackbar>;
|
|
207
208
|
export type ActionType = clearActionTypeForViews | clearActionTypeForEntities | ReturnType<typeof clearAll>;
|
|
208
209
|
export declare const clearAllEpic: (actions$: ActionsObservable<ActionType>) => import("rxjs").Observable<ActionType>;
|
|
@@ -82,6 +82,7 @@ const scheduleAccruedDetailReducer_1 = require("../../view/scheduleView/schedule
|
|
|
82
82
|
const scheduleDetailReducer_1 = require("../../view/scheduleView/scheduleDetailView/scheduleDetailReducer");
|
|
83
83
|
const scheduleListReducer_1 = require("../../view/scheduleView/scheduleListView/scheduleListReducer");
|
|
84
84
|
const settingsViewReducer_1 = require("../../view/settingsView/settingsViewReducer");
|
|
85
|
+
const autoTransferRulesReducer_1 = require("../../view/spendManagement/autotransferRules/autoTransferRulesReducer");
|
|
85
86
|
const billDetailViewReducer_1 = require("../../view/spendManagement/billPay/billDetailView/billDetailViewReducer");
|
|
86
87
|
const billListReducer_1 = require("../../view/spendManagement/billPay/billList/billListReducer");
|
|
87
88
|
const billPayConfigReducer_1 = require("../../view/spendManagement/billPay/billPayConfig/billPayConfigReducer");
|
|
@@ -226,6 +227,7 @@ const clearAllEpic = (actions$) => actions$.pipe((0, operators_1.filter)(tenantR
|
|
|
226
227
|
(0, arAgingReducer_1.clearArAging)(),
|
|
227
228
|
(0, arAgingDetailReducer_1.clearArAgingDetail)(),
|
|
228
229
|
(0, authenticationViewReducer_1.clearAuthenticationView)(),
|
|
230
|
+
(0, autoTransferRulesReducer_1.clearAutoTransferRules)(),
|
|
229
231
|
(0, balanceSheetReducer_1.clearBalanceSheet)(),
|
|
230
232
|
(0, bankAccountViewReducer_1.clearBankAccountView)(),
|
|
231
233
|
(0, billDetailViewReducer_1.clearBillDetailView)(),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ActionsObservable, StateObservable } from 'redux-observable';
|
|
2
|
+
import { RootState } from '../../../reducer';
|
|
3
|
+
import { ZeniAPI } from '../../../zeniAPI';
|
|
4
|
+
import { sendSessionHeartbeat, sessionHeartbeatFailure, sessionHeartbeatSuccess } from '../tenantReducer';
|
|
5
|
+
export type ActionType = ReturnType<typeof sendSessionHeartbeat> | ReturnType<typeof sessionHeartbeatSuccess> | ReturnType<typeof sessionHeartbeatFailure>;
|
|
6
|
+
export declare const sessionHeartbeatEpic: (actions$: ActionsObservable<ActionType>, _: StateObservable<RootState>, zeniAPI: ZeniAPI) => import("rxjs").Observable<{
|
|
7
|
+
payload: {
|
|
8
|
+
expiresAt: string;
|
|
9
|
+
};
|
|
10
|
+
type: "tenant/sessionHeartbeatSuccess";
|
|
11
|
+
} | {
|
|
12
|
+
payload: {
|
|
13
|
+
error: import("../../../responsePayload").ZeniAPIStatus<Record<string, unknown>>;
|
|
14
|
+
};
|
|
15
|
+
type: "tenant/sessionHeartbeatFailure";
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sessionHeartbeatEpic = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const operators_1 = require("rxjs/operators");
|
|
6
|
+
const responsePayload_1 = require("../../../responsePayload");
|
|
7
|
+
const tenantReducer_1 = require("../tenantReducer");
|
|
8
|
+
const sessionHeartbeatEpic = (actions$, _, zeniAPI) => actions$.pipe((0, operators_1.filter)(tenantReducer_1.sendSessionHeartbeat.match), (0, operators_1.switchMap)(() => {
|
|
9
|
+
const { userId, sessionId } = zeniAPI;
|
|
10
|
+
const headers = {
|
|
11
|
+
'zeni-user-id': userId,
|
|
12
|
+
'zeni-session-id': sessionId,
|
|
13
|
+
'zeni-tenant-id': '',
|
|
14
|
+
};
|
|
15
|
+
return zeniAPI
|
|
16
|
+
.postAndGetJSON(`${zeniAPI.apiEndPoints.authMicroServiceBaseUrl}/1.0/heartbeat`, undefined, headers)
|
|
17
|
+
.pipe((0, operators_1.mergeMap)((response) => {
|
|
18
|
+
if ((0, responsePayload_1.isSuccessResponse)(response)) {
|
|
19
|
+
return (0, rxjs_1.of)((0, tenantReducer_1.sessionHeartbeatSuccess)(response.data?.expiry ?? ''));
|
|
20
|
+
}
|
|
21
|
+
return (0, rxjs_1.of)((0, tenantReducer_1.sessionHeartbeatFailure)(response.status));
|
|
22
|
+
}), (0, operators_1.catchError)((error) => (0, rxjs_1.of)((0, tenantReducer_1.sessionHeartbeatFailure)((0, responsePayload_1.createZeniAPIStatus)('Heartbeat failed', 'Session heartbeat API call errored: ' +
|
|
23
|
+
(error instanceof Error ? error.message : String(error)))))));
|
|
24
|
+
}));
|
|
25
|
+
exports.sessionHeartbeatEpic = sessionHeartbeatEpic;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session management types for idle timeout and heartbeat.
|
|
3
|
+
*/
|
|
4
|
+
export interface SessionConfig {
|
|
5
|
+
/** Minutes between heartbeat API calls while user is active. Default: 10 */
|
|
6
|
+
heartbeatIntervalMinutes: number;
|
|
7
|
+
/** Minutes of inactivity before the warning popup appears. Default: 30 */
|
|
8
|
+
idleTimeoutMinutes: number;
|
|
9
|
+
/** Whether auto-logout is enabled. Default: true */
|
|
10
|
+
isAutoLogoutEnabled: boolean;
|
|
11
|
+
/** Seconds to count down in the warning popup before auto-logout. Default: 60 */
|
|
12
|
+
warningDurationSeconds: number;
|
|
13
|
+
}
|
|
14
|
+
export declare const DEFAULT_SESSION_CONFIG: SessionConfig;
|
|
15
|
+
export interface SessionCallbacks {
|
|
16
|
+
/** Called when the countdown reaches zero — app should sign out. */
|
|
17
|
+
onAutoLogout: () => void;
|
|
18
|
+
/** Called to send a heartbeat API request. */
|
|
19
|
+
onHeartbeat: () => void;
|
|
20
|
+
/** Called when the session is extended (heartbeat success or "Continue Session"). */
|
|
21
|
+
onSessionExtended: () => void;
|
|
22
|
+
/** Called when the warning countdown starts. */
|
|
23
|
+
onWarningStart: (secondsRemaining: number) => void;
|
|
24
|
+
/** Called every second during the countdown. */
|
|
25
|
+
onWarningTick: (secondsRemaining: number) => void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session management types for idle timeout and heartbeat.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEFAULT_SESSION_CONFIG = void 0;
|
|
7
|
+
exports.DEFAULT_SESSION_CONFIG = {
|
|
8
|
+
heartbeatIntervalMinutes: 10,
|
|
9
|
+
idleTimeoutMinutes: 30,
|
|
10
|
+
isAutoLogoutEnabled: true,
|
|
11
|
+
warningDurationSeconds: 60,
|
|
12
|
+
};
|
|
@@ -51,7 +51,11 @@ export declare const updateTenants: import("@reduxjs/toolkit").ActionCreatorWith
|
|
|
51
51
|
accountLockTime?: string;
|
|
52
52
|
accountUnlockTime?: string;
|
|
53
53
|
isAccountLocked?: boolean;
|
|
54
|
-
}, "tenant/sendEmailMagicLinkToUserFailure">, updateSignInState: import("@reduxjs/toolkit").ActionCreatorWithPayload<FetchState, "tenant/updateSignInState">, doSignOut: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"tenant/doSignOut">, signOutSuccess: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"tenant/signOutSuccess">,
|
|
54
|
+
}, "tenant/sendEmailMagicLinkToUserFailure">, updateSignInState: import("@reduxjs/toolkit").ActionCreatorWithPayload<FetchState, "tenant/updateSignInState">, doSignOut: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"tenant/doSignOut">, signOutSuccess: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"tenant/signOutSuccess">, sendSessionHeartbeat: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"tenant/sendSessionHeartbeat">, sessionHeartbeatSuccess: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[expiresAt: string], {
|
|
55
|
+
expiresAt: string;
|
|
56
|
+
}, "tenant/sessionHeartbeatSuccess", never, never>, sessionHeartbeatFailure: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[error: ZeniAPIStatus<Record<string, unknown>>], {
|
|
57
|
+
error: ZeniAPIStatus<Record<string, unknown>>;
|
|
58
|
+
}, "tenant/sessionHeartbeatFailure", never, never>, updateLoggedInUser: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[userPayload: LoggedInUserPayload, zeniSessionId: string, authProvider: AuthProvider], {
|
|
55
59
|
userPayload: LoggedInUserPayload;
|
|
56
60
|
zeniSessionId: string;
|
|
57
61
|
authProvider: AuthProvider;
|
|
@@ -4,7 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
var _a;
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
7
|
+
exports.resendVerifyDeviceOTPSuccess = exports.resendVerifyDeviceOTP = exports.verifyDeviceWithTwoFAFailure = exports.verifyDeviceWithTwoFASuccess = exports.verifyDeviceWithTwoFA = exports.updateTenantMasterTOSInfo = exports.updateTenantAccountingClassesEnabled = exports.trigger2FA = exports.resetSignInState = exports.updateTreasuryVideoViewedForLoggedInUser = exports.updateReferViewedForLoggedInUser = exports.updateTenantReimbursementInfo = exports.removeOnboardingTenant = exports.updateOnboardingTenants = exports.updateSubscriptionSummaryForTenantFailure = exports.updateSubscriptionSummaryForTenantSuccess = exports.fetchSubscriptionSummaryForTenant = exports.clearAll = exports.fetchExternalConnectionsSuccess = exports.fetchExternalConnectionsFailure = exports.saveExternalConnectionFailure = exports.saveExternalConnectionSuccess = exports.saveExternalConnection = exports.fetchExternalConnections = exports.updateLoggedInUser = exports.sessionHeartbeatFailure = exports.sessionHeartbeatSuccess = exports.sendSessionHeartbeat = exports.signOutSuccess = exports.doSignOut = exports.updateSignInState = exports.sendEmailMagicLinkToUserFailure = exports.sendEmailMagicLinkToUserSuccess = exports.sendEmailMagicLinkToUser = exports.magicLinkSignInFailure = exports.magicLinkSignInSuccess = exports.doMagicLinkSignIn = exports.doSignIn = exports.updateExcludedResourcesFailure = exports.updateExcludedResourcesSuccess = exports.fetchExcludedResources = exports.updateCurrentTenant = exports.updateTenantFailure = exports.updateTenantSuccess = exports.fetchActiveTenant = exports.updateTenantsFailure = exports.updateTenantsSuccess = exports.fetchAllTenants = exports.updateTenants = exports.initialState = void 0;
|
|
8
|
+
exports.toConnection = exports.toRoleResource = exports.resendVerifyDeviceOTPFailure = void 0;
|
|
8
9
|
exports.mapConnectionsPayloadToState = mapConnectionsPayloadToState;
|
|
9
10
|
const toolkit_1 = require("@reduxjs/toolkit");
|
|
10
11
|
const js_base64_1 = require("js-base64");
|
|
@@ -436,6 +437,26 @@ const tenant = (0, toolkit_1.createSlice)({
|
|
|
436
437
|
signOutSuccess(draft) {
|
|
437
438
|
draft.loggedInUser = undefined;
|
|
438
439
|
draft.currentTenantId = undefined;
|
|
440
|
+
draft.sessionExpiresAt = undefined;
|
|
441
|
+
},
|
|
442
|
+
sendSessionHeartbeat() {
|
|
443
|
+
// No state change needed — epic handles the API call
|
|
444
|
+
},
|
|
445
|
+
sessionHeartbeatSuccess: {
|
|
446
|
+
prepare(expiresAt) {
|
|
447
|
+
return { payload: { expiresAt } };
|
|
448
|
+
},
|
|
449
|
+
reducer(draft, action) {
|
|
450
|
+
draft.sessionExpiresAt = action.payload.expiresAt;
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
sessionHeartbeatFailure: {
|
|
454
|
+
prepare(error) {
|
|
455
|
+
return { payload: { error } };
|
|
456
|
+
},
|
|
457
|
+
reducer(draft, action) {
|
|
458
|
+
draft.error = action.payload.error;
|
|
459
|
+
},
|
|
439
460
|
},
|
|
440
461
|
fetchSubscriptionSummaryForTenant: {
|
|
441
462
|
prepare(tenantId) {
|
|
@@ -543,7 +564,7 @@ const tenant = (0, toolkit_1.createSlice)({
|
|
|
543
564
|
},
|
|
544
565
|
},
|
|
545
566
|
});
|
|
546
|
-
_a = tenant.actions, exports.updateTenants = _a.updateTenants, exports.fetchAllTenants = _a.fetchAllTenants, exports.updateTenantsSuccess = _a.updateTenantsSuccess, exports.updateTenantsFailure = _a.updateTenantsFailure, exports.fetchActiveTenant = _a.fetchActiveTenant, exports.updateTenantSuccess = _a.updateTenantSuccess, exports.updateTenantFailure = _a.updateTenantFailure, exports.updateCurrentTenant = _a.updateCurrentTenant, exports.fetchExcludedResources = _a.fetchExcludedResources, exports.updateExcludedResourcesSuccess = _a.updateExcludedResourcesSuccess, exports.updateExcludedResourcesFailure = _a.updateExcludedResourcesFailure, exports.doSignIn = _a.doSignIn, exports.doMagicLinkSignIn = _a.doMagicLinkSignIn, exports.magicLinkSignInSuccess = _a.magicLinkSignInSuccess, exports.magicLinkSignInFailure = _a.magicLinkSignInFailure, exports.sendEmailMagicLinkToUser = _a.sendEmailMagicLinkToUser, exports.sendEmailMagicLinkToUserSuccess = _a.sendEmailMagicLinkToUserSuccess, exports.sendEmailMagicLinkToUserFailure = _a.sendEmailMagicLinkToUserFailure, exports.updateSignInState = _a.updateSignInState, exports.doSignOut = _a.doSignOut, exports.signOutSuccess = _a.signOutSuccess, exports.updateLoggedInUser = _a.updateLoggedInUser, exports.fetchExternalConnections = _a.fetchExternalConnections, exports.saveExternalConnection = _a.saveExternalConnection, exports.saveExternalConnectionSuccess = _a.saveExternalConnectionSuccess, exports.saveExternalConnectionFailure = _a.saveExternalConnectionFailure, exports.fetchExternalConnectionsFailure = _a.fetchExternalConnectionsFailure, exports.fetchExternalConnectionsSuccess = _a.fetchExternalConnectionsSuccess, exports.clearAll = _a.clearAll, exports.fetchSubscriptionSummaryForTenant = _a.fetchSubscriptionSummaryForTenant, exports.updateSubscriptionSummaryForTenantSuccess = _a.updateSubscriptionSummaryForTenantSuccess, exports.updateSubscriptionSummaryForTenantFailure = _a.updateSubscriptionSummaryForTenantFailure, exports.updateOnboardingTenants = _a.updateOnboardingTenants, exports.removeOnboardingTenant = _a.removeOnboardingTenant, exports.updateTenantReimbursementInfo = _a.updateTenantReimbursementInfo, exports.updateReferViewedForLoggedInUser = _a.updateReferViewedForLoggedInUser, exports.updateTreasuryVideoViewedForLoggedInUser = _a.updateTreasuryVideoViewedForLoggedInUser, exports.resetSignInState = _a.resetSignInState, exports.trigger2FA = _a.trigger2FA, exports.updateTenantAccountingClassesEnabled = _a.updateTenantAccountingClassesEnabled, exports.updateTenantMasterTOSInfo = _a.updateTenantMasterTOSInfo, exports.verifyDeviceWithTwoFA = _a.verifyDeviceWithTwoFA, exports.verifyDeviceWithTwoFASuccess = _a.verifyDeviceWithTwoFASuccess, exports.verifyDeviceWithTwoFAFailure = _a.verifyDeviceWithTwoFAFailure, exports.resendVerifyDeviceOTP = _a.resendVerifyDeviceOTP, exports.resendVerifyDeviceOTPSuccess = _a.resendVerifyDeviceOTPSuccess, exports.resendVerifyDeviceOTPFailure = _a.resendVerifyDeviceOTPFailure;
|
|
567
|
+
_a = tenant.actions, exports.updateTenants = _a.updateTenants, exports.fetchAllTenants = _a.fetchAllTenants, exports.updateTenantsSuccess = _a.updateTenantsSuccess, exports.updateTenantsFailure = _a.updateTenantsFailure, exports.fetchActiveTenant = _a.fetchActiveTenant, exports.updateTenantSuccess = _a.updateTenantSuccess, exports.updateTenantFailure = _a.updateTenantFailure, exports.updateCurrentTenant = _a.updateCurrentTenant, exports.fetchExcludedResources = _a.fetchExcludedResources, exports.updateExcludedResourcesSuccess = _a.updateExcludedResourcesSuccess, exports.updateExcludedResourcesFailure = _a.updateExcludedResourcesFailure, exports.doSignIn = _a.doSignIn, exports.doMagicLinkSignIn = _a.doMagicLinkSignIn, exports.magicLinkSignInSuccess = _a.magicLinkSignInSuccess, exports.magicLinkSignInFailure = _a.magicLinkSignInFailure, exports.sendEmailMagicLinkToUser = _a.sendEmailMagicLinkToUser, exports.sendEmailMagicLinkToUserSuccess = _a.sendEmailMagicLinkToUserSuccess, exports.sendEmailMagicLinkToUserFailure = _a.sendEmailMagicLinkToUserFailure, exports.updateSignInState = _a.updateSignInState, exports.doSignOut = _a.doSignOut, exports.signOutSuccess = _a.signOutSuccess, exports.sendSessionHeartbeat = _a.sendSessionHeartbeat, exports.sessionHeartbeatSuccess = _a.sessionHeartbeatSuccess, exports.sessionHeartbeatFailure = _a.sessionHeartbeatFailure, exports.updateLoggedInUser = _a.updateLoggedInUser, exports.fetchExternalConnections = _a.fetchExternalConnections, exports.saveExternalConnection = _a.saveExternalConnection, exports.saveExternalConnectionSuccess = _a.saveExternalConnectionSuccess, exports.saveExternalConnectionFailure = _a.saveExternalConnectionFailure, exports.fetchExternalConnectionsFailure = _a.fetchExternalConnectionsFailure, exports.fetchExternalConnectionsSuccess = _a.fetchExternalConnectionsSuccess, exports.clearAll = _a.clearAll, exports.fetchSubscriptionSummaryForTenant = _a.fetchSubscriptionSummaryForTenant, exports.updateSubscriptionSummaryForTenantSuccess = _a.updateSubscriptionSummaryForTenantSuccess, exports.updateSubscriptionSummaryForTenantFailure = _a.updateSubscriptionSummaryForTenantFailure, exports.updateOnboardingTenants = _a.updateOnboardingTenants, exports.removeOnboardingTenant = _a.removeOnboardingTenant, exports.updateTenantReimbursementInfo = _a.updateTenantReimbursementInfo, exports.updateReferViewedForLoggedInUser = _a.updateReferViewedForLoggedInUser, exports.updateTreasuryVideoViewedForLoggedInUser = _a.updateTreasuryVideoViewedForLoggedInUser, exports.resetSignInState = _a.resetSignInState, exports.trigger2FA = _a.trigger2FA, exports.updateTenantAccountingClassesEnabled = _a.updateTenantAccountingClassesEnabled, exports.updateTenantMasterTOSInfo = _a.updateTenantMasterTOSInfo, exports.verifyDeviceWithTwoFA = _a.verifyDeviceWithTwoFA, exports.verifyDeviceWithTwoFASuccess = _a.verifyDeviceWithTwoFASuccess, exports.verifyDeviceWithTwoFAFailure = _a.verifyDeviceWithTwoFAFailure, exports.resendVerifyDeviceOTP = _a.resendVerifyDeviceOTP, exports.resendVerifyDeviceOTPSuccess = _a.resendVerifyDeviceOTPSuccess, exports.resendVerifyDeviceOTPFailure = _a.resendVerifyDeviceOTPFailure;
|
|
547
568
|
exports.default = tenant.reducer;
|
|
548
569
|
/**
|
|
549
570
|
* Converts tenants payload to Tenant and User State
|