ERP-node/frontend/lib/sessionManager.ts

239 lines
5.8 KiB
TypeScript

/**
* 세션 관리 유틸리티
* 세션 만료 감지, 자동 로그아웃, 세션 갱신 등을 담당
*/
interface SessionConfig {
checkInterval: number; // 세션 체크 간격 (ms)
warningTime: number; // 만료 경고 시간 (ms)
maxInactiveTime: number; // 최대 비활성 시간 (ms)
}
interface SessionWarningCallbacks {
onWarning?: (remainingTime: number) => void;
onExpiry?: () => void;
onActivity?: () => void;
}
export class SessionManager {
private config: SessionConfig;
private callbacks: SessionWarningCallbacks;
private checkTimer: NodeJS.Timeout | null = null;
private warningTimer: NodeJS.Timeout | null = null;
private lastActivity: number = Date.now();
private isWarningShown: boolean = false;
constructor(config: Partial<SessionConfig> = {}, callbacks: SessionWarningCallbacks = {}) {
this.config = {
checkInterval: 60000, // 1분마다 체크
warningTime: 300000, // 5분 전 경고
maxInactiveTime: 1800000, // 30분 비활성 시 만료
...config,
};
this.callbacks = callbacks;
this.setupActivityListeners();
}
/**
* 세션 모니터링 시작
*/
start() {
this.stop(); // 기존 타이머 정리
this.checkTimer = setInterval(() => {
this.checkSession();
}, this.config.checkInterval);
console.log("세션 모니터링 시작됨");
}
/**
* 세션 모니터링 중지
*/
stop() {
if (this.checkTimer) {
clearInterval(this.checkTimer);
this.checkTimer = null;
}
if (this.warningTimer) {
clearTimeout(this.warningTimer);
this.warningTimer = null;
}
this.removeActivityListeners();
console.log("세션 모니터링 중지됨");
}
/**
* 사용자 활동 기록
*/
recordActivity() {
this.lastActivity = Date.now();
this.isWarningShown = false;
// 경고 타이머가 있다면 취소
if (this.warningTimer) {
clearTimeout(this.warningTimer);
this.warningTimer = null;
}
this.callbacks.onActivity?.();
}
/**
* 세션 상태 확인
*/
private checkSession() {
const now = Date.now();
const timeSinceLastActivity = now - this.lastActivity;
const timeUntilExpiry = this.config.maxInactiveTime - timeSinceLastActivity;
// 세션 만료 체크
if (timeSinceLastActivity >= this.config.maxInactiveTime) {
this.handleSessionExpiry();
return;
}
// 경고 시간 체크
if (timeUntilExpiry <= this.config.warningTime && !this.isWarningShown) {
this.showSessionWarning(timeUntilExpiry);
}
}
/**
* 세션 만료 경고 표시
*/
private showSessionWarning(remainingTime: number) {
this.isWarningShown = true;
this.callbacks.onWarning?.(remainingTime);
// 남은 시간 후 자동 만료
this.warningTimer = setTimeout(() => {
this.handleSessionExpiry();
}, remainingTime);
}
/**
* 세션 만료 처리
*/
private handleSessionExpiry() {
console.log("세션이 만료되었습니다");
this.stop();
this.callbacks.onExpiry?.();
}
/**
* 활동 리스너 설정
*/
private setupActivityListeners() {
// 사용자 활동 이벤트들
const activityEvents = ["mousedown", "mousemove", "keypress", "scroll", "touchstart", "click"];
// 각 이벤트에 대해 리스너 등록
activityEvents.forEach((event) => {
document.addEventListener(event, this.handleActivity, true);
});
// 페이지 가시성 변경 이벤트
document.addEventListener("visibilitychange", this.handleVisibilityChange);
}
/**
* 활동 리스너 제거
*/
private removeActivityListeners() {
const activityEvents = ["mousedown", "mousemove", "keypress", "scroll", "touchstart", "click"];
activityEvents.forEach((event) => {
document.removeEventListener(event, this.handleActivity, true);
});
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
}
/**
* 활동 이벤트 핸들러
*/
private handleActivity = () => {
this.recordActivity();
};
/**
* 페이지 가시성 변경 핸들러
*/
private handleVisibilityChange = () => {
if (!document.hidden) {
this.recordActivity();
}
};
/**
* 현재 세션 상태 정보 반환
*/
getSessionInfo() {
const now = Date.now();
const timeSinceLastActivity = now - this.lastActivity;
const timeUntilExpiry = this.config.maxInactiveTime - timeSinceLastActivity;
return {
lastActivity: this.lastActivity,
timeSinceLastActivity,
timeUntilExpiry: Math.max(0, timeUntilExpiry),
isActive: timeSinceLastActivity < this.config.maxInactiveTime,
isWarningShown: this.isWarningShown,
};
}
}
/**
* 시간을 분:초 형식으로 포맷
*/
export function formatTime(milliseconds: number): string {
const seconds = Math.floor(milliseconds / 1000);
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;
}
/**
* 전역 세션 매니저 인스턴스
*/
let globalSessionManager: SessionManager | null = null;
/**
* 전역 세션 매니저 초기화
*/
export function initSessionManager(
config: Partial<SessionConfig> = {},
callbacks: SessionWarningCallbacks = {},
): SessionManager {
if (globalSessionManager) {
globalSessionManager.stop();
}
globalSessionManager = new SessionManager(config, callbacks);
return globalSessionManager;
}
/**
* 전역 세션 매니저 가져오기
*/
export function getSessionManager(): SessionManager | null {
return globalSessionManager;
}
/**
* 세션 매니저 정리
*/
export function cleanupSessionManager() {
if (globalSessionManager) {
globalSessionManager.stop();
globalSessionManager = null;
}
}
export default SessionManager;