2025-08-05 00:31:43 +03:00
|
|
|
import { ReactiveCache } from '/imports/reactiveCache';
|
|
|
|
|
|
|
|
|
|
// Method to find locked users and release them if needed
|
|
|
|
|
Meteor.methods({
|
2026-01-29 21:29:56 +02:00
|
|
|
async getLockedUsers() {
|
2025-08-05 00:31:43 +03:00
|
|
|
// Check if user has admin rights
|
|
|
|
|
const userId = Meteor.userId();
|
|
|
|
|
if (!userId) {
|
|
|
|
|
throw new Meteor.Error('error-invalid-user', 'Invalid user');
|
|
|
|
|
}
|
|
|
|
|
const user = ReactiveCache.getUser(userId);
|
|
|
|
|
if (!user || !user.isAdmin) {
|
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Not allowed');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Current time to check against unlockTime
|
|
|
|
|
const currentTime = Number(new Date());
|
|
|
|
|
|
|
|
|
|
// Find users that are locked (known users)
|
2026-01-29 21:29:56 +02:00
|
|
|
const lockedUsers = await Meteor.users.find(
|
2025-08-05 00:31:43 +03:00
|
|
|
{
|
|
|
|
|
'services.accounts-lockout.unlockTime': {
|
|
|
|
|
$gt: currentTime,
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
fields: {
|
|
|
|
|
_id: 1,
|
|
|
|
|
username: 1,
|
|
|
|
|
emails: 1,
|
|
|
|
|
'services.accounts-lockout.unlockTime': 1,
|
|
|
|
|
'services.accounts-lockout.failedAttempts': 1
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-29 21:29:56 +02:00
|
|
|
).fetchAsync();
|
2025-08-05 00:31:43 +03:00
|
|
|
|
|
|
|
|
// Format the results for the UI
|
|
|
|
|
return lockedUsers.map(user => {
|
|
|
|
|
const email = user.emails && user.emails.length > 0 ? user.emails[0].address : 'No email';
|
|
|
|
|
const remainingLockTime = Math.round((user.services['accounts-lockout'].unlockTime - currentTime) / 1000);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
_id: user._id,
|
|
|
|
|
username: user.username || 'No username',
|
|
|
|
|
email,
|
|
|
|
|
failedAttempts: user.services['accounts-lockout'].failedAttempts || 0,
|
|
|
|
|
unlockTime: user.services['accounts-lockout'].unlockTime,
|
|
|
|
|
remainingLockTime // in seconds
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2026-01-29 21:29:56 +02:00
|
|
|
async unlockUser(userId) {
|
2025-08-05 00:31:43 +03:00
|
|
|
// Check if user has admin rights
|
|
|
|
|
const adminId = Meteor.userId();
|
|
|
|
|
if (!adminId) {
|
|
|
|
|
throw new Meteor.Error('error-invalid-user', 'Invalid user');
|
|
|
|
|
}
|
|
|
|
|
const admin = ReactiveCache.getUser(adminId);
|
|
|
|
|
if (!admin || !admin.isAdmin) {
|
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Not allowed');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure the user to unlock exists
|
2026-01-29 21:29:56 +02:00
|
|
|
const userToUnlock = await Meteor.users.findOneAsync(userId);
|
2025-08-05 00:31:43 +03:00
|
|
|
if (!userToUnlock) {
|
|
|
|
|
throw new Meteor.Error('error-user-not-found', 'User not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unlock the user
|
2026-01-29 21:29:56 +02:00
|
|
|
await Meteor.users.updateAsync(
|
2025-08-05 00:31:43 +03:00
|
|
|
{ _id: userId },
|
|
|
|
|
{
|
|
|
|
|
$unset: {
|
|
|
|
|
'services.accounts-lockout': 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
2026-01-29 21:29:56 +02:00
|
|
|
async unlockAllUsers() {
|
2025-08-05 00:31:43 +03:00
|
|
|
// Check if user has admin rights
|
|
|
|
|
const adminId = Meteor.userId();
|
|
|
|
|
if (!adminId) {
|
|
|
|
|
throw new Meteor.Error('error-invalid-user', 'Invalid user');
|
|
|
|
|
}
|
|
|
|
|
const admin = ReactiveCache.getUser(adminId);
|
|
|
|
|
if (!admin || !admin.isAdmin) {
|
|
|
|
|
throw new Meteor.Error('error-not-allowed', 'Not allowed');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unlock all users
|
2026-01-29 21:29:56 +02:00
|
|
|
await Meteor.users.updateAsync(
|
2025-08-05 00:31:43 +03:00
|
|
|
{ 'services.accounts-lockout.unlockTime': { $exists: true } },
|
|
|
|
|
{
|
|
|
|
|
$unset: {
|
|
|
|
|
'services.accounts-lockout': 1
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{ multi: true }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
});
|