Spaces:
Running
Running
| ; | |
| 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 }); | |
| } | |
| } | |