const crypto = require('crypto') const mongoose = require('mongoose') const UserModel = require('../models/UserModel') const DeletedAccountTombstoneModel = require('../models/DeletedAccountTombstoneModel') const PostModel = require('../models/PostModel') const MessageModel = require('../models/MessageModel') const PostBookmarkModel = require('../models/PostBookmarkModel') const StoryModel = require('../models/StoryModel') const NotificationModel = require('../models/NotificationModel') const ExploreInteractionModel = require('../models/ExploreInteractionModel') const CourseModel = require('../models/CourseModel') const AdvertisingModel = require('../models/AdvertisingModel') const AcademyModel = require('../models/AcademyModel') const DEACTIVATION_LOCK_DAYS = 7 const DEACTIVATION_PURGE_MONTHS = 6 const DELETION_TOMBSTONE_DAYS = 30 const MS_DAY = 24 * 60 * 60 * 1000 function hashValue (value) { if (!value) return null return crypto .createHash('sha256') .update(String(value).trim().toLowerCase() + (process.env.APP_SECRET || 'modstagram')) .digest('hex') } function isDeactivated (user) { return user?.account_status === 'deactivated' } function isHiddenFromPublic (user) { return isDeactivated(user) } function formatChatUserLabel (user) { const name = `${user?.first_name || ''} ${user?.last_name || ''}`.trim() || user?.user_name || 'کاربر' if (isDeactivated(user)) { return `${name} (کاربر غیرفعال)` } return name } async function getHiddenUserIds () { return UserModel.find({ account_status: 'deactivated' }).distinct('_id') } async function isMobileBlockedByTombstone (mobile) { const hash = hashValue(mobile) if (!hash) return false const hit = await DeletedAccountTombstoneModel.findOne({ mobile_hash: hash, expires_at: { $gt: new Date() } }).lean() return Boolean(hit) } async function isUsernameBlockedByTombstone (userName) { const hash = hashValue(userName) if (!hash) return false const hit = await DeletedAccountTombstoneModel.findOne({ user_name_hash: hash, expires_at: { $gt: new Date() } }).lean() return Boolean(hit) } async function createDeletionTombstone ({ mobile, userName }) { const expiresAt = new Date(Date.now() + DELETION_TOMBSTONE_DAYS * MS_DAY) await DeletedAccountTombstoneModel.create({ mobile_hash: hashValue(mobile), user_name_hash: hashValue(userName), expires_at: expiresAt }) } async function purgeUserData (userId) { const uid = new mongoose.Types.ObjectId(String(userId)) await Promise.all([ PostModel.deleteMany({ user_id: uid }), MessageModel.deleteMany({ $or: [{ senderId: uid }, { receiverId: uid }] }), PostBookmarkModel.deleteMany({ userId: uid }), StoryModel.deleteMany({ user_id: uid }), NotificationModel.deleteMany({ user_id: uid }), ExploreInteractionModel.deleteMany({ $or: [{ viewerId: uid }, { authorId: uid }] }), CourseModel.deleteMany({ user_id: uid }), AdvertisingModel.deleteMany({ userId: uid }), AcademyModel.deleteMany({ userId: uid }) ]) } async function purgeExpiredDeactivatedAccounts () { const now = new Date() const due = await UserModel.find({ account_status: 'deactivated', scheduled_purge_at: { $lte: now } }).select('_id mobile user_name').lean() for (const user of due) { await purgeUserData(user._id) await UserModel.deleteOne({ _id: user._id }) } return due.length } async function purgeExpiredTombstones () { await DeletedAccountTombstoneModel.deleteMany({ expires_at: { $lte: new Date() } }) } module.exports = { DEACTIVATION_LOCK_DAYS, DEACTIVATION_PURGE_MONTHS, DELETION_TOMBSTONE_DAYS, MS_DAY, hashValue, isDeactivated, isHiddenFromPublic, formatChatUserLabel, getHiddenUserIds, isMobileBlockedByTombstone, isUsernameBlockedByTombstone, createDeletionTombstone, purgeUserData, purgeExpiredDeactivatedAccounts, purgeExpiredTombstones }