Add full project files

This commit is contained in:
root
2026-07-04 19:24:56 +03:30
parent d54f5441a3
commit b245e7b71a
835 changed files with 30149 additions and 0 deletions

View File

@@ -0,0 +1,211 @@
const { exec } = require('child_process')
const fs = require('fs')
const path = require('path')
const SettingModel = require('../../../models/SettingsModel')
const CDN_KEYS = [
'cdn_enabled',
'cdn_domain',
'cdn_origin_url',
'cdn_ns1',
'cdn_ns2',
'cdn_cache_ttl',
'ssl_enabled',
'ssl_auto_renew',
'ssl_status',
'ssl_expires_at',
'rate_limit_requests',
'rate_limit_window_minutes',
]
const DEFAULT_CDN_SETTINGS = {
cdn_enabled: 'false',
cdn_domain: 'cdn.modstagram.com',
cdn_origin_url: 'https://api.modstagram.com',
cdn_ns1: 'ns1.modstagram.com',
cdn_ns2: 'ns2.modstagram.com',
cdn_cache_ttl: '3600',
ssl_enabled: 'false',
ssl_auto_renew: 'true',
ssl_status: 'none',
ssl_expires_at: '',
rate_limit_requests: '3000',
rate_limit_window_minutes: '3',
}
const upsertSetting = async (key, value) => {
await SettingModel.findOneAndUpdate(
{ key },
{ value: String(value) },
{ new: true, upsert: true }
)
}
const getSettingsMap = async () => {
const settings = await SettingModel.find({ key: { $in: CDN_KEYS } })
const map = { ...DEFAULT_CDN_SETTINGS }
settings.forEach((item) => {
map[item.key] = item.value
})
return map
}
const getCdnSettings = async (req, res, next) => {
try {
const settings = await getSettingsMap()
res.status(200).json({ settings })
} catch (error) {
next(error)
}
}
const updateCdnSettings = async (req, res, next) => {
try {
const { settings } = req.body
if (!settings || typeof settings !== 'object') {
return res.status(400).json({ message: 'داده‌های تنظیمات نامعتبر است' })
}
const updates = Object.entries(settings).filter(([key]) => CDN_KEYS.includes(key))
await Promise.all(updates.map(([key, value]) => upsertSetting(key, value)))
res.status(200).json({
message: 'تنظیمات CDN با موفقیت ذخیره شد',
settings: await getSettingsMap(),
})
} catch (error) {
next(error)
}
}
const readCertInfo = (domain) =>
new Promise((resolve) => {
const certPath =
process.env.SSL_CERT_PATH ||
`/etc/letsencrypt/live/${domain}/fullchain.pem`
if (!fs.existsSync(certPath)) {
resolve({ found: false, certPath })
return
}
exec(
`openssl x509 -in "${certPath}" -noout -enddate`,
{ timeout: 10000 },
(error, stdout) => {
if (error) {
resolve({ found: true, certPath, valid: false })
return
}
const match = stdout.match(/notAfter=(.+)/)
const expiresAt = match ? new Date(match[1].trim()).toISOString() : null
const valid = expiresAt ? new Date(expiresAt) > new Date() : false
resolve({ found: true, certPath, valid, expiresAt })
}
)
})
const getSslStatus = async (req, res, next) => {
try {
const settings = await getSettingsMap()
const domain = settings.cdn_domain
const certInfo = await readCertInfo(domain)
let status = settings.ssl_status
if (certInfo.found && certInfo.valid) {
status = 'active'
if (certInfo.expiresAt) {
await upsertSetting('ssl_expires_at', certInfo.expiresAt)
await upsertSetting('ssl_status', 'active')
await upsertSetting('ssl_enabled', 'true')
}
} else if (certInfo.found && !certInfo.valid) {
status = 'expired'
}
res.status(200).json({
domain,
status,
ssl_enabled: settings.ssl_enabled === 'true',
ssl_auto_renew: settings.ssl_auto_renew === 'true',
expires_at: certInfo.expiresAt || settings.ssl_expires_at || null,
cert_path: certInfo.certPath,
cert_found: certInfo.found,
})
} catch (error) {
next(error)
}
}
const activateSsl = async (req, res, next) => {
try {
const settings = await getSettingsMap()
const domain = req.body?.domain || settings.cdn_domain
const email = req.body?.email || process.env.SSL_ADMIN_EMAIL || 'admin@modstagram.com'
if (!domain) {
return res.status(400).json({ message: 'دامنه CDN تنظیم نشده است' })
}
await upsertSetting('ssl_status', 'pending')
const scriptPath = path.join(__dirname, '../../../scripts/cdn-ssl.sh')
const command = `bash "${scriptPath}" "${domain}" "${email}"`
exec(command, { timeout: 180000 }, async (error, stdout, stderr) => {
if (error) {
await upsertSetting('ssl_status', 'failed')
return res.status(500).json({
message: 'خطا در فعال‌سازی SSL',
details: stderr || error.message,
output: stdout,
})
}
const certInfo = await readCertInfo(domain)
await upsertSetting('ssl_enabled', 'true')
await upsertSetting('ssl_status', certInfo.valid ? 'active' : 'pending')
if (certInfo.expiresAt) {
await upsertSetting('ssl_expires_at', certInfo.expiresAt)
}
return res.status(200).json({
message: 'SSL رایگان با موفقیت فعال شد',
status: certInfo.valid ? 'active' : 'pending',
expires_at: certInfo.expiresAt || null,
output: stdout,
})
})
} catch (error) {
next(error)
}
}
const getPublicCdnConfig = async (req, res, next) => {
try {
const settings = await getSettingsMap()
const cdnEnabled = settings.cdn_enabled === 'true'
const cdnDomain = settings.cdn_domain
const originUrl = settings.cdn_origin_url
res.status(200).json({
cdn_enabled: cdnEnabled,
cdn_domain: cdnDomain,
image_base_url: cdnEnabled
? `https://${cdnDomain.replace(/^https?:\/\//, '')}/storage`
: `${originUrl}/storage`,
ssl_enabled: settings.ssl_enabled === 'true',
})
} catch (error) {
next(error)
}
}
module.exports = {
getCdnSettings,
updateCdnSettings,
getSslStatus,
activateSsl,
getPublicCdnConfig,
}

View File

@@ -0,0 +1,36 @@
// controllers/settingController.js
const SettingModel = require('../../../models/SettingsModel')
// متد برای دریافت تنظیمات بر اساس key
const getSetting = async (req, res, next) => {
try {
const { key } = req.params
const setting = await SettingModel.findOne({ key })
if (!setting) {
return res.status(404).json({ message: 'تنظیمات مورد نظر یافت نشد' })
}
res.status(200).json(setting)
} catch (error) {
next(error)
}
}
// متد برای به‌روزرسانی تنظیمات بر اساس key
const updateSetting = async (req, res, next) => {
try {
const { key } = req.params
const { value } = req.body
// eslint-disable-next-line no-unused-vars
const setting = await SettingModel.findOneAndUpdate(
{ key },
{ value },
{ new: true, upsert: true } // اگر وجود نداشت، آن را ایجاد کن
)
res.status(200).json({ message: 'تنظیمات با موفقیت بروز شد' })
} catch (error) {
next(error)
}
}
module.exports = { getSetting, updateSetting }