Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94c79aa98d | |||
| 01c68fba43 | |||
| e0527e8ca6 | |||
| 1bb4c7efcc | |||
| 76f23a73e2 | |||
| a24f4ae766 | |||
| 096e653e64 | |||
| d6215b970f | |||
| 4c36880862 | |||
| dfb77cd8d2 | |||
| e95883eb6a | |||
| c73027540a | |||
| 85c8107799 | |||
| 99a9f9ace5 |
@@ -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
@@ -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,
|
||||
},
|
||||
],
|
||||
|
||||
Generated
+1154
-733
File diff suppressed because it is too large
Load Diff
+7
-1
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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,7 +1164,8 @@ module.exports.getParent = [
|
||||
module.exports.addTermsFileToCategory = [
|
||||
[body("termsTitle").notEmpty().withMessage(_sr["fa"].required.title)],
|
||||
checkValidations(validationResult),
|
||||
(req, res) => {
|
||||
async (req, res) => {
|
||||
try {
|
||||
if (!req.files?.file)
|
||||
return res
|
||||
.status(422)
|
||||
@@ -1233,42 +1175,48 @@ module.exports.addTermsFileToCategory = [
|
||||
.status(422)
|
||||
.json({ validation: { file: { msg: "فقط فرمت PDF قابل قبول است" } } });
|
||||
|
||||
const data = { title: req.body.termsTitle };
|
||||
const file = req.files.file;
|
||||
const category = await Category.findById(req.params.categoryId);
|
||||
if (!category) return res404(res, "category not found");
|
||||
|
||||
const fileName =
|
||||
req.body.termsTitle.replace(titleFilterRegex, "-") +
|
||||
"_" +
|
||||
Date.now() +
|
||||
".pdf";
|
||||
file.mv(`./static/uploads/files/terms/${fileName}`);
|
||||
data.file = fileName;
|
||||
|
||||
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 });
|
||||
});
|
||||
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);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
const _upload = async (fileType, file, galleryId) => {
|
||||
if (fileType === "graphic") {
|
||||
return uploadGalleryFile("graphic", file, galleryId, {
|
||||
mainQuality: graphicQuality,
|
||||
thumbWidth: thumbGraphicWidth,
|
||||
thumbHeight: thumbGraphicHeight,
|
||||
thumbQuality: thumbGraphicQuality,
|
||||
});
|
||||
}
|
||||
|
||||
return fileName;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return null;
|
||||
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);
|
||||
});
|
||||
}
|
||||
await deleteGalleryFile(fileType, name);
|
||||
return name;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} 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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 handleFileUploads(req, res, createNews);
|
||||
await createNews.save();
|
||||
} catch (error) {
|
||||
return res.status(422).json({
|
||||
validation: {
|
||||
cover: { msg: error.message },
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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)
|
||||
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")
|
||||
}
|
||||
|
||||
if (req.files.file.mimetype.split("/")[1] !== "pdf") {
|
||||
return res
|
||||
.status(422)
|
||||
.json({ validation: { file: { msg: "فقط فرمت PDF قابل قبول است" } } });
|
||||
}
|
||||
|
||||
const news = await News.findById(req.params.newsId);
|
||||
if (!news) {
|
||||
return res404(res, "خبر مورد نظر پیدا نشد");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 });
|
||||
});
|
||||
});
|
||||
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);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
],
|
||||
[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 } } });
|
||||
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 قابل قبول است' } } });
|
||||
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 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}`;
|
||||
const { url: pdfUrl } = await uploadPdf(file.data, fileName);
|
||||
|
||||
await file.mv(filePath);
|
||||
data.file = fileName;
|
||||
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "book-"));
|
||||
const tmpPdfPath = path.join(tmpDir, fileName);
|
||||
await fs.writeFile(tmpPdfPath, file.data);
|
||||
|
||||
const convertPromise = convertapi.convert('jpg', {
|
||||
File: filePath
|
||||
}, 'pdf');
|
||||
const result = await convertapi.convert(
|
||||
"jpg",
|
||||
{ File: tmpPdfPath },
|
||||
"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`;
|
||||
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},
|
||||
{ _id: bookId },
|
||||
{ $push: { slides: imagePath } },
|
||||
{ upsert: true }
|
||||
);
|
||||
return res.status(201).json({ msg: "اسلاید کتاب اضافه شد", Data: update });
|
||||
} else {
|
||||
return res
|
||||
.status(201)
|
||||
.json({ msg: "اسلاید کتاب اضافه شد", Data: update });
|
||||
}
|
||||
|
||||
const newBook = new Book({
|
||||
title: req.body.title,
|
||||
uri: `https://ostan-mr.ir/uploads/pdf/${fileName}`,
|
||||
slides: firstbook
|
||||
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 });
|
||||
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) => {
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const Books = await Book.find({});
|
||||
return res.status(200).json({ Books });
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
module.exports.GetOne = [
|
||||
async (req, res,next) => {
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const id = req.params.id
|
||||
const id = req.params.id;
|
||||
const book = await Book.findById(id);
|
||||
return res.status(200).json({ msg: book });
|
||||
} catch (error) {
|
||||
next(error)
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
module.exports.DeleteOne = [
|
||||
async(req,res,next)=>{
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const book = await Book.deleteOne({_id:req.params.id})
|
||||
if(!book){
|
||||
return res.status(200).json({ msg: 'کتاب وجود ندارد ' });
|
||||
const book = await Book.findById(req.params.id);
|
||||
if (!book) {
|
||||
return res.status(200).json({ msg: "کتاب وجود ندارد " });
|
||||
}
|
||||
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)
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require("dotenv").config();
|
||||
|
||||
const express = require("express");
|
||||
const database = require("./database");
|
||||
const fileUpload = require("express-fileupload");
|
||||
|
||||
+22
-7
@@ -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),
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
@@ -0,0 +1,79 @@
|
||||
# CKEditor 4 [](https://twitter.com/intent/tweet?text=Check%20out%20CKEditor%204%20on%20npm&url=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fckeditor4)
|
||||
|
||||
[](https://github.com/ckeditor/ckeditor4-releases)
|
||||
[](https://david-dm.org/ckeditor/ckeditor4)
|
||||
[](https://david-dm.org/ckeditor/ckeditor4?type=dev)
|
||||
|
||||
[](http://eepurl.com/c3zRPr)
|
||||
[](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.
|
||||
|
||||

|
||||
|
||||
## 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>
|
||||
@@ -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
@@ -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" ]
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
Vendored
+1297
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
};
|
||||
@@ -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
Reference in New Issue
Block a user