full
This commit is contained in:
@@ -6,7 +6,8 @@ const userSchema = new mongoose.Schema({
|
||||
type: String,
|
||||
required: false,
|
||||
trim: true,
|
||||
unique: true
|
||||
unique: true,
|
||||
sparse: true
|
||||
},
|
||||
otp: {
|
||||
type: String,
|
||||
@@ -21,6 +22,93 @@ const userSchema = new mongoose.Schema({
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: false,
|
||||
trim: true,
|
||||
lowercase: true,
|
||||
unique: true,
|
||||
sparse: true
|
||||
},
|
||||
google_id: {
|
||||
type: String,
|
||||
required: false,
|
||||
trim: true,
|
||||
unique: true,
|
||||
sparse: true
|
||||
},
|
||||
auth_provider: {
|
||||
type: String,
|
||||
required: false,
|
||||
enum: ['mobile', 'google', 'instagram'],
|
||||
default: 'mobile'
|
||||
},
|
||||
instagram_id: {
|
||||
type: String,
|
||||
required: false,
|
||||
trim: true,
|
||||
unique: true,
|
||||
sparse: true
|
||||
},
|
||||
instagram_username: {
|
||||
type: String,
|
||||
required: false,
|
||||
trim: true,
|
||||
default: null
|
||||
},
|
||||
instagram_account_type: {
|
||||
type: String,
|
||||
required: false,
|
||||
enum: ['BUSINESS', 'MEDIA_CREATOR', null],
|
||||
default: null
|
||||
},
|
||||
instagram_access_token_enc: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
instagram_token_expires_at: {
|
||||
type: Date,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
instagram_connected_at: {
|
||||
type: Date,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
instagram_last_synced_at: {
|
||||
type: Date,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
instagram_sync_prefs: {
|
||||
bio: { type: Boolean, default: false },
|
||||
posts: { type: Boolean, default: false },
|
||||
reels: { type: Boolean, default: false },
|
||||
profile_picture: { type: Boolean, default: false },
|
||||
auto_update: { type: Boolean, default: false }
|
||||
},
|
||||
email_verified: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
totp_secret: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
totp_pending_secret: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
totp_enabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
user_name: {
|
||||
type: String,
|
||||
required: false,
|
||||
@@ -69,6 +157,21 @@ const userSchema = new mongoose.Schema({
|
||||
maxLength: 255,
|
||||
default: 'none'
|
||||
},
|
||||
license_verified: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
gold_badge: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
verify_badge: {
|
||||
type: String,
|
||||
enum: ['none', 'gray', 'blue', 'green', 'gold'],
|
||||
default: 'none'
|
||||
},
|
||||
profile_image: {
|
||||
type: String,
|
||||
required: false,
|
||||
@@ -97,6 +200,14 @@ const userSchema = new mongoose.Schema({
|
||||
maxLength: 255,
|
||||
default: null
|
||||
},
|
||||
display_sub_expertise: {
|
||||
type: String,
|
||||
required: false,
|
||||
trim: true,
|
||||
minLength: 1,
|
||||
maxLength: 255,
|
||||
default: null
|
||||
},
|
||||
gender: {
|
||||
type: String,
|
||||
required: false,
|
||||
@@ -145,6 +256,73 @@ const userSchema = new mongoose.Schema({
|
||||
maxLength: 255,
|
||||
default: null
|
||||
},
|
||||
marital_status: {
|
||||
type: String,
|
||||
enum: ['single', 'divorced'],
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
residence_type: {
|
||||
type: String,
|
||||
enum: ['owner', 'tenant'],
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
residence_area: {
|
||||
type: String,
|
||||
required: false,
|
||||
trim: true,
|
||||
minLength: 1,
|
||||
maxLength: 255,
|
||||
default: null
|
||||
},
|
||||
vehicle_type: {
|
||||
type: String,
|
||||
enum: ['none', 'car', 'motorcycle'],
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
vehicle_details: {
|
||||
type: String,
|
||||
required: false,
|
||||
trim: true,
|
||||
minLength: 1,
|
||||
maxLength: 255,
|
||||
default: null
|
||||
},
|
||||
personal_style: {
|
||||
type: String,
|
||||
enum: ['formal', 'sporty'],
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
job: {
|
||||
type: String,
|
||||
required: false,
|
||||
trim: true,
|
||||
minLength: 1,
|
||||
maxLength: 255,
|
||||
default: null
|
||||
},
|
||||
income_range: {
|
||||
type: String,
|
||||
enum: ['under_5m', '5m_10m', '10m_20m', 'over_20m'],
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
moral_traits: {
|
||||
type: String,
|
||||
required: false,
|
||||
trim: true,
|
||||
minLength: 1,
|
||||
maxLength: 1024,
|
||||
default: null
|
||||
},
|
||||
personal_details_public: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
cooperation_type: {
|
||||
type: String,
|
||||
required: false,
|
||||
@@ -203,6 +381,10 @@ const userSchema = new mongoose.Schema({
|
||||
maxLength: 512,
|
||||
default: null
|
||||
},
|
||||
country: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
province: {
|
||||
type: Object,
|
||||
default: null
|
||||
@@ -263,7 +445,10 @@ const userSchema = new mongoose.Schema({
|
||||
trim: true,
|
||||
minLength: 1,
|
||||
maxLength: 512,
|
||||
default: null
|
||||
// پیشفرض قبلاً null بود — یعنی تا اولین کامنت امتیازدار روی کاربر (recalculateUserRating
|
||||
// در utils/commentRating.js)، هیچ سطحی نداشت. الان از همون رشتهای که getUserLevel با
|
||||
// صفر ستاره برمیگردونه استفاده میکنیم، تا هر کاربر از ابتدا سطح واقعی داشته باشه
|
||||
default: 'تازه وارد'
|
||||
},
|
||||
user_score: {
|
||||
type: String,
|
||||
@@ -322,6 +507,15 @@ const userSchema = new mongoose.Schema({
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
block_permanent: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
lifetime_free_creations: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
advertising_profiles: [{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'AdvertisingProfile'
|
||||
@@ -362,7 +556,78 @@ const userSchema = new mongoose.Schema({
|
||||
last_free_offer_date: {
|
||||
type: Date,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
account_status: {
|
||||
type: String,
|
||||
enum: ['active', 'deactivated'],
|
||||
default: 'active',
|
||||
index: true
|
||||
},
|
||||
deactivated_at: {
|
||||
type: Date,
|
||||
default: null
|
||||
},
|
||||
reactivation_locked_until: {
|
||||
type: Date,
|
||||
default: null
|
||||
},
|
||||
scheduled_purge_at: {
|
||||
type: Date,
|
||||
default: null
|
||||
},
|
||||
allow_save_posts: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
ghost_mode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
post_location_enabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
is_private: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// تنظیمات کیف پول — امکان دریافت هدیه (پروفایل + چت) و اینکه آیا درخواست همکاری
|
||||
// (offer) به این کاربر رایگانه یا طبق قیمت معمول پرداختی میشه
|
||||
gift_enabled: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
offer_payment_enabled: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
preferred_language: {
|
||||
type: String,
|
||||
enum: require('../utils/appLanguages').APP_LANGUAGE_CODES,
|
||||
default: 'fa'
|
||||
},
|
||||
e2ee_public_key: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null
|
||||
},
|
||||
pinned_post_ids: {
|
||||
type: [{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'Post'
|
||||
}],
|
||||
default: []
|
||||
},
|
||||
// توکنهای FCM دستگاههای کاربر — آرایه چون یک کاربر میتونه چند دستگاه اندروید داشته باشه
|
||||
fcm_tokens: {
|
||||
type: [String],
|
||||
default: []
|
||||
},
|
||||
// توکنهای FCM Web Push (مرورگر) — جدا از fcm_tokens چون توکنهای وب و اندروید فرمت/طول عمر متفاوتی دارن
|
||||
fcm_web_tokens: {
|
||||
type: [String],
|
||||
default: []
|
||||
},
|
||||
// contactInfo: {
|
||||
// phone: String,
|
||||
// mobile: String,
|
||||
@@ -387,5 +652,25 @@ const userSchema = new mongoose.Schema({
|
||||
|
||||
userSchema.plugin(timestamp)
|
||||
userSchema.plugin(mongoosePaginate)
|
||||
|
||||
// وقتی فیلدهای مرتبط با «یافتن همکار» تغییر میکنن (یا ثبتنام کامل میشه)، جستجوهای
|
||||
// ذخیرهشدهی فعال رو چک میکنه — همهی کنترلرهای ویرایش پروفایل در نهایت save() صدا
|
||||
// میزنن، پس اینجا تکنقطهایترین جاییه که بدون تکرار در هر کنترلر جدا کار میکنه.
|
||||
//
|
||||
// isModified() باید در pre('save') خونده بشه، نه post('save') — مونگوس وضعیت
|
||||
// «تغییر کرده» رو بهعنوان بخشی از خود save() پاک میکنه، پس تا زمانی که به
|
||||
// post('save') برسیم isModified() برای همهی فیلدها false برمیگرده. مقدار رو
|
||||
// از طریق $locals بین pre و post منتقل میکنیم.
|
||||
userSchema.pre('save', function (next) {
|
||||
this.$locals.partnerAlertsRelevant = require('../utils/partnerMatch').shouldCheck(this)
|
||||
next()
|
||||
})
|
||||
|
||||
userSchema.post('save', function (doc) {
|
||||
if (doc.$locals.partnerAlertsRelevant) {
|
||||
require('../utils/partnerMatch').checkPartnerAlerts(doc).catch(() => {})
|
||||
}
|
||||
})
|
||||
|
||||
const UserModel = mongoose.model('User', userSchema)
|
||||
module.exports = UserModel
|
||||
|
||||
Reference in New Issue
Block a user