pay
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
const UserModel = require('../models/UserModel')
|
||||
const FREE_LIFETIME_CREATION_LIMIT = 100
|
||||
|
||||
const canUseFreeCreation = (user) => {
|
||||
@@ -18,8 +19,20 @@ const consumeFreeCreation = async (user) => {
|
||||
}
|
||||
}
|
||||
|
||||
// updateOne + $inc (اتمیک، فقط همین فیلد) بهجای user.save() — save() کل سند رو
|
||||
// ریولیدیت میکنه و برای کاربرهایی با مقدار نامعتبر در فیلدهای دیگه (مثلاً
|
||||
// income_range) با ValidationError شکست میخوره
|
||||
const res = await UserModel.updateOne(
|
||||
{ _id: user._id, lifetime_free_creations: { $lt: FREE_LIFETIME_CREATION_LIMIT } },
|
||||
{ $inc: { lifetime_free_creations: 1 } }
|
||||
)
|
||||
if (!res.modifiedCount) {
|
||||
return {
|
||||
ok: false,
|
||||
message: `سقف ${FREE_LIFETIME_CREATION_LIMIT} ثبت رایگان (پروژه، بیلبورد و درخواست) به پایان رسیده است.`,
|
||||
}
|
||||
}
|
||||
user.lifetime_free_creations = (user.lifetime_free_creations || 0) + 1
|
||||
await user.save()
|
||||
|
||||
return { ok: true }
|
||||
}
|
||||
|
||||
@@ -57,3 +57,42 @@ async function optimizeImageFile(filePath, { maxDimension = DEFAULT_MAX_DIMENSIO
|
||||
}
|
||||
|
||||
module.exports = { optimizeImageFile, DEFAULT_MAX_DIMENSION, DEFAULT_JPEG_QUALITY }
|
||||
|
||||
const VARIANT_WIDTHS = [480, 800]
|
||||
|
||||
/**
|
||||
* نسخههای کوچکتر (WebP، با حفظ نسبت تصویر) کنار فایل اصلی میسازد و یک placeholder
|
||||
* خیلی کمحجم هم میسازد. فایل اصلی دستنخورده میماند و نسخهای که از اصل بزرگتر
|
||||
* نباشد ساخته نمیشود.
|
||||
*/
|
||||
async function generateImageVariants(filePath, { widths = VARIANT_WIDTHS } = {}) {
|
||||
if (!filePath) return
|
||||
try {
|
||||
if (!(await fs.pathExists(filePath))) return
|
||||
const metadata = await sharp(filePath, { failOn: 'none' }).metadata()
|
||||
if (!metadata.width || !metadata.height) return
|
||||
|
||||
const dir = path.dirname(filePath)
|
||||
const base = path.basename(filePath, path.extname(filePath))
|
||||
const source = sharp(filePath, { failOn: 'none' }).rotate()
|
||||
|
||||
for (const width of widths) {
|
||||
if (width >= metadata.width) continue
|
||||
await source
|
||||
.clone()
|
||||
.resize({ width, fit: 'inside', withoutEnlargement: true })
|
||||
.webp({ quality: 80 })
|
||||
.toFile(path.join(dir, `${base}.w${width}.webp`))
|
||||
}
|
||||
|
||||
await source
|
||||
.clone()
|
||||
.resize({ width: 24, fit: 'inside', withoutEnlargement: true })
|
||||
.webp({ quality: 40 })
|
||||
.toFile(path.join(dir, `${base}.lqip.webp`))
|
||||
} catch (error) {
|
||||
console.error('[imageOptimize] variants failed:', error.message)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.generateImageVariants = generateImageVariants
|
||||
|
||||
18
utils/zarinpalPaymentPage.js
Normal file
18
utils/zarinpalPaymentPage.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const ZARINPAL_START_PAY = 'https://www.zarinpal.com/pg/StartPay'
|
||||
|
||||
function getPaymentPageBase () {
|
||||
return (process.env.APP_SITE_CallBack || process.env.APP_URL || '').replace(/\/$/, '')
|
||||
}
|
||||
|
||||
// زرینپال صفحهی پرداخت رو فقط وقتی باز میکنه که Referer درخواست یه دامنهی مجاز باشه؛
|
||||
// لینکِ مستقیمِ StartPay از مرورگرِ خارجی (مثلاً اندروید) Referer نداره و رد میشه. این
|
||||
// آدرس روی دامنهی خودمون یه صفحهی redirect میزنه تا Referer درست ارسال بشه
|
||||
function buildPaymentPageUrl (authority) {
|
||||
return `${getPaymentPageBase()}/pay/${encodeURIComponent(String(authority))}`
|
||||
}
|
||||
|
||||
function buildZarinpalStartPayUrl (authority) {
|
||||
return `${ZARINPAL_START_PAY}/${encodeURIComponent(String(authority))}`
|
||||
}
|
||||
|
||||
module.exports = { buildPaymentPageUrl, buildZarinpalStartPayUrl }
|
||||
Reference in New Issue
Block a user