Compare commits

14 Commits

Author SHA1 Message Date
morteza 94c79aa98d db string 2026-06-20 00:18:29 +03:30
morteza 01c68fba43 package 2026-06-20 00:02:53 +03:30
morteza e0527e8ca6 remove postinstall 2026-06-19 23:54:40 +03:30
morteza 1bb4c7efcc docker 2026-06-19 23:48:35 +03:30
morteza 76f23a73e2 docker 2026-06-19 23:43:23 +03:30
morteza a24f4ae766 docker update 2026-06-19 23:31:03 +03:30
morteza 096e653e64 update db string 2026-06-19 23:28:45 +03:30
morteza d6215b970f docker 2026-06-19 23:25:24 +03:30
morteza 4c36880862 docker update 2026-06-19 23:20:19 +03:30
morteza dfb77cd8d2 use self hosted packages 2026-06-19 19:02:51 +03:30
morteza e95883eb6a S3 for other controolers 2026-06-19 15:47:02 +03:30
morteza c73027540a S3 gallery 2026-06-19 12:35:49 +03:30
morteza 85c8107799 S3 : news controller 2026-06-19 12:26:40 +03:30
morteza 99a9f9ace5 S3 : add news 2026-06-19 12:11:18 +03:30
2918 changed files with 42987 additions and 1915 deletions
+7
View File
@@ -0,0 +1,7 @@
# ParsPack S3 Storage
BUCKET_NAME=your_bucket_name
BUCKET_REGION=us-west-2
PARSPACK_S3_ENDPOINT=https://your_bucket_name.parspack.net
BUCKET_PUBLIC_URL=https://your_bucket_name.parspack.net
BUCKET_ACCESS_KEY=your_access_key
BUCKET_SECRET_KEY=your_secret_key
+4 -10
View File
@@ -43,25 +43,19 @@ export default {
type: "text/css",
href: "/js/flipBook/font-awesome.css",
},
{
rel: "stylesheet",
href: "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js",
},
],
script: [
{ src: "https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.js" },
{
src: "https://ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js",
},
{ src: "/js/vendor/jquery-1.8.3.js" },
{ src: "/js/vendor/jquery-ui-1.9.2.min.js" },
{ src: "/js/flipBook/flipbook.min.js" },
{ src: "/js/lightBox/simple-lightbox.min.js" },
{
src: "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js",
src: "/js/vendor/jquery-3.6.0.min.js",
defer: true,
},
{
src: "https://cdnjs.cloudflare.com/ajax/libs/jquery-flipbook/1.0.5/jquery.flipbook.min.js",
src: "/js/vendor/jquery.flipbook.min.js",
defer: true,
},
],
+1154 -733
View File
File diff suppressed because it is too large Load Diff
+7 -1
View File
@@ -11,6 +11,7 @@
"generate": "nuxt generate"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.1072.0",
"@coreui/coreui": "^3.2.2",
"@coreui/icons": "^2.0.0-beta.5",
"@coreui/utils": "^1.3.1",
@@ -25,10 +26,12 @@
"archiver": "^5.3.0",
"axios": "^0.21.1",
"bcryptjs": "^2.4.3",
"ckeditor4": "^4.17.2",
"ckeditor4-vue": "^1.3.0",
"convertapi": "^1.14.0",
"core-js": "^3.9.1",
"cors": "^2.8.5",
"dotenv": "^17.4.2",
"element-ui": "^2.15.1",
"express": "^4.17.1",
"express-fileupload": "^1.2.1",
@@ -49,7 +52,7 @@
"nuxt": "^2.15.3",
"nuxt-video-player": "^1.0.17",
"request-ip": "^2.1.3",
"sharp": "^0.28.1",
"sharp": "^0.33.5",
"slick-carousel": "^1.8.1",
"svg-captcha": "^1.4.0",
"swagger-jsdoc": "^6.2.8",
@@ -66,5 +69,8 @@
"sass": "^1.77.3",
"sass-loader": "^10.1.0",
"webpack": "^4.46.0"
},
"overrides": {
"sharp": "^0.33.5"
}
}
+1 -1
View File
@@ -36,7 +36,7 @@
v-for="(item, i) in items"
:key="i"
class="item">
<img :src="`/uploads/images/category/shafafiat/${item.icon}`" alt="شفافیت" />
<img :src="item.icon" alt="شفافیت" />
<div class="parent">
<div class="right"></div>
<div class="left"></div>
+15 -1
View File
@@ -1,4 +1,18 @@
import Vue from 'vue'
import CKEditor from 'ckeditor4-vue'
Vue.use(CKEditor)
const editorUrl = '/js/ckeditor/ckeditor.js'
window.CKEDITOR_BASEPATH = '/js/ckeditor/'
// editorUrl is a component prop, not a Vue.use() option — the default is the CDN.
Vue.component('ckeditor', {
...CKEditor.component,
props: {
...CKEditor.component.props,
editorUrl: {
type: String,
default: editorUrl,
},
},
})
+81 -133
View File
@@ -8,13 +8,19 @@ const {
checkValidations,
} = require("../plugins/controllersHelperFunctions");
const News = require("../models/News");
const sharp = require("sharp");
const fs = require("fs");
const { extname } = require("path");
const {
deleteCategoryImage,
deleteShafafiatIcon,
uploadProcessedImage,
uploadShafafiatIcon,
uploadTermsFile,
deleteTermsFile,
getStoredImageName,
} = require("../plugins/categoryImageStorage");
// variables
const titleFilterRegex = new RegExp("/", "g");
const noCover = "/img/no-cover.jpg";
const coverNewsWidth = 400;
const coverNewsHeight = 400;
const coverQuality = 100;
@@ -411,34 +417,13 @@ module.exports.addCategory = [
image.mimetype.split("/")[1]
}`;
try {
const target = sharp(image.data);
await target
.resize(coverNewsWidth, coverNewsHeight, { fit: "cover" })
.jpeg({ quality: coverQuality })
.toFile(`./static/uploads/images/category/${imageName}`);
/**
* if exist old pic delete it from file
*/
// if (targetNews.cover !== noCover && !targetNews.imported) {
// let oldPic = `./static${targetNews.cover}`;
//
// if (await fs.existsSync(oldPic)) {
// fs.unlink(oldPic, (err) => {
// if (err) return res500(res, err?.message);
// });
// }
// }
// if (targetNews.thumbCover !== noCover && !targetNews.imported) {
// let oldPic = `./static${targetNews.thumbCover}`;
//
// if (await fs.existsSync(oldPic)) {
// fs.unlink(oldPic, (err) => {
// if (err) return res500(res, err?.message);
// });
// }
// }
await uploadProcessedImage(
image.data,
imageName,
coverNewsWidth,
coverNewsHeight,
coverQuality
);
create.managerCover = imageName;
await create.save();
@@ -461,33 +446,12 @@ module.exports.addCategory = [
const ext = extname(image.name);
const imageName = `shafafiat_icon_${Date.now()}${ext}`;
try {
await image.mv(
`${process.cwd()}/static/uploads/images/category/shafafiat/${imageName}`
await uploadShafafiatIcon(
image.data,
imageName,
image.mimetype
);
/**
* if exist old pic delete it from file
*/
// if (targetNews.cover !== noCover && !targetNews.imported) {
// let oldPic = `./static${targetNews.cover}`;
//
// if (await fs.existsSync(oldPic)) {
// fs.unlink(oldPic, (err) => {
// if (err) return res500(res, err?.message);
// });
// }
// }
// if (targetNews.thumbCover !== noCover && !targetNews.imported) {
// let oldPic = `./static${targetNews.thumbCover}`;
//
// if (await fs.existsSync(oldPic)) {
// fs.unlink(oldPic, (err) => {
// if (err) return res500(res, err?.message);
// });
// }
// }
create.icon = imageName;
await create.save();
} catch (e) {
@@ -812,24 +776,15 @@ module.exports.updateCategory = [
image.mimetype.split("/")[1]
}`;
try {
const target = sharp(image.data);
await target
.resize(coverNewsWidth, coverNewsHeight, { fit: "cover" })
.jpeg({ quality: coverQuality })
.toFile(`./static/uploads/images/category/${imageName}`);
await deleteCategoryImage(targetCategory, "managerCover");
/**
* if exist old pic delete it from file
*/
if (targetCategory.managerCover !== noCover) {
let oldPic = `./static${targetCategory.managerCover}`;
if (await fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await uploadProcessedImage(
image.data,
imageName,
coverNewsWidth,
coverNewsHeight,
coverQuality
);
targetCategory.managerCover = imageName;
} catch (e) {
@@ -839,7 +794,6 @@ module.exports.updateCategory = [
if (req.files?.icon) {
const image = req.files.icon;
console.log(image);
if (!_sr.supportedImageFormats.includes(image.mimetype.split("/")[1])) {
return res.status(422).json({
validation: {
@@ -852,30 +806,17 @@ module.exports.updateCategory = [
const ext = extname(image.name);
const imageName = `shafafiat_icon_${Date.now()}${ext}`;
try {
await image.mv(
`${process.cwd()}/static/uploads/images/category/shafafiat/${imageName}`
await deleteShafafiatIcon(
getStoredImageName(targetCategory, "icon")
);
// const target = sharp(image.data)
// await target
// .resize(coverNewsWidth, coverNewsHeight, {fit: 'cover'})
// .jpeg({quality: coverQuality})
// .toFile(`./static/uploads/images/category/shafafiat/${imageName}`);
/**
* if exist old pic delete it from file
*/
if (targetCategory.icon !== noCover) {
let oldPic = `./static${targetCategory.icon}`;
if (await fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await uploadShafafiatIcon(
image.data,
imageName,
image.mimetype
);
targetCategory.icon = imageName;
console.log(targetCategory);
} catch (e) {
return res500(res, e?.message);
}
@@ -1223,52 +1164,59 @@ module.exports.getParent = [
module.exports.addTermsFileToCategory = [
[body("termsTitle").notEmpty().withMessage(_sr["fa"].required.title)],
checkValidations(validationResult),
(req, res) => {
if (!req.files?.file)
return res
.status(422)
.json({ validation: { file: { msg: _sr["fa"].required.file } } });
if (req.files?.file.mimetype.split("/")[1] !== "pdf")
return res
.status(422)
.json({ validation: { file: { msg: "فقط فرمت PDF قابل قبول است" } } });
async (req, res) => {
try {
if (!req.files?.file)
return res
.status(422)
.json({ validation: { file: { msg: _sr["fa"].required.file } } });
if (req.files?.file.mimetype.split("/")[1] !== "pdf")
return res
.status(422)
.json({ validation: { file: { msg: "فقط فرمت PDF قابل قبول است" } } });
const data = { title: req.body.termsTitle };
const file = req.files.file;
const fileName =
req.body.termsTitle.replace(titleFilterRegex, "-") +
"_" +
Date.now() +
".pdf";
file.mv(`./static/uploads/files/terms/${fileName}`);
data.file = fileName;
const category = await Category.findById(req.params.categoryId);
if (!category) return res404(res, "category not found");
Category.findById(req.params.categoryId, (err, category) => {
if (err || !category) return res404(res, err);
category.terms.push(data);
category.save((err) => {
if (err) return res500(res, err);
else return res.json({ message: _sr["fa"].response.success_save });
const fileName =
req.body.termsTitle.replace(titleFilterRegex, "-") +
"_" +
Date.now() +
".pdf";
await uploadTermsFile(req.files.file.data, fileName);
category.terms.push({
title: req.body.termsTitle,
file: fileName,
});
});
await category.save();
return res.json({ message: _sr["fa"].response.success_save });
} catch (err) {
return res500(res, err);
}
},
];
module.exports.removeTermsFileFromCategory = [
(req, res) => {
Category.findOne({ "terms._id": req.params.fileId }, (err, category) => {
if (err || !category) return res404(res, err);
const file = category.terms.id(req.params.fileId);
if (!file) return res404(res, "not found");
file.remove();
fs.unlink(`./static/${file.file}`, (err) => {
if (err) console.log(err);
});
category.save((err) => {
if (err) return res500(res, err);
else return res.json({ message: _sr["fa"].response.success_remove });
});
});
async (req, res) => {
try {
const category = await Category.findOne({ "terms._id": req.params.fileId });
if (!category) return res404(res, "not found");
const termsFile = category.terms.id(req.params.fileId);
if (!termsFile) return res404(res, "not found");
const fileName = getStoredImageName(termsFile, "file");
termsFile.remove();
await deleteTermsFile(fileName);
await category.save();
return res.json({ message: _sr["fa"].response.success_remove });
} catch (err) {
return res500(res, err);
}
},
];
+34 -55
View File
@@ -1,9 +1,11 @@
const {body, validationResult} = require('express-validator');
const sharp = require('sharp');
const {_sr} = require('../plugins/serverResponses');
const {res404, res500, hasWhiteSpaces, isLatinCharactersWithSymbol, generateRandomDigits} = require('../plugins/controllersHelperFunctions');
const Content = require('../models/Content');
const fs = require('fs');
const {
deleteContentImages,
uploadProcessedImage,
} = require('../plugins/contentImageStorage');
////// variables
@@ -85,26 +87,23 @@ module.exports.addContent = [
const imageName = `manager_${Date.now()}_${create._id}.${image.mimetype.split('/')[1]}`;
const thumbName = 'thumb_' + imageName
try {
const target = sharp(image.data)
await target
.resize(profileWidth, profileHeight, {
fit: 'cover'
})
.jpeg({
quality: profileQuality
})
.toFile(`./static/uploads/images/content/${imageName}`)
await target
.resize(profileThumbWidth, profileThumbHeight, {
fit: 'cover'
})
.jpeg({
quality: profileThumbQuality
})
.toFile(`./static/uploads/images/content/${thumbName}`)
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
await uploadProcessedImage(
image.data,
thumbName,
profileThumbWidth,
profileThumbHeight,
profileThumbQuality
);
create.cover = imageName;
create.thumb = imageName;
create.thumb = thumbName;
await create.save();
} catch (e) {
@@ -180,42 +179,22 @@ module.exports.updateContent = [
const imageName = `manager_${Date.now()}_${targetContent._id}.${image.mimetype.split('/')[1]}`;
const thumbName = 'thumb_' + imageName
try {
const target = sharp(image.data)
await target
.resize(profileWidth, profileHeight, {
fit: 'cover'
})
.jpeg({
quality: profileQuality
})
.toFile(`./static/uploads/images/content/${imageName}`)
await target
.resize(profileThumbWidth, profileThumbHeight, {
fit: 'cover'
})
.jpeg({
quality: profileThumbQuality
})
.toFile(`./static/uploads/images/content/${thumbName}`)
await deleteContentImages(targetContent);
/**
* if exist old pic delete it from file
*/
if (targetContent.cover) {
const oldPic = `./static${targetContent.cover}`
const oldThumb = `./static${targetContent.thumb}`
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
if (fs.existsSync(oldThumb)) {
fs.unlink(oldThumb, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
await uploadProcessedImage(
image.data,
thumbName,
profileThumbWidth,
profileThumbHeight,
profileThumbQuality
);
targetContent.cover = imageName;
targetContent.thumb = thumbName;
+63 -63
View File
@@ -1,9 +1,15 @@
const fs = require('fs')
const {PassThrough} = require('stream')
const archiver = require('archiver')
const moment = require('moment')
const jwt = require('jsonwebtoken')
const {secretKey} = require('../authentication')
const {_sr} = require('../plugins/serverResponses')
const {
appendS3UploadsToArchive,
uploadBackupArchive,
deleteOldBackups,
} = require('../plugins/backupStorage')
// models
const Category = require('../models/Category')
const Comment = require('../models/Comment')
@@ -21,6 +27,8 @@ const Slider = require('../models/Slider')
const User = require('../models/User')
const YearBanner = require('../models/YearBanner')
const hostFullError = '<h1 style="margin-top: 150px;text-align: center;direction: rtl;color: red;">فضای هاست پر میباشد.لطفا نسبت به پاک کردن بکاپ های قبلی یا افزایش فضای هاست اقدام فرمایید.</h1>'
module.exports.createBackUp = [
// check user token and permission to backup
async (req, res, next) => {
@@ -85,86 +93,72 @@ module.exports.createBackUp = [
const [categories, comments, contactus, contents, galleries, homepage, links, meetings, news, newsFiles, services, sliders, users, yearBanner, occasion] = await Promise.all(dataBaseQueries)
if (await !fs.existsSync('./backup/database')) await fs.mkdirSync('./backup/database')
await Promise.all([
fs.writeFileSync(`./backup/database/categories_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(categories)),
fs.writeFileSync(`./backup/database/comments_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(comments)),
fs.writeFileSync(`./backup/database/contactus_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(contactus)),
fs.writeFileSync(`./backup/database/contents_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(contents)),
fs.writeFileSync(`./backup/database/galleries_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(galleries)),
fs.writeFileSync(`./backup/database/homepage_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(homepage)),
fs.writeFileSync(`./backup/database/links_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(links)),
fs.writeFileSync(`./backup/database/meetings_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(meetings)),
fs.writeFileSync(`./backup/database/news_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(news)),
fs.writeFileSync(`./backup/database/newsFiles_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(newsFiles)),
fs.writeFileSync(`./backup/database/services_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(services)),
fs.writeFileSync(`./backup/database/sliders_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(sliders)),
fs.writeFileSync(`./backup/database/users_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(users)),
fs.writeFileSync(`./backup/database/yearBanner_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(yearBanner)),
fs.writeFileSync(`./backup/database/occasion_${moment(Date.now()).format('YYYY-MM-DD')}.json`, JSON.stringify(occasion))
])
/////////////////// create backup zip file
const backupName = 'backup_' + moment(Date.now()).format('YYYY-MM-DD') + '.zip'
// create a file to stream archive data to.
const output = fs.createWriteStream(`./backup/${backupName}`)
const dateStr = moment(Date.now()).format('YYYY-MM-DD')
const backupName = `backup_${dateStr}.zip`
const archive = archiver('zip', {
zlib: {level: 9} // Sets the compression level.
zlib: {level: 9}
})
const output = new PassThrough()
const chunks = []
output.on('data', (chunk) => chunks.push(chunk))
// listen for all archive data to be written
// 'close' event is fired only when a file descriptor is involved
output.on('close', function () {
console.log(archive.pointer() + ' total bytes')
console.log('archiver has been finalized and the output file descriptor has closed.')
return res.download(`./backup/${backupName}`)
})
// This event is fired when the data source is drained no matter what was the data source.
// It is not part of this library but rather from the NodeJS Stream API.
// @see: https://nodejs.org/api/stream.html#stream_event_end
output.on('end', function () {
console.log('Data has been drained')
return res.status(500).send('<h1 style="margin-top: 150px;text-align: center;direction: rtl;color: red;">فضای هاست پر میباشد.لطفا نسبت به پاک کردن بکاپ های قبلی یا افزایش فضای هاست اقدام فرمایید.</h1>')
})
// good practice to catch warnings (ie stat failures and other non-blocking errors)
archive.on('warning', function (err) {
if (err.code === 'ENOENT') {
// log warning
console.log('On archiver warning -- ENOENT error code', err)
} else {
// throw error
console.log('On archiver warning -- other error codes ', err)
}
return res.status(500).send('<h1 style="margin-top: 150px;text-align: center;direction: rtl;color: red;">فضای هاست پر میباشد.لطفا نسبت به پاک کردن بکاپ های قبلی یا افزایش فضای هاست اقدام فرمایید.</h1>')
return res.status(500).send(hostFullError)
})
// good practice to catch this error explicitly
archive.on('error', function (err) {
// throw err
console.log(err)
return res.status(500).json({message: 'مشکلی پیش آمده'})
})
// pipe archive data to the file
await archive.pipe(output)
archive.pipe(output)
// append files from a sub-directory and naming it `new-subdir` within the archive
await archive.directory(`./static/uploads/`, 'uploads')
await archive.directory(`./backup/database/`, 'database')
const databaseExports = [
['categories', categories],
['comments', comments],
['contactus', contactus],
['contents', contents],
['galleries', galleries],
['homepage', homepage],
['links', links],
['meetings', meetings],
['news', news],
['newsFiles', newsFiles],
['services', services],
['sliders', sliders],
['users', users],
['yearBanner', yearBanner],
['occasion', occasion],
]
for (const [name, data] of databaseExports) {
archive.append(JSON.stringify(data), {name: `database/${name}_${dateStr}.json`})
}
await appendS3UploadsToArchive(archive)
// finalize the archive (ie we are done appending files but streams have to finish yet)
// 'close', 'end' or 'finish' may be fired right after calling this method so register to them beforehand
await archive.finalize()
await fs.rmdirSync('./backup/database', {recursive: true})
await new Promise((resolve, reject) => {
output.on('end', resolve)
output.on('error', reject)
})
const buffer = Buffer.concat(chunks)
await uploadBackupArchive(buffer, backupName)
res.setHeader('Content-Type', 'application/zip')
res.setHeader('Content-Disposition', `attachment; filename="${backupName}"`)
return res.send(buffer)
} catch (e) {
return res.status(500).send('<h1 style="margin-top: 150px;text-align: center;direction: rtl;color: red;">فضای هاست پر میباشد.لطفا نسبت به پاک کردن بکاپ های قبلی یا افزایش فضای هاست اقدام فرمایید.</h1>')
return res.status(500).send(hostFullError)
}
}
]
@@ -173,12 +167,18 @@ module.exports.createBackUp = [
module.exports.removeOldBackups = [
async (req, res) => {
try {
const files = await fs.readdirSync('./backup')
for await (const file of files) {
const isDirectory = await fs.statSync(`./backup/${file}`).isDirectory()
if (isDirectory) await fs.rmdirSync(`./backup/${file}`, {recursive: true})
else await fs.unlinkSync(`./backup/${file}`)
await deleteOldBackups()
if (fs.existsSync('./backup')) {
const files = fs.readdirSync('./backup')
for (const file of files) {
const filePath = `./backup/${file}`
const isDirectory = fs.statSync(filePath).isDirectory()
if (isDirectory) fs.rmdirSync(filePath, {recursive: true})
else fs.unlinkSync(filePath)
}
}
return res.json({message: _sr['fa'].response.success_remove})
} catch (e) {
return res.status(500).json({message: e})
+22 -53
View File
@@ -10,8 +10,11 @@ const {
hasWhiteSpaces,
isLatinCharactersWithSymbol
} = require('../plugins/controllersHelperFunctions');
const fs = require('fs');
const sharp = require('sharp');
const {
deleteCandidateImage,
deleteElectionCandidateImages,
uploadProcessedImage,
} = require('../plugins/electionImageStorage');
////// variables
const profileWidth = 300;
@@ -292,17 +295,7 @@ module.exports.deletOneElection = [
}
if (deleteElection.candidates.length) {
const targetDir = `./static/uploads/images/election/`;
const getFiles = fs.readdirSync(targetDir);
for (const file of getFiles) {
if (file.includes(deleteElection._id)) {
fs.unlink(`./static/uploads/images/election/${file}`, (err) => {
if (err) console.log(err)
});
}
}
await deleteElectionCandidateImages(deleteElection);
}
await deleteElection.deleteOne();
@@ -457,16 +450,13 @@ module.exports.addCandidate = [
}
const imageName = 'candidate_' + Date.now() + '_' + targetElection._id + '.jpg';
data.profilePic = imageName;
const target = sharp(image.data)
await target
.resize(profileWidth, profileHeight, {
fit: 'cover'
})
.toFormat('jpg')
.jpeg({
quality: profileQuality
})
.toFile(`./static/uploads/images/election/${imageName}`)
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
targetElection.candidates.push(data);
@@ -528,29 +518,16 @@ module.exports.updateCandidate = [
return res.status(422).json({validation: {profilePic: {msg: _sr['fa'].format.image}}});
}
const imageName = 'candidate_' + Date.now() + '_' + targetElection._id + '.jpg';
const target = sharp(image.data)
await target
.resize(profileWidth, profileHeight, {
fit: 'cover'
})
.toFormat('jpg')
.jpeg({
quality: profileQuality
})
.toFile(`./static/uploads/images/election/${imageName}`)
await deleteCandidateImage(candidate);
/**
* if exist old pic delete it from file
*/
if (candidate.profilePic) {
const oldPic = `./static${candidate.profilePic}`;
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
candidate.profilePic = imageName;
}
@@ -586,15 +563,7 @@ module.exports.deleteCandidate = [
if (!candidate) return res404(res, `کاندید مورد نظر پیدا نشد`);
if (candidate.profilePic) {
const oldPic = `./static${candidate.profilePic}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await deleteCandidateImage(candidate);
await targetElection.candidates.id(candidateId).remove();
+75 -203
View File
@@ -1,12 +1,20 @@
const Gallery = require("../models/Gallery");
const { body, validationResult } = require("express-validator");
const sharp = require("sharp");
const crypto = require("crypto");
const { _sr } = require("../plugins/serverResponses");
const { res404, res500 } = require("../plugins/controllersHelperFunctions");
const fs = require("fs");
const moment = require("moment-jalaali");
const Category = require("./../models/Category");
const {
deleteGalleryFile,
uploadGalleryFile,
uploadProcessedImage,
deleteAllGalleryFiles,
deleteGalleryCover,
getStoredFileName,
getStoredArray,
GALLERY_IMAGE_PREFIX,
} = require("../plugins/galleryImageStorage");
////// variables
const picsWidth = 1920;
@@ -45,139 +53,40 @@ const imageLimit = 2;
const graphicLimit = 5;
const videoLimit = 20;
////// functions
const _upload = async (fileType, file, galleryId, Hash) => {
try {
const fileName = `gallery_${galleryId}_${Date.now()}.${
file.mimetype.split("/")[1]
}`;
const uploadPath = `./static/uploads/${fileType}s/gallery/${fileName}`;
const thumbUploadPath = `./static/uploads/${fileType}s/gallery/thumb_${fileName}`;
switch (fileType) {
case "image":
try {
const target = sharp(file.data);
await target
.resize(picsWidth, picsHeight, {
fit: "cover",
})
.toFormat("jpg")
.jpeg({
quality: picsQuality,
})
.toFile(uploadPath);
await target
.resize(thumbPicsWidth, thumbPicsHeight, {
fit: "cover",
})
.toFormat("jpg")
.jpeg({
quality: thumbPicsQuality,
})
.toFile(thumbUploadPath);
} catch (error) {
console.log(error);
}
break;
case "graphic":
try {
const target = sharp(file.data);
await target
// .resize(graphicWidth, graphicHeight, {
// fit: 'cover'
// })
.toFormat("jpg")
.jpeg({
quality: graphicQuality,
})
.toFile(uploadPath);
await target
.resize(thumbGraphicWidth, thumbGraphicHeight, {
fit: "cover",
})
.toFormat("jpg")
.jpeg({
quality: thumbGraphicQuality,
})
.toFile(thumbUploadPath);
} catch (error) {
console.log(error);
}
break;
case "video":
await file.mv(uploadPath);
break;
}
return fileName;
} catch (error) {
console.log(error);
return null;
const _upload = async (fileType, file, galleryId) => {
if (fileType === "graphic") {
return uploadGalleryFile("graphic", file, galleryId, {
mainQuality: graphicQuality,
thumbWidth: thumbGraphicWidth,
thumbHeight: thumbGraphicHeight,
thumbQuality: thumbGraphicQuality,
});
}
if (fileType === "video") {
return uploadGalleryFile("video", file, galleryId, {});
}
return uploadGalleryFile("image", file, galleryId, {
mainWidth: picsWidth,
mainHeight: picsHeight,
mainQuality: picsQuality,
thumbWidth: thumbPicsWidth,
thumbHeight: thumbPicsHeight,
thumbQuality: thumbPicsQuality,
});
};
const _deleteOne = async (fileType, name) => {
try {
let targetFile = `./static/uploads/${fileType}s/gallery/${name}`;
let thumbTargetFile = `./static/uploads/${fileType}s/gallery/thumb_${name}`;
if (fs.existsSync(targetFile)) {
fs.unlink(targetFile, (err) => {
if (err) console.log(err);
});
if (fileType !== "video") {
fs.unlink(thumbTargetFile, (err) => {
if (err) console.log(err);
});
}
return name;
} else {
return null;
}
await deleteGalleryFile(fileType, name);
return name;
} catch (error) {
console.log(error);
return null;
}
};
const _deleteAll = async (galleryId, galleryType) => {
try {
const targetDir = `./static/uploads/${galleryType}s/gallery/`;
const getFiles = fs.readdirSync(targetDir);
for (const file of getFiles) {
if (file.includes(galleryId)) {
fs.unlink(`./static/uploads/${galleryType}s/gallery/${file}`, (err) => {
if (err) console.log(err);
});
}
}
} catch (error) {
console.log(error);
}
};
const _deleteCover = async (galleryId) => {
try {
const targetDir = `./static/uploads/images/gallery/`;
const getFiles = fs.readdirSync(targetDir);
for (const file of getFiles) {
if (file.includes(galleryId) && file.includes("cover")) {
fs.unlink(`./static/uploads/images/gallery/${file}`, (err) => {
if (err) console.log(err);
});
}
}
} catch (error) {
console.log(err);
}
};
const _randomGallery = async (queryObject) => {
let setSkip;
try {
@@ -599,7 +508,7 @@ module.exports.updateGallery = [
}
if (targetGallery.galleryType !== galleryType) {
await _deleteAll(targetGallery._id, targetGallery.galleryType);
await deleteAllGalleryFiles(targetGallery);
targetGallery.images = [];
targetGallery.thumbImages = [];
targetGallery.video = null;
@@ -722,8 +631,8 @@ module.exports.deletOneGallery = [
return res500(res, `شما اجازه پاک کردن این گالری را ندارید`);
}
await _deleteAll(targetGallery._id, targetGallery.galleryType);
await _deleteCover(targetGallery._id);
await deleteAllGalleryFiles(targetGallery);
await deleteGalleryCover(targetGallery);
/** delete gallery */
await targetGallery.deleteOne();
@@ -1037,27 +946,20 @@ module.exports.uploadPDF = [
targetGallery.thumbImages.push(`thumb_${fileName}`);
break;
case "video":
if (
targetGallery.video &&
(await fs.existsSync(`./static/${targetGallery?.video}`))
) {
fs.unlink(`./static/${targetGallery.video}`, (err) => {
if (err) return res.status(500).json({ msg: err.message });
});
if (getStoredFileName(targetGallery, "video")) {
await deleteGalleryFile(
"video",
getStoredFileName(targetGallery, "video")
);
}
targetGallery.video = fileName;
break;
case "graphic":
if (
targetGallery.graphic &&
(await fs.existsSync(`./static/${targetGallery?.graphic}`))
) {
fs.unlink(`./static/${targetGallery.graphic}`, (err) => {
if (err) return res.status(500).json({ msg: err.message });
});
fs.unlink(`./static/${targetGallery.thumbGraphic}`, (err) => {
if (err) return res.status(500).json({ msg: err.message });
});
if (getStoredFileName(targetGallery, "graphic")) {
await deleteGalleryFile(
"graphic",
getStoredFileName(targetGallery, "graphic")
);
}
targetGallery.graphic = fileName;
targetGallery.thumbGraphic = `thumb_${fileName}`;
@@ -1130,9 +1032,7 @@ module.exports.uploadFile = [
return res.status(404).json({ msg: `گالری پیدا نشد` });
}
const Hash = targetGallery.uidImage.map((path) =>
path.replace("/uploads/images/gallery/", "")
);
const Hash = getStoredArray(targetGallery, "uidImage");
if (Hash.includes(fileHash)) {
return res.status(200).json({ msg: `این فایل قبلا بارگذاری شده` });
}
@@ -1206,27 +1106,20 @@ module.exports.uploadFile = [
targetGallery.thumbImages.push(`thumb_${fileName}`);
break;
case "video":
if (
targetGallery.video &&
(await fs.existsSync(`./static/${targetGallery?.video}`))
) {
fs.unlink(`./static/${targetGallery.video}`, (err) => {
if (err) return res.status(500).json({ msg: err.message });
});
if (getStoredFileName(targetGallery, "video")) {
await deleteGalleryFile(
"video",
getStoredFileName(targetGallery, "video")
);
}
targetGallery.video = fileName;
break;
case "graphic":
if (
targetGallery.graphic &&
(await fs.existsSync(`./static/${targetGallery?.graphic}`))
) {
fs.unlink(`./static/${targetGallery.graphic}`, (err) => {
if (err) return res.status(500).json({ msg: err.message });
});
fs.unlink(`./static/${targetGallery.thumbGraphic}`, (err) => {
if (err) return res.status(500).json({ msg: err.message });
});
if (getStoredFileName(targetGallery, "graphic")) {
await deleteGalleryFile(
"graphic",
getStoredFileName(targetGallery, "graphic")
);
}
targetGallery.graphic = fileName;
targetGallery.thumbGraphic = `thumb_${fileName}`;
@@ -1743,47 +1636,26 @@ module.exports.uploadCover = [
const imageName = `cover_gallery_${Date.now()}_${targetGallery._id}.jpg`;
const thumbImg = `thumb_${imageName}`;
try {
const target = sharp(file.data);
await uploadProcessedImage(
file.data,
imageName,
coverWidth,
coverHeight,
coverQuality,
GALLERY_IMAGE_PREFIX
);
await target
.resize(coverWidth, coverHeight, {
fit: "cover",
})
.toFormat("jpg")
.jpeg({
quality: coverQuality,
})
.toFile(`./static/uploads/images/gallery/${imageName}`);
await uploadProcessedImage(
file.data,
thumbImg,
thumbCoverWidth,
thumbCoverHeight,
thumbCoverQuality,
GALLERY_IMAGE_PREFIX
);
await target
.resize(thumbCoverWidth, thumbCoverHeight, {
fit: "cover",
})
.toFormat("jpg")
.jpeg({
quality: thumbCoverQuality,
})
.toFile(`./static/uploads/images/gallery/${thumbImg}`);
/**
* if exist old pic delete it from file
*/
if (targetGallery.cover) {
let oldPic = `./static/uploads/images/gallery/${targetGallery.cover}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) console.log("Error deleting old cover:", err);
});
}
}
if (targetGallery.thumbCover) {
let oldThumbPic = `./static/uploads/images/gallery/${targetGallery.thumbCover}`;
if (fs.existsSync(oldThumbPic)) {
fs.unlink(oldThumbPic, (err) => {
if (err) console.log("Error deleting old thumb cover:", err);
});
}
if (getStoredFileName(targetGallery, "cover")) {
await deleteGalleryCover(targetGallery);
}
// Update gallery record with new cover images
+20 -41
View File
@@ -3,7 +3,6 @@ const {
body,
validationResult
} = require('express-validator');
const sharp = require('sharp');
const {
_sr
} = require('../plugins/serverResponses');
@@ -14,7 +13,10 @@ const {
isLatinCharactersWithSymbol,
generateRandomDigits
} = require('../plugins/controllersHelperFunctions');
const fs = require('fs');
const {
deleteLinksImage,
uploadProcessedImage,
} = require('../plugins/linksImageStorage');
////// variables
const profileWidth = 50;
@@ -108,15 +110,13 @@ module.exports.addLinks = [
}
const imageName = `links_${Date.now()}_${createLinks._id}.${image.mimetype.split('/')[1]}`;
try {
const target = sharp(image.data)
await target
.resize(profileWidth, profileHeight, {
fit: 'cover'
})
.jpeg({
quality: profileQuality
})
.toFile(`./static/uploads/images/links/${imageName}`);
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
createLinks.icon = imageName;
@@ -243,28 +243,15 @@ module.exports.updateLinks = [
}
const imageName = `links_${Date.now()}_${targetLinks._id}.${image.mimetype.split('/')[1]}`;
try {
const target = sharp(image.data)
await target
.resize(profileWidth, profileHeight, {
fit: 'cover'
})
.jpeg({
quality: profileQuality
})
.toFile(`./static/uploads/images/links/${imageName}`);
await deleteLinksImage(targetLinks, 'icon');
/**
* if exist old pic delete it from file
*/
if (targetLinks.icon) {
var oldPic = `./static${targetLinks.icon}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
targetLinks.icon = imageName;
targetLinks.linksStatus = true;
@@ -312,15 +299,7 @@ module.exports.deletOneLinks = [
return res500(res, `شما اجازه حذف این پیوند را ندارید`);
}
if (targetLinks.icon) {
var oldPic = `./static${targetLinks.icon}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (error) => {
if (error) return res500(res, error?.message);
});
}
}
await deleteLinksImage(targetLinks, 'icon');
/** delete links */
await targetLinks.deleteOne();
+135 -254
View File
@@ -1,6 +1,5 @@
const News = require("../models/News");
const { body, validationResult } = require("express-validator");
const sharp = require("sharp");
const { _sr } = require("../plugins/serverResponses");
const {
res404,
@@ -10,13 +9,18 @@ const {
const Category = require("../models/Category");
const Comment = require("../models/Comment");
const User = require("../models/User");
const fs = require("fs");
const NewsFile = require("../models/NewsFile");
const Polls = require("../models/Polls");
const moment = require("moment-jalaali");
const {
getStoredImageName,
deleteNewsImage,
deleteNewsAttachment,
uploadProcessedImage,
uploadNewsAttachment,
} = require("../plugins/newsImageStorage");
////// variables
const noCover = "/img/no-cover.jpg";
const coverNewsWidth = 1920;
const coverNewsHeight = 1080;
const coverQuality = 100;
@@ -111,31 +115,23 @@ const handleFileUploads = async (req, res, targetNews) => {
if (req.files.cover) {
const image = req.files.cover;
if (!_sr.supportedImageFormats.includes(image.mimetype.split("/")[1])) {
if (!validateNewsImage(image)) {
throw new Error(_sr["fa"].format.image);
}
// حذف کاور قدیمی در صورت وجود
if (targetNews.cover && targetNews.cover !== noCover) {
const oldCoverPath = `./static/uploads/images/news/${targetNews.cover}`;
if (fs.existsSync(oldCoverPath)) {
fs.unlinkSync(oldCoverPath);
}
}
await deleteNewsImage(targetNews, "cover");
const imageName = `news_cover_${timestamp}_${targetNews._id}.${
image.mimetype.split("/")[1]
}`;
try {
const target = sharp(image.data);
await target
.resize(coverNewsWidth, coverNewsHeight, {
fit: "cover",
})
.jpeg({
quality: coverQuality,
})
.toFile(`./static/uploads/images/news/${imageName}`);
await processAndUploadNewsImage(
image.data,
imageName,
coverNewsWidth,
coverNewsHeight,
coverQuality
);
targetNews.cover = imageName;
hasCover = true;
@@ -143,28 +139,20 @@ const handleFileUploads = async (req, res, targetNews) => {
throw new Error(e?.message);
}
// اگر thumbCover ارسال نشده، از همین تصویر thumbCover بسازیم
if (!req.files.thumbCover) {
if (targetNews.thumbCover && targetNews.thumbCover !== noCover) {
const oldThumbPath = `./static/uploads/images/news/${targetNews.thumbCover}`;
if (fs.existsSync(oldThumbPath)) {
fs.unlinkSync(oldThumbPath);
}
}
await deleteNewsImage(targetNews, "thumbCover");
const thumbImageName = `news_thumb_${timestamp}_${targetNews._id}.${
image.mimetype.split("/")[1]
}`;
try {
const target = sharp(image.data);
await target
.resize(thumbCoverNewsWidth, thumbCoverNewsHeight, {
fit: "cover",
})
.jpeg({
quality: thumbCoverQuality,
})
.toFile(`./static/uploads/images/news/${thumbImageName}`);
await processAndUploadNewsImage(
image.data,
thumbImageName,
thumbCoverNewsWidth,
thumbCoverNewsHeight,
thumbCoverQuality
);
targetNews.thumbCover = thumbImageName;
hasThumb = true;
@@ -176,31 +164,23 @@ const handleFileUploads = async (req, res, targetNews) => {
if (req.files.thumbCover) {
const image = req.files.thumbCover;
if (!_sr.supportedImageFormats.includes(image.mimetype.split("/")[1])) {
if (!validateNewsImage(image)) {
throw new Error(_sr["fa"].format.image);
}
// حذف thumb قدیمی در صورت وجود
if (targetNews.thumbCover && targetNews.thumbCover !== noCover) {
const oldThumbPath = `./static/uploads/images/news/${targetNews.thumbCover}`;
if (fs.existsSync(oldThumbPath)) {
fs.unlinkSync(oldThumbPath);
}
}
await deleteNewsImage(targetNews, "thumbCover");
const thumbImageName = `news_thumb_${timestamp}_${targetNews._id}.${
image.mimetype.split("/")[1]
}`;
try {
const target = sharp(image.data);
await target
.resize(thumbCoverNewsWidth, thumbCoverNewsHeight, {
fit: "cover",
})
.jpeg({
quality: thumbCoverQuality,
})
.toFile(`./static/uploads/images/news/${thumbImageName}`);
await processAndUploadNewsImage(
image.data,
thumbImageName,
thumbCoverNewsWidth,
thumbCoverNewsHeight,
thumbCoverQuality
);
targetNews.thumbCover = thumbImageName;
hasThumb = true;
@@ -208,28 +188,20 @@ const handleFileUploads = async (req, res, targetNews) => {
throw new Error(e?.message);
}
// اگر cover ارسال نشده، از همین تصویر cover بسازیم
if (!req.files.cover) {
if (targetNews.cover && targetNews.cover !== noCover) {
const oldCoverPath = `./static/uploads/images/news/${targetNews.cover}`;
if (fs.existsSync(oldCoverPath)) {
fs.unlinkSync(oldCoverPath);
}
}
await deleteNewsImage(targetNews, "cover");
const coverImageName = `news_cover_${timestamp}_${targetNews._id}.${
image.mimetype.split("/")[1]
}`;
try {
const target = sharp(image.data);
await target
.resize(coverNewsWidth, coverNewsHeight, {
fit: "cover",
})
.jpeg({
quality: coverQuality,
})
.toFile(`./static/uploads/images/news/${coverImageName}`);
await processAndUploadNewsImage(
image.data,
coverImageName,
coverNewsWidth,
coverNewsHeight,
coverQuality
);
targetNews.cover = coverImageName;
hasCover = true;
@@ -239,14 +211,24 @@ const handleFileUploads = async (req, res, targetNews) => {
}
}
// به‌روزرسانی وضعیت خبر - خبر کامل است اگر حداقل یکی از تصاویر آپلود شده باشد یا قبلاً موجود باشد
const currentHasCover = targetNews.cover && targetNews.cover !== noCover;
const currentHasThumb =
targetNews.thumbCover && targetNews.thumbCover !== noCover;
const currentHasCover = getStoredImageName(targetNews, "cover");
const currentHasThumb = getStoredImageName(targetNews, "thumbCover");
targetNews.newsStatus =
hasCover || hasThumb || currentHasCover || currentHasThumb;
};
const processAndUploadNewsImage = async (
imageData,
imageName,
width,
height,
quality
) => uploadProcessedImage(imageData, imageName, width, height, quality);
const validateNewsImage = (image) => {
return _sr.supportedImageFormats.includes(image.mimetype.split("/")[1]);
};
/////////////////////////////////////////////////////////////////////// admin
module.exports.addNews = [
[
@@ -482,127 +464,16 @@ module.exports.addNews = [
* if we have pic upload to server
*/
if (req.files) {
let hasCover = false;
let hasThumb = false;
const timestamp = Date.now();
if (req.files.cover) {
const image = req.files.cover;
if (
!_sr.supportedImageFormats.includes(image.mimetype.split("/")[1])
) {
return res.status(422).json({
validation: {
cover: {
msg: _sr["fa"].format.image,
},
},
});
}
const imageName = `news_cover_${timestamp}_${createNews._id}.${
image.mimetype.split("/")[1]
}`;
try {
const target = sharp(image.data);
await target
.resize(coverNewsWidth, coverNewsHeight, {
fit: "cover",
})
.jpeg({
quality: coverQuality,
})
.toFile(`./static/uploads/images/news/${imageName}`);
createNews.cover = imageName;
hasCover = true;
} catch (e) {
return res500(res, e?.message);
}
// اگر thumbCover ارسال نشده، از همین تصویر thumbCover بسازیم
if (!req.files.thumbCover) {
const thumbImageName = `news_thumb_${timestamp}_${createNews._id}.${
image.mimetype.split("/")[1]
}`;
try {
const target = sharp(image.data);
await target
.resize(thumbCoverNewsWidth, thumbCoverNewsHeight, {
fit: "cover",
})
.jpeg({
quality: thumbCoverQuality,
})
.toFile(`./static/uploads/images/news/${thumbImageName}`);
createNews.thumbCover = thumbImageName;
hasThumb = true;
} catch (e) {
return res500(res, e?.message);
}
}
try {
await handleFileUploads(req, res, createNews);
await createNews.save();
} catch (error) {
return res.status(422).json({
validation: {
cover: { msg: error.message },
},
});
}
if (req.files.thumbCover) {
const image = req.files.thumbCover;
if (
!_sr.supportedImageFormats.includes(image.mimetype.split("/")[1])
) {
return res.status(422).json({
validation: {
thumbCover: {
msg: _sr["fa"].format.image,
},
},
});
}
const thumbImageName = `news_thumb_${timestamp}_${createNews._id}.${
image.mimetype.split("/")[1]
}`;
try {
const target = sharp(image.data);
await target
.resize(thumbCoverNewsWidth, thumbCoverNewsHeight, {
fit: "cover",
})
.jpeg({
quality: thumbCoverQuality,
})
.toFile(`./static/uploads/images/news/${thumbImageName}`);
createNews.thumbCover = thumbImageName;
hasThumb = true;
} catch (e) {
return res500(res, e?.message);
}
// اگر cover ارسال نشده، از همین تصویر cover بسازیم
if (!req.files.cover) {
const coverImageName = `news_cover_${timestamp}_${createNews._id}.${
image.mimetype.split("/")[1]
}`;
try {
const target = sharp(image.data);
await target
.resize(coverNewsWidth, coverNewsHeight, {
fit: "cover",
})
.jpeg({
quality: coverQuality,
})
.toFile(`./static/uploads/images/news/${coverImageName}`);
createNews.cover = coverImageName;
hasCover = true;
} catch (e) {
return res500(res, e?.message);
}
}
}
// خبر کامل است اگر حداقل یکی از تصاویر آپلود شده باشد
createNews.newsStatus = hasCover || hasThumb;
await createNews.save();
}
/**
@@ -987,30 +858,18 @@ module.exports.deletOneNews = [
return res500(res, `شما اجازه حذف این خبر را ندارید`);
}
if (targetNews.cover !== noCover) {
oldPic = `./static${targetNews.cover}`;
await deleteNewsImage(targetNews, "cover");
await deleteNewsImage(targetNews, "thumbCover");
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (error) => {
if (error) return res500(res, error?.message);
});
}
}
if (targetNews.thumbCover !== noCover) {
oldPic = `./static${targetNews.thumbCover}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (error) => {
if (error) return res500(res, error?.message);
});
}
for (const attachment of targetNews.attachments) {
const fileName = attachment.get("file", null, { getters: false });
await deleteNewsAttachment(fileName);
}
await targetNews.deleteOne();
await Comment.deleteMany({ modelRef: targetNews._id });
x;
return res.json(targetNews);
} catch (error) {
return res500(res, error);
@@ -1021,13 +880,22 @@ module.exports.deletOneNews = [
module.exports.deleteAllNews = [
async (req, res) => {
try {
const importedNews = await News.find({ imported: true });
for (const news of importedNews) {
await deleteNewsImage(news, "cover");
await deleteNewsImage(news, "thumbCover");
for (const attachment of news.attachments) {
const fileName = attachment.get("file", null, { getters: false });
await deleteNewsAttachment(fileName);
}
}
await News.deleteMany({ imported: true });
return res.json("ok");
} catch (error) {
/**
* if we got unkown error show to client
*/
return res500(res, error.message);
}
},
@@ -1322,52 +1190,65 @@ module.exports.searchForSlider = [
module.exports.addAttachmentToNews = [
[body("attTitle").notEmpty().withMessage(_sr["fa"].required.title)],
checkValidations(validationResult),
(req, res) => {
if (!req.files?.file)
return res
.status(422)
.json({ validation: { file: { msg: _sr["fa"].required.file } } });
if (req.files?.file.mimetype.split("/")[1] !== "pdf")
return res
.status(422)
.json({ validation: { file: { msg: "فقط فرمت PDF قابل قبول است" } } });
async (req, res) => {
try {
if (!req.files?.file) {
return res
.status(422)
.json({ validation: { file: { msg: _sr["fa"].required.file } } });
}
const data = { title: req.body.attTitle };
const file = req.files.file;
const fileName =
req.body.attTitle.replace(titleFilterRegex, "-") +
"_" +
Date.now() +
".pdf";
file.mv(`./static/uploads/files/newsAttachments/${fileName}`);
data.file = fileName;
if (req.files.file.mimetype.split("/")[1] !== "pdf") {
return res
.status(422)
.json({ validation: { file: { msg: "فقط فرمت PDF قابل قبول است" } } });
}
News.findById(req.params.newsId, (err, news) => {
if (err || !news) return res404(res, err);
news.attachments.push(data);
news.save((err) => {
if (err) return res500(res, err);
else return res.json({ message: _sr["fa"].response.success_save });
});
});
const news = await News.findById(req.params.newsId);
if (!news) {
return res404(res, "خبر مورد نظر پیدا نشد");
}
const fileName =
req.body.attTitle.replace(titleFilterRegex, "-") +
"_" +
Date.now() +
".pdf";
await uploadNewsAttachment(req.files.file.data, fileName);
news.attachments.push({ title: req.body.attTitle, file: fileName });
await news.save();
return res.json({ message: _sr["fa"].response.success_save });
} catch (error) {
return res500(res, error.message);
}
},
];
module.exports.removeAttachmentFromNews = [
(req, res) => {
News.findOne({ "attachments._id": req.params.fileId }, (err, news) => {
if (err || !news) return res404(res, err);
const file = news.attachments.id(req.params.fileId);
if (!file) return res404(res, "not found");
file.remove();
fs.unlink(`./static/${file.file}`, (err) => {
if (err) console.log(err);
});
news.save((err) => {
if (err) return res500(res, err);
else return res.json({ message: _sr["fa"].response.success_remove });
});
});
async (req, res) => {
try {
const news = await News.findOne({ "attachments._id": req.params.fileId });
if (!news) {
return res404(res, "not found");
}
const attachment = news.attachments.id(req.params.fileId);
if (!attachment) {
return res404(res, "not found");
}
const fileName = attachment.get("file", null, { getters: false });
attachment.remove();
await deleteNewsAttachment(fileName);
await news.save();
return res.json({ message: _sr["fa"].response.success_remove });
} catch (error) {
return res500(res, error.message);
}
},
];
+35 -45
View File
@@ -4,11 +4,12 @@ const {_sr} = require('../plugins/serverResponses')
const {res404, res500} = require('../plugins/controllersHelperFunctions')
const News = require('../models/News')
const Category = require('../models/Category')
const sharp = require('sharp')
const fs = require('fs')
const {
deleteNewsImage,
uploadProcessedImage,
} = require('../plugins/newsImageStorage')
////// variables
const noCover = '/img/no-cover.jpg'
const limit = 5;
const limitDetails = 11;
const titleFilterRegex = new RegExp('/', 'g')
@@ -187,19 +188,23 @@ module.exports.addNewsFile = [
})
const imageName = `news_${Date.now()}_${createNewsFile._id}.${image.mimetype.split('/')[1]}`;
try {
const target = sharp(image.data)
await target
.resize(coverNewsWidth, coverNewsHeight, {fit: 'cover'})
.jpeg({quality: coverQuality})
.toFile(`./static/uploads/images/news/${imageName}`);
await uploadProcessedImage(
image.data,
imageName,
coverNewsWidth,
coverNewsHeight,
coverQuality
);
await uploadProcessedImage(
image.data,
`thumb_${imageName}`,
thumbCoverNewsWidth,
thumbCoverNewsHeight,
thumbCoverQuality
);
createNewsFile.cover = imageName;
await target
.resize(thumbCoverNewsWidth, thumbCoverNewsHeight, {fit: 'cover'})
.jpeg({quality: thumbCoverQuality})
.toFile(`./static/uploads/images/news/thumb_${imageName}`);
createNewsFile.thumbCover = `thumb_${imageName}`;
await createNewsFile.save();
@@ -375,39 +380,24 @@ module.exports.updateNewsFile = [
}
const imageName = `news_${Date.now()}_${targetNewsFile._id}.${image.mimetype.split('/')[1]}`;
try {
const target = sharp(image.data)
await target
.resize(coverNewsWidth, coverNewsHeight, {fit: 'cover'})
.jpeg({quality: coverQuality})
.toFile(`./static/uploads/images/news/${imageName}`);
await deleteNewsImage(targetNewsFile, "cover");
await deleteNewsImage(targetNewsFile, "thumbCover");
await target
.resize(thumbCoverNewsWidth, thumbCoverNewsHeight, {fit: 'cover'})
.jpeg({quality: thumbCoverQuality})
.toFile(`./static/uploads/images/news/thumb_${imageName}`);
await uploadProcessedImage(
image.data,
imageName,
coverNewsWidth,
coverNewsHeight,
coverQuality
);
/**
* if exist old pic delete it from file
*/
if (targetNewsFile.cover !== noCover) {
let oldPic = `./static${targetNewsFile.cover}`;
if (await fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
if (targetNewsFile.thumbCover !== noCover) {
let oldPic = `./static${targetNewsFile.thumbCover}`;
if (await fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await uploadProcessedImage(
image.data,
`thumb_${imageName}`,
thumbCoverNewsWidth,
thumbCoverNewsHeight,
thumbCoverQuality
);
targetNewsFile.cover = imageName;
targetNewsFile.thumbCover = `thumb_${imageName}`;
+20 -40
View File
@@ -2,8 +2,10 @@ const Occasion = require('../models/Occasion');
const {body, validationResult} = require('express-validator');
const {_sr} = require('../plugins/serverResponses');
const {res404, res500} = require('../plugins/controllersHelperFunctions');
const fs = require('fs');
const sharp = require('sharp');
const {
deleteOccasionImage,
uploadProcessedImage,
} = require('../plugins/occasionImageStorage');
const moment = require('moment-jalaali');
////// variables
@@ -63,16 +65,13 @@ module.exports.addOccasion = [
}
const imageName = `occasion_${addOccasion._id}_${Date.now()}.jpg`;
const target = sharp(image.data)
await target
.resize(occasionWidth, occasionHeight, {
fit: 'cover'
})
.toFormat('jpg')
.jpeg({
quality: occasionQuality
})
.toFile(`./static/uploads/images/occasion/${imageName}`);
await uploadProcessedImage(
image.data,
imageName,
occasionWidth,
occasionHeight,
occasionQuality
);
addOccasion.status = true;
addOccasion.mainCover = imageName;
@@ -132,26 +131,15 @@ module.exports.updateOccasion = [
}
const imageName = `occasion_${targetOccasion._id}_${Date.now()}.jpg`;
const target = sharp(image.data)
await target
.resize(occasionWidth, occasionHeight, {
fit: 'cover'
})
.toFormat('jpg')
.jpeg({
quality: occasionQuality
})
.toFile(`./static/uploads/images/occasion/${imageName}`);
await deleteOccasionImage(targetOccasion, 'mainCover');
if (targetOccasion.mainCover) {
var oldPic = `./static${targetOccasion.mainCover}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await uploadProcessedImage(
image.data,
imageName,
occasionWidth,
occasionHeight,
occasionQuality
);
targetOccasion.status = true;
targetOccasion.mainCover = imageName;
@@ -184,15 +172,7 @@ module.exports.deleteOccasion = [
return res404(res, `مناسبت مورد نظر پیدا نشد`);
}
if (targetOccasion.mainCover) {
var oldPic = `./static${targetOccasion.mainCover}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await deleteOccasionImage(targetOccasion, 'mainCover');
await targetOccasion.deleteOne();
+130 -91
View File
@@ -1,109 +1,148 @@
const fs = require("fs/promises");
const os = require("os");
const path = require("path");
const axios = require("axios");
const { body, validationResult } = require("express-validator");
const { _sr } = require("../plugins/serverResponses");
const { checkValidations } = require("../plugins/controllersHelperFunctions");
const Book = require("../models/Book");
var convertapi = require('convertapi')('6zZB0nquI5V3P3Vg');
const {
uploadPdf,
uploadBookSlide,
deletePdf,
deleteBookSlide,
} = require("../plugins/bookImageStorage");
const convertapi = require("convertapi")("6zZB0nquI5V3P3Vg");
module.exports.AddPDF = [
[
body('title')
.notEmpty().withMessage(_sr['fa'].required.title)
],
checkValidations(validationResult),
async (req, res) => {
try {
const firstbook = [];
if (!req.files?.file) {
return res.status(422).json({ validation: { file: { msg: _sr['fa'].required.file } } });
}
if (req.files?.file.mimetype.split('/')[1] !== 'pdf') {
return res.status(422).json({ validation: { file: { msg: 'فقط فرمت PDF قابل قبول است' } } });
}
const book = await Book.findById(req.body.bookId);
const bookId = book ? book._id : null;
const data = { title: req.body.title };
const file = req.files.file;
const fileName = req.body.title.replace(Date.now(), '-') + '_' + Date.now() + '.pdf';
const imageName = req.body.title.replace(Date.now(), '-') + '_' + Date.now();
const filePath = `./static/uploads/pdf/${fileName}`;
await file.mv(filePath);
data.file = fileName;
const convertPromise = convertapi.convert('jpg', {
File: filePath
}, 'pdf');
const result = await convertPromise;
const savedFiles = await result.saveFiles(`./static/uploads/books/${imageName}.jpeg`);
const imagePath = `https://ostan-mr.ir/uploads/books/${imageName}.jpeg`;
firstbook.push(imagePath);
if (bookId) {
const update = await Book.findByIdAndUpdate(
{_id:bookId},
{ $push: { slides: imagePath } },
{ upsert: true }
);
return res.status(201).json({ msg: "اسلاید کتاب اضافه شد", Data: update });
} else {
const newBook = new Book({
title: req.body.title,
uri: `https://ostan-mr.ir/uploads/pdf/${fileName}`,
slides: firstbook
});
await newBook.save();
return res.status(201).json({ msg: "کتاب ایجاد شد", Data: newBook });
}
} catch (error) {
console.error(error);
return res.status(500).json({ msg: "خطایی در پردازش فایل رخ داد", error: error.message });
}
[body("title").notEmpty().withMessage(_sr["fa"].required.title)],
checkValidations(validationResult),
async (req, res) => {
let tmpDir;
try {
const firstbook = [];
if (!req.files?.file) {
return res
.status(422)
.json({ validation: { file: { msg: _sr["fa"].required.file } } });
}
if (req.files?.file.mimetype.split("/")[1] !== "pdf") {
return res
.status(422)
.json({ validation: { file: { msg: "فقط فرمت PDF قابل قبول است" } } });
}
const book = await Book.findById(req.body.bookId);
const bookId = book ? book._id : null;
const file = req.files.file;
const fileName =
req.body.title.replace(Date.now(), "-") + "_" + Date.now() + ".pdf";
const imageName =
req.body.title.replace(Date.now(), "-") + "_" + Date.now();
const { url: pdfUrl } = await uploadPdf(file.data, fileName);
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "book-"));
const tmpPdfPath = path.join(tmpDir, fileName);
await fs.writeFile(tmpPdfPath, file.data);
const result = await convertapi.convert(
"jpg",
{ File: tmpPdfPath },
"pdf"
);
const convertedResponse = await axios.get(result.files[0].url, {
responseType: "arraybuffer",
});
const imagePath = await uploadBookSlide(
Buffer.from(convertedResponse.data),
imageName
);
firstbook.push(imagePath);
if (bookId) {
const update = await Book.findByIdAndUpdate(
{ _id: bookId },
{ $push: { slides: imagePath } },
{ upsert: true }
);
return res
.status(201)
.json({ msg: "اسلاید کتاب اضافه شد", Data: update });
}
const newBook = new Book({
title: req.body.title,
uri: pdfUrl,
slides: firstbook,
});
await newBook.save();
return res.status(201).json({ msg: "کتاب ایجاد شد", Data: newBook });
} catch (error) {
console.error(error);
return res.status(500).json({
msg: "خطایی در پردازش فایل رخ داد",
error: error.message,
});
} finally {
if (tmpDir) {
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {});
}
}
},
];
module.exports.GetAll = [
async (req, res,next) => {
try {
const Books = await Book.find({});
return res.status(200).json({ Books });
} catch (error) {
next(error)
}
async (req, res, next) => {
try {
const Books = await Book.find({});
return res.status(200).json({ Books });
} catch (error) {
next(error);
}
},
];
module.exports.GetOne = [
async (req, res,next) => {
try {
const id = req.params.id
const book = await Book.findById(id);
return res.status(200).json({ msg: book });
} catch (error) {
next(error)
}
async (req, res, next) => {
try {
const id = req.params.id;
const book = await Book.findById(id);
return res.status(200).json({ msg: book });
} catch (error) {
next(error);
}
},
];
module.exports.DeleteOne = [
async(req,res,next)=>{
try {
const book = await Book.deleteOne({_id:req.params.id})
if(!book){
return res.status(200).json({ msg: 'کتاب وجود ندارد ' });
}
return res.status(200).json({ msg: 'کتاب حذف شد' });
} catch (error) {
next(error)
}
async (req, res, next) => {
try {
const book = await Book.findById(req.params.id);
if (!book) {
return res.status(200).json({ msg: "کتاب وجود ندارد " });
}
const uri = book.get("uri", null, { getters: false });
if (uri) {
await deletePdf(uri.split("/").pop());
}
const slides = book.get("slides", [], { getters: false }) || [];
for (const slide of slides) {
const imageName = slide.split("/").pop().replace(/\.jpeg$/i, "");
await deleteBookSlide(imageName);
}
await book.deleteOne();
return res.status(200).json({ msg: "کتاب حذف شد" });
} catch (error) {
next(error);
}
]
},
];
+20 -41
View File
@@ -3,7 +3,6 @@ const {
body,
validationResult
} = require('express-validator');
const sharp = require('sharp');
const {
_sr
} = require('../plugins/serverResponses');
@@ -14,7 +13,10 @@ const {
isLatinCharactersWithSymbol,
generateRandomDigits
} = require('../plugins/controllersHelperFunctions');
const fs = require('fs');
const {
deleteSliderImage,
uploadProcessedImage,
} = require('../plugins/sliderImageStorage');
////// variables
const profileWidth = 600;
@@ -113,15 +115,13 @@ module.exports.addSlider = [
}
const imageName = `slider_${Date.now()}_${createSlider._id}.${image.mimetype.split('/')[1]}`;
try {
const target = sharp(image.data)
await target
.resize(profileWidth, profileHeight, {
fit: 'cover'
})
.jpeg({
quality: profileQuality
})
.toFile(`./static/uploads/images/slider/${imageName}`);
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
createSlider.cover = imageName;
@@ -253,28 +253,15 @@ module.exports.updateSlider = [
}
const imageName = `slider_${Date.now()}_${targetSlider._id}.${image.mimetype.split('/')[1]}`;
try {
const target = sharp(image.data)
await target
.resize(profileWidth, profileHeight, {
fit: 'cover'
})
.jpeg({
quality: profileQuality
})
.toFile(`./static/uploads/images/slider/${imageName}`);
await deleteSliderImage(targetSlider, "cover");
/**
* if exist old pic delete it from file
*/
if (targetSlider.cover) {
var oldPic = `./static${targetSlider.cover}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
targetSlider.cover = imageName;
} catch (e) {
@@ -322,15 +309,7 @@ module.exports.deletOneSlider = [
return res500(res, `شما اجازه حذف این اسلایدر را ندارید`);
}
if (targetSlider.cover) {
var oldPic = `./static${targetSlider.cover}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (error) => {
if (error) return res500(res, error?.message);
});
}
}
await deleteSliderImage(targetSlider, "cover");
/** delete slider */
await targetSlider.deleteOne();
+20 -41
View File
@@ -3,7 +3,6 @@ const {
body,
validationResult
} = require('express-validator');
const sharp = require('sharp');
const {
_sr
} = require('../plugins/serverResponses');
@@ -14,7 +13,10 @@ const {
isLatinCharactersWithSymbol,
generateRandomDigits
} = require('../plugins/controllersHelperFunctions');
const fs = require('fs');
const {
deleteSliderImage,
uploadProcessedImage,
} = require('../plugins/sliderImageStorage');
////// variables
const profileWidth = 880;
@@ -113,15 +115,13 @@ module.exports.addSlider = [
}
const imageName = `slider_${Date.now()}_${createSlider._id}.${image.mimetype.split('/')[1]}`;
try {
const target = sharp(image.data)
await target
.resize(profileWidth, profileHeight, {
fit: 'cover'
})
.jpeg({
quality: profileQuality
})
.toFile(`./static/uploads/images/slider/${imageName}`);
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
createSlider.cover = imageName;
@@ -253,28 +253,15 @@ module.exports.updateSlider = [
}
const imageName = `slider_${Date.now()}_${targetSlider._id}.${image.mimetype.split('/')[1]}`;
try {
const target = sharp(image.data)
await target
.resize(profileWidth, profileHeight, {
fit: 'cover'
})
.jpeg({
quality: profileQuality
})
.toFile(`./static/uploads/images/slider/${imageName}`);
await deleteSliderImage(targetSlider, "cover");
/**
* if exist old pic delete it from file
*/
if (targetSlider.cover) {
var oldPic = `./static${targetSlider.cover}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
targetSlider.cover = imageName;
} catch (e) {
@@ -322,15 +309,7 @@ module.exports.deletOneSlider = [
return res500(res, `شما اجازه حذف این اسلایدر را ندارید`);
}
if (targetSlider.cover) {
var oldPic = `./static${targetSlider.cover}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (error) => {
if (error) return res500(res, error?.message);
});
}
}
await deleteSliderImage(targetSlider, "cover");
/** delete slider */
await targetSlider.deleteOne();
+28 -65
View File
@@ -4,7 +4,6 @@ const { rateLimit } = require("express-rate-limit");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");
const sharp = require("sharp");
const { secretKey } = require("./../authentication");
const { _sr } = require("../plugins/serverResponses");
const {
@@ -14,7 +13,10 @@ const {
isLatinCharactersWithSymbol,
generateRandomDigits,
} = require("../plugins/controllersHelperFunctions");
const fs = require("fs");
const {
deleteUserImage,
uploadProcessedImage,
} = require("../plugins/userImageStorage");
const nodemailer = require("nodemailer");
const Category = require("../models/Category");
const Logger = require("../models/Logger");
@@ -566,15 +568,13 @@ module.exports.registerAdmin = [
image.mimetype.split("/")[1];
data.profilePic = imageName;
try {
const target = sharp(image.data);
await target
.resize(profileWidth, profileHeight, {
fit: "cover",
})
.jpeg({
quality: profileQuality,
})
.toFile(`./static/uploads/images/users/${imageName}`);
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
} catch (e) {
return res500(res, e?.message);
}
@@ -713,18 +713,7 @@ module.exports.deleteOneUser = [
.json({ message: `شما نمیتوانید این کاربر را حذف کنید` });
}
/**
* if exist old pic delete it from file
*/
if (user.profilePic) {
var oldPic = `./static${user.profilePic}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await deleteUserImage(user, "profilePic");
/** delete user */
await user.deleteOne();
@@ -922,28 +911,15 @@ module.exports.updateUser = [
image.mimetype.split("/")[1];
try {
const target = sharp(image.data);
await target
.resize(profileWidth, profileHeight, {
fit: "cover",
})
.jpeg({
quality: profileQuality,
})
.toFile(`./static/uploads/images/users/${imageName}`);
await deleteUserImage(user, "profilePic");
/**
* if exist old pic delete it from file
*/
if (user.profilePic) {
var oldPic = `./static${user.profilePic}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
} catch (e) {
return res500(res, e?.message);
}
@@ -1129,28 +1105,15 @@ module.exports.updateProfile = [
image.mimetype.split("/")[1];
try {
const target = sharp(image.data);
await target
.resize(profileWidth, profileHeight, {
fit: "cover",
})
.jpeg({
quality: profileQuality,
})
.toFile(`./static/uploads/images/users/${imageName}`);
await deleteUserImage(user, "profilePic");
/**
* if exist old pic delete it from file
*/
if (user.profilePic) {
var oldPic = `./static${user.profilePic}`;
if (fs.existsSync(oldPic)) {
fs.unlink(oldPic, (err) => {
if (err) return res500(res, err?.message);
});
}
}
await uploadProcessedImage(
image.data,
imageName,
profileWidth,
profileHeight,
profileQuality
);
} catch (e) {
return res500(res, e?.message);
}
+8 -8
View File
@@ -2,8 +2,7 @@ const YearBanner = require('../models/YearBanner');
const {body, validationResult, checkSchema} = require('express-validator');
const {_sr} = require('../plugins/serverResponses');
const {res404, res500} = require('../plugins/controllersHelperFunctions');
const fs = require('fs');
const sharp = require('sharp');
const {uploadProcessedImage} = require('../plugins/yearBannerImageStorage');
////// variables
const yearBannerWidth = 1920
@@ -51,12 +50,13 @@ module.exports.updateYearBanner = [
imageName = `mainPage_cover.jpg`;
const target = sharp(image.data)
await target
.resize(yearBannerWidth, yearBannerHeight, {fit: 'cover'})
.toFormat('jpg')
.jpeg({quality: yearBannerQuality})
.toFile(`./static/uploads/images/mainCover/${imageName}`);
await uploadProcessedImage(
image.data,
imageName,
yearBannerWidth,
yearBannerHeight,
yearBannerQuality
);
}
if (yearBanner) {
+2 -1
View File
@@ -7,7 +7,8 @@
// const databaseURL = 'mongodb://ostanmri_user:admin1234@localhost:27017/ostanmri_database'
const databaseURL =
// "mongodb://root:f2d63313cc3ed6ff@srv-captain--ostandari-db:27017/ostan_db?authSource=admin";
"mongodb://root:f2d63313cc3ed6ff@89.32.249.24:50123/ostan_db?authSource=admin";
"mongodb://root:e9a15xftSaqNgR1fNMSUEHVqYDVh5XiDSW8Dt9sG1UyHvOE6nSTtQguaO4vor4Fp@noxkpqjq8ufxq969ut0v5p3m:27017/ostan_db?directConnection=true&authSource=admin"
// "mongodb://root:f2d63313cc3ed6ff@89.32.249.24:50123/ostan_db?authSource=admin";
//const databaseURL = 'mongodb://danakcorp:danakcorp%212024@ostan-mr.ir:27017/ostan_db?serverSelectionTimeoutMS=5000&connectTimeoutMS=10000&authSource=admin&authMechanism=SCRAM-SHA-1'
// const databaseURL = 'mongodb://ostanmri_user:admin1234@91.98.96.174:27017/ostanmri_database?serverSelectionTimeoutMS=5000&connectTimeoutMS=10000&authSource=ostanmri_database'
+2
View File
@@ -1,3 +1,5 @@
require("dotenv").config();
const express = require("express");
const database = require("./database");
const fileUpload = require("express-fileupload");
+22 -7
View File
@@ -1,16 +1,31 @@
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const s3Storage = require('../plugins/s3Storage');
function PDF(pdf) {
if (pdf) return '/uploads/files/ReadOnly-PDF' + pdf
function pdfUri(uri) {
if (!uri) return null;
if (uri.startsWith('http://') || uri.startsWith('https://')) return uri;
const fileName = uri.startsWith('/') ? uri.split('/').pop() : uri;
return s3Storage.getPublicUrl(`uploads/pdf/${fileName}`);
}
function slideUrl(slide) {
if (!slide) return null;
if (slide.startsWith('http://') || slide.startsWith('https://')) return slide;
const imageName = slide.startsWith('/') ? slide.split('/').pop() : slide;
const key = imageName.endsWith('.jpeg')
? `uploads/books/${imageName}`
: `uploads/books/${imageName}.jpeg`;
return s3Storage.getPublicUrl(key);
}
const BookSchema = mongoose.Schema({
title: String,
uri: String,
slides:{
default:[],
type:Array
uri: { type: String, get: pdfUri, default: null },
slides: {
default: [],
type: [String],
get: (slides) => (slides || []).map(slideUrl),
}
});
+14 -4
View File
@@ -1,4 +1,5 @@
const mongoose = require("mongoose");
const s3Storage = require("../plugins/s3Storage");
function autoPopulateSubs(next) {
this.populate({
@@ -14,12 +15,21 @@ function autoPopulateSubs(next) {
}
function file(file) {
return "/uploads/files/terms/" + file;
if (!file) return null;
if (file.startsWith("http://") || file.startsWith("https://")) return file;
return s3Storage.getPublicUrl(`uploads/files/terms/${file}`);
}
function image(img) {
if (img) return "/uploads/images/category/" + img;
else return "/img/no-cover.jpg";
if (!img) return "/img/no-cover.jpg";
if (img.startsWith("http://") || img.startsWith("https://")) return img;
return s3Storage.getPublicUrl(`uploads/images/category/${img}`);
}
function shafafiatIcon(icon) {
if (!icon) return null;
if (icon.startsWith("http://") || icon.startsWith("https://")) return icon;
return s3Storage.getPublicUrl(`uploads/images/category/shafafiat/${icon}`);
}
const CategoryTermsFile = mongoose.Schema({
@@ -71,7 +81,7 @@ const CategorySchema = mongoose.Schema({
managerName: String,
managerDescription: String,
/** if category was shafafiat this icon is for that */
icon: String,
icon: { type: String, get: shafafiatIcon, default: null },
});
CategorySchema.index({ parent: 1, childs: 1 });
+4 -1
View File
@@ -1,7 +1,10 @@
const mongoose = require('mongoose');
const s3Storage = require('../plugins/s3Storage');
function image(img) {
if (img) return '/uploads/images/content/' + img
if (!img) return null;
if (img.startsWith('http://') || img.startsWith('https://')) return img;
return s3Storage.getPublicUrl(`uploads/images/content/${img}`);
}
+4 -1
View File
@@ -1,8 +1,11 @@
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const s3Storage = require('../plugins/s3Storage');
function image(img) {
if (img) return '/uploads/images/election/' + img
if (!img) return null;
if (img.startsWith('http://') || img.startsWith('https://')) return img;
return s3Storage.getPublicUrl(`uploads/images/election/${img}`);
}
const CandidatesSchema = mongoose.Schema({
+10 -3
View File
@@ -1,16 +1,23 @@
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const s3Storage = require('../plugins/s3Storage');
function galleryUrl(img, prefix) {
if (!img) return null;
if (img.startsWith('http://') || img.startsWith('https://')) return img;
return s3Storage.getPublicUrl(`${prefix}/${img}`);
}
function image(img) {
if (img) return '/uploads/images/gallery/' + img
return galleryUrl(img, 'uploads/images/gallery');
}
function graphic(img) {
if (img) return '/uploads/graphics/gallery/' + img
return galleryUrl(img, 'uploads/graphics/gallery');
}
function video(vdo) {
if (vdo) return '/uploads/videos/gallery/' + vdo
return galleryUrl(vdo, 'uploads/videos/gallery');
}
const GallerySchema = mongoose.Schema({
+4 -1
View File
@@ -1,7 +1,10 @@
const mongoose = require('mongoose');
const s3Storage = require('../plugins/s3Storage');
function image(img) {
if (img) return '/uploads/images/links/' + img
if (!img) return null;
if (img.startsWith('http://') || img.startsWith('https://')) return img;
return s3Storage.getPublicUrl(`uploads/images/links/${img}`);
}
const LinksSchema = mongoose.Schema({
+9 -5
View File
@@ -1,17 +1,21 @@
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const s3Storage = require('../plugins/s3Storage');
function image(img) {
if (img) return '/uploads/images/news/' + img
else return '/img/no-cover.jpg'
if (!img) return '/img/no-cover.jpg';
if (img.startsWith('http://') || img.startsWith('https://')) return img;
return s3Storage.getPublicUrl(`uploads/images/news/${img}`);
}
function file(file) {
return '/uploads/files/newsAttachments/' + file
function attachmentFile(file) {
if (!file) return null;
if (file.startsWith('http://') || file.startsWith('https://')) return file;
return s3Storage.getPublicUrl(`uploads/files/newsAttachments/${file}`);
}
const NewsAttachmentSchema = mongoose.Schema({
file: {type: String, get: file},
file: {type: String, get: attachmentFile},
title: String
})
+4 -2
View File
@@ -1,9 +1,11 @@
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const s3Storage = require('../plugins/s3Storage');
function image(img) {
if (img) return '/uploads/images/news/' + img
else return '/img/no-cover.jpg'
if (!img) return '/img/no-cover.jpg';
if (img.startsWith('http://') || img.startsWith('https://')) return img;
return s3Storage.getPublicUrl(`uploads/images/news/${img}`);
}
const NewsFileSchema = mongoose.Schema({
+4 -1
View File
@@ -1,8 +1,11 @@
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const s3Storage = require('../plugins/s3Storage');
function image(img) {
if (img) return '/uploads/images/occasion/' + img
if (!img) return null;
if (img.startsWith('http://') || img.startsWith('https://')) return img;
return s3Storage.getPublicUrl(`uploads/images/occasion/${img}`);
}
const OccasionSchema = mongoose.Schema({
+4 -1
View File
@@ -1,8 +1,11 @@
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const s3Storage = require('../plugins/s3Storage');
function image(img) {
if (img) return '/uploads/images/slider/' + img
if (!img) return null;
if (img.startsWith('http://') || img.startsWith('https://')) return img;
return s3Storage.getPublicUrl(`uploads/images/slider/${img}`);
}
const SliderSchema = mongoose.Schema({
+4 -1
View File
@@ -1,8 +1,11 @@
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const s3Storage = require('../plugins/s3Storage');
function image(img) {
if (img) return '/uploads/images/users/' + img
if (!img) return null;
if (img.startsWith('http://') || img.startsWith('https://')) return img;
return s3Storage.getPublicUrl(`uploads/images/users/${img}`);
}
const AdminSchema = mongoose.Schema({
+4 -1
View File
@@ -1,7 +1,10 @@
const mongoose = require('mongoose');
const s3Storage = require('../plugins/s3Storage');
function image(img) {
if (img) return '/uploads/images/mainCover/' + img
if (!img) return null;
if (img.startsWith('http://') || img.startsWith('https://')) return img;
return s3Storage.getPublicUrl(`uploads/images/mainCover/${img}`);
}
const YearBannerSchema = mongoose.Schema({
+33
View File
@@ -0,0 +1,33 @@
const s3Storage = require("./s3Storage");
const BACKUP_PREFIX = "backup";
const UPLOADS_PREFIX = "uploads";
const appendS3UploadsToArchive = async (archive) => {
const keys = await s3Storage.listObjects(UPLOADS_PREFIX);
for (const key of keys) {
const buffer = await s3Storage.getObjectBuffer(key);
archive.append(buffer, { name: key });
}
};
const uploadBackupArchive = async (buffer, fileName) => {
await s3Storage.uploadBuffer(
`${BACKUP_PREFIX}/${fileName}`,
buffer,
"application/zip"
);
};
const deleteOldBackups = async () => {
await s3Storage.deleteObjectsByPrefix(BACKUP_PREFIX);
};
module.exports = {
BACKUP_PREFIX,
UPLOADS_PREFIX,
appendS3UploadsToArchive,
uploadBackupArchive,
deleteOldBackups,
};
+48
View File
@@ -0,0 +1,48 @@
const s3Storage = require("./s3Storage");
const PDF_PREFIX = "uploads/pdf";
const BOOK_SLIDE_PREFIX = "uploads/books";
const uploadPdf = async (fileData, fileName) => {
const key = `${PDF_PREFIX}/${fileName}`;
await s3Storage.uploadBuffer(key, fileData, "application/pdf");
return {
fileName,
url: s3Storage.getPublicUrl(key),
};
};
const uploadBookSlide = async (buffer, imageName) => {
const key = `${BOOK_SLIDE_PREFIX}/${imageName}.jpeg`;
await s3Storage.uploadBuffer(key, buffer, "image/jpeg");
return s3Storage.getPublicUrl(key);
};
const deletePdf = async (fileName) => {
if (!fileName) return;
try {
await s3Storage.deleteObject(`${PDF_PREFIX}/${fileName}`);
} catch (error) {
console.log("Failed to delete PDF from S3:", error?.message);
}
};
const deleteBookSlide = async (imageName) => {
if (!imageName) return;
try {
await s3Storage.deleteObject(`${BOOK_SLIDE_PREFIX}/${imageName}.jpeg`);
} catch (error) {
console.log("Failed to delete book slide from S3:", error?.message);
}
};
module.exports = {
PDF_PREFIX,
BOOK_SLIDE_PREFIX,
uploadPdf,
uploadBookSlide,
deletePdf,
deleteBookSlide,
};
+94
View File
@@ -0,0 +1,94 @@
const sharp = require("sharp");
const s3Storage = require("./s3Storage");
const CATEGORY_IMAGE_PREFIX = "uploads/images/category";
const CATEGORY_SHAFAFIAT_PREFIX = "uploads/images/category/shafafiat";
const TERMS_FILE_PREFIX = "uploads/files/terms";
const getStoredImageName = (doc, field) =>
doc.get(field, null, { getters: false });
const deleteCategoryImage = async (doc, field) => {
const fileName = getStoredImageName(doc, field);
if (!fileName) return;
try {
await s3Storage.deleteObject(`${CATEGORY_IMAGE_PREFIX}/${fileName}`);
} catch (error) {
console.log(`Failed to delete ${field} from S3:`, error?.message);
}
};
const deleteShafafiatIcon = async (fileName) => {
if (!fileName) return;
try {
await s3Storage.deleteObject(`${CATEGORY_SHAFAFIAT_PREFIX}/${fileName}`);
} catch (error) {
console.log("Failed to delete shafafiat icon from S3:", error?.message);
}
};
const uploadProcessedImage = async (
imageData,
imageName,
width,
height,
quality
) => {
const buffer = await sharp(imageData)
.resize(width, height, { fit: "cover" })
.jpeg({ quality })
.toBuffer();
await s3Storage.uploadBuffer(
`${CATEGORY_IMAGE_PREFIX}/${imageName}`,
buffer,
"image/jpeg"
);
return imageName;
};
const uploadShafafiatIcon = async (fileData, fileName, contentType) => {
await s3Storage.uploadBuffer(
`${CATEGORY_SHAFAFIAT_PREFIX}/${fileName}`,
fileData,
contentType
);
return fileName;
};
const uploadTermsFile = async (fileData, fileName) => {
await s3Storage.uploadBuffer(
`${TERMS_FILE_PREFIX}/${fileName}`,
fileData,
"application/pdf"
);
return fileName;
};
const deleteTermsFile = async (fileName) => {
if (!fileName) return;
try {
await s3Storage.deleteObject(`${TERMS_FILE_PREFIX}/${fileName}`);
} catch (error) {
console.log("Failed to delete terms file from S3:", error?.message);
}
};
module.exports = {
CATEGORY_IMAGE_PREFIX,
CATEGORY_SHAFAFIAT_PREFIX,
TERMS_FILE_PREFIX,
getStoredImageName,
deleteCategoryImage,
deleteShafafiatIcon,
uploadProcessedImage,
uploadShafafiatIcon,
uploadTermsFile,
deleteTermsFile,
};
+52
View File
@@ -0,0 +1,52 @@
const sharp = require("sharp");
const s3Storage = require("./s3Storage");
const CONTENT_IMAGE_PREFIX = "uploads/images/content";
const getStoredImageName = (doc, field) =>
doc.get(field, null, { getters: false });
const deleteContentImage = async (doc, field) => {
const fileName = getStoredImageName(doc, field);
if (!fileName) return;
try {
await s3Storage.deleteObject(`${CONTENT_IMAGE_PREFIX}/${fileName}`);
} catch (error) {
console.log(`Failed to delete ${field} from S3:`, error?.message);
}
};
const deleteContentImages = async (doc) => {
await deleteContentImage(doc, "cover");
await deleteContentImage(doc, "thumb");
};
const uploadProcessedImage = async (
imageData,
imageName,
width,
height,
quality
) => {
const buffer = await sharp(imageData)
.resize(width, height, { fit: "cover" })
.jpeg({ quality })
.toBuffer();
await s3Storage.uploadBuffer(
`${CONTENT_IMAGE_PREFIX}/${imageName}`,
buffer,
"image/jpeg"
);
return imageName;
};
module.exports = {
CONTENT_IMAGE_PREFIX,
getStoredImageName,
deleteContentImage,
deleteContentImages,
uploadProcessedImage,
};
+58
View File
@@ -0,0 +1,58 @@
const sharp = require("sharp");
const s3Storage = require("./s3Storage");
const ELECTION_IMAGE_PREFIX = "uploads/images/election";
const getStoredImageName = (doc, field) =>
doc.get(field, null, { getters: false });
const deleteElectionImage = async (fileName) => {
if (!fileName) return;
try {
await s3Storage.deleteObject(`${ELECTION_IMAGE_PREFIX}/${fileName}`);
} catch (error) {
console.log(`Failed to delete election image from S3:`, error?.message);
}
};
const deleteCandidateImage = async (candidate) => {
const fileName = getStoredImageName(candidate, "profilePic");
await deleteElectionImage(fileName);
};
const deleteElectionCandidateImages = async (election) => {
for (const candidate of election.candidates) {
await deleteCandidateImage(candidate);
}
};
const uploadProcessedImage = async (
imageData,
imageName,
width,
height,
quality
) => {
const buffer = await sharp(imageData)
.resize(width, height, { fit: "cover" })
.jpeg({ quality })
.toBuffer();
await s3Storage.uploadBuffer(
`${ELECTION_IMAGE_PREFIX}/${imageName}`,
buffer,
"image/jpeg"
);
return imageName;
};
module.exports = {
ELECTION_IMAGE_PREFIX,
getStoredImageName,
deleteElectionImage,
deleteCandidateImage,
deleteElectionCandidateImages,
uploadProcessedImage,
};
+191
View File
@@ -0,0 +1,191 @@
const sharp = require("sharp");
const s3Storage = require("./s3Storage");
const GALLERY_IMAGE_PREFIX = "uploads/images/gallery";
const GALLERY_GRAPHIC_PREFIX = "uploads/graphics/gallery";
const GALLERY_VIDEO_PREFIX = "uploads/videos/gallery";
const getPrefixForFileType = (fileType) => {
switch (fileType) {
case "graphic":
return GALLERY_GRAPHIC_PREFIX;
case "video":
return GALLERY_VIDEO_PREFIX;
default:
return GALLERY_IMAGE_PREFIX;
}
};
const getGalleryObjectKey = (fileType, fileName) =>
`${getPrefixForFileType(fileType)}/${fileName}`;
const getStoredFileName = (doc, field) =>
doc.get(field, null, { getters: false });
const getStoredArray = (doc, field) => {
const value = doc.get(field, null, { getters: false });
return Array.isArray(value) ? value : [];
};
const deleteGalleryFile = async (fileType, fileName) => {
if (!fileName) return;
const baseName = fileName.replace(/^thumb_/, "");
try {
await s3Storage.deleteObject(getGalleryObjectKey(fileType, baseName));
} catch (error) {
console.log(`Failed to delete ${baseName} from S3:`, error?.message);
}
if (fileType !== "video") {
try {
await s3Storage.deleteObject(
getGalleryObjectKey(fileType, `thumb_${baseName}`)
);
} catch (error) {
console.log(`Failed to delete thumb_${baseName} from S3:`, error?.message);
}
}
};
const uploadProcessedImage = async (
imageData,
imageName,
width,
height,
quality,
prefix = GALLERY_IMAGE_PREFIX
) => {
let pipeline = sharp(imageData);
if (width && height) {
pipeline = pipeline.resize(width, height, { fit: "cover" });
}
const buffer = await pipeline.jpeg({ quality }).toBuffer();
await s3Storage.uploadBuffer(
`${prefix}/${imageName}`,
buffer,
"image/jpeg"
);
return imageName;
};
const uploadGalleryVideo = async (fileData, fileName, contentType) => {
await s3Storage.uploadBuffer(
`${GALLERY_VIDEO_PREFIX}/${fileName}`,
fileData,
contentType
);
return fileName;
};
const uploadGalleryFile = async (fileType, file, galleryId, options = {}) => {
const fileName = `gallery_${galleryId}_${Date.now()}.${
file.mimetype.split("/")[1]
}`;
const prefix = getPrefixForFileType(fileType);
switch (fileType) {
case "image":
await uploadProcessedImage(
file.data,
fileName,
options.mainWidth,
options.mainHeight,
options.mainQuality,
prefix
);
await uploadProcessedImage(
file.data,
`thumb_${fileName}`,
options.thumbWidth,
options.thumbHeight,
options.thumbQuality,
prefix
);
break;
case "graphic":
await uploadProcessedImage(
file.data,
fileName,
null,
null,
options.mainQuality,
prefix
);
await uploadProcessedImage(
file.data,
`thumb_${fileName}`,
options.thumbWidth,
options.thumbHeight,
options.thumbQuality,
prefix
);
break;
case "video":
await uploadGalleryVideo(file.data, fileName, file.mimetype);
break;
}
return fileName;
};
const deleteAllGalleryFiles = async (gallery) => {
const galleryType = getStoredFileName(gallery, "galleryType");
if (galleryType === "image") {
const images = getStoredArray(gallery, "images");
for (const fileName of images) {
await deleteGalleryFile("image", fileName);
}
return;
}
if (galleryType === "graphic") {
await deleteGalleryFile("graphic", getStoredFileName(gallery, "graphic"));
return;
}
if (galleryType === "video") {
await deleteGalleryFile("video", getStoredFileName(gallery, "video"));
}
};
const deleteGalleryCover = async (gallery) => {
const cover = getStoredFileName(gallery, "cover");
const thumbCover = getStoredFileName(gallery, "thumbCover");
if (cover) {
try {
await s3Storage.deleteObject(`${GALLERY_IMAGE_PREFIX}/${cover}`);
} catch (error) {
console.log(`Failed to delete cover from S3:`, error?.message);
}
}
if (thumbCover) {
try {
await s3Storage.deleteObject(`${GALLERY_IMAGE_PREFIX}/${thumbCover}`);
} catch (error) {
console.log(`Failed to delete thumb cover from S3:`, error?.message);
}
}
};
module.exports = {
GALLERY_IMAGE_PREFIX,
GALLERY_GRAPHIC_PREFIX,
GALLERY_VIDEO_PREFIX,
getStoredFileName,
getStoredArray,
deleteGalleryFile,
uploadProcessedImage,
uploadGalleryFile,
deleteAllGalleryFiles,
deleteGalleryCover,
};
+46
View File
@@ -0,0 +1,46 @@
const sharp = require("sharp");
const s3Storage = require("./s3Storage");
const LINKS_IMAGE_PREFIX = "uploads/images/links";
const getStoredImageName = (doc, field) =>
doc.get(field, null, { getters: false });
const deleteLinksImage = async (doc, field) => {
const fileName = getStoredImageName(doc, field);
if (!fileName) return;
try {
await s3Storage.deleteObject(`${LINKS_IMAGE_PREFIX}/${fileName}`);
} catch (error) {
console.log(`Failed to delete ${field} from S3:`, error?.message);
}
};
const uploadProcessedImage = async (
imageData,
imageName,
width,
height,
quality
) => {
const buffer = await sharp(imageData)
.resize(width, height, { fit: "cover" })
.jpeg({ quality })
.toBuffer();
await s3Storage.uploadBuffer(
`${LINKS_IMAGE_PREFIX}/${imageName}`,
buffer,
"image/jpeg"
);
return imageName;
};
module.exports = {
LINKS_IMAGE_PREFIX,
getStoredImageName,
deleteLinksImage,
uploadProcessedImage,
};
+74
View File
@@ -0,0 +1,74 @@
const sharp = require("sharp");
const s3Storage = require("./s3Storage");
const NEWS_IMAGE_PREFIX = "uploads/images/news";
const NEWS_ATTACHMENT_PREFIX = "uploads/files/newsAttachments";
const getStoredImageName = (doc, field) =>
doc.get(field, null, { getters: false });
const deleteNewsImage = async (doc, field) => {
const fileName = getStoredImageName(doc, field);
if (!fileName) return;
try {
await s3Storage.deleteObject(`${NEWS_IMAGE_PREFIX}/${fileName}`);
} catch (error) {
console.log(`Failed to delete ${field} from S3:`, error?.message);
}
};
const uploadProcessedImage = async (
imageData,
imageName,
width,
height,
quality
) => {
const buffer = await sharp(imageData)
.resize(width, height, { fit: "cover" })
.jpeg({ quality })
.toBuffer();
await s3Storage.uploadBuffer(
`${NEWS_IMAGE_PREFIX}/${imageName}`,
buffer,
"image/jpeg"
);
return imageName;
};
const uploadNewsAttachment = async (
fileData,
fileName,
contentType = "application/pdf"
) => {
await s3Storage.uploadBuffer(
`${NEWS_ATTACHMENT_PREFIX}/${fileName}`,
fileData,
contentType
);
return fileName;
};
const deleteNewsAttachment = async (fileName) => {
if (!fileName) return;
try {
await s3Storage.deleteObject(`${NEWS_ATTACHMENT_PREFIX}/${fileName}`);
} catch (error) {
console.log(`Failed to delete attachment from S3:`, error?.message);
}
};
module.exports = {
NEWS_IMAGE_PREFIX,
NEWS_ATTACHMENT_PREFIX,
getStoredImageName,
deleteNewsImage,
uploadProcessedImage,
uploadNewsAttachment,
deleteNewsAttachment,
};
+46
View File
@@ -0,0 +1,46 @@
const sharp = require("sharp");
const s3Storage = require("./s3Storage");
const OCCASION_IMAGE_PREFIX = "uploads/images/occasion";
const getStoredImageName = (doc, field) =>
doc.get(field, null, { getters: false });
const deleteOccasionImage = async (doc, field) => {
const fileName = getStoredImageName(doc, field);
if (!fileName) return;
try {
await s3Storage.deleteObject(`${OCCASION_IMAGE_PREFIX}/${fileName}`);
} catch (error) {
console.log(`Failed to delete ${field} from S3:`, error?.message);
}
};
const uploadProcessedImage = async (
imageData,
imageName,
width,
height,
quality
) => {
const buffer = await sharp(imageData)
.resize(width, height, { fit: "cover" })
.jpeg({ quality })
.toBuffer();
await s3Storage.uploadBuffer(
`${OCCASION_IMAGE_PREFIX}/${imageName}`,
buffer,
"image/jpeg"
);
return imageName;
};
module.exports = {
OCCASION_IMAGE_PREFIX,
getStoredImageName,
deleteOccasionImage,
uploadProcessedImage,
};
+140
View File
@@ -0,0 +1,140 @@
const {
S3Client,
PutObjectCommand,
DeleteObjectCommand,
ListObjectsV2Command,
GetObjectCommand,
} = require("@aws-sdk/client-s3");
const config = {
bucket: process.env.BUCKET_NAME,
region: process.env.BUCKET_REGION || "us-west-2",
endpoint: process.env.PARSPACK_S3_ENDPOINT,
accessKeyId: process.env.BUCKET_ACCESS_KEY,
secretAccessKey: process.env.BUCKET_SECRET_KEY,
publicUrl: process.env.BUCKET_PUBLIC_URL || process.env.PARSPACK_S3_ENDPOINT,
};
let s3Client = null;
const isConfigured = () =>
Boolean(
config.bucket &&
config.endpoint &&
config.accessKeyId &&
config.secretAccessKey
);
const getClient = () => {
if (!isConfigured()) {
throw new Error("S3 storage is not configured");
}
if (!s3Client) {
s3Client = new S3Client({
region: config.region,
endpoint: config.endpoint,
credentials: {
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
},
forcePathStyle: true,
});
}
return s3Client;
};
const normalizeKey = (key) => key.replace(/^\/+/, "");
const getPublicUrl = (key) => {
const normalizedKey = normalizeKey(key);
const baseUrl = (config.publicUrl || config.endpoint).replace(/\/+$/, "");
return `${baseUrl}/${normalizedKey}`;
};
const uploadBuffer = async (
key,
buffer,
contentType = "application/octet-stream"
) => {
const normalizedKey = normalizeKey(key);
await getClient().send(
new PutObjectCommand({
Bucket: config.bucket,
Key: normalizedKey,
Body: buffer,
ContentType: contentType,
ACL: "public-read",
})
);
return {
key: normalizedKey,
url: getPublicUrl(normalizedKey),
};
};
const deleteObject = async (key) => {
const normalizedKey = normalizeKey(key);
await getClient().send(
new DeleteObjectCommand({
Bucket: config.bucket,
Key: normalizedKey,
})
);
};
const listObjects = async (prefix = "") => {
const keys = [];
let continuationToken;
do {
const response = await getClient().send(
new ListObjectsV2Command({
Bucket: config.bucket,
Prefix: normalizeKey(prefix),
ContinuationToken: continuationToken,
})
);
for (const item of response.Contents || []) {
if (item.Key) keys.push(item.Key);
}
continuationToken = response.IsTruncated
? response.NextContinuationToken
: undefined;
} while (continuationToken);
return keys;
};
const getObjectBuffer = async (key) => {
const response = await getClient().send(
new GetObjectCommand({
Bucket: config.bucket,
Key: normalizeKey(key),
})
);
return Buffer.from(await response.Body.transformToByteArray());
};
const deleteObjectsByPrefix = async (prefix) => {
const keys = await listObjects(prefix);
await Promise.all(keys.map((key) => deleteObject(key)));
};
module.exports = {
isConfigured,
getPublicUrl,
uploadBuffer,
deleteObject,
listObjects,
getObjectBuffer,
deleteObjectsByPrefix,
};
+46
View File
@@ -0,0 +1,46 @@
const sharp = require("sharp");
const s3Storage = require("./s3Storage");
const SLIDER_IMAGE_PREFIX = "uploads/images/slider";
const getStoredImageName = (doc, field) =>
doc.get(field, null, { getters: false });
const deleteSliderImage = async (doc, field) => {
const fileName = getStoredImageName(doc, field);
if (!fileName) return;
try {
await s3Storage.deleteObject(`${SLIDER_IMAGE_PREFIX}/${fileName}`);
} catch (error) {
console.log(`Failed to delete ${field} from S3:`, error?.message);
}
};
const uploadProcessedImage = async (
imageData,
imageName,
width,
height,
quality
) => {
const buffer = await sharp(imageData)
.resize(width, height, { fit: "cover" })
.jpeg({ quality })
.toBuffer();
await s3Storage.uploadBuffer(
`${SLIDER_IMAGE_PREFIX}/${imageName}`,
buffer,
"image/jpeg"
);
return imageName;
};
module.exports = {
SLIDER_IMAGE_PREFIX,
getStoredImageName,
deleteSliderImage,
uploadProcessedImage,
};
+12 -7
View File
@@ -1,18 +1,24 @@
const axios = require("axios");
const SMS_API_KEY = "QkkNxhyXZ6GtEf1soOTtikomO3mA4LaNQDH8mol8huDIwh00";
module.exports.sms = async (phoneNumber, message) => {
const smsService = new SmsService("QkkNxhyXZ6GtEf1soOTtikomO3mA4LaNQDH8mol8huDIwh00");
const smsService = new SmsService(SMS_API_KEY);
return smsService.sendMessage(phoneNumber, message);
};
/** Sends login/verification OTP via sms.ir template (verify endpoint). */
module.exports.sendOtpSms = async (phoneNumber, otp) => {
const smsService = new SmsService(SMS_API_KEY);
return smsService.sendOTPSms(otp, phoneNumber);
};
class SmsService {
constructor(apiKey) {
this.API_URL = "https://api.sms.ir/v1";
this.OTP_PATTERN = "83604";
this.OTP_PATTERN = "628767";
this.SMS_API_KEY = apiKey;
}
//******************************** */
//******************************** */
async sendMessage(phone, message) {
try {
@@ -32,7 +38,7 @@ class SmsService {
console.error("Error in sending SMS", error);
}
}
//******************************** */
async getLineNumber() {
try {
const { data } = await axios.get(`${this.API_URL}/line`, {
@@ -45,7 +51,6 @@ class SmsService {
console.error("Error in getting line number", error);
}
}
//******************************** */
async sendOTPSms(otp, phone) {
const smsData = {
+46
View File
@@ -0,0 +1,46 @@
const sharp = require("sharp");
const s3Storage = require("./s3Storage");
const USER_IMAGE_PREFIX = "uploads/images/users";
const getStoredImageName = (doc, field) =>
doc.get(field, null, { getters: false });
const deleteUserImage = async (doc, field) => {
const fileName = getStoredImageName(doc, field);
if (!fileName) return;
try {
await s3Storage.deleteObject(`${USER_IMAGE_PREFIX}/${fileName}`);
} catch (error) {
console.log(`Failed to delete ${field} from S3:`, error?.message);
}
};
const uploadProcessedImage = async (
imageData,
imageName,
width,
height,
quality
) => {
const buffer = await sharp(imageData)
.resize(width, height, { fit: "cover" })
.jpeg({ quality })
.toBuffer();
await s3Storage.uploadBuffer(
`${USER_IMAGE_PREFIX}/${imageName}`,
buffer,
"image/jpeg"
);
return imageName;
};
module.exports = {
USER_IMAGE_PREFIX,
getStoredImageName,
deleteUserImage,
uploadProcessedImage,
};
+46
View File
@@ -0,0 +1,46 @@
const sharp = require("sharp");
const s3Storage = require("./s3Storage");
const YEAR_BANNER_IMAGE_PREFIX = "uploads/images/mainCover";
const getStoredImageName = (doc, field) =>
doc.get(field, null, { getters: false });
const deleteYearBannerImage = async (doc, field) => {
const fileName = getStoredImageName(doc, field);
if (!fileName) return;
try {
await s3Storage.deleteObject(`${YEAR_BANNER_IMAGE_PREFIX}/${fileName}`);
} catch (error) {
console.log(`Failed to delete ${field} from S3:`, error?.message);
}
};
const uploadProcessedImage = async (
imageData,
imageName,
width,
height,
quality
) => {
const buffer = await sharp(imageData)
.resize(width, height, { fit: "cover" })
.jpeg({ quality })
.toBuffer();
await s3Storage.uploadBuffer(
`${YEAR_BANNER_IMAGE_PREFIX}/${imageName}`,
buffer,
"image/jpeg"
);
return imageName;
};
module.exports = {
YEAR_BANNER_IMAGE_PREFIX,
getStoredImageName,
deleteYearBannerImage,
uploadProcessedImage,
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+79
View File
@@ -0,0 +1,79 @@
# CKEditor 4 [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Check%20out%20CKEditor%204%20on%20npm&url=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fckeditor4)
[![GitHub tag](https://img.shields.io/github/tag/ckeditor/ckeditor4-releases.svg)](https://github.com/ckeditor/ckeditor4-releases)
[![Dependencies](https://img.shields.io/david/ckeditor/ckeditor4.svg)](https://david-dm.org/ckeditor/ckeditor4)
[![Dev dependencies](https://img.shields.io/david/dev/ckeditor/ckeditor4.svg)](https://david-dm.org/ckeditor/ckeditor4?type=dev)
[![Join newsletter](https://img.shields.io/badge/join-newsletter-00cc99.svg)](http://eepurl.com/c3zRPr)
[![Follow twitter](https://img.shields.io/badge/follow-twitter-00cc99.svg)](https://twitter.com/ckeditor)
A highly configurable WYSIWYG HTML editor with hundreds of features, from creating rich text content with captioned images, videos, tables, or media embeds to pasting from Word and drag&drop image upload.
Supports a broad range of browsers, including legacy ones.
![CKEditor 4 screenshot](https://c.cksource.com/a/1/img/npm/ckeditor4.png)
## Getting Started
```
npm install --save ckeditor4
```
Use it on your website:
```html
<div id="editor">
<p>This is the editor content.</p>
</div>
<script src="./node_modules/ckeditor4/ckeditor.js"></script>
<script>
CKEDITOR.replace( 'editor' );
</script>
```
You can also load CKEditor 4 using [CDN](https://cdn.ckeditor.com/#ckeditor4).
## Features
* Over 500 plugins in the [Add-ons Repository](https://ckeditor.com/cke4/addons).
* Pasting from Microsoft Word and Excel.
* Drag&drop image uploads.
* Media embeds to insert videos, tweets, maps, slideshows.
* Powerful clipboard integration.
* Content quality control with Advanced Content Filter.
* Extensible widget system.
* Custom table selection.
* Accessibility conforming to WCAG and Section 508.
* Over 60 localizations available with full RTL support.
## Presets
The CKEditor 4 npm package comes in the `standard-all` preset, so it includes all official CKEditor plugins, with those from the [standard package](https://sdk.ckeditor.com/samples/standardpreset.html) active by default.
## Further Resources
* [CKEditor 4 demo](https://ckeditor.com/ckeditor-4/)
* [Documentation](https://ckeditor.com/docs/ckeditor4/latest/)
* [API documentation](https://ckeditor.com/docs/ckeditor4/latest/api/index.html)
* [Configuration reference](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html)
* [CKEditor SDK with more samples](https://sdk.ckeditor.com/)
If you are looking for CKEditor 5, here's a link to the relevant npm package: <https://www.npmjs.com/package/ckeditor5>
## Browser Support
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome (Android) | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari-ios/safari-ios_48x48.png" alt="iOS Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>iOS Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera |
| --------- | --------- | --------- | --------- | --------- | --------- | --------- |
| IE8, IE9, IE10, IE11, Edge| latest version| latest version| latest version| latest version| latest version| latest version
Find out more in the [Browser Compatibility guide](https://ckeditor.com/docs/ckeditor4/latest/guide/dev_browsers.html#officially-supported-browsers).
## Contribute
If you would like to help maintain the project, follow the [Contribution instructions](https://github.com/ckeditor/ckeditor4/blob/master/.github/CONTRIBUTING.md).
## License
Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.md or <https://ckeditor.com/legal/ckeditor-oss-license>
+10
View File
@@ -0,0 +1,10 @@
# Reporting a security issues
If you believe you have found a security issue in the CKEditor 4 software, please contact us immediately.
When reporting a potential security problem, please bear this in mind:
* Make sure to provide as many details as possible about the vulnerability.
* Please do not disclose publicly any security issues until we fix them and publish security releases.
Contact the security team at security@cksource.com. As soon as we receive the security report, we will work promptly to confirm the issue and then to provide a security fix.
+10
View File
@@ -0,0 +1,10 @@
/*
Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
(function(a){if("undefined"==typeof a)throw Error("jQuery should be loaded before CKEditor jQuery adapter.");if("undefined"==typeof CKEDITOR)throw Error("CKEditor should be loaded before CKEditor jQuery adapter.");CKEDITOR.config.jqueryOverrideVal="undefined"==typeof CKEDITOR.config.jqueryOverrideVal?!0:CKEDITOR.config.jqueryOverrideVal;a.extend(a.fn,{ckeditorGet:function(){var a=this.eq(0).data("ckeditorInstance");if(!a)throw"CKEditor is not initialized yet, use ckeditor() with a callback.";return a},
ckeditor:function(g,e){if(!CKEDITOR.env.isCompatible)throw Error("The environment is incompatible.");if(!a.isFunction(g)){var m=e;e=g;g=m}var k=[];e=e||{};this.each(function(){var b=a(this),c=b.data("ckeditorInstance"),f=b.data("_ckeditorInstanceLock"),h=this,l=new a.Deferred;k.push(l.promise());if(c&&!f)g&&g.apply(c,[this]),l.resolve();else if(f)c.once("instanceReady",function(){setTimeout(function d(){c.element?(c.element.$==h&&g&&g.apply(c,[h]),l.resolve()):setTimeout(d,100)},0)},null,null,9999);
else{if(e.autoUpdateElement||"undefined"==typeof e.autoUpdateElement&&CKEDITOR.config.autoUpdateElement)e.autoUpdateElementJquery=!0;e.autoUpdateElement=!1;b.data("_ckeditorInstanceLock",!0);c=a(this).is("textarea")?CKEDITOR.replace(h,e):CKEDITOR.inline(h,e);b.data("ckeditorInstance",c);c.on("instanceReady",function(e){var d=e.editor;setTimeout(function n(){if(d.element){e.removeListener();d.on("dataReady",function(){b.trigger("dataReady.ckeditor",[d])});d.on("setData",function(a){b.trigger("setData.ckeditor",
[d,a.data])});d.on("getData",function(a){b.trigger("getData.ckeditor",[d,a.data])},999);d.on("destroy",function(){b.trigger("destroy.ckeditor",[d])});d.on("save",function(){a(h.form).submit();return!1},null,null,20);if(d.config.autoUpdateElementJquery&&b.is("textarea")&&a(h.form).length){var c=function(){b.ckeditor(function(){d.updateElement()})};a(h.form).submit(c);a(h.form).bind("form-pre-serialize",c);b.bind("destroy.ckeditor",function(){a(h.form).unbind("submit",c);a(h.form).unbind("form-pre-serialize",
c)})}d.on("destroy",function(){b.removeData("ckeditorInstance")});b.removeData("_ckeditorInstanceLock");b.trigger("instanceReady.ckeditor",[d]);g&&g.apply(d,[h]);l.resolve()}else setTimeout(n,100)},0)},null,null,9999)}});var f=new a.Deferred;this.promise=f.promise();a.when.apply(this,k).then(function(){f.resolve()});this.editor=this.eq(0).data("ckeditorInstance");return this}});CKEDITOR.config.jqueryOverrideVal&&(a.fn.val=CKEDITOR.tools.override(a.fn.val,function(g){return function(e){if(arguments.length){var m=
this,k=[],f=this.each(function(){var b=a(this),c=b.data("ckeditorInstance");if(b.is("textarea")&&c){var f=new a.Deferred;c.setData(e,function(){f.resolve()});k.push(f.promise());return!0}return g.call(b,e)});if(k.length){var b=new a.Deferred;a.when.apply(this,k).done(function(){b.resolveWith(m)});return b.promise()}return f}var f=a(this).eq(0),c=f.data("ckeditorInstance");return f.is("textarea")&&c?c.getData():g.call(f)}}))})(window.jQuery);
Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

@@ -0,0 +1,16 @@
{
"bender": {
"port": 9001
},
"server": {
"port": 9002
},
"paths": {
"ckeditor4": "../ckeditor4/",
"runner": "./src/runner.html"
},
"browsers": {
"linux": [ "chrome", "firefox" ],
"macos": [ "safari" ]
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"name": "ckeditor",
"description": "JavaScript WYSIWYG web text editor.",
"keywords": [ "ckeditor4", "ckeditor", "fckeditor", "editor", "wysiwyg", "html", "richtext", "text", "javascript" ],
"authors": "CKSource (https://cksource.com/)",
"license": "For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license/.",
"homepage": "https://ckeditor.com",
"main": "./ckeditor.js",
"moduleType": "globals"
}
+1297
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
{
"name": "ckeditor/ckeditor",
"description": "JavaScript WYSIWYG web text editor.",
"type": "library",
"keywords": [ "ckeditor4", "ckeditor", "fckeditor", "editor", "wysiwyg", "html", "richtext", "text", "javascript" ],
"homepage": "https://ckeditor.com/ckeditor-4/",
"license": [ "GPL-2.0+", "LGPL-2.1+", "MPL-1.1+" ],
"authors": [
{
"name": "CKSource",
"homepage": "https://cksource.com"
}
],
"support": {
"issues": "https://github.com/ckeditor/ckeditor4/issues",
"forum": "https://stackoverflow.com/tags/ckeditor",
"wiki": "https://ckeditor.com/docs/ckeditor4/latest/",
"source": "https://github.com/ckeditor/ckeditor4"
}
}
+38
View File
@@ -0,0 +1,38 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
CKEDITOR.editorConfig = function( config ) {
// Define changes to default configuration here.
// For complete reference see:
// https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html
// The toolbar groups arrangement, optimized for two toolbar rows.
config.toolbarGroups = [
{ name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
{ name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] },
{ name: 'links' },
{ name: 'insert' },
{ name: 'forms' },
{ name: 'tools' },
{ name: 'document', groups: [ 'mode', 'document', 'doctools' ] },
{ name: 'others' },
'/',
{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
{ name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
{ name: 'styles' },
{ name: 'colors' },
{ name: 'about' }
];
// Remove some buttons provided by the standard plugins, which are
// not needed in the Standard(s) toolbar.
config.removeButtons = 'Underline,Subscript,Superscript';
// Set the most common block elements.
config.format_tags = 'p;h1;h2;h3;pre';
// Simplify the dialog windows.
config.removeDialogTabs = 'image:advanced;link:advanced';
};
+208
View File
@@ -0,0 +1,208 @@
/*
Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
body
{
/* Font */
/* Emoji fonts are added to visualise them nicely in Internet Explorer. */
font-family: sans-serif, Arial, Verdana, "Trebuchet MS", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 12px;
/* Text color */
color: #333;
/* Remove the background color to make it transparent. */
background-color: #fff;
margin: 20px;
}
.cke_editable
{
font-size: 13px;
line-height: 1.6;
/* Fix for missing scrollbars with RTL texts. (#10488) */
word-wrap: break-word;
}
blockquote
{
font-style: italic;
font-family: Georgia, Times, "Times New Roman", serif;
padding: 2px 0;
border-style: solid;
border-color: #ccc;
border-width: 0;
}
.cke_contents_ltr blockquote
{
padding-left: 20px;
padding-right: 8px;
border-left-width: 5px;
}
.cke_contents_rtl blockquote
{
padding-left: 8px;
padding-right: 20px;
border-right-width: 5px;
}
a
{
color: #0782C1;
}
ol,ul,dl
{
/* IE7: reset rtl list margin. (#7334) */
*margin-right: 0px;
/* Preserved spaces for list items with text direction different than the list. (#6249,#8049)*/
padding: 0 40px;
}
h1,h2,h3,h4,h5,h6
{
font-weight: normal;
line-height: 1.2;
}
hr
{
border: 0px;
border-top: 1px solid #ccc;
}
img.right
{
border: 1px solid #ccc;
float: right;
margin-left: 15px;
padding: 5px;
}
img.left
{
border: 1px solid #ccc;
float: left;
margin-right: 15px;
padding: 5px;
}
pre
{
white-space: pre-wrap; /* CSS 2.1 */
word-wrap: break-word; /* IE7 */
-moz-tab-size: 4;
tab-size: 4;
}
.marker
{
background-color: Yellow;
}
span[lang]
{
font-style: italic;
}
figure
{
text-align: center;
outline: solid 1px #ccc;
background: rgba(0,0,0,0.05);
padding: 10px;
margin: 10px 20px;
display: inline-block;
}
figure > figcaption
{
text-align: center;
display: block; /* For IE8 */
}
a > img {
padding: 1px;
margin: 1px;
border: none;
outline: 1px solid #0782C1;
}
/* Widget Styles */
.code-featured
{
border: 5px solid red;
}
.math-featured
{
padding: 20px;
box-shadow: 0 0 2px rgba(200, 0, 0, 1);
background-color: rgba(255, 0, 0, 0.05);
margin: 10px;
}
.image-clean
{
border: 0;
background: none;
padding: 0;
}
.image-clean > figcaption
{
font-size: .9em;
text-align: right;
}
.image-grayscale
{
background-color: white;
color: #666;
}
.image-grayscale img, img.image-grayscale
{
filter: grayscale(100%);
}
.embed-240p
{
max-width: 426px;
max-height: 240px;
margin:0 auto;
}
.embed-360p
{
max-width: 640px;
max-height: 360px;
margin:0 auto;
}
.embed-480p
{
max-width: 854px;
max-height: 480px;
margin:0 auto;
}
.embed-720p
{
max-width: 1280px;
max-height: 720px;
margin:0 auto;
}
.embed-1080p
{
max-width: 1920px;
max-height: 1080px;
margin:0 auto;
}
@@ -0,0 +1,63 @@
Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
af.js Found: 62 Missing: 4
ar.js Found: 51 Missing: 15
bg.js Found: 58 Missing: 8
bn.js Found: 40 Missing: 26
bs.js Found: 29 Missing: 37
ca.js Found: 61 Missing: 5
cs.js Found: 66 Missing: 0
cy.js Found: 66 Missing: 0
da.js Found: 66 Missing: 0
de.js Found: 66 Missing: 0
el.js Found: 59 Missing: 7
en-au.js Found: 38 Missing: 28
en-ca.js Found: 37 Missing: 29
en-gb.js Found: 61 Missing: 5
eo.js Found: 66 Missing: 0
es.js Found: 66 Missing: 0
et.js Found: 66 Missing: 0
eu.js Found: 48 Missing: 18
fa.js Found: 66 Missing: 0
fi.js Found: 66 Missing: 0
fo.js Found: 66 Missing: 0
fr-ca.js Found: 42 Missing: 24
fr.js Found: 66 Missing: 0
gl.js Found: 40 Missing: 26
gu.js Found: 66 Missing: 0
he.js Found: 66 Missing: 0
hi.js Found: 43 Missing: 23
hr.js Found: 66 Missing: 0
hu.js Found: 63 Missing: 3
is.js Found: 41 Missing: 25
it.js Found: 66 Missing: 0
ja.js Found: 62 Missing: 4
ka.js Found: 62 Missing: 4
km.js Found: 40 Missing: 26
ko.js Found: 40 Missing: 26
lt.js Found: 66 Missing: 0
lv.js Found: 40 Missing: 26
mk.js Found: 0 Missing: 66
mn.js Found: 40 Missing: 26
ms.js Found: 39 Missing: 27
nb.js Found: 66 Missing: 0
nl.js Found: 65 Missing: 1
no.js Found: 66 Missing: 0
pl.js Found: 66 Missing: 0
pt-br.js Found: 66 Missing: 0
pt.js Found: 52 Missing: 14
ro.js Found: 61 Missing: 5
ru.js Found: 66 Missing: 0
sk.js Found: 49 Missing: 17
sl.js Found: 48 Missing: 18
sr-latn.js Found: 40 Missing: 26
sr.js Found: 40 Missing: 26
sv.js Found: 62 Missing: 4
th.js Found: 40 Missing: 26
tr.js Found: 66 Missing: 0
ug.js Found: 66 Missing: 0
uk.js Found: 66 Missing: 0
vi.js Found: 66 Missing: 0
zh-cn.js Found: 66 Missing: 0
zh.js Found: 58 Missing: 8
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More