session-manager.tsā¢2.95 kB
import { DatabaseManager } from './database.js';
import { randomBytes } from 'crypto';
export interface Session {
sessionId: string;
createdAt: number;
lastActivity: number;
}
/**
* Manages sessions for the MCP Cut-Copy-Paste Clipboard Server
*/
export class SessionManager {
private dbManager: DatabaseManager;
private sessionTimeout: number;
/**
* @param dbManager - Database manager instance
* @param sessionTimeout - Session timeout in milliseconds (default: 24 hours)
*/
constructor(dbManager: DatabaseManager, sessionTimeout: number = 24 * 60 * 60 * 1000) {
this.dbManager = dbManager;
this.sessionTimeout = sessionTimeout;
}
/**
* Create a new session with a cryptographically secure ID
* @returns The generated session ID
*/
createSession(): string {
const sessionId = this.generateSecureId();
const timestamp = Date.now();
const db = this.dbManager.getConnection();
db.prepare(
`INSERT INTO sessions (session_id, created_at, last_activity)
VALUES (?, ?, ?)`
).run(sessionId, timestamp, timestamp);
return sessionId;
}
/**
* Retrieve a session by ID and update its last activity timestamp
* This method implements a "touch" behavior - accessing a session updates its activity time
* @param sessionId - The session ID to retrieve
* @returns The session object or null if not found
*/
getSession(sessionId: string): Session | null {
if (!sessionId || typeof sessionId !== 'string') {
return null;
}
const db = this.dbManager.getConnection();
// First, check if session exists
const session = db.prepare('SELECT * FROM sessions WHERE session_id = ?').get(sessionId) as
| { session_id: string; created_at: number; last_activity: number }
| undefined;
if (!session) {
return null;
}
// Update last activity timestamp (touch behavior)
const timestamp = Date.now();
db.prepare('UPDATE sessions SET last_activity = ? WHERE session_id = ?').run(
timestamp,
sessionId
);
return {
sessionId: session.session_id,
createdAt: session.created_at,
lastActivity: timestamp,
};
}
/**
* Remove sessions that have exceeded the timeout period
* @returns Number of sessions removed
*/
cleanupExpiredSessions(): number {
const db = this.dbManager.getConnection();
const cutoffTime = Date.now() - this.sessionTimeout;
const result = db.prepare('DELETE FROM sessions WHERE last_activity < ?').run(cutoffTime);
return result.changes;
}
/**
* Generate a cryptographically secure session ID
* @returns A secure random string suitable for use as a session ID
*/
private generateSecureId(): string {
// Generate 32 random bytes and convert to base64url format
const bytes = randomBytes(32);
return bytes.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
}