full
This commit is contained in:
134
utils/accountLifecycle.js
Normal file
134
utils/accountLifecycle.js
Normal file
@@ -0,0 +1,134 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user