ci-validation
Version:
🇺🇾 Complete TypeScript/JavaScript library for validating Uruguayan CI (Cédula de Identidad) with official algorithm and government service integration
191 lines • 6.68 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileSessionStorage = void 0;
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
/**
* File-based session storage implementation
* Stores sessions as JSON files in the filesystem
*/
class FileSessionStorage {
constructor(options = {}) {
this.sessionDir = options.sessionDir || (process.env.VERCEL ? "/tmp/sessions" : path_1.default.join(process.cwd(), "sessions"));
this.expirationTime = options.expirationTime || 24 * 60 * 60 * 1000; // 24 hours
this.autoCleanup = options.autoCleanup !== false;
// Initialize session directory
this.ensureSessionDirectory();
// Start cleanup timer if auto cleanup is enabled
if (this.autoCleanup) {
const cleanupInterval = options.cleanupInterval || 60 * 60 * 1000; // 1 hour
this.cleanupTimer = setInterval(() => {
this.cleanupExpiredSessions().catch(console.error);
}, cleanupInterval);
}
}
/**
* Ensures the session directory exists
*/
async ensureSessionDirectory() {
try {
await fs_1.promises.mkdir(this.sessionDir, { recursive: true });
}
catch (error) {
console.error("Error creating session directory:", error);
}
}
/**
* Gets the file path for a session
*/
getSessionFilePath(sessionId) {
return path_1.default.join(this.sessionDir, `${sessionId}.json`);
}
/**
* Save session data to file
*/
async saveSession(sessionId, sessionData) {
try {
const now = Date.now();
const enrichedSessionData = {
...sessionData,
createdAt: sessionData.createdAt || now,
lastUsed: now,
expiresAt: now + this.expirationTime,
};
const filePath = this.getSessionFilePath(sessionId);
await fs_1.promises.writeFile(filePath, JSON.stringify(enrichedSessionData, null, 2), "utf-8");
console.log(`✅ Session saved to file: ${sessionId}`);
}
catch (error) {
console.error(`❌ Error saving session ${sessionId}:`, error);
throw error;
}
}
/**
* Load session data from file
*/
async loadSession(sessionId) {
try {
const filePath = this.getSessionFilePath(sessionId);
const sessionData = await fs_1.promises.readFile(filePath, "utf-8");
const parsedSession = JSON.parse(sessionData);
// Check if session is expired
if (parsedSession.expiresAt && Date.now() > parsedSession.expiresAt) {
console.log(`⏰ Session ${sessionId} has expired, removing...`);
await this.deleteSession(sessionId);
return null;
}
// Update last used timestamp
await this.touchSession(sessionId);
console.log(`✅ Session loaded from file: ${sessionId}`);
return parsedSession;
}
catch (error) {
if (error.code === "ENOENT") {
console.log(`ℹ️ Session file not found: ${sessionId}`);
return null;
}
console.error(`❌ Error loading session ${sessionId}:`, error);
throw error;
}
}
/**
* Delete session file
*/
async deleteSession(sessionId) {
try {
const filePath = this.getSessionFilePath(sessionId);
await fs_1.promises.unlink(filePath);
console.log(`🗑️ Session deleted: ${sessionId}`);
}
catch (error) {
if (error.code === "ENOENT") {
console.log(`ℹ️ Session file already deleted: ${sessionId}`);
return;
}
console.error(`❌ Error deleting session ${sessionId}:`, error);
throw error;
}
}
/**
* Check if session file exists
*/
async sessionExists(sessionId) {
try {
const filePath = this.getSessionFilePath(sessionId);
await fs_1.promises.access(filePath);
return true;
}
catch {
return false;
}
}
/**
* Update session's last used timestamp
*/
async touchSession(sessionId) {
try {
const session = await this.loadSessionRaw(sessionId);
if (session) {
session.lastUsed = Date.now();
const filePath = this.getSessionFilePath(sessionId);
await fs_1.promises.writeFile(filePath, JSON.stringify(session, null, 2), "utf-8");
}
}
catch (error) {
console.error(`❌ Error touching session ${sessionId}:`, error);
}
}
/**
* Load session data without updating last used timestamp
*/
async loadSessionRaw(sessionId) {
try {
const filePath = this.getSessionFilePath(sessionId);
const sessionData = await fs_1.promises.readFile(filePath, "utf-8");
return JSON.parse(sessionData);
}
catch {
return null;
}
}
/**
* Clean up expired session files
*/
async cleanupExpiredSessions() {
try {
const files = await fs_1.promises.readdir(this.sessionDir);
const now = Date.now();
let cleanedCount = 0;
for (const file of files) {
if (file.endsWith(".json")) {
const sessionId = file.replace(".json", "");
const session = await this.loadSessionRaw(sessionId);
if (session && session.expiresAt && now > session.expiresAt) {
await this.deleteSession(sessionId);
cleanedCount++;
}
}
}
if (cleanedCount > 0) {
console.log(`🧹 Cleaned up ${cleanedCount} expired sessions`);
}
}
catch (error) {
console.error("❌ Error during session cleanup:", error);
}
}
/**
* Cleanup resources
*/
destroy() {
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = undefined;
}
}
}
exports.FileSessionStorage = FileSessionStorage;
//# sourceMappingURL=FileSessionStorage.js.map