fix(ops): dedupe removes inactive duplicate accounts, not just never-logged-in

Frédérique's duplicate (frederique@targointernet.com) had an old last_login but
was already deactivated, so the "never-logged-in only" rule skipped it and
nothing happened. Now a non-keeper duplicate is deleted when it's inactive OR
never logged in; only a genuinely second active+used account is skipped for
manual resolution. Keeper = active first, then most-recent login, then oldest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-07-10 14:16:10 -04:00
parent d0f899cf6d
commit 6826f11587

View File

@ -364,12 +364,17 @@ async function handle (req, res, method, path, url) {
const r = await akFetch('/core/users/?search=' + encodeURIComponent(email) + '&page_size=50')
const recs = (r.data.results || []).filter(u => String(u.email || '').toLowerCase() === email)
if (recs.length < 2) return json(res, 200, { ok: true, noop: true, count: recs.length })
const used = recs.filter(u => u.last_login).sort((a, b) => new Date(b.last_login) - new Date(a.last_login))
const keeper = used[0] || recs.slice().sort((a, b) => new Date(a.date_joined) - new Date(b.date_joined))[0]
// KEEPER = compte à GARDER : actif d'abord, puis dernier last_login, puis plus ancien (l'original).
const keeper = recs.slice().sort((a, b) =>
(b.is_active ? 1 : 0) - (a.is_active ? 1 : 0) ||
(new Date(b.last_login || 0) - new Date(a.last_login || 0)) ||
(new Date(a.date_joined || 0) - new Date(b.date_joined || 0)))[0]
const deleted = [], skipped = []
for (const u of recs) {
if (u.pk === keeper.pk) continue
if (u.last_login) { skipped.push(u.username + ' (déjà utilisé — résoudre manuellement)'); continue } // ne jamais supprimer un compte utilisé
// Supprimer un doublon SÛR : inactif OU jamais connecté (aucune session active). On NE supprime PAS
// un 2e compte ENCORE actif et utilisé (rare) → à résoudre manuellement.
if (u.is_active && u.last_login) { skipped.push(u.username + ' (2e compte actif utilisé — résoudre manuellement)'); continue }
const d = await akFetch('/core/users/' + u.pk + '/', 'DELETE')
if (d.status < 300) deleted.push(u.username); else skipped.push(u.username + ' (erreur ' + d.status + ')')
}