first structure

This commit is contained in:
daniel
2024-04-29 16:19:43 +03:30
parent 52984759ee
commit d2f5dabae5
43 changed files with 7601 additions and 5775 deletions
+4 -2
View File
@@ -3,7 +3,6 @@ SECRET=fDS9j269DSfd823FhdfG65
ACCESS_TOKEN_SECERT=PNd98dsDS089ds3SDh7C5hg265SFC42bgHxTg34x
ACCESS_TOKEN_SECERT_ADMIN=G89dsJsd90DFdsf96F8se3sgL7JB29265SFC42bgHxTg34x
CONNECTION_STRING=
CONNECTION_STRING_LOCAL=mongodb://127.0.0.1:27017/delearn?authSource=admin
MERCHANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
EMAIL_HOST=
EMAIL_PORT=
@@ -13,4 +12,7 @@ EMAIL_ADDRESS=
SMS_USER=
SMS_PASS=
SMS_NUMBER=
URLIMAGE=https://api-delearn.iran.liara.run
URLIMAGE=https://api-delearn.iran.liara.run
MONGO_CONNECTION_HOST=127.0.0.1 mongodb://127.0.0.1:27017/delearn
MONGO_CONNECTION_PORT=27017
MONGO_CONNECTION_NAME=delearn
+66 -22
View File
@@ -1,23 +1,67 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
jquery: true
},
parserOptions: {
parser: '@babel/eslint-parser',
requireConfigFile: false
},
extends: ['@nuxtjs', 'plugin:nuxt/recommended', 'prettier'],
plugins: [],
// add your custom rules here
rules: {
'no-console': 'off',
camelcase: 'off',
'no-async-promise-executor': 'off',
'vue/no-v-html': 'off',
'vue/prop-name-casing': 'off'
}
}
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'linebreak-style': 0,
strict: 0,
'max-len': ['error', { code: 120 }],
'quote-props': 'off',
'no-param-reassign': 'off',
'no-underscore-dangle': 'off',
'func-names': ['error', 'as-needed'],
'prefer-const': 'warn',
radix: 'off',
'no-restricted-syntax': ['error', "BinaryExpression[operator='at']"],
'no-await-in-loop': 'off',
'no-trailing-spaces': 'off',
'object-curly-spacing': 'off',
'object-curly-newline': [
'error',
{
ObjectExpression: {
multiline: true,
minProperties: 6,
consistent: true,
},
ObjectPattern: { multiline: true, minProperties: 6, consistent: true },
ImportDeclaration: {
multiline: true,
minProperties: 6,
consistent: true,
},
ExportDeclaration: {
multiline: true,
minProperties: 6,
consistent: true,
},
},
],
'global-require': 'off',
'import/no-dynamic-require': 'off',
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'prettier/prettier': [
'error',
{
endOfLine: 'auto',
},
],
},
};
-2
View File
@@ -1,2 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto
+39 -4
View File
@@ -1,6 +1,41 @@
# compiled output
/dist
/node_modules
.png
.jpg
.web
*.png
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# package json
package-lock.json
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# Environments
.env
+4
View File
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}
-3
View File
@@ -1,3 +0,0 @@
# Asanmarket
-19
View File
@@ -1,19 +0,0 @@
const mongoose = require("mongoose");
const db = async () => {
try {
const connect = await mongoose.connect(process.env.CONNECTION_STRING);
console.log(
"Database connected: ",
connect.connection.host,
connect.connection.name
);
} catch (err) {
console.log(err);
setTimeout(() => {
console.log("Retry for DB");
db();
}, 5000);
}
};
module.exports = db;
-26
View File
@@ -1,26 +0,0 @@
const nodemailer = require("nodemailer");
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_HOST,
port: process.env.EMAIL_PORT,
secure: true,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
});
const email = async (to, subject, text, html) => {
const info = await transporter.sendMail({
from: `"Asan Market" ${process.env.EMAIL_ADDRESS}`, // sender address
to: to, // list of receivers
subject: subject, // Subject line
text: text, // plain text body
html: html, // html body
});
};
module.exports = email;
-57
View File
@@ -1,57 +0,0 @@
const axios = require('axios')
const accountInfo = {
Username: '000',
Password: '000',
Number: '000',
url: 'http://www.afe.ir/WebService/V4/BoxService.asmx'
}
/////// http post soap method
module.exports.sms = (mobileNumbersArray, message) => {
return new Promise((resolve, reject) => {
function renderMobileNumbers(array) {
if (Array.isArray(array)) {
return array.map(item => `<string>${item}</string>`).join('')
} else {
return `<string>${array}</string>`
}
}
const xml = `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<SendMessage xmlns="http://www.afe.ir/">
<Username>${accountInfo.Username}</Username>
<Password>${accountInfo.Password}</Password>
<Number>${accountInfo.Number}</Number>
<Mobile>
${renderMobileNumbers(mobileNumbersArray)}
</Mobile>
<Message>${message}</Message>
<Type>1</Type>
</SendMessage>
</soap:Body>
</soap:Envelope>`
axios
.post(accountInfo.url, xml, {
headers: {
'Content-Type': 'text/xml; charset=utf-8',
SOAPAction: 'http://www.afe.ir/SendMessage'
}
})
.then(res => {
resolve(res)
})
.catch(err => {
console.log(
'this is sms module error ------------------ status code: %s error message: %s ',
err.response.status,
err.response.data
)
resolve(err)
})
})
}
-9
View File
@@ -1,9 +0,0 @@
exports.constants = {
VALIDATION_ERROR: 400,
UNAUTHORIZED: 401,
FORBIDOEN: 403,
NOT_FOUND: 404,
SERVER_ERROR: 500,
SITE_UNAUTHORIZED: 422,
TOO_MANY: 429,
};
-55
View File
@@ -1,55 +0,0 @@
const asyncHandler = require("express-async-handler");
const News = require("../models/newsModels");
const { email } = require("../config/emailConnection");
const { checkValidations } = require("../plugins/HelperFunctions")
const { body, validationResult } = require('express-validator')
const { _sr } = require("../plugins/resServer")
const { rateLimit } = require('express-rate-limit')
const limiter = rateLimit({
windowMs: 24 * 60 * 60 * 1000, // 1 minutes
limit: 1, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
message: { msg: "Too many requests, please try again later." }
})
/**
*@desc Add new email
*@route POST /api/news/
*@access private
*/
const addEmail = [
[
body('email').notEmpty().isEmail().withMessage(_sr.fa.required.email),
],
checkValidations(validationResult),
limiter,
asyncHandler(async (req, res) => {
const { email } = req.body
const alreadyEmail = await News.findOne({ email })
if (alreadyEmail) {
res.status(400);
throw new Error(_sr.fa.duplicated.email);
}
const emailAdded = await News.create({
email
})
res.status(200).json(emailAdded)
})
]
module.exports = {
baseRoute: "/news",
items: [
{
off: false,
route: "/",
method: "post",
middleware: [],
use: addEmail
},
]
};
-60
View File
@@ -1,60 +0,0 @@
const asyncHandler = require("express-async-handler");
const Site = require("../models/siteModels");
const { _sr } = require("../plugins/resServer")
/**
*@desc Get all site
*@route GET /api/sites/
*@access Public
*/
const getSite = asyncHandler(async (req, res) => {
const profile = await Site.findOne({ profile: "main" });
res.status(200).json(profile);
});
/**
*@desc Update a site
*@route PUT /api/sites/
*@access private
*/
const updateSite = [
asyncHandler(async (req, res) => {
const site = await Site.findOneAndUpdate(
{ profile: "main" },
req.body,
{ new: true }
);
if (!site) {
res.status(404);
throw new Error(_sr.fa.not_found.item_id);
}
res.status(200).json(site);
})
]
module.exports = {
baseRoute: "/sites",
items: [
{
off: false,
route: "/",
method: "get",
middleware: ['validateAdminToken', 'hasPermission'],
permission: "SITE",
use: getSite
},
{
off: false,
route: "/id/:id",
method: "put",
middleware: ['validateAdminToken', 'hasPermission'],
permission: "SITE",
use: updateSite
},
]
};
-11
View File
@@ -1,11 +0,0 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["./*"],
"@/*": ["./*"],
"~~/*": ["./*"],
"@@/*": ["./*"]
}
}
}
-13
View File
@@ -1,13 +0,0 @@
{
"app":"api-delearn",
"port":"6060",
"disks":[
{
"name": "uploads",
"mountTo": "uploads"
}
],
"node": {
"version": "18"
}
}
-28
View File
@@ -1,28 +0,0 @@
const asyncHandler = require("express-async-handler");
const jwt = require("jsonwebtoken");
const User = require("../models/userModels");
const validateToken = asyncHandler(async (req, res, next) => {
let token = req.headers.authorization || req.headers.Authorization;
if (!token) {
res.status(401);
throw new Error("User is not authorized(N)");
} else {
const tokenA = await User.findOne({ token })
if (tokenA) {
jwt.verify(token, process.env.ACCESS_TOKEN_SECERT, (err, decoded) => {
if (err) {
res.status(401);
throw new Error("User is not authorized(JWT E)");
}
req.user = decoded.user;
next();
});
} else {
res.status(401);
throw new Error("User is not authorized(DB N)");
}
}
});
module.exports = { name: "validateToken", run: validateToken };
-62
View File
@@ -1,62 +0,0 @@
const { constants } = require("../constants");
const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode ? res.statusCode : 500;
switch (statusCode) {
case constants.VALIDATION_ERROR:
res.status(statusCode).json({
title: "Validation Failed ",
msg: err.message,
stackTrace: err.stack,
});
break;
case constants.NOT_FOUND:
res.status(statusCode).json({
title: "Not found ",
msg: err.message,
stackTrace: err.stack,
});
break;
case constants.FORBIDOEN:
res.status(statusCode).json({
title: "Forbidoen ",
msg: err.message,
stackTrace: err.stack,
});
break;
case constants.SERVER_ERROR:
res.status(statusCode).json({
title: "Server Error ",
msg: err.message,
stackTrace: err.stack,
});
break;
case constants.UNAUTHORIZED:
res.status(statusCode).json({
title: "Unauthorized ",
msg: err.message,
stackTrace: err.stack,
});
break;
case constants.SITE_UNAUTHORIZED:
res.status(statusCode).json({
title: "Site Unauthorized ",
msg: err.message,
stackTrace: err.stack,
});
break;
case constants.TOO_MANY:
res.status(statusCode).json({
title: "Too many request",
msg: err.message,
stackTrace: err.stack,
});
break;
default:
console.log(err);
res.status(500).json({ err: err.message, msg: "Unhandled error" })
break;
}
};
module.exports = { name: "errorHandler", run: errorHandler };
-15
View File
@@ -1,15 +0,0 @@
const asyncHandler = require("express-async-handler");
const User = require("../models/userModels");
const hasPerma = (perm) => {
return asyncHandler(async (req, res, next) => {
const user = await User.findId(req.user.id)
if (user && (user.permissions.includes(perm) || !user.permissions.includes("ADMIN"))) {
return next()
} else {
res.status(401); throw new Error('کاربر مجوز لازم را ندارد');
}
})
}
module.exports = { name: "hasPermission", run: hasPerma };
-28
View File
@@ -1,28 +0,0 @@
const asyncHandler = require("express-async-handler");
const jwt = require("jsonwebtoken");
const Admin = require("../models/adminModels");
const validateAdminToken = asyncHandler(async (req, res, next) => {
let token = req.headers.siteuser || req.headers.SiteUser;
if (!token) {
res.status(401);
throw new Error("User is not authorized(N)");
} else {
const tokenA = await Admin.findOne({ token })
if (tokenA) {
jwt.verify(token, process.env.ACCESS_TOKEN_SECERT_ADMIN, (err, decoded) => {
if (err) {
res.status(401);
throw new Error("User is not authorized(JWT E)");
}
req.admin = decoded.user;
next();
});
} else {
res.status(401);
throw new Error("User is not authorized(DB N)");
}
}
});
module.exports = { name: "validateAdminToken", run: validateAdminToken };
-28
View File
@@ -1,28 +0,0 @@
const asyncHandler = require("express-async-handler");
const jwt = require("jsonwebtoken");
const User = require("../models/userModels");
const validateToken = asyncHandler(async (req, res, next) => {
let token = req.headers.authorization || req.headers.Authorization;
if (!token) {
res.status(401);
throw new Error("User is not authorized(N)");
} else {
const tokenA = await User.findOne({ token })
if (tokenA) {
jwt.verify(token, process.env.ACCESS_TOKEN_SECERT, (err, decoded) => {
if (err) {
res.status(401);
throw new Error("User is not authorized(JWT E)");
}
req.user = decoded.user;
next();
});
} else {
res.status(401);
throw new Error("User is not authorized(DB N)");
}
}
});
module.exports = { name: "validateToken", run: validateToken };
-15
View File
@@ -1,15 +0,0 @@
const mongoose = require("mongoose");
const newsSchema = new mongoose.Schema({
email: {
type: String,
unique: true
}
},
{
timestamps: true,
versionKey: false
}
);
module.exports = mongoose.model("News", newsSchema);
-59
View File
@@ -1,59 +0,0 @@
const mongoose = require("mongoose");
const siteSchema = new mongoose.Schema(
{
profile: {
type: String,
default: "main",
unique: true
},
name: {
type: String,
default: "Dlearn",
},
desc: {
type: String,
default: "Dlearn, marketplace core by Swift",
},
logo: {
type: String,
default: "/upload/Default.png",
},
tel1: {
type: String,
default: "+989391885657",
},
tel2: {
type: String,
default: "+989391885657",
},
address: {
type: String,
default: "Iran, Markazi",
},
socialMedia: [
{
kind: String,
address: String
}
],
emailHost: String,
emailPort: String,
emailUser: String,
emailPass: String,
smsNumber: String,
smsUser: String,
smsPass: String,
underConstruction: {
type: Boolean,
default: false,
},
},
{
timestamps: true,
versionKey: false
}
);
module.exports = mongoose.model("Site", siteSchema);
+4
View File
@@ -0,0 +1,4 @@
{
"collection": "@nestjs/schematics",
"sourceRoot": "src"
}
-4648
View File
File diff suppressed because it is too large Load Diff
+109 -26
View File
@@ -1,34 +1,117 @@
{
"name": "asanmarket",
"name": "DLearn",
"version": "1.0.0",
"description": "",
"main": "index.js",
"private": true,
"license": "UNLICENSED",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"author": "swift",
"license": "MIT",
"author": "daniel.3380",
"dependencies": {
"axios": "^1.6.2",
"bcrypt": "^5.1.0",
"cors": "^2.8.5",
"cron": "^3.1.6",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-async-handler": "^1.2.0",
"express-fileupload": "^1.4.1",
"express-rate-limit": "^7.2.0",
"express-route-detector": "^1.1.0",
"express-sharp": "^4.2.41",
"express-validator": "^7.0.1",
"jsonwebtoken": "^9.0.0",
"moment": "^2.29.4",
"mongodb": "^5.6.0",
"mongoose": "^7.3.1",
"mongoose-sequence": "^6.0.1",
"nodemailer": "^6.9.4",
"nodemon": "^2.0.22",
"swiftly-tools": "1.2.82"
"@nestjs/axios": "^2.0.0",
"@nestjs/common": "^10.0.5",
"@nestjs/core": "^10.0.5",
"@nestjs/mongoose": "^10.0.6",
"@nestjs/platform-express": "^10.0.5",
"@nestjs/platform-socket.io": "^10.0.5",
"@nestjs/serve-static": "^4.0.0",
"@nestjs/swagger": "^7.1.1",
"@nestjs/testing": "^9.4.0",
"@nestjs/throttler": "^4.0.0",
"@types/axios": "^0.14.0",
"@types/crypto-js": "^4.1.1",
"@types/express-session": "1.17.0",
"@types/moment": "^2.13.0",
"@types/multer": "^1.4.7",
"@types/request": "^2.48.8",
"@types/request-ip": "^0.0.37",
"axios": "^1.3.6",
"base64-async": "^2.1.3",
"bluebird": "^3.7.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"cookie-parser": "^1.4.6",
"crypto-js": "^4.1.1",
"dotenv": "^16.4.5",
"ejs": "^3.1.8",
"express-session": "^1.17.2",
"fs": "^0.0.1-security",
"jalali-moment": "^3.3.11",
"jsonwebtoken": "^8.5.1",
"md5": "^2.3.0",
"mongoose": "^8.3.2",
"mysql": "^2.18.1",
"mysql2": "^3.2.3",
"path": "^0.12.7",
"pg": "^8.11.1",
"pm2": "^5.2.0",
"qrcode": "^1.5.0",
"random-number": "^0.0.9",
"reflect-metadata": "^0.1.13",
"request": "^2.88.2",
"request-ip": "^2.1.3",
"rimraf": "^3.0.2",
"rxjs": "^7.5.5",
"sequelize": "^6.32.1",
"sequelize-typescript": "^2.1.5",
"shelljs": "^0.8.5",
"soap": "^1.0.0",
"swagger-ui-express": "^4.6.3",
"ts-loader": "^9.4.2",
"typescript": "^5.1.6"
},
"devDependencies": {
"@nestjs/cli": "^8.2.5",
"@nestjs/schematics": "^8.0.11",
"@types/bluebird": "^3.5.36",
"@types/express": "^4.17.13",
"@types/jest": "27.0.2",
"@types/jsonwebtoken": "^8.5.8",
"@types/md5": "^2.3.2",
"@types/node": "^16.11.33",
"@types/qrcode": "^1.4.2",
"@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.22.0",
"@typescript-eslint/parser": "^5.22.0",
"eslint": "^8.15.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^27.5.1",
"prettier": "^2.6.2",
"source-map-support": "^0.5.20",
"supertest": "^6.2.3",
"ts-jest": "^27.1.4",
"ts-node": "^10.7.0",
"tsconfig-paths": "^3.14.1"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
-8
View File
@@ -1,8 +0,0 @@
module.exports = {
populate: {
},
select: {
}
}
-195
View File
@@ -1,195 +0,0 @@
const fs = require("fs");
const path = require("path");
const moment = require("moment");
const date = moment();
const uploadImage = (image, res, type) => {
const maxSize = 15 * 1024 * 1024; // 15MB (example limit)
if (image.size > maxSize) {
res.status(500)
throw new Error("Image file size exceeds the limit")
}
let imageName =
date.format("YYYY-MM-DD") +
"-Img-" +
Math.floor(Math.random() * 900) +
100 +
image.name;
let saveToPath
let preFile
switch (type) {
case "1":
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/products", imageName);
preFile = "/uploads/products/"
break;
case "2":
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/blogs", imageName);
preFile = "/uploads/blogs/"
break;
case "3":
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/personal", imageName);
preFile = "/uploads/personal/"
break;
default:
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/none", imageName);
preFile = "/uploads/none/"
break;
}
fs.writeFile(saveToPath, image.data, (err) => {
if (err) {
console.error(err);
res.status(500).json({ msg: "Error saving the image" });
} else {
res.status(201).json({ url: `${preFile}${imageName}` });
}
});
};
const uploadImages = (images, res, type) => {
const maxSize = 15 * 1024 * 1024; // 15MB (example limit)
const imageUrls = [];
if (!Array.isArray(images)) {
if (images.size > maxSize) {
res.status(500)
throw new Error("Image file size exceeds the limit")
}
let imageName =
date.format("YYYY-MM-DD") +
"-Img-" +
Math.floor(Math.random() * 900) +
100 +
images.name;
let saveToPath
let preFile
switch (type) {
case "1":
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/products", imageName);
preFile = "/uploads/products/"
break;
case "2":
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/blogs", imageName);
preFile = "/uploads/blogs/"
break;
case "3":
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/personal", imageName);
preFile = "/uploads/personal/"
break;
default:
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/none", imageName);
preFile = "/uploads/none/"
break;
}
fs.writeFile(saveToPath, images.data, (err) => {
if (err) {
console.error(err);
res.status(500).json({ msg: "Error saving the image" });
} else {
imageUrls.push(`${preFile}${imageName}`)
res.status(201).json({ urls: imageUrls });
}
});
} else {
images.forEach(async (image) => {
if (image.size > maxSize) {
res.status(400)
throw new Error("Image file size exceeds the limit")
}
let imageName =
date.format("YYYY-MM-DD") +
"-Img-" +
Math.floor(Math.random() * 900) +
100 +
image.name;
let saveToPath
let preFile
switch (type) {
case "1":
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/products", imageName);
preFile = "/uploads/products/"
break;
case "2":
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/blogs", imageName);
preFile = "/uploads/blogs/"
break;
case "3":
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/personal", imageName);
preFile = "/uploads/personal/"
break;
default:
// Specify the save location
saveToPath = path.join(__dirname, "..", "uploads/none", imageName);
preFile = "/uploads/none/"
break;
}
imageUrls.push(`${preFile}${imageName}`);
fs.writeFile(saveToPath, image.data, (err) => {
if (err) {
console.log(err);
res.status(500)
throw new Error("Can't save images")
okd = false
}
});
});
res.status(201).json({ urls: imageUrls });
}
};
const deleteImage = (address, res) => {
const addressOf = path.join(__dirname, "..", address);
fs.unlink(addressOf, (err) => {
if (err) {
console.error("Error removing image:", err);
return;
}
});
};
module.exports = {
deleteImage,
uploadImage,
uploadImages,
};
-314
View File
@@ -1,314 +0,0 @@
module.exports._sr = {
fa: {
required: {
token: 'توکن را وارد کنید',
field: 'این فیلد نباید خالی باشد',
// developer validations
remember_me: 'پارامتر remember_me الزامی است',
// registration validations
name: 'نام را وارد کنید',
first_name: 'نام را وارد کنید',
last_name: 'نام خانوادگی را وارد کنید',
email: 'ایمیل را وارد کنید',
national_code: 'کد ملی الزامی است',
email_personal: 'ایمیل شخصی الزامی است',
email_company: 'ایمیل کمپانی برای اشخاص حقوقی الزامی است',
company_name: 'درصورتی که شخص حقوقی هستید، نام کمپانی را وارد کنید',
username: 'نام کاربری را وارد کنید',
phone_number: 'شماره تماس را وارد کنید',
gender: "جنسیت خود را انتخاب کنید",
password: 'پسورد را وارد کنید',
birthdate: 'تاریخ تولد را وارد کنید',
permissions: 'مقدار دسترسی را وارد کنید',
// addressing validations
country: 'کشور را وارد کنید',
province: 'استان را وارد کنید',
city: 'شهر را وارد کنید',
address: 'آدرس را وارد کنید',
postal_code: 'کد پستی را وارد کنید',
plaque: 'پلاک را وارد کنید',
receiver_first_name: 'نام گیرنده را وارد کنید',
receiver_last_name: 'نام خانوادگی گیرنده را وارد کنید',
receiver_mobile: 'شماره موبایل گیرنده را وارد کنید',
// global (post - gallery - ...)
title: 'عنوان را وارد کنید',
caption: 'متن را وارد کنید',
description: 'توضیحات را وارد کنید',
category: 'دسته بندی را انتخاب کنید',
image: 'عکس اجباری است',
cover: 'کاور اجباری است',
file: 'فایل اجباری است',
message: 'پیام را بنویسید',
quantity: 'تعداد را وارد کنید',
date: 'تاریخ را وارد کنید',
reason: 'دلیل را انتخاب کنید',
question: 'سوال را بنویسید',
answer: 'جواب را بنویسید',
price: 'قیمت را وارد کنید',
type: 'نوع را انتخاب کنید',
discount_code: 'کد تخفیف را وارد کنید',
discount_amount: 'مقدار تخفیف را وارد کنید',
discount_expire_date: 'تاریخ انقضای کد تخفیف را مشخص کنید',
user_id: 'کاربر را انتخاب کنید',
video: 'ویدیو اجباری است',
setting_name: 'نام تنظیمات اجباری است',
setting_value: 'مقدار تنظیمات اجباری است'
},
format: {
phone_number: 'فرمت شماره تماس صحیح نیست',
email: 'فرمت ایمیل قابل قبول نیست',
number: 'مقدار باید از جنس عدد باشد',
boolean: 'مقدار باید از جنس Boolean باشد',
image: 'فرمت عکس قابل قبول نیست',
video: 'فرمت ویدیو قابل قبول نیست',
national_code: 'کد ملی صحیح نیست'
},
min_char: {
min2: 'حداقل 2 کاراکتر',
min4: 'حداقل 4 کاراکتر',
min8: 'حداقل 8 کاراکتر',
min10: 'حداقل 10 کاراکتر',
min20: 'حداقل 20 کاراکتر',
min30: 'حداقل 30 کاراکتر',
min40: 'حداقل 40 کاراکتر',
min50: 'حداقل 50 کاراکتر',
min60: 'حداقل 60 کاراکتر',
min100: 'حداقل 100 کاراکتر',
min120: 'حداقل 120 کاراکتر',
min150: 'حداقل 150 کاراکتر',
min200: 'حداقل 200 کاراکتر',
data_size: 'حجم قابل قبول حداقل: ',
image_width_size: 'عرض حداقل: ',
image_height_size: 'ارتفاع حداقل: '
},
max_char: {
max4: 'حداکثر 4 کاراکتر',
max8: 'حداکثر 8 کاراکتر',
max10: 'حداکثر 10 کاراکتر',
max15: 'حداکثر 15 کاراکتر',
max20: 'حداکثر 20 کاراکتر',
max30: 'حداکثر 30 کاراکتر',
max40: 'حداکثر 40 کاراکتر',
max50: 'حداکثر 50 کاراکتر',
max60: 'حداکثر 60 کاراکتر',
max100: 'حداکثر 100 کاراکتر',
max120: 'حداکثر 120 کاراکتر',
max150: 'حداکثر 150 کاراکتر',
max200: 'حداکثر 200 کاراکتر',
data_size: 'حجم قابل قبول حداکثر: ',
image_width_size: 'عرض حداکثر: ',
image_height_size: 'ارتفاع حداکثر: '
},
not_found: {
user_id: 'کاربری با این مشخصات وجود ندارد',
admin_id: 'ادمینی با این مشخصات وجود ندارد',
order_id: 'سفارشی با این مشخصات وجود ندارد',
item_id: 'موردی با این مشخصات وجود ندارد',
password: 'رمز عبور یا آیدی درست نیست',
user_not_exist: 'اطلاعات یوزر موجود نیست'
},
duplicated: {
username: 'این نام کاربری از قبل وجود دارد',
email: 'این ایمیل از قبل وجود دارد',
name: 'نام تکراری است',
title: 'عنوان تکراری است',
phone_number: 'این شماره تماس از قبل وجود دارد',
link: 'این لینک تکراری است',
code: ' این کد تکراری است',
item: 'این مورد تگراری است',
shop: 'این فروشگاه وجود دارد'
},
response: {
permissions: 'شما مجوز کافی ندارید ',
logged_in: 'شما وارد سیستم شدید',
not_logged_in: 'شما وارد سیستم نشدید',
logged_out: 'شما از سیستم خارج شدید',
unauthenticated: 'غیرمجاز',
recovery_link: 'لینک بازیابی فرستاده شد',
success_save: 'با موفقیت ثبت شد',
success_remove: 'با موفقیت حذف شد',
cart_empty: 'سبد خرید خالی است',
already_has_address: 'آدرس قبلا اضافه شده',
activation_code: 'کد فعالسازی برای شما ارسال شد',
activation_email: 'ایمیل فعال سازی برای شما ارسال شد',
expired_reset_link: 'این لینک بازیابی منقضی شده است',
email_not_confirmed: 'ابتدا ایمیل خود را تایید کنید،لینک فعال سازی قبلا برای شما فرستاده شده است.',
expired_activation_link: 'این لینک فعال سازی منقضی شده است',
success_activation: 'اکانت شما با موفقیت فعال شد',
passwords_not_match: 'پسورد ها یکی نیست',
problem: 'مشکلی پیش آمده، لطفا دوباره تلاش کنید.',
latinChar: 'لطفا با حروف لاتین بنویسید',
persianChar: 'لطفا با حروف فارسی بنویسید',
whiteSpace: 'بین حروف و کلمات نباید فاصله باشد',
wrongLoginEmail: 'ایمیل یا رمز ورود اشتباه است',
wrongLoginNumber: 'شماره یا رمز ورود اشتباه است',
user_not_valid: 'اطلاعات یوزر معتبر نیست',
otp: 'کد اشتباه است'
},
file_types: {
jpg: 'فقط فرمت jpg قابل قبول است',
png: 'فقط فرمت png قابل قبول است',
gif: 'فقط فرمت gif قابل قبول است',
pdf: 'فقط فرمت pdf قابل قبول است',
txt: 'فقط فرمت txt قابل قبول است',
log: 'فقط فرمت log قابل قبول است',
mp3: 'فقط فرمت mp3 قابل قبول است',
ogg: 'فقط فرمت ogg قابل قبول است',
wmv: 'فقط فرمت wmv قابل قبول است',
mp4: 'فقط فرمت mp4 قابل قبول است',
mov: 'فقط فرمت mov قابل قبول است',
mkv: 'فقط فرمت mkv قابل قبول است',
flv: 'فقط فرمت flv قابل قبول است'
}
},
en: {
required: {
token: 'Token is required',
field: 'This field is required',
// developer validations
remember_me: 'Remember_me parameter required',
// registration validations
name: 'Enter name',
first_name: 'Enter first name',
last_name: 'Enter last name',
national_code: 'National code is required',
email: 'Enter email',
email_personal: 'Personal email is required',
email_company: 'Company email is required for legal entities',
company_name: 'Enter company name if you are a legal entity',
username: 'Enter username',
phone_number: 'Enter phone number',
password: 'Enter password',
birthdate: 'Enter birthdate',
// addressing validations
country: 'Enter country',
province: 'Enter province',
city: 'Enter city',
address: 'Enter address',
postal_code: 'Enter postal code',
plaque: 'Enter plaque',
receiver_first_name: 'Enter recipient name',
receiver_last_name: 'Enter recipient surname',
receiver_mobile: "Enter recipient's mobile number",
// global (post - gallery - ...)
title: 'Enter title',
caption: 'Enter caption',
description: 'Enter description',
category: 'Select category',
image: 'Image is required',
cover: 'Cover is required',
file: 'File is required',
message: 'Write message',
quantity: 'Enter quantity',
date: 'Enter date',
reason: 'Select reason',
question: 'Write the question',
answer: 'Write the answer',
price: 'Enter Price',
type: 'Select type',
discount_code: 'Enter discount code',
discount_amount: 'Enter discount amount',
discount_expire_date: 'Specify discount code expiration date',
user_id: 'Select user',
video: 'Video is required'
},
format: {
phone_number: 'Phone number format in incorrect',
email: 'Email format is not correct',
number: 'Value must be number',
boolean: 'Value must be Boolean',
image: 'Photo format is not acceptable',
video: 'Video format is not acceptable',
national_code: 'National code format is not correct'
},
min_char: {
min2: 'At least 2 characters',
min4: 'At least 4 characters',
min8: 'At least 8 characters',
min10: 'At least 10 characters',
min20: 'At least 20 characters',
min30: 'At least 30 characters',
min40: 'At least 40 characters',
min50: 'At least 50 characters',
min60: 'At least 60 characters',
min100: 'At least 100 characters',
min120: 'At least 120 characters',
min150: 'At least 150 characters',
min200: 'At least 200 characters',
data_size: 'Acceptable minimum size: ',
image_width_size: 'Minimum width: ',
image_height_size: 'Minimum height: '
},
max_char: {
max4: 'Maximum 4 characters',
max8: 'Maximum 8 characters',
max10: 'Maximum 10 characters',
max15: 'Maximum 15 characters',
max20: 'Maximum 20 characters',
max30: 'Maximum 30 characters',
max40: 'Maximum 40 characters',
max50: 'Maximum 50 characters',
max60: 'Maximum 60 characters',
max100: 'Maximum 100 characters',
max120: 'Maximum 120 characters',
max150: 'Maximum 150 characters',
max200: 'Maximum 200 characters',
data_size: 'Maximum acceptable size: ',
image_width_size: 'Maximum width: ',
image_height_size: 'Maximum height: '
},
not_found: {
user_id: 'There is no user with this profile',
admin_id: 'There is no admin with this profile',
order_id: 'There is no order with this profile',
item_id: 'There is no item with this specification',
password: 'Password or ID is incorrect'
},
duplicated: {
username: 'This username already exists',
email: 'This email already exists',
name: 'Name already exists',
title: 'Title already exists',
phone_number: 'Phone number already exists'
},
response: {
logged_in: 'You are logged in',
not_logged_in: 'You are not logged in',
logged_out: 'You are logged out',
unauthenticated: 'unauthenticated',
recovery_link: 'Recovery link sent',
success_save: 'Successfully saved',
success_remove: 'Successfully removed',
cart_empty: 'Cart is empty',
already_has_address: 'Already has address',
activation_code: 'Activation code sent to you',
activation_email: 'Activation email sent to you',
expired_reset_link: 'The current reset pass link has been expired',
email_not_confirmed: 'Confirm your email first, the activation link has already been sent to you.',
expired_activation_link: 'This activation link has expired',
success_activation: 'Your account has been successfully activated',
passwords_not_match: 'Passwords are not the same',
problem: 'There is a problem, please try again.',
latinChar: 'Please write in Latin letters',
persianChar: 'Please write in Persian letters',
whiteSpace: 'There should be no space between letters and words'
},
file_types: {
jpg: 'Only jpg format is acceptable',
png: 'Only png format is acceptable',
gif: 'Only gif format is acceptable',
pdf: 'Only pdf format is acceptable',
txt: 'Only txt format is acceptable',
log: 'Only log format is acceptable',
mp3: 'Only mp3 format is acceptable',
ogg: 'Only ogg format is acceptable',
wmv: 'Only wmv format is acceptable',
mp4: 'Only mp4 format is acceptable',
mov: 'Only mov format is acceptable',
mkv: 'Only mkv format is acceptable',
flv: 'Only flv format is acceptable'
}
},
supportedImageFormats: ['jpg', 'jpeg', 'png', 'svg', 'svg+xml', 'webp'],
supportedVideoFormats: ['mp4', 'wmv']
}
-56
View File
@@ -1,56 +0,0 @@
const express = require("express");
const errorHandler = require("./middleware/errorHandler");
const videovalidate = require("./middleware/vidoeValidate");
const db = require("./config/dbConnectoin");
const fileUpload = require("express-fileupload");
const path = require("path");
const swiftly = require("swiftly-tools");
const { expressSharp, FsAdapter } = require('express-sharp')
const { autoFetch } = require('express-route-detector')
var cors = require('cors')
require("dotenv").config();
// Init database
db();
const app = express();
const port = process.env.PORT || 6060;
// Middleware
app.use(cors())
app.use(fileUpload());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(swiftly.middleware.hsts)
app.use(swiftly.middleware.sign("Mr Swift"))
// Routes
app.use(autoFetch(express))
app.use(autoFetch(express, pAddress = "hasPermission", cAddress = "Admin", mAddress = "middleware"))
app.use('/uploads', expressSharp({
imageAdapter: new FsAdapter(path.join(__dirname, 'uploads')),
}));
app.use('/vid', videovalidate.run, expressSharp({
imageAdapter: new FsAdapter(path.join(__dirname, 'Videos')),
}));
//Error handler
app.use(errorHandler.run);
//Init site data
Site.findOne({ profile: "main" }).then(async (data) => {
if (!data) {
await Site.create({
profile: "main",
});
}
}).catch(async (err) => {
await Site.create({
profile: "main",
});
})
app.listen(port, () => {
console.log(`Server runing on port: ${port}`);
});
+20
View File
@@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { ThrottlerModule } from '@nestjs/throttler';
@Module({
imports: [
// ServeStaticModule.forRoot({
// rootPat h: join(__dirname, '..', 'public'),
// }),
// ConfigModule.forRoot({
// isGlobal: true,
// }),
ThrottlerModule.forRoot({
ttl: 60,
limit: 10,
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
+1
View File
@@ -0,0 +1 @@
{ "mongoDb": "mongodb://127.0.0.1:27017/admin" }
+16
View File
@@ -0,0 +1,16 @@
import * as path from 'path';
const pathes = {
staticFiles: './../public',
viewFiles: './views',
userQrCodeFiles: '../public/user/qrCodes',
uploadedFiles: '/files/',
tinymceUpload: '/tinymceUpload/',
// siteUrl: 'http://172.16.27.32/api',
// clientUrl: 'http://172.16.27.32/api',
siteUrl: 'http://localhost:4123',
clientUrl: 'http://localhost:4123',
appRoot: path.resolve(),
};
export default pathes;
+6
View File
@@ -0,0 +1,6 @@
{
"title": "DLearn ",
"description": "DLearn APIs",
"version": "1.0.0",
"tag": "DLearn Apis"
}
+65
View File
@@ -0,0 +1,65 @@
import { NestFactory } from '@nestjs/core';
import * as session from 'express-session';
import { AppModule } from './app.module';
import mongoose from 'mongoose';
import * as dotenv from 'dotenv';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import * as swaggerConfig from './config/swagger.json';
import { ValidationPipe } from '@nestjs/common';
import { NestExpressApplication } from '@nestjs/platform-express';
import paths from './config/paths';
dotenv.config();
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
cors: true,
});
const db =
// app.setGlobalPrefix('api/v1');
// app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
app.useGlobalPipes(new ValidationPipe());
app.use(
session({
name: 'DLearn',
secret: '68yzNk6SQuPFY#WREFDF^&%TERGH765rtgfHJ%$ERFDF@#$!dg^5r%aALM1',
resave: false,
saveUninitialized: false,
}),
);
app.useStaticAssets(paths.staticFiles);
app.setBaseViewsDir(paths.viewFiles);
app.setViewEngine('ejs');
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('X-Powered-By', 'NestJs');
res.setHeader(
'Access-Control-Allow-Methods',
'GET, POST, OPTIONS, PUT, PATCH, DELETE',
);
res.setHeader(
'Access-Control-Allow-Headers',
'X-Requested-With,content-type',
);
res.setHeader('Access-Control-Allow-Credentials', true);
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept',
);
next();
});
mongoose.connect(
// eslint-disable-next-line max-len
`mongodb://${process.env.MONGO_CONNECTION_HOST}:${process.env.MONGO_CONNECTION_PORT}/${process.env.MONGO_CONNECTION_NAME}`,
);
const config = new DocumentBuilder()
.setTitle(swaggerConfig.title)
.setDescription(swaggerConfig.description)
.setVersion(swaggerConfig.version)
.addTag(swaggerConfig.tag)
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/v1', app, document);
await app.listen(3000);
}
bootstrap();
+44
View File
@@ -0,0 +1,44 @@
/* eslint-disable no-param-reassign */
/**
* A mongoose schema plugin which applies the following in the toJSON transform call:
* - removes __v, createdAt, updatedAt, and any path that has private: true
* - replaces _id with id
*/
const deleteAtPath = (obj, path, index) => {
if (index === path.length - 1) {
delete obj[path[index]];
return;
}
deleteAtPath(obj[path[index]], path, index + 1);
};
const cleanJson = (schema) => {
let transform;
if (schema.options.toJSON && schema.options.toJSON.transform) {
transform = schema.options.toJSON.transform;
}
schema.options.toJSON = Object.assign(schema.options.toJSON || {}, {
transform(doc, ret, options) {
Object.keys(schema.paths).forEach((path) => {
if (schema.paths[path].options && schema.paths[path].options.private) {
deleteAtPath(ret, path.split('.'), 0);
}
});
ret.id = ret._id.toString();
delete ret._id;
delete ret.isDeleted;
delete ret.deletedAt;
delete ret.__v;
delete ret.updatedAt;
if (transform) {
return transform(doc, ret, options);
}
},
});
};
export default cleanJson;
+2
View File
@@ -0,0 +1,2 @@
import cleanJson from './cleanJson.plugin';
export { cleanJson };
-3
View File
@@ -1,3 +0,0 @@
var x = 2
var m = 3
return x + m
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
+22
View File
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false,
"resolveJsonModule": true
}
}
-3
View File
@@ -1,3 +0,0 @@
# Asanmarket
-3
View File
@@ -1,3 +0,0 @@
# Asanmarket
-1
View File
@@ -1 +0,0 @@
# Asanmarket
+7195
View File
File diff suppressed because it is too large Load Diff