diff --git a/src/models/index.js b/src/models/index.js index c0236fa..02437a9 100644 --- a/src/models/index.js +++ b/src/models/index.js @@ -1,6 +1,10 @@ const db = require('../config/database'); const bcrypt = require('bcryptjs'); const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); + +const uploadsDir = process.env.UPLOADS_DIR || './data/uploads'; // ─── Helpers ────────────────────────────────────────────── function generateCode(length = 5) { @@ -150,6 +154,8 @@ const Users = { // get username from userId before scrambling const user = this.findById(userId); const scrambled = `_deleted_${user.username}_${Date.now()}`; + // Delete uploaded images from disk and clear from DB + this._scrubUserContent(userId); db.prepare('UPDATE users SET username = ?, display_name = ?, password_hash = ?, is_admin = 0, is_organizer = 0 WHERE id = ?') .run(scrambled, '[deleted]', '', userId); db.prepare('UPDATE password_reset_tokens SET used = 1 WHERE user_id = ?').run(userId); @@ -157,12 +163,25 @@ const Users = { }, hardDeleteUser(userId) { + this._scrubUserContent(userId); db.prepare('DELETE FROM scans WHERE user_id = ?').run(userId); - db.prepare('UPDATE packages SET first_scanned_by = NULL, first_scan_image = NULL WHERE first_scanned_by = ?').run(userId); - db.prepare('UPDATE packages SET last_scanned_by = NULL, last_scan_hint = NULL WHERE last_scanned_by = ?').run(userId); + db.prepare('UPDATE packages SET first_scanned_by = NULL WHERE first_scanned_by = ?').run(userId); + db.prepare('UPDATE packages SET last_scanned_by = NULL WHERE last_scanned_by = ?').run(userId); db.prepare('DELETE FROM password_reset_tokens WHERE user_id = ?').run(userId); db.prepare("DELETE FROM sessions WHERE sess LIKE ?").run('%"userId":' + userId + '%'); db.prepare('DELETE FROM users WHERE id = ?').run(userId); + }, + + _scrubUserContent(userId) { + // Delete images uploaded by this user + const images = db.prepare('SELECT first_scan_image FROM packages WHERE first_scanned_by = ? AND first_scan_image IS NOT NULL').all(userId); + for (const row of images) { + const filePath = path.resolve(uploadsDir, path.basename(row.first_scan_image)); + try { fs.unlinkSync(filePath); } catch (_) { /* file may already be gone */ } + } + db.prepare('UPDATE packages SET first_scan_image = NULL WHERE first_scanned_by = ?').run(userId); + // Clear hints left by this user + db.prepare('UPDATE packages SET last_scan_hint = NULL WHERE last_scanned_by = ?').run(userId); } }; diff --git a/src/views/admin/dashboard.ejs b/src/views/admin/dashboard.ejs index 204dfc4..18d4407 100644 --- a/src/views/admin/dashboard.ejs +++ b/src/views/admin/dashboard.ejs @@ -74,7 +74,7 @@
<% if (typeof users !== 'undefined' && users) { users.filter(u => !u.is_admin).forEach(u => { const deleted = u.password_hash === ''; %>