123 lines
2.8 KiB
JavaScript
123 lines
2.8 KiB
JavaScript
|
|
const fs = require('fs');
|
||
|
|
const path = require('path');
|
||
|
|
|
||
|
|
const LOCK_FILE = path.join(__dirname, '../data/login_locks.json');
|
||
|
|
|
||
|
|
const DEFAULT_CONFIG = {
|
||
|
|
maxAttempts: 3,
|
||
|
|
lockDuration: 10 * 60 * 1000
|
||
|
|
};
|
||
|
|
|
||
|
|
function ensureDataDir() {
|
||
|
|
const dataDir = path.join(__dirname, '../data');
|
||
|
|
if (!fs.existsSync(dataDir)) {
|
||
|
|
fs.mkdirSync(dataDir, { recursive: true });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function loadLocks() {
|
||
|
|
ensureDataDir();
|
||
|
|
try {
|
||
|
|
if (fs.existsSync(LOCK_FILE)) {
|
||
|
|
const data = fs.readFileSync(LOCK_FILE, 'utf8');
|
||
|
|
return JSON.parse(data);
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
console.error('Error loading login locks:', e);
|
||
|
|
}
|
||
|
|
return { users: {}, config: DEFAULT_CONFIG };
|
||
|
|
}
|
||
|
|
|
||
|
|
function saveLocks(locks) {
|
||
|
|
ensureDataDir();
|
||
|
|
try {
|
||
|
|
fs.writeFileSync(LOCK_FILE, JSON.stringify(locks, null, 2));
|
||
|
|
} catch (e) {
|
||
|
|
console.error('Error saving login locks:', e);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function getLockStatus(username) {
|
||
|
|
const locks = loadLocks();
|
||
|
|
const userLock = locks.users[username];
|
||
|
|
|
||
|
|
if (!userLock) {
|
||
|
|
return { locked: false, attempts: 0 };
|
||
|
|
}
|
||
|
|
|
||
|
|
const now = Date.now();
|
||
|
|
if (userLock.lockedUntil && now < userLock.lockedUntil) {
|
||
|
|
const remainingMs = userLock.lockedUntil - now;
|
||
|
|
return {
|
||
|
|
locked: true,
|
||
|
|
attempts: userLock.attempts,
|
||
|
|
lockedUntil: userLock.lockedUntil,
|
||
|
|
remainingMs: remainingMs
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
if (userLock.lockedUntil && now >= userLock.lockedUntil) {
|
||
|
|
const newLocks = loadLocks();
|
||
|
|
delete newLocks.users[username];
|
||
|
|
saveLocks(newLocks);
|
||
|
|
return { locked: false, attempts: 0 };
|
||
|
|
}
|
||
|
|
|
||
|
|
return { locked: false, attempts: userLock.attempts || 0 };
|
||
|
|
}
|
||
|
|
|
||
|
|
function recordFailedAttempt(username) {
|
||
|
|
const locks = loadLocks();
|
||
|
|
const now = Date.now();
|
||
|
|
|
||
|
|
if (!locks.users[username]) {
|
||
|
|
locks.users[username] = { attempts: 0 };
|
||
|
|
}
|
||
|
|
|
||
|
|
const userLock = locks.users[username];
|
||
|
|
|
||
|
|
if (userLock.lockedUntil && now >= userLock.lockedUntil) {
|
||
|
|
userLock.attempts = 0;
|
||
|
|
userLock.lockedUntil = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
userLock.attempts = (userLock.attempts || 0) + 1;
|
||
|
|
userLock.lastAttempt = now;
|
||
|
|
|
||
|
|
const maxAttempts = locks.config?.maxAttempts || DEFAULT_CONFIG.maxAttempts;
|
||
|
|
const lockDuration = locks.config?.lockDuration || DEFAULT_CONFIG.lockDuration;
|
||
|
|
|
||
|
|
if (userLock.attempts >= maxAttempts) {
|
||
|
|
userLock.lockedUntil = now + lockDuration;
|
||
|
|
}
|
||
|
|
|
||
|
|
saveLocks(locks);
|
||
|
|
|
||
|
|
return getLockStatus(username);
|
||
|
|
}
|
||
|
|
|
||
|
|
function clearLock(username) {
|
||
|
|
const locks = loadLocks();
|
||
|
|
delete locks.users[username];
|
||
|
|
saveLocks(locks);
|
||
|
|
}
|
||
|
|
|
||
|
|
function getConfig() {
|
||
|
|
const locks = loadLocks();
|
||
|
|
return locks.config || DEFAULT_CONFIG;
|
||
|
|
}
|
||
|
|
|
||
|
|
function updateConfig(newConfig) {
|
||
|
|
const locks = loadLocks();
|
||
|
|
locks.config = { ...DEFAULT_CONFIG, ...newConfig };
|
||
|
|
saveLocks(locks);
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
getLockStatus,
|
||
|
|
recordFailedAttempt,
|
||
|
|
clearLock,
|
||
|
|
getConfig,
|
||
|
|
updateConfig
|
||
|
|
};
|