Exocore-Backend / dist /auth /delete.js
ChoruYt's picture
Upload 38 files
a21d6a5 verified
Raw
History Blame Contribute Delete
6.92 kB
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deleteUserFolder = exports.drive = void 0;
exports.default = deleteHandler;
const drive_1 = require("../services/drive");
Object.defineProperty(exports, "drive", { enumerable: true, get: function () { return drive_1.drive; } });
Object.defineProperty(exports, "deleteUserFolder", { enumerable: true, get: function () { return drive_1.deleteUserFolder; } });
/**
* Delete the account belonging to the supplied token / identifier.
* Body / query params:
* - token (optional) — the user's auth token
* - pass (required) — current password, used as a safety re-confirm
* - user / username / email / id (optional) — fallback identifier
*
* FAST PATH: if a username is supplied, we look up that ONE folder in Drive
* and read only its database.json (≈2 Drive calls). The slow fallback that
* scans every user is only used when we have no username at all.
*/
async function deleteHandler(req, res) {
try {
const params = { ...req.query, ...req.body };
const token = params.token;
const pass = params.pass;
const username = (params.username ?? params.user);
const email = params.email;
const idParam = params.id;
const identifier = username ?? email ?? idParam;
console.log(`[delete] hit — method=${req.method} hasToken=${!!token} tokenLen=${token?.length ?? 0} hasPass=${!!pass} username=${username ?? "(none)"} email=${email ?? "(none)"}`);
if (!pass) {
console.log("[delete] reject: no pass");
return res.status(400).json({ success: false, message: "Password is required to confirm deletion" });
}
if (!token && !identifier) {
console.log("[delete] reject: no token and no identifier");
return res.status(400).json({ success: false, message: "Token or username/email is required" });
}
let found;
let folderId = null;
// Normalize the username candidates we'll try (with and without @ prefix,
// lowercased) so a small mismatch doesn't break delete.
const usernameCandidates = new Set();
if (username) {
const u = String(username).trim();
usernameCandidates.add(u);
usernameCandidates.add(u.toLowerCase());
const noAt = u.replace(/^@+/, "");
usernameCandidates.add(noAt);
usernameCandidates.add(noAt.toLowerCase());
usernameCandidates.add(`@${noAt}`);
usernameCandidates.add(`@${noAt.toLowerCase()}`);
}
// FAST PATH — try every username candidate against the cache directly.
for (const cand of usernameCandidates) {
const t0 = Date.now();
const fid = await (0, drive_1.getUserFolder)(cand);
console.log(`[delete] fast-path lookup "${cand}" → ${fid ?? "NOT FOUND"} (${Date.now() - t0}ms)`);
if (fid) {
folderId = fid;
const db = await (0, drive_1.readUserDb)(fid);
if (db) {
found = db;
break;
}
}
}
// SLOW PATH — scan all users and match by ANY identifier we have.
if (!found) {
console.log("[delete] slow-path: scanning all users…");
const t0 = Date.now();
const users = await (0, drive_1.getAllUsers)();
console.log(`[delete] loaded ${users.length} users (${Date.now() - t0}ms)`);
const emailLower = (email ?? "").toString().toLowerCase().trim();
const idLower = (identifier ?? "").toString().toLowerCase().trim();
const idNoAt = idLower.replace(/^@+/, "");
found = users.find(u => {
const uname = (u.username || "").toLowerCase();
const unameNoAt = uname.replace(/^@+/, "");
const uemail = (u.email || "").toLowerCase();
if (token && u.token === token)
return true;
if (emailLower && uemail === emailLower)
return true;
if (idParam && String(u.id) === String(idParam))
return true;
if (identifier) {
if (uname === idLower || unameNoAt === idNoAt)
return true;
if (uemail === idLower)
return true;
}
for (const cand of usernameCandidates) {
if (uname === cand.toLowerCase())
return true;
}
return false;
});
if (found) {
console.log(`[delete] slow-path matched user=${found.username} email=${found.email}`);
folderId = await (0, drive_1.getUserFolder)(found.username);
console.log(`[delete] folder lookup for ${found.username}${folderId ?? "NOT FOUND"}`);
}
else {
// Helpful diagnostic — show first chars of a few cached usernames so
// we can tell whether the input simply doesn't exist.
const sample = users.slice(0, 5).map(u => u.username).join(", ");
console.log(`[delete] no match. tried candidates=${[...usernameCandidates].join("|")} email="${emailLower}" id="${idParam ?? ""}". cache sample: ${sample}`);
}
}
if (!found) {
console.log("[delete] reject: no user matched");
return res.status(401).json({ success: false, message: "Account not found for that token or identifier" });
}
if (token && found.token !== token) {
console.log(`[delete] note: provided token does not match stored token (using identifier match)`);
}
if (found.pass !== pass) {
console.log("[delete] reject: password mismatch");
return res.status(403).json({ success: false, message: "Password does not match — deletion cancelled" });
}
if (!folderId) {
console.log("[delete] reject: no drive folder found for user");
return res.status(404).json({ success: false, message: "User folder not found" });
}
const t2 = Date.now();
await (0, drive_1.deleteUserFolder)(folderId);
console.log(`[delete] ✅ account removed: ${found.username} (${found.email}) — drive delete ${Date.now() - t2}ms`);
return res.status(200).json({
success: true,
message: "Account deleted",
username: found.username,
email: found.email,
});
}
catch (err) {
console.error("[delete] error:", err?.message, err?.stack);
return res.status(500).json({ success: false, message: "Failed to delete account", error: err?.message });
}
}