feat: mask Iranian phone numbers in user-generated text fields

کاربران نتونن با گذاشتن شماره موبایل/تلفن توی بیو، کپشن پست/ریلز، توضیحاتِ
فروشگاه/کالا/پروژه/دوره/پروفایلِ آموزشگاه، بیرون از پلتفرم تماس بدن. یک
util مشترک (utils/phoneNumberMasking.js) شماره‌های ایرانی رو حتی وقتی
رقم‌ها با فاصله/خط‌فاصله/نقطه از هم جدا شده باشن تشخیص می‌ده و فقط همون
بخش رو با * جایگزین می‌کنه — بدون اینکه به قیمت/سایز/کد سفارش و بقیه‌ی
اعدادِ عادی دست بزنه. موقعِ ذخیره (نه نمایش) اعمال می‌شه، پس هیچ endpointِ
خوندنی نیاز به تغییر نداره.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
payacom
2026-09-15 15:54:26 +03:30
parent 5f99516078
commit 26979057d7
8 changed files with 102 additions and 14 deletions

View File

@@ -19,6 +19,7 @@ const { buildUnifiedInterestScore, blendFreshIntoRanked: blendInterestIntoRanked
const { resolveCommentModerationStatus } = require('../../../utils/contentModeration')
const { saveCommentWithModerationStatus } = require('../../../utils/commentStatusSave')
const { isVerifiedBadge } = require('../../../utils/verifyBadge')
const { maskPhoneNumbers } = require('../../../utils/phoneNumberMasking')
const {
coursePublicStatusFilter,
academyContentPublicStatusFilter,
@@ -232,7 +233,7 @@ const academyProfile = async (req, res, next) => {
academy.academy_image = `/profiles/${uniqueFileName}`
academy.academy_name = name
academy.sheba = `IR${shebaDigits}`
academy.bio = bio
academy.bio = maskPhoneNumbers(bio)
academy.tag = tag
await academy.save()
@@ -363,7 +364,7 @@ const creatCourse = async (req, res, next) => {
offer: offer || '0',
teacher_name,
academyId: academy._id,
caption,
caption: maskPhoneNumbers(caption),
course_time,
number_of_course_content,
is_free: isFreePackage,
@@ -460,7 +461,7 @@ const updateCourse = async (req, res, next) => {
if (cuorse_name !== undefined) course.cuorse_name = cuorse_name
if (category !== undefined) course.category = category
if (offer !== undefined) course.offer = offer || '0'
if (caption !== undefined) course.caption = caption
if (caption !== undefined) course.caption = maskPhoneNumbers(caption)
if (course_time !== undefined) course.course_time = course_time
if (number_of_course_content !== undefined) { course.number_of_course_content = number_of_course_content }
if (teacher_name !== undefined) course.teacher_name = teacher_name
@@ -5039,4 +5040,4 @@ module.exports = {
// دریافت دسته‌بندی‌های ویژه (برای نمایش در صفحه اصلی)
getFeatured
}
}

View File

@@ -18,6 +18,7 @@ const { isHiddenFromPublic } = require('../../../utils/accountLifecycle')
const { viewerCanSeeUserContent } = require('../../../utils/privateProfile')
const { isPostPubliclyVisible } = require('../../../utils/postModeration')
const { postStatusFilter, PUBLIC_POST_STATUSES, commentStatusFilter } = require('../../../utils/moderationStatus')
const { maskPhoneNumbers } = require('../../../utils/phoneNumberMasking')
const { resolvePostModerationStatus } = require('../../../utils/contentModeration')
const { savePostWithModerationStatus } = require('../../../utils/postStatusSave')
const { createTagNotifications } = require('../../../utils/tagNotification')
@@ -85,7 +86,7 @@ const createPost = async (req, res) => {
const captionText =
caption != null && String(caption).trim()
? String(caption).trim().slice(0, 2000)
? maskPhoneNumbers(String(caption).trim().slice(0, 2000))
: null
const filePaths = files.map((file) => ({
@@ -317,7 +318,7 @@ const updatePost = async (req, res) => {
if (caption != null) {
const captionText = String(caption).trim()
? String(caption).trim().slice(0, 2000)
? maskPhoneNumbers(String(caption).trim().slice(0, 2000))
: null
const moderation = resolvePostModerationStatus({
caption: captionText,

View File

@@ -9,6 +9,7 @@ const {
respondProjectProfileIncomplete
} = require('../../../utils/projectProfile')
const { isVerifiedBadge } = require('../../../utils/verifyBadge')
const { maskPhoneNumbers } = require('../../../utils/phoneNumberMasking')
async function activateFreeProjectIfEligible (user, project) {
if (project.project_type !== 'free') return { activated: false }
@@ -207,7 +208,7 @@ const createProject = async (req, res, next) => {
address: address || null,
offer_time,
offer_price,
description,
description: maskPhoneNumbers(description),
...(payment_method ? { payment_method } : {}),
start_date: start_date || null,
work_hours_start: work_hours_start || null,

View File

@@ -15,6 +15,7 @@ const {
canCreateProject,
respondProjectProfileIncomplete
} = require('../../../utils/projectProfile')
const { maskPhoneNumbers } = require('../../../utils/phoneNumberMasking')
const saveProjectComment = async ({
targetUserId,
projectId,
@@ -386,7 +387,7 @@ const editProject = async (req, res, next) => {
project.address = address || null
project.offer_time = offer_time
project.offer_price = offer_price
project.description = description
project.description = maskPhoneNumbers(description)
if (payment_method) project.payment_method = payment_method
project.start_date = start_date || null
project.work_hours_start = work_hours_start || null

View File

@@ -10,6 +10,7 @@ const PostModel = require('../../../models/PostModel')
const OrderModel = require('../../../models/OrderModel')
const CommentModel = require('../../../models/CommentModel')
const UserModel = require('../../../models/UserModel')
const { maskPhoneNumbers } = require('../../../utils/phoneNumberMasking')
const MessageModel = require('../../../models/MessageModel')
const { commentStatusFilter } = require('../../../utils/moderationStatus')
const { saveCommentWithModerationStatus } = require('../../../utils/commentStatusSave')
@@ -163,7 +164,7 @@ const createShopProduct = async (req, res, next) => {
shop: shop._id,
catalogProduct: catalogProduct._id,
title: name || catalogProduct.name,
description: description || catalogProduct.description || null,
description: maskPhoneNumbers(description || catalogProduct.description) || null,
images: clonedFields.images || images,
primaryImageIndex: clonedFields.primaryImageIndex ?? (Number(primaryImageIndex) || 0),
variants: clonedFields.variants || variants,
@@ -195,7 +196,7 @@ const updateShopProduct = async (req, res, next) => {
const { title, description, category, sub_category: subCategory, sub_sub_category: subSubCategory, status } = req.body
if (title) listing.title = title
if (description !== undefined) listing.description = description
if (description !== undefined) listing.description = maskPhoneNumbers(description)
if (category) listing.category = category
if (subCategory) listing.sub_category = subCategory
if (subSubCategory) listing.sub_sub_category = subSubCategory

View File

@@ -7,6 +7,7 @@ const ShopProductListingModel = require('../../../models/ShopProductListingModel
const ShopRatingModel = require('../../../models/ShopRatingModel')
const ShopFollowModel = require('../../../models/ShopFollowModel')
const OrderModel = require('../../../models/OrderModel')
const { maskPhoneNumbers } = require('../../../utils/phoneNumberMasking')
const MS_DAY = 24 * 60 * 60 * 1000
const ANALYTICS_RANGE_DAYS = { '7d': 7, '30d': 30, '90d': 90, all: null }
@@ -44,7 +45,7 @@ const createDraftShop = async (req, res, next) => {
const shop = await ShopModel.create({
owner: req.user._id,
name,
description: description || null,
description: description ? maskPhoneNumbers(description) : null,
status: 'draft'
})
@@ -191,7 +192,7 @@ const updateShopBasic = async (req, res, next) => {
const { name, description } = req.body
if (name) shop.name = name
if (description !== undefined) shop.description = description || null
if (description !== undefined) shop.description = description ? maskPhoneNumbers(description) : null
await shop.save()
return res.json({ message: 'اطلاعات فروشگاه ذخیره شد', shop })

View File

@@ -1,6 +1,7 @@
/* eslint-disable camelcase */
const UserModel = require('../../../models/UserModel')
const jwt = require('jsonwebtoken')
const { maskPhoneNumbers } = require('../../../utils/phoneNumberMasking')
const setConversation = async (req, res, next) => {
try {
@@ -23,7 +24,7 @@ const setConversation = async (req, res, next) => {
})
}
user.bio = bio
user.bio = maskPhoneNumbers(bio)
user.conversation_projects = conversation_projects
await user.save()
@@ -58,7 +59,7 @@ const updateConversation = async (req, res, next) => {
message: 'کاربری با این شناسه یافت نشد'
})
}
user.bio = bio
user.bio = maskPhoneNumbers(bio)
user.conversation_projects = conversation_projects
await user.save()