if user has pass ask for it before deleting it
This commit is contained in:
@@ -168,20 +168,39 @@ export async function deleteUser(req: FastifyRequest, reply: FastifyReply) {
|
|||||||
const { id } = req.params as { id: string };
|
const { id } = req.params as { id: string };
|
||||||
const userId = parseInt(id, 10);
|
const userId = parseInt(id, 10);
|
||||||
|
|
||||||
|
const body = (req.body ?? {}) as { password?: string };
|
||||||
|
const password = body.password;
|
||||||
|
|
||||||
if (!userId || isNaN(userId)) {
|
if (!userId || isNaN(userId)) {
|
||||||
return reply.code(400).send({ error: "Invalid user id" });
|
return reply.code(400).send({ error: "Invalid user id" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await userService.deleteUser(userId);
|
const user = await userService.getUserById(userId);
|
||||||
|
if (!user) {
|
||||||
if (result && result.changes > 0) {
|
|
||||||
return { success: true, message: "User deleted successfully" };
|
|
||||||
} else {
|
|
||||||
return reply.code(404).send({ error: "User not found" });
|
return reply.code(404).send({ error: "User not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.has_password) {
|
||||||
|
if (!password) {
|
||||||
|
return reply.code(401).send({ error: "Password required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValid = await userService.verifyPassword(userId, password);
|
||||||
|
if (!isValid) {
|
||||||
|
return reply.code(401).send({ error: "Incorrect password" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await userService.deleteUser(userId);
|
||||||
|
|
||||||
|
if (result.changes > 0) {
|
||||||
|
return reply.send({ success: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.code(500).send({ error: "Failed to delete user" });
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Delete User Error:", (err as Error).message);
|
console.error("Delete User Error:", err);
|
||||||
return reply.code(500).send({ error: "Failed to delete user" });
|
return reply.code(500).send({ error: "Failed to delete user" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,16 +265,17 @@ export async function changePassword(req: FastifyRequest, reply: FastifyReply) {
|
|||||||
return reply.code(404).send({ error: "User not found" });
|
return reply.code(404).send({ error: "User not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si el usuario tiene contraseña actual, debe proporcionar la contraseña actual
|
if (user.has_password) {
|
||||||
if (user.has_password && currentPassword) {
|
if (!currentPassword) {
|
||||||
const isValid = await userService.verifyPassword(userId, currentPassword);
|
return reply.code(401).send({ error: "Current password required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValid = await userService.verifyPassword(userId, currentPassword);
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
return reply.code(401).send({ error: "Current password is incorrect" });
|
return reply.code(401).send({ error: "Current password is incorrect" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar la contraseña (null para eliminarla, string para establecerla)
|
|
||||||
await userService.updateUser(userId, { password: newPassword });
|
await userService.updateUser(userId, { password: newPassword });
|
||||||
|
|
||||||
return reply.send({
|
return reply.send({
|
||||||
|
|||||||
@@ -694,30 +694,102 @@ window.handleDeleteConfirmation = function(userId) {
|
|||||||
|
|
||||||
closeModal();
|
closeModal();
|
||||||
|
|
||||||
|
if (user.has_password) {
|
||||||
|
modalAniList.innerHTML = `
|
||||||
|
<div class="modal-overlay"></div>
|
||||||
|
<div class="modal-content" style="max-width:400px;">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>Confirm Deletion</h2>
|
||||||
|
<button class="modal-close" onclick="closeModal()">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="deleteWithPasswordForm">
|
||||||
|
<p style="margin-bottom:1.25rem; color:var(--color-text-secondary)">
|
||||||
|
Enter your password to permanently delete
|
||||||
|
<b>${user.username}</b>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="deletePassword">Password</label>
|
||||||
|
<div class="password-toggle-wrapper">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="deletePassword"
|
||||||
|
required
|
||||||
|
placeholder="Enter password"
|
||||||
|
autofocus
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="password-toggle-btn"
|
||||||
|
onclick="togglePasswordVisibility('deletePassword', this)"
|
||||||
|
>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||||||
|
<circle cx="12" cy="12" r="3"></circle>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button type="button" class="btn-secondary" onclick="closeModal()">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn-disconnect">
|
||||||
|
Delete Profile
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
modalAniList.classList.add('active');
|
||||||
|
|
||||||
|
document
|
||||||
|
.getElementById('deleteWithPasswordForm')
|
||||||
|
.addEventListener('submit', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
const password = document.getElementById('deletePassword').value;
|
||||||
|
handleConfirmedDeleteUser(userId, password);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
showConfirmationModal(
|
showConfirmationModal(
|
||||||
'Confirm Deletion',
|
'Confirm Deletion',
|
||||||
`Are you absolutely sure you want to delete profile ${user.username}? This action cannot be undone.`,
|
`Are you absolutely sure you want to delete profile ${user.username}?`,
|
||||||
`handleConfirmedDeleteUser(${userId})`
|
`handleConfirmedDeleteUser(${userId})`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.handleConfirmedDeleteUser = async function(userId) {
|
window.handleConfirmedDeleteUser = async function(userId, password = null) {
|
||||||
closeModal();
|
closeModal();
|
||||||
showUserToast('Deleting user...', 'info');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/users/${userId}`, { method: 'DELETE' });
|
const options = { method: 'DELETE' };
|
||||||
|
|
||||||
|
if (password) {
|
||||||
|
options.headers = { 'Content-Type': 'application/json' };
|
||||||
|
options.body = JSON.stringify({ password });
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch(`${API_BASE}/users/${userId}`, options);
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const error = await res.json();
|
const error = await res.json();
|
||||||
throw new Error(error.error || 'Error deleting user');
|
throw new Error(error.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadUsers();
|
await loadUsers();
|
||||||
showUserToast('User deleted successfully!', 'success');
|
showUserToast('User deleted successfully!', 'success');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
showUserToast(err.message || 'Error deleting user', 'error');
|
||||||
showUserToast('Error deleting user', 'error');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -168,20 +168,39 @@ export async function deleteUser(req: FastifyRequest, reply: FastifyReply) {
|
|||||||
const { id } = req.params as { id: string };
|
const { id } = req.params as { id: string };
|
||||||
const userId = parseInt(id, 10);
|
const userId = parseInt(id, 10);
|
||||||
|
|
||||||
|
const body = (req.body ?? {}) as { password?: string };
|
||||||
|
const password = body.password;
|
||||||
|
|
||||||
if (!userId || isNaN(userId)) {
|
if (!userId || isNaN(userId)) {
|
||||||
return reply.code(400).send({ error: "Invalid user id" });
|
return reply.code(400).send({ error: "Invalid user id" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await userService.deleteUser(userId);
|
const user = await userService.getUserById(userId);
|
||||||
|
if (!user) {
|
||||||
if (result && result.changes > 0) {
|
|
||||||
return { success: true, message: "User deleted successfully" };
|
|
||||||
} else {
|
|
||||||
return reply.code(404).send({ error: "User not found" });
|
return reply.code(404).send({ error: "User not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.has_password) {
|
||||||
|
if (!password) {
|
||||||
|
return reply.code(401).send({ error: "Password required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValid = await userService.verifyPassword(userId, password);
|
||||||
|
if (!isValid) {
|
||||||
|
return reply.code(401).send({ error: "Incorrect password" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await userService.deleteUser(userId);
|
||||||
|
|
||||||
|
if (result.changes > 0) {
|
||||||
|
return reply.send({ success: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.code(500).send({ error: "Failed to delete user" });
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Delete User Error:", (err as Error).message);
|
console.error("Delete User Error:", err);
|
||||||
return reply.code(500).send({ error: "Failed to delete user" });
|
return reply.code(500).send({ error: "Failed to delete user" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,16 +265,17 @@ export async function changePassword(req: FastifyRequest, reply: FastifyReply) {
|
|||||||
return reply.code(404).send({ error: "User not found" });
|
return reply.code(404).send({ error: "User not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si el usuario tiene contraseña actual, debe proporcionar la contraseña actual
|
if (user.has_password) {
|
||||||
if (user.has_password && currentPassword) {
|
if (!currentPassword) {
|
||||||
const isValid = await userService.verifyPassword(userId, currentPassword);
|
return reply.code(401).send({ error: "Current password required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValid = await userService.verifyPassword(userId, currentPassword);
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
return reply.code(401).send({ error: "Current password is incorrect" });
|
return reply.code(401).send({ error: "Current password is incorrect" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar la contraseña (null para eliminarla, string para establecerla)
|
|
||||||
await userService.updateUser(userId, { password: newPassword });
|
await userService.updateUser(userId, { password: newPassword });
|
||||||
|
|
||||||
return reply.send({
|
return reply.send({
|
||||||
|
|||||||
@@ -694,30 +694,102 @@ window.handleDeleteConfirmation = function(userId) {
|
|||||||
|
|
||||||
closeModal();
|
closeModal();
|
||||||
|
|
||||||
|
if (user.has_password) {
|
||||||
|
modalAniList.innerHTML = `
|
||||||
|
<div class="modal-overlay"></div>
|
||||||
|
<div class="modal-content" style="max-width:400px;">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>Confirm Deletion</h2>
|
||||||
|
<button class="modal-close" onclick="closeModal()">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="deleteWithPasswordForm">
|
||||||
|
<p style="margin-bottom:1.25rem; color:var(--color-text-secondary)">
|
||||||
|
Enter your password to permanently delete
|
||||||
|
<b>${user.username}</b>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="deletePassword">Password</label>
|
||||||
|
<div class="password-toggle-wrapper">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="deletePassword"
|
||||||
|
required
|
||||||
|
placeholder="Enter password"
|
||||||
|
autofocus
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="password-toggle-btn"
|
||||||
|
onclick="togglePasswordVisibility('deletePassword', this)"
|
||||||
|
>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||||||
|
<circle cx="12" cy="12" r="3"></circle>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button type="button" class="btn-secondary" onclick="closeModal()">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn-disconnect">
|
||||||
|
Delete Profile
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
modalAniList.classList.add('active');
|
||||||
|
|
||||||
|
document
|
||||||
|
.getElementById('deleteWithPasswordForm')
|
||||||
|
.addEventListener('submit', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
const password = document.getElementById('deletePassword').value;
|
||||||
|
handleConfirmedDeleteUser(userId, password);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
showConfirmationModal(
|
showConfirmationModal(
|
||||||
'Confirm Deletion',
|
'Confirm Deletion',
|
||||||
`Are you absolutely sure you want to delete profile ${user.username}? This action cannot be undone.`,
|
`Are you absolutely sure you want to delete profile ${user.username}?`,
|
||||||
`handleConfirmedDeleteUser(${userId})`
|
`handleConfirmedDeleteUser(${userId})`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.handleConfirmedDeleteUser = async function(userId) {
|
window.handleConfirmedDeleteUser = async function(userId, password = null) {
|
||||||
closeModal();
|
closeModal();
|
||||||
showUserToast('Deleting user...', 'info');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/users/${userId}`, { method: 'DELETE' });
|
const options = { method: 'DELETE' };
|
||||||
|
|
||||||
|
if (password) {
|
||||||
|
options.headers = { 'Content-Type': 'application/json' };
|
||||||
|
options.body = JSON.stringify({ password });
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch(`${API_BASE}/users/${userId}`, options);
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const error = await res.json();
|
const error = await res.json();
|
||||||
throw new Error(error.error || 'Error deleting user');
|
throw new Error(error.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadUsers();
|
await loadUsers();
|
||||||
showUserToast('User deleted successfully!', 'success');
|
showUserToast('User deleted successfully!', 'success');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
showUserToast(err.message || 'Error deleting user', 'error');
|
||||||
showUserToast('Error deleting user', 'error');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user