update:add ci cd files ==> do not edit those file
This commit is contained in:
@@ -1,18 +1,27 @@
|
|||||||
PORT=6060
|
PORT=3000
|
||||||
SECRET=fDS9j269DSfd823FhdfG65
|
SECRET=fDS9j269DSfd823FhdfG65
|
||||||
ACCESS_TOKEN_SECERT=PNd98dsDS089ds3SDh7C5hg265SFC42bgHxTg34x
|
ACCESS_TOKEN_SECERT=PNd98dsDS089ds3SDh7C5hg265SFC42bgHxTg34x
|
||||||
ACCESS_TOKEN_SECERT_ADMIN=G89dsJsd90DFdsf96F8se3sgL7JB29265SFC42bgHxTg34x
|
ACCESS_TOKEN_SECERT_ADMIN=G89dsJsd90DFdsf96F8se3sgL7JB29265SFC42bgHxTg34x
|
||||||
CONNECTION_STRING=
|
CONNECTION_STRING=
|
||||||
MERCHANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
MERCHANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
EMAIL_HOST=
|
EMAIL_HOST=
|
||||||
EMAIL_PORT=
|
EMAIL_PORT=
|
||||||
EMAIL_USER=
|
EMAIL_USER=
|
||||||
EMAIL_PASS=
|
EMAIL_PASS=
|
||||||
EMAIL_ADDRESS=
|
EMAIL_ADDRESS=
|
||||||
SMS_USER=
|
SMS_USER=
|
||||||
SMS_PASS=
|
SMS_PASS=
|
||||||
SMS_NUMBER=
|
SMS_NUMBER=
|
||||||
URLIMAGE=https://api-delearn.iran.liara.run
|
URLIMAGE=https://delearn-api.run.danakcorp.com
|
||||||
MONGO_CONNECTION_HOST=127.0.0.1 mongodb://127.0.0.1:27017/delearn
|
MONGO_CONNECTION_HOST=127.0.0.1
|
||||||
MONGO_CONNECTION_PORT=27017
|
MONGO_CONNECTION_PORT=27017
|
||||||
MONGO_CONNECTION_NAME=delearn
|
MONGO_CONNECTION_NAME=delearn
|
||||||
|
MONGO_CONNECTION_OPTION=authSource=admin
|
||||||
|
STORAGE_ACCESS_KEY=d4eo96t2mklfe228
|
||||||
|
STORAGE_SECRET_KEY=95e78068-ff60-41c0-82c3-71a0de0a3e2f
|
||||||
|
STORAGE_ENDPOINT=https://storage.iran.liara.space
|
||||||
|
STORAGE_BUCKET=dlearn
|
||||||
|
STORAGE_REGION=default
|
||||||
|
|
||||||
|
MONGO_CONNECTION_DANAK=mongodb://root:ahh62bv6o7jndf32@srv-captain--danak-mongo-db:27017/delearn?authSource=admin
|
||||||
|
NODE_ENV=development
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
PORT=3000
|
||||||
|
SECRET=fDS9j269DSfd823FhdfG65
|
||||||
|
ACCESS_TOKEN_SECERT=PNd98dsDS089ds3SDh7C5hg265SFC42bgHxTg34x
|
||||||
|
ACCESS_TOKEN_SECERT_ADMIN=G89dsJsd90DFdsf96F8se3sgL7JB29265SFC42bgHxTg34x
|
||||||
|
CONNECTION_STRING=
|
||||||
|
MERCHANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
|
EMAIL_HOST=
|
||||||
|
EMAIL_PORT=
|
||||||
|
EMAIL_USER=
|
||||||
|
EMAIL_PASS=
|
||||||
|
EMAIL_ADDRESS=
|
||||||
|
SMS_USER=
|
||||||
|
SMS_PASS=
|
||||||
|
SMS_NUMBER=
|
||||||
|
URLIMAGE=https://delearn-api.run.danakcorp.com
|
||||||
|
MONGO_CONNECTION_HOST=danak-db-public.danakcorp.com
|
||||||
|
MONGO_CONNECTION_PORT=45698
|
||||||
|
MONGO_CONNECTION_NAME=delearn
|
||||||
|
MONGO_CONNECTION_USER=root
|
||||||
|
MONGO_CONNECTION_PASS=ahh62bv6o7jndf32
|
||||||
|
MONGO_CONNECTION_OPTION=authSource=admin
|
||||||
|
STORAGE_ACCESS_KEY=d4eo96t2mklfe228
|
||||||
|
STORAGE_SECRET_KEY=95e78068-ff60-41c0-82c3-71a0de0a3e2f
|
||||||
|
STORAGE_ENDPOINT=https://storage.iran.liara.space
|
||||||
|
STORAGE_BUCKET=dlearn
|
||||||
|
STORAGE_REGION=default
|
||||||
|
MONGO_CONNECTION_DANAK=mongodb://root:ahh62bv6o7jndf32@srv-captain--danak-mongo-db:27017/delearn?authSource=admin
|
||||||
|
NODE_ENV=development
|
||||||
+67
-67
@@ -1,67 +1,67 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
project: 'tsconfig.json',
|
project: 'tsconfig.json',
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
},
|
},
|
||||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||||
extends: [
|
extends: [
|
||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/recommended',
|
||||||
'plugin:prettier/recommended',
|
'plugin:prettier/recommended',
|
||||||
],
|
],
|
||||||
root: true,
|
root: true,
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true,
|
||||||
jest: true,
|
jest: true,
|
||||||
},
|
},
|
||||||
ignorePatterns: ['.eslintrc.js'],
|
ignorePatterns: ['.eslintrc.js'],
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
'linebreak-style': 0,
|
'linebreak-style': 0,
|
||||||
strict: 0,
|
strict: 0,
|
||||||
'max-len': ['error', { code: 120 }],
|
'max-len': ['error', { code: 120 }],
|
||||||
'quote-props': 'off',
|
'quote-props': 'off',
|
||||||
'no-param-reassign': 'off',
|
'no-param-reassign': 'off',
|
||||||
'no-underscore-dangle': 'off',
|
'no-underscore-dangle': 'off',
|
||||||
'func-names': ['error', 'as-needed'],
|
'func-names': ['error', 'as-needed'],
|
||||||
'prefer-const': 'warn',
|
'prefer-const': 'warn',
|
||||||
radix: 'off',
|
radix: 'off',
|
||||||
'no-restricted-syntax': ['error', "BinaryExpression[operator='at']"],
|
'no-restricted-syntax': ['error', "BinaryExpression[operator='at']"],
|
||||||
'no-await-in-loop': 'off',
|
'no-await-in-loop': 'off',
|
||||||
'no-trailing-spaces': 'off',
|
'no-trailing-spaces': 'off',
|
||||||
'object-curly-spacing': 'off',
|
'object-curly-spacing': 'off',
|
||||||
'object-curly-newline': [
|
'object-curly-newline': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
ObjectExpression: {
|
ObjectExpression: {
|
||||||
multiline: true,
|
multiline: true,
|
||||||
minProperties: 6,
|
minProperties: 6,
|
||||||
consistent: true,
|
consistent: true,
|
||||||
},
|
},
|
||||||
ObjectPattern: { multiline: true, minProperties: 6, consistent: true },
|
ObjectPattern: { multiline: true, minProperties: 6, consistent: true },
|
||||||
ImportDeclaration: {
|
ImportDeclaration: {
|
||||||
multiline: true,
|
multiline: true,
|
||||||
minProperties: 6,
|
minProperties: 6,
|
||||||
consistent: true,
|
consistent: true,
|
||||||
},
|
},
|
||||||
ExportDeclaration: {
|
ExportDeclaration: {
|
||||||
multiline: true,
|
multiline: true,
|
||||||
minProperties: 6,
|
minProperties: 6,
|
||||||
consistent: true,
|
consistent: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'global-require': 'off',
|
'global-require': 'off',
|
||||||
'import/no-dynamic-require': 'off',
|
'import/no-dynamic-require': 'off',
|
||||||
'@typescript-eslint/interface-name-prefix': 'off',
|
'@typescript-eslint/interface-name-prefix': 'off',
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
'prettier/prettier': [
|
'prettier/prettier': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
endOfLine: 'auto',
|
endOfLine: 'auto',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
name: deploy to danak
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
DANAK_SERVER: "https://captain.run.danakcorp.com"
|
||||||
|
APP_TOKEN: fb6db122b78d5362d5b9c9297172d3145fa616420fca464b556008da9f86b3f1
|
||||||
|
CAPROVER_APP_NAME: dlearn-api
|
||||||
|
GITHUB_TOKEN: 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
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repositorys
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: zmihamid
|
||||||
|
password: ${{ env.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Preset Image Name
|
||||||
|
run: echo "IMAGE_URL=$(echo ghcr.io/zmihamid/${{ github.event.repository.name }}:$(echo ${{ github.sha }} | cut -c1-7) | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Build and push Docker Image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ env.IMAGE_URL }}
|
||||||
|
|
||||||
|
- name: Install CapRover CLI
|
||||||
|
run: npm install -g caprover
|
||||||
|
|
||||||
|
- name: deploy to server
|
||||||
|
run: |
|
||||||
|
caprover deploy -a $CAPROVER_APP_NAME -u $DANAK_SERVER --appToken $APP_TOKEN -i "$IMAGE_URL"
|
||||||
+40
-40
@@ -1,41 +1,41 @@
|
|||||||
# compiled output
|
# compiled output
|
||||||
/dist
|
/dist
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
pnpm-debug.log*
|
pnpm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
# package json
|
# package json
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
/coverage
|
/coverage
|
||||||
/.nyc_output
|
/.nyc_output
|
||||||
|
|
||||||
# IDEs and editors
|
# IDEs and editors
|
||||||
/.idea
|
/.idea
|
||||||
.project
|
.project
|
||||||
.classpath
|
.classpath
|
||||||
.c9/
|
.c9/
|
||||||
*.launch
|
*.launch
|
||||||
.settings/
|
.settings/
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
|
||||||
# IDE - VSCode
|
# IDE - VSCode
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|
||||||
# Environments
|
# Environments
|
||||||
.env
|
.env
|
||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "all"
|
"trailingComma": "all"
|
||||||
}
|
}
|
||||||
|
|||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
FROM node:20-alpine AS base
|
||||||
|
|
||||||
|
RUN apk add --no-cache tzdata
|
||||||
|
|
||||||
|
# Set the timezone to Asia/Tehran
|
||||||
|
RUN cp /usr/share/zoneinfo/Asia/Tehran /etc/localtime && echo "Asia/Tehran" > /etc/timezone
|
||||||
|
|
||||||
|
FROM base AS deps
|
||||||
|
WORKDIR /temp-deps
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN yarn install
|
||||||
|
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /build
|
||||||
|
COPY . ./
|
||||||
|
COPY --from=deps /temp-deps/node_modules ./node_modules
|
||||||
|
|
||||||
|
# Check if a build script exists and run the build if it does
|
||||||
|
RUN if [ -f package.json ] && grep -q '"build":' package.json; then yarn build; fi
|
||||||
|
RUN yarn install --production --frozen-lockfile
|
||||||
|
|
||||||
|
|
||||||
|
FROM base AS runner
|
||||||
|
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /build ./
|
||||||
|
|
||||||
|
RUN chown -R appuser:appgroup /app
|
||||||
|
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"collection": "@nestjs/schematics",
|
"collection": "@nestjs/schematics",
|
||||||
"sourceRoot": "src"
|
"sourceRoot": "src"
|
||||||
}
|
}
|
||||||
|
|||||||
+128
-117
@@ -1,117 +1,128 @@
|
|||||||
{
|
{
|
||||||
"name": "DLearn",
|
"name": "DLearn",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "rimraf dist",
|
"prebuild": "rimraf dist",
|
||||||
"build": "nest build",
|
"build": "nest build",
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
"start": "nest start",
|
"start": "node dist/main",
|
||||||
"start:dev": "nest start --watch",
|
"start:dev": "cross-env NODE_ENV=development nest start --watch",
|
||||||
"start:debug": "nest start --debug --watch",
|
"start:debug": "nest start --debug --watch",
|
||||||
"start:prod": "node dist/main",
|
"start:prod": "node dist/main",
|
||||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"test:cov": "jest --coverage",
|
"test:cov": "jest --coverage",
|
||||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
"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"
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
},
|
},
|
||||||
"author": "daniel.3380",
|
"author": "daniel.3380",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/axios": "^2.0.0",
|
"@aws-sdk/client-s3": "^3.574.0",
|
||||||
"@nestjs/common": "^10.0.5",
|
"@ffprobe-installer/ffprobe": "^2.1.2",
|
||||||
"@nestjs/core": "^10.0.5",
|
"@liara/cli": "^6.0.3",
|
||||||
"@nestjs/mongoose": "^10.0.6",
|
"@nestjs/axios": "^2.0.0",
|
||||||
"@nestjs/platform-express": "^10.0.5",
|
"@nestjs/common": "^10.0.5",
|
||||||
"@nestjs/platform-socket.io": "^10.0.5",
|
"@nestjs/config": "^3.2.2",
|
||||||
"@nestjs/serve-static": "^4.0.0",
|
"@nestjs/core": "^10.0.5",
|
||||||
"@nestjs/swagger": "^7.1.1",
|
"@nestjs/event-emitter": "^2.0.4",
|
||||||
"@nestjs/testing": "^9.4.0",
|
"@nestjs/jwt": "^10.2.0",
|
||||||
"@nestjs/throttler": "^4.0.0",
|
"@nestjs/mongoose": "^10.0.6",
|
||||||
"@types/axios": "^0.14.0",
|
"@nestjs/platform-express": "^10.0.5",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@nestjs/platform-socket.io": "^10.0.5",
|
||||||
"@types/express-session": "1.17.0",
|
"@nestjs/serve-static": "^4.0.0",
|
||||||
"@types/moment": "^2.13.0",
|
"@nestjs/swagger": "^7.1.1",
|
||||||
"@types/multer": "^1.4.7",
|
"@nestjs/testing": "^9.4.0",
|
||||||
"@types/request": "^2.48.8",
|
"@nestjs/throttler": "^4.0.0",
|
||||||
"@types/request-ip": "^0.0.37",
|
"@types/axios": "^0.14.0",
|
||||||
"axios": "^1.3.6",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"base64-async": "^2.1.3",
|
"@types/express-session": "1.17.0",
|
||||||
"bluebird": "^3.7.2",
|
"@types/moment": "^2.13.0",
|
||||||
"class-transformer": "^0.5.1",
|
"@types/multer": "^1.4.7",
|
||||||
"class-validator": "^0.14.0",
|
"@types/request": "^2.48.8",
|
||||||
"cookie-parser": "^1.4.6",
|
"@types/request-ip": "^0.0.37",
|
||||||
"crypto-js": "^4.1.1",
|
"aws-sdk": "^2.1618.0",
|
||||||
"dotenv": "^16.4.5",
|
"axios": "^1.3.6",
|
||||||
"ejs": "^3.1.8",
|
"base64-async": "^2.1.3",
|
||||||
"express-session": "^1.17.2",
|
"bluebird": "^3.7.2",
|
||||||
"fs": "^0.0.1-security",
|
"class-transformer": "^0.5.1",
|
||||||
"jalali-moment": "^3.3.11",
|
"class-validator": "^0.14.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"cookie-parser": "^1.4.6",
|
||||||
"md5": "^2.3.0",
|
"cross-env": "^7.0.3",
|
||||||
"mongoose": "^8.3.2",
|
"crypto-js": "^4.1.1",
|
||||||
"mysql": "^2.18.1",
|
"dotenv": "^16.4.5",
|
||||||
"mysql2": "^3.2.3",
|
"ejs": "^3.1.8",
|
||||||
"path": "^0.12.7",
|
"express-session": "^1.17.2",
|
||||||
"pg": "^8.11.1",
|
"fs": "^0.0.1-security",
|
||||||
"pm2": "^5.2.0",
|
"get-video-duration": "^4.1.0",
|
||||||
"qrcode": "^1.5.0",
|
"jalali-moment": "^3.3.11",
|
||||||
"random-number": "^0.0.9",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"reflect-metadata": "^0.1.13",
|
"md5": "^2.3.0",
|
||||||
"request": "^2.88.2",
|
"moment": "^2.29.4",
|
||||||
"request-ip": "^2.1.3",
|
"mongoose": "^8.3.2",
|
||||||
"rimraf": "^3.0.2",
|
"mysql": "^2.18.1",
|
||||||
"rxjs": "^7.5.5",
|
"mysql2": "^3.2.3",
|
||||||
"sequelize": "^6.32.1",
|
"path": "^0.12.7",
|
||||||
"sequelize-typescript": "^2.1.5",
|
"pg": "^8.11.1",
|
||||||
"shelljs": "^0.8.5",
|
"pm2": "^5.2.0",
|
||||||
"soap": "^1.0.0",
|
"qrcode": "^1.5.0",
|
||||||
"swagger-ui-express": "^4.6.3",
|
"random-number": "^0.0.9",
|
||||||
"ts-loader": "^9.4.2",
|
"reflect-metadata": "^0.1.13",
|
||||||
"typescript": "^5.1.6"
|
"request": "^2.88.2",
|
||||||
},
|
"request-ip": "^3.3.0",
|
||||||
"devDependencies": {
|
"rimraf": "^3.0.2",
|
||||||
"@nestjs/cli": "^8.2.5",
|
"rxjs": "^7.5.5",
|
||||||
"@nestjs/schematics": "^8.0.11",
|
"sequelize": "^6.32.1",
|
||||||
"@types/bluebird": "^3.5.36",
|
"sequelize-typescript": "^2.1.5",
|
||||||
"@types/express": "^4.17.13",
|
"shelljs": "^0.8.5",
|
||||||
"@types/jest": "27.0.2",
|
"soap": "^1.0.0",
|
||||||
"@types/jsonwebtoken": "^8.5.8",
|
"soft-delete-plugin-mongoose": "^1.0.17",
|
||||||
"@types/md5": "^2.3.2",
|
"swagger-ui-express": "^4.6.3",
|
||||||
"@types/node": "^16.11.33",
|
"ts-loader": "^9.4.2",
|
||||||
"@types/qrcode": "^1.4.2",
|
"typescript": "^5.1.6"
|
||||||
"@types/supertest": "^2.0.12",
|
},
|
||||||
"@typescript-eslint/eslint-plugin": "^5.22.0",
|
"devDependencies": {
|
||||||
"@typescript-eslint/parser": "^5.22.0",
|
"@nestjs/cli": "^10.3.2",
|
||||||
"eslint": "^8.15.0",
|
"@nestjs/schematics": "^8.0.11",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"@types/bluebird": "^3.5.36",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"@types/express": "^4.17.13",
|
||||||
"jest": "^27.5.1",
|
"@types/jest": "27.0.2",
|
||||||
"prettier": "^2.6.2",
|
"@types/jsonwebtoken": "^8.5.8",
|
||||||
"source-map-support": "^0.5.20",
|
"@types/md5": "^2.3.2",
|
||||||
"supertest": "^6.2.3",
|
"@types/node": "^16.11.33",
|
||||||
"ts-jest": "^27.1.4",
|
"@types/qrcode": "^1.4.2",
|
||||||
"ts-node": "^10.7.0",
|
"@types/supertest": "^2.0.12",
|
||||||
"tsconfig-paths": "^3.14.1"
|
"@typescript-eslint/eslint-plugin": "^5.22.0",
|
||||||
},
|
"@typescript-eslint/parser": "^5.22.0",
|
||||||
"jest": {
|
"eslint": "^8.15.0",
|
||||||
"moduleFileExtensions": [
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"js",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"json",
|
"jest": "^27.5.1",
|
||||||
"ts"
|
"prettier": "^2.6.2",
|
||||||
],
|
"source-map-support": "^0.5.20",
|
||||||
"rootDir": "src",
|
"supertest": "^6.2.3",
|
||||||
"testRegex": ".*\\.spec\\.ts$",
|
"ts-jest": "^27.1.4",
|
||||||
"transform": {
|
"ts-node": "^10.7.0",
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
"tsconfig-paths": "^3.14.1"
|
||||||
},
|
},
|
||||||
"collectCoverageFrom": [
|
"jest": {
|
||||||
"**/*.(t|j)s"
|
"moduleFileExtensions": [
|
||||||
],
|
"js",
|
||||||
"coverageDirectory": "../coverage",
|
"json",
|
||||||
"testEnvironment": "node"
|
"ts"
|
||||||
}
|
],
|
||||||
}
|
"rootDir": "src",
|
||||||
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
},
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"**/*.(t|j)s"
|
||||||
|
],
|
||||||
|
"coverageDirectory": "../coverage",
|
||||||
|
"testEnvironment": "node"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsEnum, IsInt, IsNotEmpty, IsOptional } from 'class-validator';
|
||||||
|
import blogStatuses, { ItemStatus } from '../common/eNums/itemStatus.enum';
|
||||||
|
import { PostDto, postObjDto } from './blogPost.dto';
|
||||||
|
import { CategoryDataAccess } from '../dataAccess/blogCategory.dataAccess';
|
||||||
|
import itemStatuses from '../common/eNums/itemStatus.enum';
|
||||||
|
|
||||||
|
const categoryDataAccess = new CategoryDataAccess();
|
||||||
|
async function findParent(id) {
|
||||||
|
const parent = await categoryDataAccess.findById(id);
|
||||||
|
return parent.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function blogCatObjDto(category) {
|
||||||
|
const active = Object.keys(blogStatuses).find(
|
||||||
|
(key) => blogStatuses[key].code === category.active,
|
||||||
|
);
|
||||||
|
let Posts = [];
|
||||||
|
if (category.Posts && category.Posts.length !== 0) {
|
||||||
|
Posts = category.Posts.map((post) => postObjDto(post));
|
||||||
|
}
|
||||||
|
const parent =
|
||||||
|
category.parentId !== null ? await findParent(category.parentId) : 'root';
|
||||||
|
return {
|
||||||
|
id: category.id,
|
||||||
|
parentId: category.parentId,
|
||||||
|
parent: parent,
|
||||||
|
title: category.title,
|
||||||
|
description: category.description,
|
||||||
|
statusText: itemStatuses[category.status],
|
||||||
|
status: category.status,
|
||||||
|
createdAt: category.createdAt,
|
||||||
|
Posts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function blogCatArrObjDto(category) {
|
||||||
|
console.log(category);
|
||||||
|
const parent =
|
||||||
|
category.parentId !== null ? await findParent(category.parentId) : 'root';
|
||||||
|
return {
|
||||||
|
id: category.id,
|
||||||
|
parentId: category.parentId,
|
||||||
|
parent: parent,
|
||||||
|
parents: category.parents,
|
||||||
|
title: category.title,
|
||||||
|
description: category.description,
|
||||||
|
status: category.status,
|
||||||
|
statusText: itemStatuses[category.status],
|
||||||
|
createdAt: category.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function blogCatObjInfoDto(category) {
|
||||||
|
return {
|
||||||
|
title: category.title,
|
||||||
|
statusText: itemStatuses[category.status],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function childBlogCatObj(category) {
|
||||||
|
return {
|
||||||
|
id: category.id,
|
||||||
|
title: category.title,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAllBlogCategoriesObj(category) {
|
||||||
|
let BlogCategories = [];
|
||||||
|
if (category.BlogCategories && category.BlogCategories !== 0) {
|
||||||
|
BlogCategories = category.BlogCategories.map((category) =>
|
||||||
|
childBlogCatObj(category),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: category.id,
|
||||||
|
title: category.title,
|
||||||
|
BlogCategories,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BlogCatInfoDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
statusText: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChildBlogCatDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GetAllBlogCategoriesDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: ChildBlogCatDto, isArray: true })
|
||||||
|
BlogCategories: ChildBlogCatDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BlogCatDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
parentId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
parent: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
statusText: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
createdAt: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: PostDto, isArray: true })
|
||||||
|
Posts: PostDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BlogCatListDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
parentId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
parent: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
statusText: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateBlogCatDto {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
parentId: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateBlogCatDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
parentId: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StatusBlogCatDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class getParentListBlogCatDto {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
parentId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
IsEnum,
|
||||||
|
IsNotEmpty,
|
||||||
|
IsNumberString,
|
||||||
|
IsOptional,
|
||||||
|
} from 'class-validator';
|
||||||
|
import blogStatuses, { ItemStatus } from '../common/eNums/itemStatus.enum';
|
||||||
|
|
||||||
|
import { postCommentObj } from './blogPostComment.dto';
|
||||||
|
import { BlogCatInfoDto, blogCatObjInfoDto } from './blogCategory.dto';
|
||||||
|
import { UserMidDto, userMidObj } from './user.dto';
|
||||||
|
|
||||||
|
export function postObjDto(post) {
|
||||||
|
const blogCat = blogCatObjInfoDto(post.blogCat);
|
||||||
|
return {
|
||||||
|
id: post.id,
|
||||||
|
adminId: post.adminId,
|
||||||
|
blogCat,
|
||||||
|
title: post.title,
|
||||||
|
slug: post.slug,
|
||||||
|
visitCount: post.visitCount,
|
||||||
|
statusText: blogStatuses[post.status],
|
||||||
|
status: post.status,
|
||||||
|
createdAt: post.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function viewPostObjDto(post) {
|
||||||
|
const blogCat = blogCatObjInfoDto(post.blogCat);
|
||||||
|
return {
|
||||||
|
id: post.id,
|
||||||
|
blogCat,
|
||||||
|
mediaUrl: post.mediaUrl,
|
||||||
|
title: post.title,
|
||||||
|
slug: post.slug,
|
||||||
|
description: post.description,
|
||||||
|
visitCount: post.visitCount,
|
||||||
|
createdAt: post.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postInfoObjDto(post) {
|
||||||
|
// let PostMedias = [];
|
||||||
|
// if (post.PostMedias && post.PostMedias.length !== 0) {
|
||||||
|
// PostMedias = post.PostMedias.map((postMedia) => postMediaObj(postMedia));
|
||||||
|
// }
|
||||||
|
let PostComments = [];
|
||||||
|
if (post.PostComments && post.PostComments.length !== 0) {
|
||||||
|
PostComments = post.PostComments.map((postComment) =>
|
||||||
|
postCommentObj(postComment),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const admin = userMidObj(post.admin);
|
||||||
|
const blogCat = blogCatObjInfoDto(post.blogCat);
|
||||||
|
// let media = null;
|
||||||
|
// if (post.Media) {
|
||||||
|
// media = mediaObj(post.Media);
|
||||||
|
// }
|
||||||
|
return {
|
||||||
|
id: post.id,
|
||||||
|
admin,
|
||||||
|
blogCat,
|
||||||
|
mediaUrl: post.mediaUrl,
|
||||||
|
// media,
|
||||||
|
description: post.description,
|
||||||
|
title: post.title,
|
||||||
|
slug: post.slug,
|
||||||
|
text: post.text,
|
||||||
|
writer: post.writer,
|
||||||
|
mediaAlt: post.mediaAlt,
|
||||||
|
metaTitle: post.metaTitle,
|
||||||
|
metaDescription: post.metaDescription,
|
||||||
|
metaKeywords: post.metaKeywords,
|
||||||
|
visitCount: post.visitCount,
|
||||||
|
sort: post.sort,
|
||||||
|
isInLanding: post.isInLanding,
|
||||||
|
statusText: blogStatuses[post.status],
|
||||||
|
status: post.status,
|
||||||
|
createdAt: post.createdAt,
|
||||||
|
PostComments,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postTitleObjDto(post) {
|
||||||
|
return {
|
||||||
|
id: post.id,
|
||||||
|
title: post.title,
|
||||||
|
slug: post.slug,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: () => UserMidDto })
|
||||||
|
admin: UserMidDto;
|
||||||
|
|
||||||
|
@ApiProperty({ type: () => BlogCatInfoDto })
|
||||||
|
blogCat: BlogCatInfoDto;
|
||||||
|
|
||||||
|
// @IsNotEmpty()
|
||||||
|
// @ApiProperty({ type: Number })
|
||||||
|
// adminId: number;
|
||||||
|
|
||||||
|
// @ApiProperty({ type: AdminInfoDto })
|
||||||
|
// admin: AdminInfoDto;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
mediaUrl: string;
|
||||||
|
|
||||||
|
// @ApiProperty({ type: MediaDto })
|
||||||
|
// media: MediaDto;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
slug: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
writer: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
mediaAlt: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
metaTitle: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
metaDescription: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
metaKeywords: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
visitCount: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
statusText: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
sort: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Boolean })
|
||||||
|
isInLanding: boolean;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
createdAt: string;
|
||||||
|
|
||||||
|
// @ApiProperty({ isArray: true, type: PostMediaDto })
|
||||||
|
// PostMedias: PostMediaDto[];
|
||||||
|
|
||||||
|
// @ApiProperty({ isArray: true, type: PostCommentDto })
|
||||||
|
// PostComments: PostCommentDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ListPostDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: () => BlogCatInfoDto })
|
||||||
|
blogCat: BlogCatInfoDto;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
adminId: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
slug: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
visitCount: number;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
statusText: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ViewListPostDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: () => BlogCatInfoDto })
|
||||||
|
blogCat: BlogCatInfoDto;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
mediaUrl: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
slug: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
visitCount: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FilterListPostDto {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
blogCatId: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
slug: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number, required: false })
|
||||||
|
visitCount: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Boolean, required: false })
|
||||||
|
isInLanding: boolean;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreatePostDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'دسته بندی اجباری می باشد' })
|
||||||
|
blogCatId: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'مدیا اجباری می باشد' })
|
||||||
|
mediaUrl: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsNotEmpty({ message: 'عنوان اجباری می باشد' })
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'توضیحات کوتاه اجباری می باشد' })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'متن اجباری می باشد' })
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'توضیحات عکس اجباری می باشد' })
|
||||||
|
mediaAlt: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'عنوان متا اجباری می باشد' })
|
||||||
|
metaTitle: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'توضیحات متا اجباری می باشد' })
|
||||||
|
metaDescription: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'کلمات کلیدی اجباری می باشد' })
|
||||||
|
metaKeywords: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Boolean })
|
||||||
|
@IsNotEmpty({ message: 'توضیحات متا اجباری می باشد' })
|
||||||
|
isInLanding: boolean;
|
||||||
|
|
||||||
|
// @Optional()
|
||||||
|
// @ApiProperty({ isArray: true, type: CreatePostMediaDto })
|
||||||
|
// postMedias: CreatePostMediaDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdatePostDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
blogCat: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
mediaUrl: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
writer: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
mediaAlt: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
metaTitle: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
metaDescription: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
metaKeywords: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Boolean })
|
||||||
|
isInLanding: boolean;
|
||||||
|
|
||||||
|
// @Optional()
|
||||||
|
// @ApiProperty({ isArray: true, type: CreatePostMediaDto })
|
||||||
|
// postMedias: CreatePostMediaDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostStatusDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeSortPostDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
fromId: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
toId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class getPostInputDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumberString()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
blogCatId: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number, required: false })
|
||||||
|
page: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number, required: false })
|
||||||
|
pageSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SearchParameter {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
page: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
pageSize: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsEnum, IsMongoId, IsNotEmpty, IsOptional } from 'class-validator';
|
||||||
|
import { ItemStatus } from '../common/eNums/itemStatus.enum';
|
||||||
|
import { postTitleObjDto } from './blogPost.dto';
|
||||||
|
|
||||||
|
export function postCommentObj(postComment) {
|
||||||
|
return {
|
||||||
|
id: postComment.id,
|
||||||
|
postId: postComment.postId,
|
||||||
|
parentId: postComment.parentId,
|
||||||
|
userName: postComment.userName,
|
||||||
|
text: postComment.text,
|
||||||
|
status: postComment.status,
|
||||||
|
createdAt: postComment.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postCommentInfoObj(postComment) {
|
||||||
|
let replies = [];
|
||||||
|
if (postComment.Replies && postComment.Replies.length !== 0) {
|
||||||
|
replies = postComment.Replies.map((postComment) =>
|
||||||
|
postCommentObj(postComment),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const post = postTitleObjDto(postComment.blogPost);
|
||||||
|
return {
|
||||||
|
id: postComment.id,
|
||||||
|
postId: postComment.postId,
|
||||||
|
post: post,
|
||||||
|
parentId: postComment.parentId,
|
||||||
|
userName: postComment.userName,
|
||||||
|
text: postComment.text,
|
||||||
|
status: postComment.status,
|
||||||
|
createdAt: postComment.createdAt,
|
||||||
|
replies,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postCommentArrObjDto(postComment) {
|
||||||
|
const post = postTitleObjDto(postComment.Post);
|
||||||
|
return {
|
||||||
|
id: postComment.id,
|
||||||
|
postId: postComment.postId,
|
||||||
|
post: post.title,
|
||||||
|
parentId: postComment.parentId,
|
||||||
|
userName: postComment.userName,
|
||||||
|
status: postComment.status,
|
||||||
|
text: postComment.text,
|
||||||
|
createdAt: postComment.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ListPostCommentDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
postId: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
post: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
parentId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
userName: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FilterListPostCommentDto {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
postId: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Date, required: false })
|
||||||
|
fromDate: Date;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Date, required: false })
|
||||||
|
toDate: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostCommentDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
postId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
parentId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
userName: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostCommentInfoDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
postId: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
parentId: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
userName: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@ApiProperty({ isArray: true, type: PostCommentDto })
|
||||||
|
replies: PostCommentDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreatePostCommentDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StatusPostCommentDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsMongoId, IsNotEmpty, IsOptional } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateCommentDto {
|
||||||
|
@ApiProperty({type:String})
|
||||||
|
@IsNotEmpty({message:"متن کامنت نمیتواند خالی باشد"})
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
@ApiProperty({type:String})
|
||||||
|
@IsOptional()
|
||||||
|
@IsMongoId({message:"شناسه والد نامعتبر است"})
|
||||||
|
parent: string;
|
||||||
|
|
||||||
|
course: string;
|
||||||
|
|
||||||
|
createdBy: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
import { ApiProperty, PartialType } from '@nestjs/swagger';
|
||||||
|
import { IsMongoId, IsNotEmpty, IsOptional } from 'class-validator';
|
||||||
|
|
||||||
|
export class CreateCourseDto {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'نام دوره نباید خالی باشد.' })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'توضیحات دوره نباید خالی باشد.' })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'آدرس دوره نباید خالی باشد.' })
|
||||||
|
slug: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'مدت دوره نباید خالی باشد.' })
|
||||||
|
duration: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'عکس دوره نباید خالی باشد.' })
|
||||||
|
image: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: [String] })
|
||||||
|
@IsNotEmpty({ message: 'دستهبندیهای دوره نباید خالی باشد.' })
|
||||||
|
categories: string[];
|
||||||
|
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsNotEmpty({ message: 'قیمت دوره نباید خالی باشد.' })
|
||||||
|
price: number;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'ایدی مدرس نباید خالی باشد.' })
|
||||||
|
@IsMongoId({ message: 'ایدی مدرس نامعتبر است.' })
|
||||||
|
teacher: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsNotEmpty({ message: 'درصد تکمیل دوره نباید خالی باشد.' })
|
||||||
|
completionPercentage: number;
|
||||||
|
|
||||||
|
@ApiProperty({ enum: ['مبتدی', 'متوسط', 'پیشرفته'], enumName: 'CourseLevel' })
|
||||||
|
@IsNotEmpty({ message: 'سطح دوره نباید خالی باشد.' })
|
||||||
|
level: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'زبان دوره نباید خالی باشد.' })
|
||||||
|
language: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsNotEmpty({ message: 'تعداد ویدیوهای دوره نباید خالی باشد.' })
|
||||||
|
countOfVideos: number;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'حجم فایل دوره نباید خالی باشد.' })
|
||||||
|
totalFileSize: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateCourseDto {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'ایدی دوره نباید خالی باشد.' })
|
||||||
|
@IsMongoId({ message: 'ایدی دوره نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
slug: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
duration: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
image: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsOptional()
|
||||||
|
price: number;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
teacher: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: [String] })
|
||||||
|
@IsOptional()
|
||||||
|
categories: string[];
|
||||||
|
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsOptional()
|
||||||
|
completionPercentage: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
enum: ['Beginner', 'Intermediate', 'Advanced'],
|
||||||
|
enumName: 'CourseLevel',
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
level: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
language: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsOptional()
|
||||||
|
countOfVideos: number;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
totalFileSize: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PaginationCourseDto {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number, required: false })
|
||||||
|
page: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number, required: false })
|
||||||
|
pageSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FilterCategoryPaginationCourseDto {
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
@IsOptional()
|
||||||
|
@IsMongoId({ message: 'ایدی دستهبندی نامعتبر است.' })
|
||||||
|
categoryId: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number, required: false })
|
||||||
|
page: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number, required: false })
|
||||||
|
pageSize: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
price: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MidCourseDtoObj(item) {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
image: item.image,
|
||||||
|
price: item.price,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export class MidCourseDto {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'نام دوره نباید خالی باشد.' })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'عکس دوره نباید خالی باشد.' })
|
||||||
|
image: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsNotEmpty({ message: 'قیمت دوره نباید خالی باشد.' })
|
||||||
|
price: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsNotEmpty } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateCourseCategoryDto {
|
||||||
|
@ApiProperty({type: String})
|
||||||
|
@IsNotEmpty({message: 'نام دسته بندی را وارد کنید'})
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsEnum, IsNotEmpty, IsOptional } from 'class-validator';
|
||||||
|
import { ItemStatusCourseHeadline } from '../common/eNums/itemStatusCourseHeadline.enum';
|
||||||
|
|
||||||
|
export class CreateCourseHeadlineDTO {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'عنوان دوره نباید خالی باشد.' })
|
||||||
|
headline: string;
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'توضیحات دوره نباید خالی باشد.' })
|
||||||
|
description: string;
|
||||||
|
episodes: EpisodeDTO[];
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'ایدی دوره نباید خالی باشد.' })
|
||||||
|
course: string; // Assuming course is referenced by its ID
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
videoUrl: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatusCourseHeadline)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatusCourseHeadline })
|
||||||
|
@IsNotEmpty({ message: 'وضعیت دوره نباید خالی باشد.' })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateCourseHeadlineDTO {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
headline: string;
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
description: string;
|
||||||
|
episodes: EpisodeDTO[];
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
course: string; // Assuming course is referenced by its ID
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsOptional()
|
||||||
|
videoUrl: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatusCourseHeadline)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatusCourseHeadline })
|
||||||
|
@IsOptional()
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EpisodeDTO {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'عنوان دوره نباید خالی باشد.' })
|
||||||
|
title: string;
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsNotEmpty({ message: 'مدت دوره نباید خالی باشد.' })
|
||||||
|
duration: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsEnum, IsMongoId, IsNotEmpty, IsOptional } from 'class-validator';
|
||||||
|
import { ItemStatus } from '../common/eNums/itemStatus.enum';
|
||||||
|
|
||||||
|
export function courseHeadlineCommentObj(item) {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
courseHeadlineId: item.courseHeadlineId,
|
||||||
|
parentId: item.parentId,
|
||||||
|
userName: item.userName,
|
||||||
|
text: item.text,
|
||||||
|
createdAt: item.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function courseHeadlineCommentInfoObj(courseHeadlineComment) {
|
||||||
|
let replies = [];
|
||||||
|
if (
|
||||||
|
courseHeadlineComment.Comment &&
|
||||||
|
courseHeadlineComment.Comment.length !== 0
|
||||||
|
) {
|
||||||
|
replies = courseHeadlineComment.Comment.map((courseHeadlineComment) =>
|
||||||
|
courseHeadlineCommentObj(courseHeadlineComment),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: courseHeadlineComment.id,
|
||||||
|
courseHeadlineId: courseHeadlineComment.courseHeadlineId,
|
||||||
|
parentId: courseHeadlineComment.parentId,
|
||||||
|
userName: courseHeadlineComment.userName,
|
||||||
|
text: courseHeadlineComment.text,
|
||||||
|
createdAt: courseHeadlineComment.createdAt,
|
||||||
|
replies,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function courseHeadlineCommentArrObjDto(courseHeadlineComment) {
|
||||||
|
return {
|
||||||
|
id: courseHeadlineComment.id,
|
||||||
|
courseHeadlineId: courseHeadlineComment.courseHeadlineId,
|
||||||
|
parentId: courseHeadlineComment.parentId,
|
||||||
|
userName: courseHeadlineComment.userName,
|
||||||
|
text: courseHeadlineComment.text,
|
||||||
|
createdAt: courseHeadlineComment.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ListPostCommentDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsMongoId({ message: 'آیدی سرفصل نامعتبر است.' })
|
||||||
|
courseHeadlineId: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
courseHeadline: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
parentId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
userName: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
active: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
activeText: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FilterListPostCommentDto {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
@IsMongoId({ message: 'آیدی سرفصل نامعتبر است.' })
|
||||||
|
courseHeadlineId: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Date, required: false })
|
||||||
|
fromDate: Date;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Date, required: false })
|
||||||
|
toDate: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostCommentDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsMongoId({ message: 'آیدی سرفصل نامعتبر است.' })
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
courseHeadlineId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
parentId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
userName: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
active: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
activeText: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PostCommentInfoDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
courseHeadlineId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
parentId: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
userName: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
active: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
activeText: string;
|
||||||
|
|
||||||
|
@ApiProperty({ isArray: true, type: PostCommentDto })
|
||||||
|
replies: PostCommentDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateCourseHeadlineCommentDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsMongoId({ message: 'آیدی سرفصل نامعتبر است.' })
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StatusCommentDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
active: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsNotEmpty, IsMongoId, IsEnum, IsOptional } from 'class-validator';
|
||||||
|
import discountStatus from '../common/eNums/discountStatus.enum';
|
||||||
|
import discountTypes, {
|
||||||
|
DiscountTypeEnum,
|
||||||
|
} from '../common/eNums/discountType.enum';
|
||||||
|
import discountUser from '../common/eNums/discountUser.enum';
|
||||||
|
import itemStatuses, { ItemStatus } from '../common/eNums/itemStatus.enum';
|
||||||
|
|
||||||
|
import { UserMidDto, userMidObj } from './user.dto';
|
||||||
|
|
||||||
|
// OBJ *******************
|
||||||
|
|
||||||
|
export function detailsAdminDiscountObj(discount) {
|
||||||
|
const adminObj = userMidObj(discount.user);
|
||||||
|
return {
|
||||||
|
id: discount.id,
|
||||||
|
admin: adminObj,
|
||||||
|
startDate: discount.startDate,
|
||||||
|
endDate: discount.endDate,
|
||||||
|
code: discount.code,
|
||||||
|
price: discount.price,
|
||||||
|
percent: discount.percent,
|
||||||
|
allUsers: discount.allUsers,
|
||||||
|
status: discount.status,
|
||||||
|
discountType: discount.discountType,
|
||||||
|
createdAt: discount.createdAt,
|
||||||
|
updatedAt: discount.updatedAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listDiscountObj(discount) {
|
||||||
|
const adminObj = userMidObj(discount.user);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: discount.id,
|
||||||
|
admin: adminObj,
|
||||||
|
startDate: discount.startDate,
|
||||||
|
endDate: discount.endDate,
|
||||||
|
code: discount.code,
|
||||||
|
discountType: discount.discountType,
|
||||||
|
status: discount.status,
|
||||||
|
statusText: itemStatuses[discount.status],
|
||||||
|
createdAt: discount.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLASS *******************
|
||||||
|
|
||||||
|
export class CreateDiscountDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
startDate: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
endDate: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@IsEnum(DiscountTypeEnum)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
discountType: string;
|
||||||
|
|
||||||
|
@ApiProperty({ required: false, type: String })
|
||||||
|
price: string;
|
||||||
|
|
||||||
|
@ApiProperty({ required: false, type: Number })
|
||||||
|
percent: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Boolean })
|
||||||
|
allUsers: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateDiscountDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
startDate: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
endDate: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@IsEnum(DiscountTypeEnum)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
discountType: string;
|
||||||
|
|
||||||
|
@ApiProperty({ required: false, type: String })
|
||||||
|
price: string;
|
||||||
|
|
||||||
|
@ApiProperty({ required: false, type: Number })
|
||||||
|
percent: number;
|
||||||
|
|
||||||
|
@ApiProperty({ type: Boolean })
|
||||||
|
allUsers: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdatedStatusDiscountDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ListDiscountDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: UserMidDto })
|
||||||
|
admin: UserMidDto;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
startDate: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
endDate: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
discountType: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DetailsAdminDiscountDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: UserMidDto })
|
||||||
|
admin: UserMidDto;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
startDate: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
endDate: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@ApiProperty({ required: false, type: String })
|
||||||
|
price: string;
|
||||||
|
|
||||||
|
@ApiProperty({ required: false, type: Number })
|
||||||
|
percent: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Boolean })
|
||||||
|
allUsers: boolean;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
discountType: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
updatedAt: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IdDiscountDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsNotEmpty } from 'class-validator';
|
||||||
|
import { UserMidDto, userMidObj } from './user.dto';
|
||||||
|
import {
|
||||||
|
DetailsAdminDiscountDto,
|
||||||
|
detailsAdminDiscountObj,
|
||||||
|
} from './discount.dto';
|
||||||
|
import { ListUserDiscountDto, listUserDiscountObj } from './userDiscount.dto';
|
||||||
|
|
||||||
|
// OBJ *******************
|
||||||
|
|
||||||
|
export function listDiscountHistoryObj(result) {
|
||||||
|
const user = result.user ? userMidObj(result.user) : null;
|
||||||
|
const discount = result.discount
|
||||||
|
? detailsAdminDiscountObj(result.discount)
|
||||||
|
: null;
|
||||||
|
const userDiscount = result.userDiscount
|
||||||
|
? listUserDiscountObj(result.userDiscount)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: discount.id,
|
||||||
|
user,
|
||||||
|
discount,
|
||||||
|
userDiscount,
|
||||||
|
createdAt: discount.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLASS *******************
|
||||||
|
|
||||||
|
export class ListDiscountHistoryDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: UserMidDto })
|
||||||
|
user: UserMidDto;
|
||||||
|
|
||||||
|
@ApiProperty({ type: ListUserDiscountDto })
|
||||||
|
userDiscount: ListUserDiscountDto;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: DetailsAdminDiscountDto })
|
||||||
|
discount: DetailsAdminDiscountDto;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
createdAt: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
IsEnum,
|
||||||
|
IsInt,
|
||||||
|
IsMongoId,
|
||||||
|
IsNotEmpty,
|
||||||
|
IsOptional,
|
||||||
|
} from 'class-validator';
|
||||||
|
import { ItemStatus } from '../common/eNums/itemStatus.enum';
|
||||||
|
|
||||||
|
// OBJ *******************
|
||||||
|
|
||||||
|
export function faqObj(faq) {
|
||||||
|
let faqCatTitle = null;
|
||||||
|
if (faq.FaqCategory) {
|
||||||
|
faqCatTitle = faq.FaqCategory.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: faq.id,
|
||||||
|
status: faq.status,
|
||||||
|
faqCat: faq.faqCat,
|
||||||
|
faqCatTitle,
|
||||||
|
question: faq.question,
|
||||||
|
answer: faq.answer,
|
||||||
|
createdAt: faq.createdAt,
|
||||||
|
updatedAt: faq.updatedAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLASS *******************
|
||||||
|
|
||||||
|
export class FaqDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
faqCat: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
faqCatTitle: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
question: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
answer: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
export class CreateFaqDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
faqCat: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
question: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
answer: string;
|
||||||
|
}
|
||||||
|
export class UpdateFaqDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
faqCat: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
question: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
answer: string;
|
||||||
|
}
|
||||||
|
export class UpdateStatusFaqDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
export class FilterFaqDto {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
@IsMongoId({ message: 'آیدی دسته بندی نامعتبر است.' })
|
||||||
|
faqCat: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ParamsPositionDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
fId: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
tId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsEnum, IsMongoId, IsNotEmpty, IsOptional } from 'class-validator';
|
||||||
|
import { FaqDto, faqObj } from './faq.dto';
|
||||||
|
import { ItemStatus } from '../common/eNums/itemStatus.enum';
|
||||||
|
|
||||||
|
// OBJ *******************
|
||||||
|
|
||||||
|
export function faqCategoryObj(faqCategory) {
|
||||||
|
let Faqs = [];
|
||||||
|
if (faqCategory.Faqs && faqCategory.Faqs.length !== 0) {
|
||||||
|
Faqs = faqCategory.Faqs.map((faq) => faqObj(faq));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: faqCategory.id,
|
||||||
|
title: faqCategory.title,
|
||||||
|
status: faqCategory.status,
|
||||||
|
description: faqCategory.description,
|
||||||
|
Faqs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLASS *******************
|
||||||
|
|
||||||
|
export class FaqCategoryDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@ApiProperty({ isArray: true, type: FaqDto })
|
||||||
|
Faqs: FaqDto[];
|
||||||
|
}
|
||||||
|
export class CreateFaqCategoryDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
export class UpdateFaqCategoryDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
export class FilterFaqCategoryDto {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { IsNumber, IsOptional } from "class-validator";
|
||||||
|
|
||||||
|
export class PaginationDto {
|
||||||
|
@IsNumber()
|
||||||
|
@IsOptional()
|
||||||
|
page: number;
|
||||||
|
|
||||||
|
@IsNumber()
|
||||||
|
@IsOptional()
|
||||||
|
pageSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsNotEmpty, IsNumber, Max, Min } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateRateDto {
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsNotEmpty({message:"امتیاز نمیتواند خالی باشد"})
|
||||||
|
@IsNumber()
|
||||||
|
@Min(0,{message:"امتیاز باید بیشتر از 0 باشد"})
|
||||||
|
@Max(5,{message:"امتیاز باید کمتر از 5 باشد"})
|
||||||
|
rating: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsOptional } from 'class-validator';
|
||||||
|
|
||||||
|
export class CreateDto {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
about: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
student: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
hours: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
title: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
expertise: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
nameCompany: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
mission: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
start: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
goals: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
rate: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
titleText: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
header: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsNotEmpty } from "class-validator";
|
||||||
|
|
||||||
|
export class CreateTicketDto {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'موضوع تیکت اجباری میباشد' })
|
||||||
|
subject: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateTicketMessageDto {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'متن تیکت اجباری میباشد' })
|
||||||
|
content: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'آیدی تیکت اجباری میباشد' })
|
||||||
|
ticketId: string;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsNotEmpty } from 'class-validator';
|
||||||
|
|
||||||
|
export class UploaderDTO {
|
||||||
|
@ApiProperty({ type: 'string', format: 'binary' })
|
||||||
|
file: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: 'string' })
|
||||||
|
@IsNotEmpty({ message: 'نام فولدر الزامی است' })
|
||||||
|
directoryName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GetFileDTO {
|
||||||
|
@ApiProperty({ type: 'string' })
|
||||||
|
@IsNotEmpty({ message: 'نام فایل الزامی است' })
|
||||||
|
fileName: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
IsEnum,
|
||||||
|
IsMobilePhone,
|
||||||
|
IsMongoId,
|
||||||
|
IsNotEmpty,
|
||||||
|
IsNumberString,
|
||||||
|
IsOptional,
|
||||||
|
} from 'class-validator';
|
||||||
|
import itemStatuses, { ItemStatus } from '../common/eNums/itemStatus.enum';
|
||||||
|
import role, { Role } from '../common/eNums/role.enum';
|
||||||
|
import { MidCourseDto, MidCourseDtoObj } from './course.dto';
|
||||||
|
|
||||||
|
export function userMidObj(user) {
|
||||||
|
return {
|
||||||
|
id: user.id,
|
||||||
|
phone: user.phone,
|
||||||
|
token: user.jwtToken,
|
||||||
|
email: user.email,
|
||||||
|
userName: user.userName,
|
||||||
|
name: user.name,
|
||||||
|
avatarMedia: user.avatarMedia,
|
||||||
|
roles: user.roles,
|
||||||
|
rolesText: role[user.roles],
|
||||||
|
status: user.status,
|
||||||
|
statusText: itemStatuses[user.status],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function userObj(user) {
|
||||||
|
let course = [];
|
||||||
|
if (user.courseIds && user.courseIds !== 0) {
|
||||||
|
course = user.courseIds.map((item) => MidCourseDtoObj(item));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: user.id,
|
||||||
|
phone: user.phone,
|
||||||
|
avatarMedia: user.avatarMedia,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
userName: user.userName,
|
||||||
|
lastName: user.lastName,
|
||||||
|
cityId: user.cityId,
|
||||||
|
tel: user.tel,
|
||||||
|
address: user.address,
|
||||||
|
description: user.description,
|
||||||
|
gender: user.gender,
|
||||||
|
roles: user.roles,
|
||||||
|
rolesText: role[user.roles],
|
||||||
|
course,
|
||||||
|
status: user.status,
|
||||||
|
createdAt: user.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LoginDto {
|
||||||
|
@IsNumberString()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'شماره موبایل اجباری می باشد' })
|
||||||
|
phone: any;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'نوع تماس اجباری می باشد' })
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LoginStepTwoDto {
|
||||||
|
@IsNumberString()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'شماره موبایل اجباری می باشد' })
|
||||||
|
phone: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: 'کد تایید اجباری می باشد' })
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
code: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LoginRes {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserMidDto {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
phone: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
token: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
statusText: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserDto {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
phone: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
avatarMedia: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
userName: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
lastName: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
cityId: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
tel: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
address: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
gender: string;
|
||||||
|
|
||||||
|
@IsEnum(Role)
|
||||||
|
@ApiProperty({ type: String, enum: Role })
|
||||||
|
roles: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
rolesText: string;
|
||||||
|
|
||||||
|
@ApiProperty({ isArray: true, type: MidCourseDto })
|
||||||
|
course: MidCourseDto[];
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryUserList {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
page: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String, required: false })
|
||||||
|
pageSize: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserStatus {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
userId: string;
|
||||||
|
|
||||||
|
@IsEnum(ItemStatus)
|
||||||
|
@ApiProperty({ type: String, enum: ItemStatus })
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserUpdateDto {
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
avatarMedia: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
lastName: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
userName: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RegisterCourseDto {
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsMongoId({ message: 'شناسه دوره اجباری می باشد' })
|
||||||
|
@IsNotEmpty({ message: 'شناسه دوره اجباری می باشد' })
|
||||||
|
courseId: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddBalanceDto {
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
@IsNotEmpty({ message: 'مبلغ اجباری می باشد' })
|
||||||
|
amount: number;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'شماره پرداخت اجباری می باشد' })
|
||||||
|
paymentNumber: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
@IsNotEmpty({ message: 'دلیل اجباری می باشد' })
|
||||||
|
reason: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsMongoId, IsNotEmpty } from 'class-validator';
|
||||||
|
import {
|
||||||
|
DetailsAdminDiscountDto,
|
||||||
|
detailsAdminDiscountObj,
|
||||||
|
} from './discount.dto';
|
||||||
|
import { UserMidDto, userMidObj } from './user.dto';
|
||||||
|
|
||||||
|
// OBJ *******************
|
||||||
|
|
||||||
|
export function listUserDiscountObj(userDiscount) {
|
||||||
|
const user = userMidObj(userDiscount.user);
|
||||||
|
const discount = detailsAdminDiscountObj(userDiscount.discount);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: userDiscount.id,
|
||||||
|
user,
|
||||||
|
discount,
|
||||||
|
updatedAt: userDiscount.updatedAt,
|
||||||
|
createdAt: userDiscount.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLASS *******************
|
||||||
|
|
||||||
|
export class ListUserDiscountDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Number })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: UserMidDto })
|
||||||
|
user: UserMidDto;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: DetailsAdminDiscountDto })
|
||||||
|
discount: DetailsAdminDiscountDto;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: Date })
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateUserDiscountDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
user: string;
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsMongoId({ message: 'آیدی نامعتبر است.' })
|
||||||
|
@ApiProperty({ type: String })
|
||||||
|
discount: string;
|
||||||
|
}
|
||||||
+52
-20
@@ -1,20 +1,52 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { AdminModule } from './application/admin/admin.module';
|
||||||
import { ThrottlerModule } from '@nestjs/throttler';
|
import { CommonModule } from './application/common/common.module';
|
||||||
|
import { TicketModule } from './application/ticket/ticket.module';
|
||||||
@Module({
|
import { UserModule } from './application/user/user.module';
|
||||||
imports: [
|
import { AuthModule } from './auth/auth.module';
|
||||||
// ServeStaticModule.forRoot({
|
import { Module } from '@nestjs/common';
|
||||||
// rootPat h: join(__dirname, '..', 'public'),
|
import { ThrottlerModule } from '@nestjs/throttler';
|
||||||
// }),
|
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||||
// ConfigModule.forRoot({
|
import { UploaderService } from './application/common/uploader/uploader.service';
|
||||||
// isGlobal: true,
|
import { CourseModule } from './application/course/course.module';
|
||||||
// }),
|
import { UploaderModule } from './application/common/uploader/uploader.module';
|
||||||
ThrottlerModule.forRoot({
|
import { CourseHeadlineModule } from './application/courseHeadline/courseHeadline.module';
|
||||||
ttl: 60,
|
import { ConfigModule } from '@nestjs/config';
|
||||||
limit: 10,
|
import { CourseCategoryModule } from './application/courseCategory/courseCategory.module';
|
||||||
}),
|
|
||||||
],
|
@Module({
|
||||||
controllers: [],
|
imports: [
|
||||||
providers: [],
|
AdminModule,
|
||||||
})
|
// EventEmitterModule.forRoot({
|
||||||
export class AppModule {}
|
// wildcard: false,
|
||||||
|
// delimiter: '.',
|
||||||
|
// newListener: false,
|
||||||
|
// removeListener: false,
|
||||||
|
// maxListeners: 10,
|
||||||
|
// verboseMemoryLeak: false,
|
||||||
|
// ignoreErrors: false,
|
||||||
|
// }),
|
||||||
|
EventEmitterModule.forRoot(),
|
||||||
|
CommonModule,
|
||||||
|
AuthModule,
|
||||||
|
UserModule,
|
||||||
|
TicketModule,
|
||||||
|
CourseModule,
|
||||||
|
UploaderModule,
|
||||||
|
CourseHeadlineModule,
|
||||||
|
CourseCategoryModule,
|
||||||
|
// ServeStaticModule.forRoot({
|
||||||
|
// rootPat h: join(__dirname, '..', 'public'),
|
||||||
|
// }),
|
||||||
|
ConfigModule.forRoot({
|
||||||
|
isGlobal: true,
|
||||||
|
}),
|
||||||
|
ThrottlerModule.forRoot({
|
||||||
|
ttl: 60,
|
||||||
|
limit: 10,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
controllers: [],
|
||||||
|
providers: [UploaderService],
|
||||||
|
exports: [UploaderService],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Controller } from '@nestjs/common';
|
||||||
|
import { AdminService } from './admin.service';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
@ApiTags('admin')
|
||||||
|
@Controller('admin')
|
||||||
|
export class AdminController {
|
||||||
|
constructor(private readonly adminService: AdminService) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AdminService } from './admin.service';
|
||||||
|
import { AdminController } from './admin.controller';
|
||||||
|
import { UserModule } from './user/user.module';
|
||||||
|
import { BlogModule } from './blog/blog.module';
|
||||||
|
import { FaqModule } from './faq/faq.module';
|
||||||
|
import { DiscountModule } from './discount/discount.module';
|
||||||
|
import { SettingsModule } from './settings/settings.module';
|
||||||
|
@Module({
|
||||||
|
imports: [UserModule, SettingsModule, BlogModule, FaqModule, DiscountModule],
|
||||||
|
controllers: [AdminController],
|
||||||
|
providers: [AdminService],
|
||||||
|
})
|
||||||
|
export class AdminModule {}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AdminService {}
|
||||||
@@ -0,0 +1,400 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Post,
|
||||||
|
HttpStatus,
|
||||||
|
HttpCode,
|
||||||
|
Get,
|
||||||
|
Put,
|
||||||
|
Query,
|
||||||
|
Patch,
|
||||||
|
Param,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApiTags,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiBody,
|
||||||
|
ApiOperation,
|
||||||
|
ApiBearerAuth,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
BlogCatDto,
|
||||||
|
CreateBlogCatDto,
|
||||||
|
UpdateBlogCatDto,
|
||||||
|
StatusBlogCatDto,
|
||||||
|
BlogCatListDto,
|
||||||
|
getParentListBlogCatDto,
|
||||||
|
} from '../../..//DTO/blogCategory.dto';
|
||||||
|
|
||||||
|
import {
|
||||||
|
PostDto,
|
||||||
|
CreatePostDto,
|
||||||
|
UpdatePostDto,
|
||||||
|
PostStatusDto,
|
||||||
|
ListPostDto,
|
||||||
|
FilterListPostDto,
|
||||||
|
ChangeSortPostDto,
|
||||||
|
} from '../../../DTO/blogPost.dto';
|
||||||
|
|
||||||
|
// import { DeletePostMediaDto } from '../../../DTO/postMedia.dto';
|
||||||
|
import {
|
||||||
|
StatusPostCommentDto,
|
||||||
|
ListPostCommentDto,
|
||||||
|
PostCommentDto,
|
||||||
|
CreatePostCommentDto,
|
||||||
|
FilterListPostCommentDto,
|
||||||
|
PostCommentInfoDto,
|
||||||
|
} from '../../../DTO/blogPostComment.dto';
|
||||||
|
import { BlogService } from './blog.service';
|
||||||
|
import { Roles } from '../../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../../common/eNums/role.enum';
|
||||||
|
import { AuthGuard } from '../../../auth/auth.guard';
|
||||||
|
import { CurrentUser } from '../../../decorators/currentUser.decorator';
|
||||||
|
|
||||||
|
@ApiTags('admin blog')
|
||||||
|
@Controller('admins/blog')
|
||||||
|
export class BlogController {
|
||||||
|
constructor(private readonly blogService: BlogService) {}
|
||||||
|
// create new post **************************************************************************
|
||||||
|
@ApiOperation({ summary: ' ساخت پست برای بلاگ ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'PostDto info',
|
||||||
|
type: [PostDto],
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreatePostDto,
|
||||||
|
description: 'create new post',
|
||||||
|
})
|
||||||
|
@Post('post')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async createPost(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() createPostDto: CreatePostDto,
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this.blogService.createPost(createPostDto, user);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update post ******************************************************************************
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@ApiOperation({ summary: ' بروز رسانی پست ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'update post',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: UpdatePostDto,
|
||||||
|
description: 'update post DTO',
|
||||||
|
})
|
||||||
|
@Put('post')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async updatePost(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() updatePostDto: UpdatePostDto,
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
await this.blogService.updatePost(updatePostDto, user);
|
||||||
|
return { status: true };
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// change status of post ********************************************************************
|
||||||
|
@ApiOperation({ summary: 'تغییر وضعیت پست بلاگ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'api status',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: PostStatusDto,
|
||||||
|
description: 'change Status of post',
|
||||||
|
})
|
||||||
|
@Patch('post')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async changeStatusPost(@Body() postStatusDto: PostStatusDto): Promise<any> {
|
||||||
|
try {
|
||||||
|
await this.blogService.changeStatusPost(postStatusDto);
|
||||||
|
return { status: true };
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Filter / list post ***********************************************************************
|
||||||
|
@ApiOperation({ summary: ' فیلتر و لیست پست های بلاگ' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'posts list',
|
||||||
|
type: [ListPostDto],
|
||||||
|
})
|
||||||
|
@Get('post')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async ListPosts(
|
||||||
|
@Query() filterListPostDto: FilterListPostDto,
|
||||||
|
): Promise<ListPostDto[]> {
|
||||||
|
const blogService = await this.blogService.listPosts(filterListPostDto);
|
||||||
|
return blogService;
|
||||||
|
}
|
||||||
|
// get detail post by id *****************************************************************************
|
||||||
|
@ApiOperation({ summary: 'جزییات کامل پست بلاگ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'post detail info',
|
||||||
|
type: PostDto,
|
||||||
|
})
|
||||||
|
@Get('post/:postId')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async postDetail(
|
||||||
|
@Param('postId')
|
||||||
|
postId: string,
|
||||||
|
): Promise<PostDto> {
|
||||||
|
try {
|
||||||
|
const postDetail = await this.blogService.postDetail(postId);
|
||||||
|
return postDetail;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// change sort post ************************************************************************
|
||||||
|
@ApiOperation({ summary: 'تغییر ترتیب پست' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'change sort post',
|
||||||
|
type: [ListPostDto],
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: ChangeSortPostDto,
|
||||||
|
description: 'change sort post',
|
||||||
|
})
|
||||||
|
@Put('post/sort')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async changeSortPost(
|
||||||
|
@Body() changeSortPostDto: ChangeSortPostDto,
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this.blogService.changeSortPost(changeSortPostDto);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create new blogCat ***********************************************************************
|
||||||
|
@ApiOperation({ summary: ' ساخت دسته بندی بلاگ ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'blog Category Dto info',
|
||||||
|
type: [BlogCatDto],
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateBlogCatDto,
|
||||||
|
description: 'create new blog category',
|
||||||
|
})
|
||||||
|
@Post('category')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async createCategory(
|
||||||
|
@Body() createBlogCatDto: CreateBlogCatDto,
|
||||||
|
): Promise<BlogCatDto> {
|
||||||
|
try {
|
||||||
|
const category = await this.blogService.createCategory(createBlogCatDto);
|
||||||
|
return category;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// list blogCat ***********************************************************************
|
||||||
|
@ApiOperation({ summary: ' لیست دسته بندی های بلاگ ' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'blog category list',
|
||||||
|
type: [BlogCatListDto],
|
||||||
|
})
|
||||||
|
@Get('category')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async blogCatList(
|
||||||
|
@Query() getParentForListDto: getParentListBlogCatDto,
|
||||||
|
): Promise<BlogCatListDto[]> {
|
||||||
|
try {
|
||||||
|
const categories = await this.blogService.blogCatList(
|
||||||
|
getParentForListDto,
|
||||||
|
);
|
||||||
|
return categories;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update blogCat **********************************************************************
|
||||||
|
@ApiOperation({ summary: ' بروز رسانی دسته بندی بلاگ ' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'update blog category',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: UpdateBlogCatDto,
|
||||||
|
description: 'update blog category',
|
||||||
|
})
|
||||||
|
@Put('category')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async updateBlogCategory(
|
||||||
|
@Body() updateBlogCategory: UpdateBlogCatDto,
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
await this.blogService.updateBlogCategory(updateBlogCategory);
|
||||||
|
return { status: true };
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//change status of blog Category **********************************************************************
|
||||||
|
@ApiOperation({ summary: ' وضعیت دسته بندی بلاگ ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'api status',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: StatusBlogCatDto,
|
||||||
|
description: 'change Status of blog category',
|
||||||
|
})
|
||||||
|
@Patch('category')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async changeStatusCategory(
|
||||||
|
@Body() statusBlogCatDto: StatusBlogCatDto,
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
await this.blogService.changeStatusCategory(statusBlogCatDto);
|
||||||
|
return { status: true };
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Filter/ list post comment ********************************************************************
|
||||||
|
@ApiOperation({ summary: ' لیست پست ها به همراه فیلتر ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'postComments List',
|
||||||
|
type: [ListPostCommentDto],
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Get('postComment')
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async listPostComment(
|
||||||
|
@Query() filterListPostCommentDto: FilterListPostCommentDto,
|
||||||
|
): Promise<ListPostCommentDto[]> {
|
||||||
|
const list = await this.blogService.listPostComments(
|
||||||
|
filterListPostCommentDto,
|
||||||
|
);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
//get post comment detail by id ****************************************************************
|
||||||
|
@ApiOperation({ summary: 'detail PostComment' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'post comment detail info',
|
||||||
|
type: PostCommentInfoDto,
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Get('postComment/:id')
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async postCommentDetail(
|
||||||
|
@Param('id')
|
||||||
|
id: string,
|
||||||
|
): Promise<PostCommentInfoDto> {
|
||||||
|
try {
|
||||||
|
const postCommentDetail = await this.blogService.postCommentDetail(id);
|
||||||
|
return postCommentDetail;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// changeStatus postComment *********************************************************************
|
||||||
|
@ApiOperation({ summary: 'change status PostComment' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'api status',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: StatusPostCommentDto,
|
||||||
|
description: 'change status of postComment',
|
||||||
|
})
|
||||||
|
@Patch('postComment')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async changeStatusPostComment(
|
||||||
|
@Body() statusPostCommentDto: StatusPostCommentDto,
|
||||||
|
): Promise<any> {
|
||||||
|
try {
|
||||||
|
await this.blogService.changeStatusPostComment(statusPostCommentDto);
|
||||||
|
return { status: true };
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reply post comment *****************************************************
|
||||||
|
@ApiOperation({ summary: 'reply PostComment' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'PostComment Dto',
|
||||||
|
type: [PostCommentDto],
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreatePostCommentDto,
|
||||||
|
description: 'reply post comment',
|
||||||
|
})
|
||||||
|
@Post('postComment/reply/:id')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async replyPostComment(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() createPostCommentDto: CreatePostCommentDto,
|
||||||
|
@Param('id')
|
||||||
|
id: string,
|
||||||
|
): Promise<PostCommentDto> {
|
||||||
|
try {
|
||||||
|
const result = await this.blogService.replyPostComment(
|
||||||
|
createPostCommentDto,
|
||||||
|
id,
|
||||||
|
user,
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { BlogController } from './blog.controller';
|
||||||
|
import { BlogService } from './blog.service';
|
||||||
|
import { BlogPostDataAccess } from '../../../dataAccess/blogPost.dataAccess';
|
||||||
|
import { CategoryDataAccess } from '../../../dataAccess/blogCategory.dataAccess';
|
||||||
|
import { BlogPostCommentDataAccess } from '../../../dataAccess/blogPostComment.dataAccess';
|
||||||
|
import { Tools } from '../../../common/helpers/tools.helpers';
|
||||||
|
import { UserDataAccess } from '../../../dataAccess/user.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [BlogController],
|
||||||
|
providers: [
|
||||||
|
BlogService,
|
||||||
|
CategoryDataAccess,
|
||||||
|
BlogPostDataAccess,
|
||||||
|
BlogPostCommentDataAccess,
|
||||||
|
Tools,
|
||||||
|
UserDataAccess,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class BlogModule {}
|
||||||
@@ -0,0 +1,476 @@
|
|||||||
|
/* eslint-disable object-curly-newline */
|
||||||
|
/* eslint-disable prefer-const */
|
||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import bluebirdPromise = require('bluebird');
|
||||||
|
import { CategoryDataAccess } from '../../../dataAccess/blogCategory.dataAccess';
|
||||||
|
import { BlogPostDataAccess } from '../../../dataAccess/blogPost.dataAccess';
|
||||||
|
import { BlogPostCommentDataAccess } from '../../../dataAccess/blogPostComment.dataAccess';
|
||||||
|
import { Tools } from '../../../common/helpers/tools.helpers';
|
||||||
|
import { blogCatArrObjDto, blogCatObjDto } from '../../../DTO/blogCategory.dto';
|
||||||
|
import {
|
||||||
|
postObjDto,
|
||||||
|
postInfoObjDto,
|
||||||
|
ListPostDto,
|
||||||
|
} from '../../../DTO/blogPost.dto';
|
||||||
|
import { ItemStatus } from '../../../common/eNums/itemStatus.enum';
|
||||||
|
import {
|
||||||
|
postCommentArrObjDto,
|
||||||
|
postCommentObj,
|
||||||
|
postCommentInfoObj,
|
||||||
|
PostCommentInfoDto,
|
||||||
|
PostCommentDto,
|
||||||
|
} from '../../../DTO/blogPostComment.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BlogService {
|
||||||
|
constructor(
|
||||||
|
private readonly categoryDataAccess: CategoryDataAccess,
|
||||||
|
private readonly blogPostDataAccess: BlogPostDataAccess,
|
||||||
|
private readonly postCommentDataAccess: BlogPostCommentDataAccess,
|
||||||
|
private readonly tools: Tools,
|
||||||
|
) {}
|
||||||
|
// create post ******************************************************************
|
||||||
|
async createPost(createPostDto, admin) {
|
||||||
|
const {
|
||||||
|
blogCatId,
|
||||||
|
mediaUrl,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
text,
|
||||||
|
mediaAlt,
|
||||||
|
metaTitle,
|
||||||
|
metaDescription,
|
||||||
|
metaKeywords,
|
||||||
|
isInLanding,
|
||||||
|
} = createPostDto;
|
||||||
|
const blogCat = await this.categoryDataAccess.findById(blogCatId);
|
||||||
|
if (!blogCat) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'دسته بندی یافت نشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// if (blogCat.parents.length !== 0) {
|
||||||
|
// throw new HttpException(
|
||||||
|
// {
|
||||||
|
// status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
// error: 'دسته بندی ریشه نیست و قابل ثبت نمی باشد',
|
||||||
|
// },
|
||||||
|
// HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
const slug = this.tools.convertToSlug(title);
|
||||||
|
const checkPost = await this.blogPostDataAccess.checkDuplicate(slug);
|
||||||
|
if (checkPost) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.CONFLICT,
|
||||||
|
error: 'این آدرس قبلا رزرو شده است',
|
||||||
|
},
|
||||||
|
HttpStatus.CONFLICT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// const media = await this.mediaDataAccess.findById(mediaUrl);
|
||||||
|
// if (!media) {
|
||||||
|
// throw new HttpException(
|
||||||
|
// {
|
||||||
|
// status: HttpStatus.NOT_FOUND,
|
||||||
|
// error: 'مدیا یافت نشد',
|
||||||
|
// },
|
||||||
|
// HttpStatus.NOT_FOUND,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
const length = await this.blogPostDataAccess.findAllLength();
|
||||||
|
const post = await this.blogPostDataAccess.createBlogPostRequest(
|
||||||
|
admin.id,
|
||||||
|
blogCatId,
|
||||||
|
mediaUrl,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
slug,
|
||||||
|
text,
|
||||||
|
length,
|
||||||
|
mediaAlt,
|
||||||
|
metaTitle,
|
||||||
|
metaDescription,
|
||||||
|
metaKeywords,
|
||||||
|
isInLanding,
|
||||||
|
);
|
||||||
|
await this.blogPostDataAccess.updateSortBlogPost(post._id);
|
||||||
|
blogCat.Posts.push(post._id ? post._id : post.id);
|
||||||
|
await blogCat.save();
|
||||||
|
return postObjDto(await this.blogPostDataAccess.findById(post._id));
|
||||||
|
}
|
||||||
|
// update post *********************************************************************
|
||||||
|
async updatePost(updatePostDto, admin) {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
mediaUrl,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
text,
|
||||||
|
writerId,
|
||||||
|
mediaAlt,
|
||||||
|
metaTitle,
|
||||||
|
metaDescription,
|
||||||
|
metaKeywords,
|
||||||
|
isInLanding,
|
||||||
|
} = updatePostDto;
|
||||||
|
let { blogCat } = updatePostDto;
|
||||||
|
let blogCatOne;
|
||||||
|
const post = await this.blogPostDataAccess.findById(id);
|
||||||
|
if (!post) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'POST_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (blogCat) {
|
||||||
|
blogCatOne = await this.categoryDataAccess.findById(blogCat);
|
||||||
|
if (!blogCatOne) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'BLOG_CATEGORY_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// if (blogCatOne.parents.length !== 0) {
|
||||||
|
// throw new HttpException(
|
||||||
|
// {
|
||||||
|
// status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
// error: 'BLOG_CATEGORY_NOT_ACCEPTABLE',
|
||||||
|
// },
|
||||||
|
// HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
blogCat = post.blogCat;
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkTitle = await this.blogPostDataAccess.checkTitle(title);
|
||||||
|
if (checkTitle && title !== post.title) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.CONFLICT,
|
||||||
|
error: 'TITLE_CONFLICT',
|
||||||
|
},
|
||||||
|
HttpStatus.CONFLICT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (post.blogCat._id.toString() !== blogCat) {
|
||||||
|
blogCatOne.Posts.push(post._id);
|
||||||
|
await blogCatOne.save();
|
||||||
|
await this.categoryDataAccess.pullCate(post.blogCat._id, post._id);
|
||||||
|
}
|
||||||
|
await this.blogPostDataAccess.updateBlogPost(
|
||||||
|
id,
|
||||||
|
admin.id,
|
||||||
|
blogCat,
|
||||||
|
mediaUrl,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
text,
|
||||||
|
writerId,
|
||||||
|
mediaAlt,
|
||||||
|
metaTitle,
|
||||||
|
metaDescription,
|
||||||
|
metaKeywords,
|
||||||
|
isInLanding,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// post detail ***********************************************************************
|
||||||
|
async postDetail(postId) {
|
||||||
|
const postDetail = await this.blogPostDataAccess.findByIdDetails(postId);
|
||||||
|
if (postDetail) {
|
||||||
|
postDetail.Comment = [];
|
||||||
|
return postInfoObjDto(postDetail);
|
||||||
|
} else {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'POST_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// change status post ********************************************************************
|
||||||
|
async changeStatusPost(postStatusDto) {
|
||||||
|
const { id, status } = postStatusDto;
|
||||||
|
const post = await this.blogPostDataAccess.findById(id);
|
||||||
|
if (!post) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'پست مورد نظر وجود ندارد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.blogPostDataAccess.changeStatusBlogPost(id, status);
|
||||||
|
}
|
||||||
|
// list posts ***************************************************************************
|
||||||
|
async listPosts(filterListPostDto): Promise<ListPostDto[]> {
|
||||||
|
const list = await this.blogPostDataAccess.filterListAndFilter(
|
||||||
|
filterListPostDto,
|
||||||
|
);
|
||||||
|
return list.map((item) => postObjDto(item));
|
||||||
|
}
|
||||||
|
//change Sort post *************************************************************************
|
||||||
|
async changeSortPost(changeSortPostDto) {
|
||||||
|
const { fromId, toId } = changeSortPostDto;
|
||||||
|
const from = await this.blogPostDataAccess.findById(fromId);
|
||||||
|
if (!from) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'پست مبدا مورد نظر وجود ندارد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const to = await this.blogPostDataAccess.findById(toId);
|
||||||
|
if (!to) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'پست مقصد مورد نظر وجود ندارد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.blogPostDataAccess.changeSortBlogPost(from, to);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// create category *************************************************************************
|
||||||
|
async createCategory(createBlogCatDto) {
|
||||||
|
let { parentId } = createBlogCatDto;
|
||||||
|
let parent;
|
||||||
|
const { title, description } = createBlogCatDto;
|
||||||
|
if (parentId && parentId !== null) {
|
||||||
|
parent = await this.categoryDataAccess.findById(parentId);
|
||||||
|
if (!parent) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'منوی والد پیدا نشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (parent.parentId !== null) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: 'فقط برای منو های اصلی می توانید منو اضافه کنید',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parentId = null;
|
||||||
|
}
|
||||||
|
const checkTitle = await this.categoryDataAccess.checkDuplicate(title);
|
||||||
|
if (checkTitle) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.CONFLICT,
|
||||||
|
error: 'TITLE_CONFLICT',
|
||||||
|
},
|
||||||
|
HttpStatus.CONFLICT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const category = await this.categoryDataAccess.createCategory(
|
||||||
|
parentId,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
);
|
||||||
|
if (parentId && parentId !== null) {
|
||||||
|
parent.parents.push(category._id);
|
||||||
|
await parent.save();
|
||||||
|
}
|
||||||
|
return blogCatObjDto(category);
|
||||||
|
}
|
||||||
|
// list category ****************************************************************************
|
||||||
|
async blogCatList(getParentForListDto) {
|
||||||
|
const { parentId } = getParentForListDto;
|
||||||
|
|
||||||
|
const categories = await this.categoryDataAccess.findAll(parentId);
|
||||||
|
|
||||||
|
const categoryList = await bluebirdPromise.map(
|
||||||
|
categories,
|
||||||
|
async (category) => {
|
||||||
|
return blogCatArrObjDto(category);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return categoryList;
|
||||||
|
}
|
||||||
|
// update category *************************************************************************
|
||||||
|
async updateBlogCategory(updateBlogCategory): Promise<boolean> {
|
||||||
|
let { parentId } = updateBlogCategory;
|
||||||
|
const { id, title, description } = updateBlogCategory;
|
||||||
|
let parent;
|
||||||
|
const category = await this.categoryDataAccess.findById(id);
|
||||||
|
if (!category) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'CATEGORY_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (parentId && parentId !== null && parentId !== id) {
|
||||||
|
parent = await this.categoryDataAccess.findById(parentId);
|
||||||
|
if (!parent) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'منوی اصلی پیدا نشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (parent.parentId !== null) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: 'فقط برای منو های اصلی می توانید منو اضافه کنید',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// if (parent.Posts.length !== 0) {
|
||||||
|
// throw new HttpException(
|
||||||
|
// {
|
||||||
|
// status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
// error: 'قابل ثبت نمی باشد منوی مورد نظر دارای پست می باشد',
|
||||||
|
// },
|
||||||
|
// HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
parentId = null;
|
||||||
|
}
|
||||||
|
const checkTitle = await this.categoryDataAccess.checkDuplicate(title);
|
||||||
|
if (checkTitle && title !== category.title) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.CONFLICT,
|
||||||
|
error: 'این عنوان وجود دارد ',
|
||||||
|
},
|
||||||
|
HttpStatus.CONFLICT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
parentId &&
|
||||||
|
parentId !== null &&
|
||||||
|
category.parentId._id.toString() !== parentId
|
||||||
|
) {
|
||||||
|
parent.parents.push(category._id);
|
||||||
|
await parent.save();
|
||||||
|
await this.categoryDataAccess.pullCateParents(
|
||||||
|
category.parentId._id,
|
||||||
|
category._id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.categoryDataAccess.updateBlogCategory(
|
||||||
|
id,
|
||||||
|
parentId,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// change status category *************************************************************************
|
||||||
|
async changeStatusCategory(statusBlogCatDto): Promise<boolean> {
|
||||||
|
const { id, status } = statusBlogCatDto;
|
||||||
|
const category = await this.categoryDataAccess.findById(id);
|
||||||
|
if (!category) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'CATEGORY_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.categoryDataAccess.changeStatusCategory(id, status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//list post comment *****************************************************************************
|
||||||
|
async listPostComments(filterListPostCommentDto) {
|
||||||
|
const list = await this.postCommentDataAccess.listAndFilter(
|
||||||
|
filterListPostCommentDto,
|
||||||
|
);
|
||||||
|
return list.map((item) => postCommentArrObjDto(item));
|
||||||
|
}
|
||||||
|
// change status post comment ********************************************************************
|
||||||
|
async changeStatusPostComment(statusPostCommentDto): Promise<boolean> {
|
||||||
|
const { id, status } = statusPostCommentDto;
|
||||||
|
const postComment = await this.postCommentDataAccess.findById(id);
|
||||||
|
if (!postComment) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'POST_COMMENT_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.postCommentDataAccess.changeStatus(id, status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// get detail post comment by id *****************************************************************
|
||||||
|
async postCommentDetail(id: string): Promise<PostCommentInfoDto> {
|
||||||
|
const postComment = await this.postCommentDataAccess.findById(id);
|
||||||
|
if (!postComment) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'POST_COMMENT-NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return postCommentInfoObj(postComment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reply post Comment ****************************************************************************
|
||||||
|
async replyPostComment(
|
||||||
|
createPostCommentDto,
|
||||||
|
id,
|
||||||
|
admin,
|
||||||
|
): Promise<PostCommentDto> {
|
||||||
|
const postComment = await this.postCommentDataAccess.findById(id);
|
||||||
|
if (!postComment || postComment.status !== ItemStatus.Active) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'COMMENT_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const { text } = createPostCommentDto;
|
||||||
|
const replyPostComment = await this.postCommentDataAccess.replyComment(
|
||||||
|
postComment.blogPost.id,
|
||||||
|
id,
|
||||||
|
admin.id,
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
postComment.Replies.push(replyPostComment.id);
|
||||||
|
postComment.save();
|
||||||
|
return postCommentObj(replyPostComment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
// NEST
|
||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Post,
|
||||||
|
HttpStatus,
|
||||||
|
Get,
|
||||||
|
Res,
|
||||||
|
Param,
|
||||||
|
Put,
|
||||||
|
Patch,
|
||||||
|
HttpCode,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Response } from 'express';
|
||||||
|
// SWAGGER
|
||||||
|
import {
|
||||||
|
ApiTags,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiBody,
|
||||||
|
ApiOperation,
|
||||||
|
ApiBearerAuth,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { DiscountService } from './discount.service';
|
||||||
|
|
||||||
|
// DTO
|
||||||
|
import {
|
||||||
|
CreateDiscountDto,
|
||||||
|
UpdateDiscountDto,
|
||||||
|
UpdatedStatusDiscountDto,
|
||||||
|
ListDiscountDto,
|
||||||
|
DetailsAdminDiscountDto,
|
||||||
|
IdDiscountDto,
|
||||||
|
} from '../../../DTO/discount.dto';
|
||||||
|
import { AuthGuard } from '../../../auth/auth.guard';
|
||||||
|
import { Roles } from '../../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../../common/eNums/role.enum';
|
||||||
|
import { CurrentUser } from '../../../decorators/currentUser.decorator';
|
||||||
|
|
||||||
|
@ApiTags('admin discounts')
|
||||||
|
@Controller('admins/discount')
|
||||||
|
export class DiscountController {
|
||||||
|
constructor(private readonly discountService: DiscountService) {}
|
||||||
|
// CREATE DISCOUNT ****************************
|
||||||
|
@Post('')
|
||||||
|
@ApiOperation({ summary: 'create discount' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiBody({
|
||||||
|
description: 'create discount',
|
||||||
|
type: CreateDiscountDto,
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'response created discount',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
async createDiscount(
|
||||||
|
@Body() created: CreateDiscountDto,
|
||||||
|
@CurrentUser() user,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
await this.discountService.createDiscount(created, user.id);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// UPDATE DISCOUNT ****************************
|
||||||
|
@Put('')
|
||||||
|
@ApiOperation({ summary: 'updated discount' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiBody({
|
||||||
|
description: 'updated discount',
|
||||||
|
type: UpdateDiscountDto,
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'response updated discount',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
async updateDiscount(
|
||||||
|
@Body() updated: UpdateDiscountDto,
|
||||||
|
@CurrentUser() user,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
await this.discountService.updatedDiscount(updated, user.id);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// UPDATE STATUS DISCOUNT ****************************
|
||||||
|
@Patch('/status')
|
||||||
|
@ApiOperation({ summary: 'updated discount' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiBody({
|
||||||
|
description: 'updated status discount',
|
||||||
|
type: UpdatedStatusDiscountDto,
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'response updated status discount',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
async updateStatusDiscount(@Body() updatedStatus: UpdatedStatusDiscountDto) {
|
||||||
|
try {
|
||||||
|
await this.discountService.updatedStatusDiscount(updatedStatus);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LIST DISCOUNT ****************************
|
||||||
|
@Get('')
|
||||||
|
@ApiOperation({ summary: 'list discount' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'response list discount',
|
||||||
|
type: [ListDiscountDto],
|
||||||
|
})
|
||||||
|
async listDiscount(@Res() res: Response) {
|
||||||
|
try {
|
||||||
|
const discounts = await this.discountService.listDiscount();
|
||||||
|
res.json(discounts);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// DETAILS DISCOUNT ****************************
|
||||||
|
@Get('/:id')
|
||||||
|
@ApiOperation({ summary: 'details discount' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'response details discount',
|
||||||
|
type: DetailsAdminDiscountDto,
|
||||||
|
})
|
||||||
|
async detailsDiscount(
|
||||||
|
@Param()
|
||||||
|
id: IdDiscountDto,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const discounts = await this.discountService.detailsDiscount(id);
|
||||||
|
return discounts;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { DiscountController } from './discount.controller';
|
||||||
|
import { DiscountService } from './discount.service';
|
||||||
|
import { DiscountDataAccess } from '../../../dataAccess/discount.dataAccess';
|
||||||
|
import { UserDataAccess } from '../../../dataAccess/user.dataAccess';
|
||||||
|
import { DiscountHistoryModule } from './discountHistory/discountHistory.module';
|
||||||
|
import { UserDiscountModule } from './userDiscount/userDiscount.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [UserDiscountModule, DiscountHistoryModule],
|
||||||
|
controllers: [DiscountController],
|
||||||
|
providers: [DiscountService, DiscountDataAccess, UserDataAccess],
|
||||||
|
})
|
||||||
|
export class DiscountModule {}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { DiscountDataAccess } from '../../../dataAccess/discount.dataAccess';
|
||||||
|
import {
|
||||||
|
DetailsAdminDiscountDto,
|
||||||
|
IdDiscountDto,
|
||||||
|
ListDiscountDto,
|
||||||
|
detailsAdminDiscountObj,
|
||||||
|
listDiscountObj,
|
||||||
|
} from '../../../DTO/discount.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DiscountService {
|
||||||
|
constructor(private readonly discountDataAccess: DiscountDataAccess) {}
|
||||||
|
// CREATE DISCOUNT
|
||||||
|
async createDiscount(createDiscountDto, adminId): Promise<boolean> {
|
||||||
|
const { startDate, endDate, code, discountType, price, percent, allUsers } =
|
||||||
|
createDiscountDto;
|
||||||
|
// validate startDate longer
|
||||||
|
if (startDate > endDate) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' تاریخ پایان نباید از تاریخ شروع کم تر باشد ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.discountDataAccess.create(
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
code,
|
||||||
|
discountType,
|
||||||
|
price,
|
||||||
|
percent,
|
||||||
|
allUsers,
|
||||||
|
adminId,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// UPDATED DISCOUNT
|
||||||
|
async updatedDiscount(updatedDiscountDto, adminId): Promise<boolean> {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
code,
|
||||||
|
discountType,
|
||||||
|
price,
|
||||||
|
percent,
|
||||||
|
allUsers,
|
||||||
|
} = updatedDiscountDto;
|
||||||
|
// validate startDate longer
|
||||||
|
if (startDate > endDate) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' تاریخ پایان نباید از تاریخ شروع کم تر باشد ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// validate exist discount
|
||||||
|
const discount = await this.discountDataAccess.findOne(id);
|
||||||
|
if (!discount) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: ' تخفیف وجود ندارد ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.discountDataAccess.update(
|
||||||
|
id,
|
||||||
|
adminId,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
code,
|
||||||
|
discountType,
|
||||||
|
price,
|
||||||
|
percent,
|
||||||
|
allUsers,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// UPDATED STATUS DISCOUNT
|
||||||
|
async updatedStatusDiscount(updatedStatusDiscountDto): Promise<boolean> {
|
||||||
|
const { id, status } = updatedStatusDiscountDto;
|
||||||
|
// validate exist discount
|
||||||
|
const discount = await this.discountDataAccess.findOne(id);
|
||||||
|
if (!discount) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: ' تخفیف موجود نمی باشد ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// update status discount
|
||||||
|
await this.discountDataAccess.updateStatus(id, status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// LIST DISCOUNT
|
||||||
|
async listDiscount(): Promise<{
|
||||||
|
discount: ListDiscountDto[];
|
||||||
|
count: number;
|
||||||
|
}> {
|
||||||
|
// list discount
|
||||||
|
const discounts = await this.discountDataAccess.findAll();
|
||||||
|
const count = await this.discountDataAccess.count();
|
||||||
|
return {
|
||||||
|
discount: discounts.map((discount) => listDiscountObj(discount)),
|
||||||
|
count: count,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// DETAILS DISCOUNT
|
||||||
|
async detailsDiscount(
|
||||||
|
idDiscountDto: IdDiscountDto,
|
||||||
|
): Promise<DetailsAdminDiscountDto> {
|
||||||
|
const { id } = idDiscountDto;
|
||||||
|
// details discount
|
||||||
|
const discounts = await this.discountDataAccess.findOne(id);
|
||||||
|
if (!discounts) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'تخفیف مورد نظر موجود نمی باشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return detailsAdminDiscountObj(discounts);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// NEST
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
HttpStatus,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
// SWAGGER
|
||||||
|
import {
|
||||||
|
ApiTags,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiBearerAuth,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { DiscountHistoryService } from './discountHistory.service';
|
||||||
|
|
||||||
|
// DTO
|
||||||
|
import { ListDiscountHistoryDto } from '../../../../DTO/discountHistory.dto';
|
||||||
|
import { AuthGuard } from '../../../../auth/auth.guard';
|
||||||
|
import { Roles } from '../../../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../../../common/eNums/role.enum';
|
||||||
|
|
||||||
|
@ApiTags('admin discountHistory')
|
||||||
|
@Controller('admins/discountHistory')
|
||||||
|
export class DiscountHistoryController {
|
||||||
|
constructor(
|
||||||
|
private readonly discountHistoryService: DiscountHistoryService,
|
||||||
|
) {}
|
||||||
|
// LIST DISCOUNT ****************************
|
||||||
|
@Get('')
|
||||||
|
@ApiOperation({ summary: 'list discountHistory' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'response list discountHistory',
|
||||||
|
type: [ListDiscountHistoryDto],
|
||||||
|
})
|
||||||
|
async listDiscountHistory(): Promise<{
|
||||||
|
discount: ListDiscountHistoryDto[];
|
||||||
|
count: number;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
return await this.discountHistoryService.listDiscountHistory();
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { DiscountHistoryController } from './discountHistory.controller';
|
||||||
|
import { DiscountHistoryService } from './discountHistory.service';
|
||||||
|
import { DiscountHistoryDataAccess } from '../../../../dataAccess/discountHistory.dataAccess';
|
||||||
|
import { UserDataAccess } from '../../../../dataAccess/user.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [DiscountHistoryController],
|
||||||
|
providers: [
|
||||||
|
DiscountHistoryService,
|
||||||
|
DiscountHistoryDataAccess,
|
||||||
|
UserDataAccess,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class DiscountHistoryModule {}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { DiscountHistoryDataAccess } from '../../../../dataAccess/discountHistory.dataAccess';
|
||||||
|
import {
|
||||||
|
ListDiscountHistoryDto,
|
||||||
|
listDiscountHistoryObj,
|
||||||
|
} from '../../../../DTO/discountHistory.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DiscountHistoryService {
|
||||||
|
constructor(
|
||||||
|
private readonly discountHistoryDataAccess: DiscountHistoryDataAccess,
|
||||||
|
) {}
|
||||||
|
// LIST DISCOUNT
|
||||||
|
async listDiscountHistory(): Promise<{
|
||||||
|
discount: ListDiscountHistoryDto[];
|
||||||
|
count: number;
|
||||||
|
}> {
|
||||||
|
// list discountHistory
|
||||||
|
const result = await this.discountHistoryDataAccess.findAll();
|
||||||
|
const count = await this.discountHistoryDataAccess.count();
|
||||||
|
|
||||||
|
return {
|
||||||
|
discount: result.map((discountHistory) =>
|
||||||
|
listDiscountHistoryObj(discountHistory),
|
||||||
|
),
|
||||||
|
count: count,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// NEST
|
||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Post,
|
||||||
|
HttpStatus,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
// SWAGGER
|
||||||
|
import {
|
||||||
|
ApiTags,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiBody,
|
||||||
|
ApiOperation,
|
||||||
|
ApiBearerAuth,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
|
||||||
|
import { UserDiscountService } from './userDiscount.service';
|
||||||
|
|
||||||
|
// DTO
|
||||||
|
import { AuthGuard } from '../../../../auth/auth.guard';
|
||||||
|
import { Roles } from '../../../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../../../common/eNums/role.enum';
|
||||||
|
import {
|
||||||
|
CreateUserDiscountDto,
|
||||||
|
ListUserDiscountDto,
|
||||||
|
} from '../../../../DTO/userDiscount.dto';
|
||||||
|
|
||||||
|
@ApiTags('admin UserDiscounts')
|
||||||
|
@Controller('admins/UserDiscount')
|
||||||
|
export class UserDiscountController {
|
||||||
|
constructor(private readonly userDiscountService: UserDiscountService) {}
|
||||||
|
// CREATE DISCOUNT ****************************
|
||||||
|
@Post('')
|
||||||
|
@ApiOperation({ summary: 'create userUserDiscount' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiBody({
|
||||||
|
description: 'create userUserDiscount',
|
||||||
|
type: CreateUserDiscountDto,
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'response created userUserDiscount',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
async createUserDiscount(
|
||||||
|
@Body() created: CreateUserDiscountDto,
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this.userDiscountService.createUserDiscount(created);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LIST DISCOUNT ****************************
|
||||||
|
@Get('')
|
||||||
|
@ApiOperation({ summary: 'list userUserDiscount' })
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'response list userUserDiscount',
|
||||||
|
type: [ListUserDiscountDto],
|
||||||
|
})
|
||||||
|
async listUserDiscount(): Promise<ListUserDiscountDto[]> {
|
||||||
|
try {
|
||||||
|
return await this.userDiscountService.listUserDiscount();
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { UserDiscountController } from './userDiscount.controller';
|
||||||
|
import { UserDiscountService } from './userDiscount.service';
|
||||||
|
import { UserDiscountDataAccess } from '../../../../dataAccess/userDiscount.dataAccess';
|
||||||
|
import { UserDataAccess } from '../../../../dataAccess/user.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [UserDiscountController],
|
||||||
|
providers: [UserDiscountService, UserDiscountDataAccess, UserDataAccess],
|
||||||
|
})
|
||||||
|
export class UserDiscountModule {}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
// DATAACCESS
|
||||||
|
import { UserDiscountDataAccess } from '../../../../dataAccess/userDiscount.dataAccess';
|
||||||
|
// ENUM
|
||||||
|
// DTO
|
||||||
|
import {
|
||||||
|
CreateUserDiscountDto,
|
||||||
|
ListUserDiscountDto,
|
||||||
|
listUserDiscountObj,
|
||||||
|
} from '../../../../DTO/userDiscount.dto';
|
||||||
|
|
||||||
|
// APPLICATION
|
||||||
|
@Injectable()
|
||||||
|
export class UserDiscountService {
|
||||||
|
constructor(
|
||||||
|
private readonly userUserDiscountDataAccess: UserDiscountDataAccess,
|
||||||
|
) {}
|
||||||
|
// CREATE DISCOUNT
|
||||||
|
async createUserDiscount(
|
||||||
|
createUserDiscountDto: CreateUserDiscountDto,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const { user, discount } = createUserDiscountDto;
|
||||||
|
// validate startDate longer
|
||||||
|
|
||||||
|
await this.userUserDiscountDataAccess.create(user, discount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// LIST DISCOUNT
|
||||||
|
async listUserDiscount(): Promise<ListUserDiscountDto[]> {
|
||||||
|
// list userUserDiscount
|
||||||
|
const userUserDiscounts = await this.userUserDiscountDataAccess.findAll();
|
||||||
|
return userUserDiscounts.map((userUserDiscount) =>
|
||||||
|
listUserDiscountObj(userUserDiscount),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Post,
|
||||||
|
HttpStatus,
|
||||||
|
HttpCode,
|
||||||
|
Get,
|
||||||
|
Put,
|
||||||
|
Query,
|
||||||
|
Delete,
|
||||||
|
Param,
|
||||||
|
Patch,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { AuthGuard } from '../../../auth/auth.guard';
|
||||||
|
import { Roles } from '../../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../../common/eNums/role.enum';
|
||||||
|
import {
|
||||||
|
ApiTags,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiInternalServerErrorResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiBearerAuth,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
CreateFaqDto,
|
||||||
|
FaqDto,
|
||||||
|
FilterFaqDto,
|
||||||
|
ParamsPositionDto,
|
||||||
|
UpdateFaqDto,
|
||||||
|
UpdateStatusFaqDto,
|
||||||
|
} from '../../../DTO/faq.dto';
|
||||||
|
import {
|
||||||
|
CreateFaqCategoryDto,
|
||||||
|
FaqCategoryDto,
|
||||||
|
FilterFaqCategoryDto,
|
||||||
|
UpdateFaqCategoryDto,
|
||||||
|
} from '../../../DTO/faqCategory.dto';
|
||||||
|
import { FaqService } from './faq.service';
|
||||||
|
|
||||||
|
@ApiTags('admin faqs')
|
||||||
|
@Controller('admins/faq')
|
||||||
|
export class FaqController {
|
||||||
|
constructor(private readonly faqService: FaqService) {}
|
||||||
|
|
||||||
|
// faqCategory list
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'faqs list',
|
||||||
|
type: [FaqDto],
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Get('/get')
|
||||||
|
async faqList(@Query() filterFaqDto: FilterFaqDto): Promise<FaqDto[]> {
|
||||||
|
const faqList = await this.faqService.faqList(filterFaqDto);
|
||||||
|
return faqList;
|
||||||
|
}
|
||||||
|
// create new faqCategory ************************************
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'create new faqCategory',
|
||||||
|
type: FaqCategoryDto,
|
||||||
|
})
|
||||||
|
@ApiInternalServerErrorResponse({
|
||||||
|
description: 'create new faqCategory failed',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('category')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async createFaqCategory(
|
||||||
|
@Body() createFaqCategoryDto: CreateFaqCategoryDto,
|
||||||
|
): Promise<FaqCategoryDto> {
|
||||||
|
try {
|
||||||
|
const faqCategory = await this.faqService.createCategory(
|
||||||
|
createFaqCategoryDto,
|
||||||
|
);
|
||||||
|
return faqCategory;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'faq list',
|
||||||
|
type: [FaqCategoryDto],
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Get('category')
|
||||||
|
async faqCategoryList(
|
||||||
|
@Query() filterFaqCategoryDto: FilterFaqCategoryDto,
|
||||||
|
): Promise<FaqCategoryDto[]> {
|
||||||
|
const faqCategoryList = await this.faqService.faqCategoryList(
|
||||||
|
filterFaqCategoryDto,
|
||||||
|
);
|
||||||
|
return faqCategoryList;
|
||||||
|
}
|
||||||
|
// update faqCategory ***********************************
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'category detail',
|
||||||
|
type: FaqCategoryDto,
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Put('category')
|
||||||
|
async updateFaqCategory(
|
||||||
|
@Body() updateFaqCategoryDto: UpdateFaqCategoryDto,
|
||||||
|
): Promise<FaqCategoryDto> {
|
||||||
|
try {
|
||||||
|
const faqCategory = await this.faqService.updateFaqCategory(
|
||||||
|
updateFaqCategoryDto,
|
||||||
|
);
|
||||||
|
return faqCategory;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// delete faqCategory ***********************************
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'categories list',
|
||||||
|
type: [FaqCategoryDto],
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Delete('category/:id')
|
||||||
|
async deleteFaqCategory(
|
||||||
|
@Param('id')
|
||||||
|
id: string,
|
||||||
|
): Promise<FaqCategoryDto[]> {
|
||||||
|
try {
|
||||||
|
const faqCategoryList = await this.faqService.deleteFaqCategory(id);
|
||||||
|
return faqCategoryList;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create new faq ************************************
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'create new faq',
|
||||||
|
type: FaqDto,
|
||||||
|
})
|
||||||
|
@ApiInternalServerErrorResponse({
|
||||||
|
description: 'create new faq failed',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async createFaq(@Body() createFaqDto: CreateFaqDto): Promise<FaqDto> {
|
||||||
|
try {
|
||||||
|
const faq = await this.faqService.createFaq(createFaqDto);
|
||||||
|
return faq;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update faqCategory ***********************************
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'faq detail',
|
||||||
|
type: FaqDto,
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Put('')
|
||||||
|
async updateFaq(@Body() updateFaqDto: UpdateFaqDto): Promise<FaqDto> {
|
||||||
|
try {
|
||||||
|
const faq = await this.faqService.updateFaq(updateFaqDto);
|
||||||
|
return faq;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update Status ***********************************
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'faq detail',
|
||||||
|
type: FaqDto,
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Patch('status')
|
||||||
|
async updateStatusFaq(
|
||||||
|
@Body() updateStatusFaqDto: UpdateStatusFaqDto,
|
||||||
|
): Promise<FaqDto> {
|
||||||
|
try {
|
||||||
|
const faq = await this.faqService.updateStatusFaq(updateStatusFaqDto);
|
||||||
|
return faq;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// delete faq ***********************************
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'faqs list',
|
||||||
|
type: [FaqDto],
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Delete('/:id')
|
||||||
|
async deleteFaq(
|
||||||
|
@Param('id')
|
||||||
|
id: string,
|
||||||
|
): Promise<FaqDto[]> {
|
||||||
|
try {
|
||||||
|
const faqList = await this.faqService.deleteFaq(id);
|
||||||
|
return faqList;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update faqs position *********************************************************************************
|
||||||
|
@ApiOperation({ summary: 'update faqs position' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'boolean',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiInternalServerErrorResponse({
|
||||||
|
description: 'menu not found',
|
||||||
|
})
|
||||||
|
@Patch(':fId/:tId')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async changePosition(
|
||||||
|
@Param()
|
||||||
|
paramsPositionDto: ParamsPositionDto,
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const status = await this.faqService.changePosition(paramsPositionDto);
|
||||||
|
return status;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { FaqController } from './faq.controller';
|
||||||
|
import { FaqService } from './faq.service';
|
||||||
|
import { FaqDataAccess } from '../../../dataAccess/faq.dataAccess';
|
||||||
|
import { UserDataAccess } from '../../../dataAccess/user.dataAccess';
|
||||||
|
import { Tools } from '../../../common/helpers/tools.helpers';
|
||||||
|
import { ConvertDate } from '../../../common/helpers/convertDate.helper';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [FaqController],
|
||||||
|
providers: [FaqService, UserDataAccess, FaqDataAccess, Tools, ConvertDate],
|
||||||
|
})
|
||||||
|
export class FaqModule {}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { faqCategoryObj } from '../../../DTO/faqCategory.dto';
|
||||||
|
import { faqObj, ParamsPositionDto } from '../../../DTO/faq.dto';
|
||||||
|
import { Types } from 'mongoose';
|
||||||
|
|
||||||
|
import { FaqDataAccess } from '../../../dataAccess/faq.dataAccess';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FaqService {
|
||||||
|
constructor(private readonly faqDataAccess: FaqDataAccess) {}
|
||||||
|
|
||||||
|
// create new category ************************************
|
||||||
|
async createCategory(createFaqCategoryDto) {
|
||||||
|
const { title, description } = createFaqCategoryDto;
|
||||||
|
const newFaqCategory = await this.faqDataAccess.createFaqCategory(
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
);
|
||||||
|
return faqCategoryObj(newFaqCategory);
|
||||||
|
}
|
||||||
|
// faqCategoryList list
|
||||||
|
async faqCategoryList(filterFaqCategoryDto) {
|
||||||
|
const list = await this.faqDataAccess.findAllFaqCategory(
|
||||||
|
filterFaqCategoryDto,
|
||||||
|
);
|
||||||
|
return list.map((item) => faqCategoryObj(item));
|
||||||
|
}
|
||||||
|
// update FaqCategory ************************************
|
||||||
|
async updateFaqCategory(updateFaqCategoryDto) {
|
||||||
|
const { id, status, title, description } = updateFaqCategoryDto;
|
||||||
|
const faqCategory = await this.faqDataAccess.findCatById(id);
|
||||||
|
if (!faqCategory) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'FaqCategory_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.faqDataAccess.updateFaqCategory(id, status, title, description);
|
||||||
|
|
||||||
|
return faqCategoryObj(await this.faqDataAccess.findCatById(id));
|
||||||
|
}
|
||||||
|
// create new faq ************************************
|
||||||
|
async createFaq(createFaqDto) {
|
||||||
|
const { faqCat, question, answer, language } = createFaqDto;
|
||||||
|
const faqCategory = await this.faqDataAccess.findCatById(faqCat);
|
||||||
|
if (!faqCategory) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'FaqCategory_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const newFaq = await this.faqDataAccess.createFaq(
|
||||||
|
faqCat,
|
||||||
|
question,
|
||||||
|
answer,
|
||||||
|
language,
|
||||||
|
);
|
||||||
|
faqCategory.faqs.push(newFaq);
|
||||||
|
faqCategory.save();
|
||||||
|
const maxFaq = await this.faqDataAccess.findAll();
|
||||||
|
this.faqDataAccess.updatePosition(newFaq.id, maxFaq.length);
|
||||||
|
return faqObj(await this.faqDataAccess.findFaqById(newFaq._id));
|
||||||
|
}
|
||||||
|
// faqList list
|
||||||
|
async faqList(filterFaqDto) {
|
||||||
|
const list = await this.faqDataAccess.findAllFaq(filterFaqDto);
|
||||||
|
return list.map((item) => faqObj(item));
|
||||||
|
}
|
||||||
|
// update Faq ************************************
|
||||||
|
async updateFaq(updateFaqDto) {
|
||||||
|
// eslint-disable-next-line object-curly-newline
|
||||||
|
const { id, status, faqCat, question, answer, language } = updateFaqDto;
|
||||||
|
const faqCategory = await this.faqDataAccess.findCatById(faqCat);
|
||||||
|
if (!faqCategory) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'FaqCategory_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const faq = await this.faqDataAccess.findFaqById(id);
|
||||||
|
if (!faq) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'Faq_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (faq.faqCat._id.toString() !== faqCat) {
|
||||||
|
const parents = await this.faqDataAccess.findCatById(faq.faqCat);
|
||||||
|
parents.faqs = faqCategory.faqs.filter((sub) => sub._id !== id);
|
||||||
|
parents.save();
|
||||||
|
faqCategory.faqs.push(faq);
|
||||||
|
faqCategory.save();
|
||||||
|
}
|
||||||
|
await this.faqDataAccess.updateFaq(
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
faqCat,
|
||||||
|
question,
|
||||||
|
answer,
|
||||||
|
language,
|
||||||
|
);
|
||||||
|
|
||||||
|
return faqObj(await this.faqDataAccess.findFaqById(id));
|
||||||
|
}
|
||||||
|
// delete FaqCategory ************************************
|
||||||
|
async deleteFaqCategory(id) {
|
||||||
|
if (!Types.ObjectId.isValid(id)) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.FAILED_DEPENDENCY,
|
||||||
|
error: ' رشته وارد شده برای شناسه معتبر نیست',
|
||||||
|
},
|
||||||
|
HttpStatus.FAILED_DEPENDENCY,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.faqDataAccess.deleteFaqCat(id);
|
||||||
|
const filter: any = {};
|
||||||
|
const list = await this.faqDataAccess.findAllFaqCategory(filter);
|
||||||
|
return list.map((item) => faqCategoryObj(item));
|
||||||
|
}
|
||||||
|
// update Status
|
||||||
|
async updateStatusFaq(updateFaqDto) {
|
||||||
|
const { id, status } = updateFaqDto;
|
||||||
|
if (!Types.ObjectId.isValid(id)) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.FAILED_DEPENDENCY,
|
||||||
|
error: ' رشته وارد شده برای شناسه معتبر نیست',
|
||||||
|
},
|
||||||
|
HttpStatus.FAILED_DEPENDENCY,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const faq = await this.faqDataAccess.findFaqById(id);
|
||||||
|
if (!faq) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'Faq_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.faqDataAccess.updateStatusFaq(id, status);
|
||||||
|
|
||||||
|
return faqObj(await this.faqDataAccess.findFaqById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete Faq ************************************
|
||||||
|
async deleteFaq(id) {
|
||||||
|
if (!Types.ObjectId.isValid(id)) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.FAILED_DEPENDENCY,
|
||||||
|
error: ' رشته وارد شده برای شناسه معتبر نیست',
|
||||||
|
},
|
||||||
|
HttpStatus.FAILED_DEPENDENCY,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const filter = { faqCat: null };
|
||||||
|
const category = await this.faqDataAccess.findFaqById(id);
|
||||||
|
if (category) {
|
||||||
|
filter.faqCat = category.faqCat;
|
||||||
|
}
|
||||||
|
await this.faqDataAccess.deleteFaq(id);
|
||||||
|
|
||||||
|
const list = await this.faqDataAccess.findAllFaq(filter);
|
||||||
|
return list.map((item) => faqObj(item));
|
||||||
|
}
|
||||||
|
async changePosition(paramsPositionDto: ParamsPositionDto) {
|
||||||
|
try {
|
||||||
|
const { fId, tId } = paramsPositionDto;
|
||||||
|
|
||||||
|
const fFaq = await this.faqDataAccess.findFaqById(fId);
|
||||||
|
const tFaq = await this.faqDataAccess.findFaqById(tId);
|
||||||
|
if (!fFaq || !tFaq) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'پرسش وجود ندارد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.faqDataAccess.updatePosition(fFaq.id, tFaq.position);
|
||||||
|
await this.faqDataAccess.updatePosition(tFaq.id, fFaq.position);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiBody,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { SettingsService } from './settings.service';
|
||||||
|
import { Roles } from '../../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../../common/eNums/role.enum';
|
||||||
|
import { AuthGuard } from '../../../auth/auth.guard';
|
||||||
|
import { CreateDto } from '../../../DTO/settings.dto';
|
||||||
|
@ApiTags('admin settings')
|
||||||
|
@Controller('admin/settings')
|
||||||
|
export class SettingController {
|
||||||
|
constructor(private readonly settingsService: SettingsService) {}
|
||||||
|
|
||||||
|
// get ************************************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' تنظیمات سایت',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: ' Create Dto ',
|
||||||
|
type: CreateDto,
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Get()
|
||||||
|
async list(): Promise<CreateDto> {
|
||||||
|
try {
|
||||||
|
const result = await this.settingsService.find();
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create ************************************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' افزودن تنظیمات',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: ' Boolean',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateDto,
|
||||||
|
description: 'get body',
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('create')
|
||||||
|
async create(@Body() createDto: CreateDto): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const result = await this.settingsService.create(createDto);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update ************************************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' تغییر تنظیمات',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: ' Boolean',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateDto,
|
||||||
|
description: 'get body',
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Patch('edit')
|
||||||
|
async edit(@Body() createDto: CreateDto): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const result = await this.settingsService.edit(createDto);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { SettingsDataAccess } from '../../../dataAccess/settings.dataAccess';
|
||||||
|
import { SettingsService } from './settings.service';
|
||||||
|
import { SettingController } from './settings.controller';
|
||||||
|
import { UserDataAccess } from '../../../dataAccess/user.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [SettingController],
|
||||||
|
providers: [SettingsService, SettingsDataAccess, UserDataAccess],
|
||||||
|
})
|
||||||
|
export class SettingsModule {}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { SettingsDataAccess } from '../../../dataAccess/settings.dataAccess';
|
||||||
|
import { CreateDto } from '../../../DTO/settings.dto';
|
||||||
|
import { Settings } from '../../../models/schemas/settings.schema';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SettingsService {
|
||||||
|
constructor(private readonly settingDataAccess: SettingsDataAccess) {}
|
||||||
|
|
||||||
|
async find(): Promise<Settings> {
|
||||||
|
const result = await this.settingDataAccess.findOne();
|
||||||
|
if (result.length === 0) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' تنظیمات وجود ندارد ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(createDto: CreateDto): Promise<boolean> {
|
||||||
|
const {
|
||||||
|
about,
|
||||||
|
student,
|
||||||
|
hours,
|
||||||
|
title,
|
||||||
|
expertise,
|
||||||
|
nameCompany,
|
||||||
|
mission,
|
||||||
|
start,
|
||||||
|
goals,
|
||||||
|
rate,
|
||||||
|
titleText,
|
||||||
|
header,
|
||||||
|
} = createDto;
|
||||||
|
const result = await this.settingDataAccess.findOne();
|
||||||
|
if (result.length === 0) {
|
||||||
|
await this.settingDataAccess.create(
|
||||||
|
about,
|
||||||
|
student,
|
||||||
|
hours,
|
||||||
|
title,
|
||||||
|
expertise,
|
||||||
|
nameCompany,
|
||||||
|
mission,
|
||||||
|
start,
|
||||||
|
goals,
|
||||||
|
rate,
|
||||||
|
titleText,
|
||||||
|
header,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' قابل ساخت دوباره نیست شما می توانید تغییر دهید ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async edit(createDto: CreateDto): Promise<boolean> {
|
||||||
|
const {
|
||||||
|
about,
|
||||||
|
student,
|
||||||
|
hours,
|
||||||
|
title,
|
||||||
|
expertise,
|
||||||
|
nameCompany,
|
||||||
|
mission,
|
||||||
|
start,
|
||||||
|
goals,
|
||||||
|
rate,
|
||||||
|
titleText,
|
||||||
|
header,
|
||||||
|
} = createDto;
|
||||||
|
const result = await this.settingDataAccess.findOne();
|
||||||
|
if (result.length === 0) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' تنظیمات وجود ندارد ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.settingDataAccess.update(
|
||||||
|
result[0]._id,
|
||||||
|
about,
|
||||||
|
student,
|
||||||
|
hours,
|
||||||
|
title,
|
||||||
|
expertise,
|
||||||
|
nameCompany,
|
||||||
|
mission,
|
||||||
|
start,
|
||||||
|
goals,
|
||||||
|
rate,
|
||||||
|
titleText,
|
||||||
|
header,
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Patch,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiBody,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
import { Roles } from '../../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../../common/eNums/role.enum';
|
||||||
|
import { AuthGuard } from '../../../auth/auth.guard';
|
||||||
|
import { QueryUserList, UserMidDto, UserStatus } from '../../../DTO/user.dto';
|
||||||
|
import { CurrentUser } from '../../../decorators/currentUser.decorator';
|
||||||
|
|
||||||
|
@ApiTags('admin user')
|
||||||
|
@Controller('admin/user')
|
||||||
|
export class UserController {
|
||||||
|
constructor(private readonly userService: UserService) {}
|
||||||
|
|
||||||
|
// list user ************************************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' لیست کاربران ',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: ' لیست کاربران به صورت آرایه زیر ',
|
||||||
|
type: [UserMidDto],
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Get('list')
|
||||||
|
async listUser(
|
||||||
|
@Query() queryUserList: QueryUserList,
|
||||||
|
): Promise<{ user: UserMidDto[]; count: number }> {
|
||||||
|
try {
|
||||||
|
const result = await this.userService.list(queryUserList);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update user ************************************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' تغییر وضعیت کاربر',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: ' پاسخ تغییر وضعیت ',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: UserStatus,
|
||||||
|
description: 'get body',
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Patch('status')
|
||||||
|
async statusUser(@Body() userStatus: UserStatus): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const result = await this.userService.status(userStatus);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { UserDataAccess } from '../../../dataAccess/user.dataAccess';
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
import { UserController } from './user.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [UserController],
|
||||||
|
providers: [UserService, UserDataAccess],
|
||||||
|
})
|
||||||
|
export class UserModule {}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
QueryUserList,
|
||||||
|
UserMidDto,
|
||||||
|
userMidObj,
|
||||||
|
UserStatus,
|
||||||
|
} from '../../../DTO/user.dto';
|
||||||
|
import { UserDataAccess } from '../../../dataAccess/user.dataAccess';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserService {
|
||||||
|
constructor(private readonly userDataAccess: UserDataAccess) {}
|
||||||
|
|
||||||
|
async list(
|
||||||
|
queryUserList: QueryUserList,
|
||||||
|
): Promise<{ user: UserMidDto[]; count: number }> {
|
||||||
|
const { page, pageSize } = queryUserList;
|
||||||
|
const result = await this.userDataAccess.list(page, pageSize);
|
||||||
|
const count = await this.userDataAccess.count();
|
||||||
|
return { user: result.map((item) => userMidObj(item)), count: count };
|
||||||
|
}
|
||||||
|
// user status
|
||||||
|
async status(userStatus: UserStatus): Promise<boolean> {
|
||||||
|
const { userId, status } = userStatus;
|
||||||
|
const user = await this.userDataAccess.findOne(userId);
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' کاربر وجود ندارد ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.userDataAccess.status(userId, status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
import { IpAddress } from '../../../decorators/ipAddress.decorator';
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
HttpStatus,
|
||||||
|
HttpCode,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Query,
|
||||||
|
Res,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Response } from 'express';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ApiTags,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiBody,
|
||||||
|
ApiBearerAuth,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
PostDto,
|
||||||
|
getPostInputDto,
|
||||||
|
ViewListPostDto,
|
||||||
|
SearchParameter,
|
||||||
|
} from '../../../DTO/blogPost.dto';
|
||||||
|
import { BlogService } from './blog.service';
|
||||||
|
import {
|
||||||
|
CreatePostCommentDto,
|
||||||
|
PostCommentDto,
|
||||||
|
} from '../../../DTO/blogPostComment.dto';
|
||||||
|
import { AuthGuard } from '../../../auth/auth.guard';
|
||||||
|
import { Roles } from '../../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../../common/eNums/role.enum';
|
||||||
|
import { CurrentUser } from '../../../decorators/currentUser.decorator';
|
||||||
|
import { CheckGuard } from './blog.guard';
|
||||||
|
|
||||||
|
@ApiTags('common blog')
|
||||||
|
@Controller('common/blog')
|
||||||
|
export class BlogController {
|
||||||
|
constructor(private readonly blogService: BlogService) {}
|
||||||
|
// blog cat ***********************************************************
|
||||||
|
@ApiOperation({ summary: 'فیلتر و لیست پست های بلاگ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'list Posts',
|
||||||
|
type: [ViewListPostDto],
|
||||||
|
})
|
||||||
|
@Get('postList')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async listPost(@Query() query: getPostInputDto): Promise<ViewListPostDto[]> {
|
||||||
|
const list = await this.blogService.listPost(query);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
// post **********************************************************************
|
||||||
|
@ApiOperation({ summary: 'جزییات پست بلاگ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'Post info',
|
||||||
|
type: [PostDto],
|
||||||
|
})
|
||||||
|
@Get('post/:slug')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(CheckGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async Post(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Param('slug')
|
||||||
|
slug: string,
|
||||||
|
@IpAddress() ip: string,
|
||||||
|
@Res() res: Response,
|
||||||
|
): Promise<PostDto> {
|
||||||
|
try {
|
||||||
|
const post = await this.blogService.post(slug, user, ip);
|
||||||
|
res.json(post);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create post comment **********************************************************
|
||||||
|
@ApiOperation({ summary: ' ساخت کامنت ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'post comment Dto',
|
||||||
|
type: [PostCommentDto],
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreatePostCommentDto,
|
||||||
|
description: 'create postComment Dto',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('postComment/:id')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async createPostComment(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() createPostCommentDto: CreatePostCommentDto,
|
||||||
|
@Param('id')
|
||||||
|
id: string,
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this.blogService.createPostComment(user, createPostCommentDto, id);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reply post comment *********************************************************
|
||||||
|
@ApiOperation({ summary: ' پاسخ دادن کامنت یک پست ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'PostComment Dto',
|
||||||
|
type: [PostCommentDto],
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreatePostCommentDto,
|
||||||
|
description: 'reply post comment',
|
||||||
|
})
|
||||||
|
// @Post('postComment/reply/:id')
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('postComment/:id')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async replyPostComment(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() createPostCommentDto: CreatePostCommentDto,
|
||||||
|
@Param('id')
|
||||||
|
id: string,
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this.blogService.replyPostComment(user, createPostCommentDto, id);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// search blog ******************************************************************
|
||||||
|
@ApiOperation({ summary: ' جست و جو در بلاگ ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'search result',
|
||||||
|
type: [ViewListPostDto],
|
||||||
|
})
|
||||||
|
@Get('search')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async search(@Query() query: SearchParameter): Promise<ViewListPostDto[]> {
|
||||||
|
try {
|
||||||
|
const result = await this.blogService.search(query);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
import { jwtConstants } from './constants';
|
||||||
|
import { Request } from 'express';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CheckGuard implements CanActivate {
|
||||||
|
constructor(private jwtService: JwtService) {}
|
||||||
|
|
||||||
|
async canActivate(context: ExecutionContext): Promise<any> {
|
||||||
|
const request = context.switchToHttp().getRequest();
|
||||||
|
|
||||||
|
const token = this.extractTokenFromHeader(request);
|
||||||
|
if (!token) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const payload = await this.jwtService.verifyAsync(token, {
|
||||||
|
secret: jwtConstants.secret,
|
||||||
|
});
|
||||||
|
request['user'] = payload;
|
||||||
|
return payload;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractTokenFromHeader(request: Request): string | undefined {
|
||||||
|
const [type, token] = request.headers.authorization?.split(' ') ?? [];
|
||||||
|
return type === 'Bearer' ? token : undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { BlogController } from './blog.controller';
|
||||||
|
import { BlogService } from './blog.service';
|
||||||
|
import { BlogPostDataAccess } from '../../../dataAccess/blogPost.dataAccess';
|
||||||
|
import { CategoryDataAccess } from '../../../dataAccess/blogCategory.dataAccess';
|
||||||
|
import { BlogPostCommentDataAccess } from '../../../dataAccess/blogPostComment.dataAccess';
|
||||||
|
import { PostVisitDataAccess } from '../../../dataAccess/postVisit.dataAccess';
|
||||||
|
import { UserDataAccess } from '../../../dataAccess/user.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [BlogController],
|
||||||
|
providers: [
|
||||||
|
BlogService,
|
||||||
|
CategoryDataAccess,
|
||||||
|
BlogPostDataAccess,
|
||||||
|
BlogPostCommentDataAccess,
|
||||||
|
PostVisitDataAccess,
|
||||||
|
UserDataAccess,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class BlogModule {}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { getAllBlogCategoriesObj } from '../../../DTO/blogCategory.dto';
|
||||||
|
import { CategoryDataAccess } from '../../../dataAccess/blogCategory.dataAccess';
|
||||||
|
import { ItemStatus } from '../../../common/eNums/itemStatus.enum';
|
||||||
|
import { BlogPostCommentDataAccess } from '../../../dataAccess/blogPostComment.dataAccess';
|
||||||
|
import { BlogPostDataAccess } from '../../../dataAccess/blogPost.dataAccess';
|
||||||
|
import { PostVisitDataAccess } from '../../../dataAccess/postVisit.dataAccess';
|
||||||
|
import {
|
||||||
|
postInfoObjDto,
|
||||||
|
viewPostObjDto,
|
||||||
|
ViewListPostDto,
|
||||||
|
} from '../../../DTO/blogPost.dto';
|
||||||
|
import commentStatuses from '../../../common/eNums/itemStatus.enum';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BlogService {
|
||||||
|
constructor(
|
||||||
|
private readonly categoryDataAccess: CategoryDataAccess,
|
||||||
|
private readonly blogPostCommentDataAccess: BlogPostCommentDataAccess,
|
||||||
|
private readonly blogPostDataAccess: BlogPostDataAccess,
|
||||||
|
private readonly postVisitDataAccess: PostVisitDataAccess,
|
||||||
|
) {}
|
||||||
|
// blogCategory ******************************************************
|
||||||
|
async listPost(query) {
|
||||||
|
const listPost = await this.blogPostDataAccess.listBlogPosts(query);
|
||||||
|
return listPost.map((item) => viewPostObjDto(item));
|
||||||
|
}
|
||||||
|
// post *****************************************************************
|
||||||
|
async post(slug, user, ip) {
|
||||||
|
const post = await this.blogPostDataAccess.findBlogPostBySlug(slug);
|
||||||
|
if (!post) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'POST_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (!user) {
|
||||||
|
user = { id: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
const visit = await this.postVisitDataAccess.findById(
|
||||||
|
user.id,
|
||||||
|
post._id,
|
||||||
|
ip,
|
||||||
|
);
|
||||||
|
if (!visit) {
|
||||||
|
if (user) {
|
||||||
|
await this.blogPostDataAccess.updateVisitCount(
|
||||||
|
post._id,
|
||||||
|
post.visitCount + 1,
|
||||||
|
);
|
||||||
|
await this.postVisitDataAccess.create(user.id, post._id, ip);
|
||||||
|
} else {
|
||||||
|
user = { id: null };
|
||||||
|
await this.blogPostDataAccess.updateVisitCount(
|
||||||
|
post._id,
|
||||||
|
post.visitCount + 1,
|
||||||
|
);
|
||||||
|
await this.postVisitDataAccess.create(user.id, post._id, ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// await this.blogPostDataAccess.updateVisitCount(post.id, post.visitCount + 1);
|
||||||
|
if (post.Comment.length !== 0) {
|
||||||
|
post.Comment = post.Comment.filter(
|
||||||
|
(x) => x.status === commentStatuses.Active,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
post.Comment = [];
|
||||||
|
}
|
||||||
|
return postInfoObjDto(post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create postComment ***************************************************
|
||||||
|
async createPostComment(userInf, createPostCommentDto, id): Promise<boolean> {
|
||||||
|
const { text } = createPostCommentDto;
|
||||||
|
const post = await this.blogPostDataAccess.findById(id);
|
||||||
|
if (!post || post.status !== ItemStatus.Active) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'پست وجود ندارد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const comment =
|
||||||
|
await this.blogPostCommentDataAccess.createBlogPostComment(
|
||||||
|
id,
|
||||||
|
userInf.id,
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
post.Comment.push(comment._id ? comment._id : comment.id);
|
||||||
|
post.save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// blogCat structure ******************************************************
|
||||||
|
async getAllBlogCategories() {
|
||||||
|
const list = await this.categoryDataAccess.getAll();
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
list[i].parents = list[i].parents.filter(
|
||||||
|
(x) => x.status === ItemStatus.Active,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return list.map((item) => getAllBlogCategoriesObj(item));
|
||||||
|
}
|
||||||
|
// replay post comment *******************************************************
|
||||||
|
async replyPostComment(userInf, createPostCommentDto, id): Promise<boolean> {
|
||||||
|
const postComment = await this.blogPostCommentDataAccess.findById(id);
|
||||||
|
if (!postComment || postComment.status !== ItemStatus.Active) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'کامنت وجود ندارد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const { text } = createPostCommentDto;
|
||||||
|
const replyComment = await this.blogPostCommentDataAccess.replyComment(
|
||||||
|
postComment.blogPost._id,
|
||||||
|
id,
|
||||||
|
userInf.id,
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
postComment.Replies.push(replyComment.id);
|
||||||
|
postComment.save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// search blog *************************************************************
|
||||||
|
async search(query): Promise<ViewListPostDto[]> {
|
||||||
|
const { title, page, pageSize } = query;
|
||||||
|
const resultPosts = await this.blogPostDataAccess.search(
|
||||||
|
title,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
);
|
||||||
|
return resultPosts.map((item) => viewPostObjDto(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const jwtConstants = {
|
||||||
|
secret: '8FWQDF%$ERDFGH#$FDSJ&*#@R1!34Vh',
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Controller } from '@nestjs/common';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { CommonService } from './common.service';
|
||||||
|
|
||||||
|
@ApiTags('commons')
|
||||||
|
@Controller('commons')
|
||||||
|
export class CommonController {
|
||||||
|
constructor(private readonly commonService: CommonService) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { Tools } from '../../common/helpers/tools.helpers';
|
||||||
|
import { UserDataAccess } from '../../dataAccess/user.dataAccess';
|
||||||
|
import { CommonController } from './common.controller';
|
||||||
|
import { CommonService } from './common.service';
|
||||||
|
import { BlogModule } from './blog/blog.module';
|
||||||
|
import { FaqModule } from './faq/faq.module';
|
||||||
|
import { SettingsModule } from './settings/settings.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [BlogModule, FaqModule, SettingsModule],
|
||||||
|
controllers: [CommonController],
|
||||||
|
providers: [CommonService, Tools, UserDataAccess],
|
||||||
|
})
|
||||||
|
export class CommonModule {}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CommonService {
|
||||||
|
constructor() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Controller, HttpStatus, HttpCode, Get, Query } from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiOkResponse } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
FaqCategoryDto,
|
||||||
|
FilterFaqCategoryDto,
|
||||||
|
} from '../../../DTO/faqCategory.dto';
|
||||||
|
import { FaqService } from './faq.service';
|
||||||
|
|
||||||
|
@ApiTags('common faqs')
|
||||||
|
@Controller('common/faq')
|
||||||
|
export class FaqController {
|
||||||
|
constructor(private readonly faqService: FaqService) {}
|
||||||
|
|
||||||
|
// faqCategory list
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'faqs list',
|
||||||
|
type: [FaqCategoryDto],
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Get('faq')
|
||||||
|
async faqList(
|
||||||
|
@Query() filterFaqCategoryDto: FilterFaqCategoryDto,
|
||||||
|
): Promise<FaqCategoryDto[]> {
|
||||||
|
const faqList = await this.faqService.faqList(filterFaqCategoryDto);
|
||||||
|
return faqList;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { FaqController } from './faq.controller';
|
||||||
|
import { FaqService } from './faq.service';
|
||||||
|
import { FaqDataAccess } from '../../../dataAccess/faq.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [FaqController],
|
||||||
|
providers: [FaqService, FaqDataAccess],
|
||||||
|
})
|
||||||
|
export class FaqModule {}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
FaqCategoryDto,
|
||||||
|
faqCategoryObj,
|
||||||
|
FilterFaqCategoryDto,
|
||||||
|
} from '../../../DTO/faqCategory.dto';
|
||||||
|
|
||||||
|
import { FaqDataAccess } from '../../../dataAccess/faq.dataAccess';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FaqService {
|
||||||
|
constructor(private readonly faqDataAccess: FaqDataAccess) {}
|
||||||
|
|
||||||
|
async faqList(
|
||||||
|
filterFaqCategoryDto: FilterFaqCategoryDto,
|
||||||
|
): Promise<FaqCategoryDto[]> {
|
||||||
|
const list = await this.faqDataAccess.findAllFaqCategory(
|
||||||
|
filterFaqCategoryDto,
|
||||||
|
);
|
||||||
|
return list.map((item) => faqCategoryObj(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { Controller, Get, HttpCode, HttpStatus } from '@nestjs/common';
|
||||||
|
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { SettingsService } from './settings.service';
|
||||||
|
import { CreateDto } from '../../../DTO/settings.dto';
|
||||||
|
@ApiTags('common settings')
|
||||||
|
@Controller('common/settings')
|
||||||
|
export class SettingController {
|
||||||
|
constructor(private readonly settingsService: SettingsService) {}
|
||||||
|
|
||||||
|
// get ************************************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' تنظیمات سایت',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: ' Create Dto ',
|
||||||
|
type: CreateDto,
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Get()
|
||||||
|
async list(): Promise<CreateDto> {
|
||||||
|
try {
|
||||||
|
const result = await this.settingsService.find();
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { SettingsDataAccess } from '../../../dataAccess/settings.dataAccess';
|
||||||
|
import { SettingsService } from './settings.service';
|
||||||
|
import { SettingController } from './settings.controller';
|
||||||
|
import { UserDataAccess } from '../../../dataAccess/user.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [SettingController],
|
||||||
|
providers: [SettingsService, SettingsDataAccess, UserDataAccess],
|
||||||
|
})
|
||||||
|
export class SettingsModule {}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { SettingsDataAccess } from '../../../dataAccess/settings.dataAccess';
|
||||||
|
import { Settings } from '../../../models/schemas/settings.schema';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SettingsService {
|
||||||
|
constructor(private readonly settingDataAccess: SettingsDataAccess) {}
|
||||||
|
|
||||||
|
async find(): Promise<Settings> {
|
||||||
|
const result = await this.settingDataAccess.findOne();
|
||||||
|
if (result.length === 0) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' تنظیمات وجود ندارد ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
|
UploadedFile,
|
||||||
|
UseGuards,
|
||||||
|
UseInterceptors,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiBody,
|
||||||
|
ApiConsumes,
|
||||||
|
ApiOperation,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { GetFileDTO, UploaderDTO } from '../../../DTO/uploader.dto';
|
||||||
|
import { UploaderService } from './uploader.service';
|
||||||
|
import { AuthGuard } from '../../../auth/auth.guard';
|
||||||
|
import { Role } from '../../../common/eNums/role.enum';
|
||||||
|
import { Roles } from '../../../decorators/role.decorators';
|
||||||
|
|
||||||
|
@ApiTags('Uploader')
|
||||||
|
@Controller('uploader')
|
||||||
|
export class UploaderController {
|
||||||
|
constructor(private readonly uploaderService: UploaderService) {}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' آپلود فایل ',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: UploaderDTO,
|
||||||
|
description: 'Uploader DTO',
|
||||||
|
})
|
||||||
|
@UseInterceptors(FileInterceptor('file'))
|
||||||
|
@ApiConsumes('multipart/form-data')
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User, Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post()
|
||||||
|
uploadFile(@UploadedFile() file, @Body() uploaderDto: UploaderDTO) {
|
||||||
|
try {
|
||||||
|
if (file) {
|
||||||
|
return this.uploaderService.upload(file, uploaderDto.directoryName);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.BAD_REQUEST,
|
||||||
|
error: 'BAD_REQUEST',
|
||||||
|
},
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' چک کردن وجود داشتن مدیا ',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User, Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Get('file')
|
||||||
|
getFile(@Query() getFileDTO: GetFileDTO) {
|
||||||
|
try {
|
||||||
|
return this.uploaderService.getFileUrl(getFileDTO.fileName);
|
||||||
|
} catch (err) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.BAD_REQUEST,
|
||||||
|
error: 'BAD_REQUEST',
|
||||||
|
},
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { UploaderService } from './uploader.service';
|
||||||
|
import { UploaderController } from './uploader.controller';
|
||||||
|
import { UserDataAccess } from '../../../dataAccess/user.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [UploaderController],
|
||||||
|
providers: [UploaderService, UserDataAccess],
|
||||||
|
exports: [UploaderService],
|
||||||
|
})
|
||||||
|
export class UploaderModule {}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { S3 } from 'aws-sdk';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UploaderService {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
client: any = new S3({
|
||||||
|
accessKeyId: process.env.STORAGE_ACCESS_KEY,
|
||||||
|
secretAccessKey: process.env.STORAGE_SECRET_KEY,
|
||||||
|
endpoint: process.env.STORAGE_ENDPOINT,
|
||||||
|
region: process.env.STORAGE_REGION,
|
||||||
|
});
|
||||||
|
|
||||||
|
async upload(file, directoryName) {
|
||||||
|
const params = {
|
||||||
|
Body: file.buffer,
|
||||||
|
Bucket: process.env.STORAGE_BUCKET,
|
||||||
|
Key: directoryName + '/' + uuidv4() + file.originalname,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadFile = await this.client.upload(params).promise();
|
||||||
|
return {
|
||||||
|
url: uploadFile.Location,
|
||||||
|
key: uploadFile.key,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFileUrl(fileName) {
|
||||||
|
const params = {
|
||||||
|
Bucket: process.env.STORAGE_BUCKET,
|
||||||
|
Key: fileName,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
// Check if the file exists in storage
|
||||||
|
const data = await this.client.headObject(params).promise();
|
||||||
|
// If the file exists, return the file
|
||||||
|
return {
|
||||||
|
result: true,
|
||||||
|
url: `${process.env.STORAGE_ENDPOINT}/${params.Bucket}/${params.Key}`,
|
||||||
|
data: data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
// If the file does not exist, return an error
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
error: `File ${params.Key} does not exist in storage`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Param,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
Query,
|
||||||
|
Req,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { CourseService } from './course.service';
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiBody,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiQuery,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
CreateCourseDto,
|
||||||
|
FilterCategoryPaginationCourseDto,
|
||||||
|
UpdateCourseDto,
|
||||||
|
} from '../../DTO/course.dto';
|
||||||
|
import { AuthGuard } from '../../auth/auth.guard';
|
||||||
|
import { UploaderService } from '../common/uploader/uploader.service';
|
||||||
|
import { Role } from '../../common/eNums/role.enum';
|
||||||
|
import { Roles } from '../../decorators/role.decorators';
|
||||||
|
import { CreateCommentDto } from '../../DTO/comment.dto';
|
||||||
|
import { CurrentUser } from '../../decorators/currentUser.decorator';
|
||||||
|
import { CreateRateDto } from '../../DTO/rate.dto';
|
||||||
|
import { StatusCommentDto } from 'src/DTO/courseHeadlineComment.dto';
|
||||||
|
@ApiTags('course')
|
||||||
|
@Controller('course')
|
||||||
|
export class CourseController {
|
||||||
|
constructor(
|
||||||
|
private courseService: CourseService,
|
||||||
|
private uploaderService: UploaderService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'ایجاد دوره',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateCourseDto,
|
||||||
|
description: 'get Body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@Post()
|
||||||
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
async createCourse(@Body() createCourseDto: CreateCourseDto) {
|
||||||
|
return this.courseService.createCourse(createCourseDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
// profile user ************************************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' دریافت دوره های من',
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User, Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Get('/course-user')
|
||||||
|
async getProfile(@CurrentUser() user) {
|
||||||
|
try {
|
||||||
|
const result = await this.courseService.getCourseUser(user._id);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'دریافت دوره با آیدی',
|
||||||
|
})
|
||||||
|
@ApiQuery({
|
||||||
|
name: 'userId',
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
description: 'آیدی کاربر',
|
||||||
|
})
|
||||||
|
@Get(':id')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async getCourse(@Param('id') id: string, @Query('userId') userId?: string) {
|
||||||
|
return this.courseService.getCourse(id, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'دریافت همه دوره ها',
|
||||||
|
})
|
||||||
|
@Get('')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async getAllCourses(
|
||||||
|
@Query()
|
||||||
|
filterCategoryPaginationCourseDto: FilterCategoryPaginationCourseDto,
|
||||||
|
) {
|
||||||
|
return this.courseService.getAllCourses(filterCategoryPaginationCourseDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'بروزرسانی دوره',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: UpdateCourseDto,
|
||||||
|
description: 'get Body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@Put('')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async updateCourse(@Body() updateCourseDto: UpdateCourseDto) {
|
||||||
|
return this.courseService.updateCourse(updateCourseDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
// changeStatus postComment *********************************************************************
|
||||||
|
@ApiOperation({ summary: 'change status' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'api status',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: StatusCommentDto,
|
||||||
|
description: 'change status ',
|
||||||
|
})
|
||||||
|
@Patch('status')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async changeStatus(@Body() statusCommentDto: StatusCommentDto): Promise<any> {
|
||||||
|
try {
|
||||||
|
await this.courseService.changeStatus(statusCommentDto);
|
||||||
|
return { status: true };
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'دریافت دوره ها بر اساس فیلتر دسته بندی',
|
||||||
|
})
|
||||||
|
@Get('category/:id')
|
||||||
|
async findCoursesByCategory(@Param('id') id: string) {
|
||||||
|
return this.courseService.findCoursesByCategory(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'ایجاد کامنت',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateCommentDto,
|
||||||
|
description: 'get Body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin, Role.User, Role.Reader, Role.Writer)
|
||||||
|
@Post(':id/comment')
|
||||||
|
async createComment(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() createCommentDto: CreateCommentDto,
|
||||||
|
) {
|
||||||
|
return this.courseService.createComment(createCommentDto, id, user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'ایجاد امتیاز',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateRateDto,
|
||||||
|
description: 'get Body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin, Role.User, Role.Reader, Role.Writer)
|
||||||
|
@Post(':id/rate')
|
||||||
|
async createRate(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() createRateDto: CreateRateDto,
|
||||||
|
) {
|
||||||
|
return this.courseService.addRating(createRateDto, id, user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'دریافت امتیاز',
|
||||||
|
})
|
||||||
|
@Get(':id/rate')
|
||||||
|
async getRating(@Param('id') id: string) {
|
||||||
|
return this.courseService.getRating(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'حذف دوره',
|
||||||
|
})
|
||||||
|
@Delete(':id')
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
async deleteCourse(@Param('id') id: string, @CurrentUser() user) {
|
||||||
|
return this.courseService.deleteCourse(id, user.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { CourseController } from './course.controller';
|
||||||
|
import { CourseService } from './course.service';
|
||||||
|
import { UserModule } from '../user/user.module';
|
||||||
|
import { UserDataAccess } from '../../dataAccess/user.dataAccess';
|
||||||
|
import { CourseDataAccess } from '../../dataAccess/course.dataAccess';
|
||||||
|
import { UploaderService } from '../common/uploader/uploader.service';
|
||||||
|
import { CourseHeadlineDataAccess } from '../../dataAccess/courseHeadline.dataAccess';
|
||||||
|
import { CourseCategoryDataAccess } from '../../dataAccess/courseCategory.dataAccess';
|
||||||
|
import { CommentDataAccess } from '../../dataAccess/comment.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
UserModule,
|
||||||
|
],
|
||||||
|
controllers: [CourseController],
|
||||||
|
providers: [
|
||||||
|
CourseService,
|
||||||
|
CourseDataAccess,
|
||||||
|
UserDataAccess,
|
||||||
|
CourseHeadlineDataAccess,
|
||||||
|
UploaderService,
|
||||||
|
CourseCategoryDataAccess,
|
||||||
|
CommentDataAccess
|
||||||
|
],
|
||||||
|
exports: [CourseService]
|
||||||
|
})
|
||||||
|
export class CourseModule { }
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { CourseDataAccess } from '../../dataAccess/course.dataAccess';
|
||||||
|
import {
|
||||||
|
CreateCourseDto,
|
||||||
|
FilterCategoryPaginationCourseDto,
|
||||||
|
UpdateCourseDto,
|
||||||
|
} from '../../DTO/course.dto';
|
||||||
|
import { UserDataAccess } from '../../dataAccess/user.dataAccess';
|
||||||
|
import { Course } from '../../models/schemas/course.schema';
|
||||||
|
import { CourseCategoryDataAccess } from '../../dataAccess/courseCategory.dataAccess';
|
||||||
|
import { CreateCommentDto } from '../../DTO/comment.dto';
|
||||||
|
import { CommentDataAccess } from '../../dataAccess/comment.dataAccess';
|
||||||
|
import { Comment } from '../../models/schemas/comment.schema';
|
||||||
|
import { Rate } from '../../models/schemas/rate.schema';
|
||||||
|
import { CreateRateDto } from '../../DTO/rate.dto';
|
||||||
|
import { CourseHeadlineDataAccess } from '../../dataAccess/courseHeadline.dataAccess';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CourseService {
|
||||||
|
constructor(
|
||||||
|
private courseDataAccess: CourseDataAccess,
|
||||||
|
private userDataAccess: UserDataAccess,
|
||||||
|
private courseCategoryDataAccess: CourseCategoryDataAccess,
|
||||||
|
private commentDataAccess: CommentDataAccess,
|
||||||
|
private courseHeadlineDataAccess: CourseHeadlineDataAccess,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async createCourse(
|
||||||
|
createCourseDto: CreateCourseDto,
|
||||||
|
): Promise<{ message: string; course: Course }> {
|
||||||
|
try {
|
||||||
|
const teacher = await this.userDataAccess.findOne(
|
||||||
|
createCourseDto.teacher,
|
||||||
|
);
|
||||||
|
if (!teacher) {
|
||||||
|
throw new HttpException('استاد یافت نشد', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
// if (createCourseDto.image) {
|
||||||
|
// const fileUrl = await this.uploaderService.getFileUrl(createCourseDto.image);
|
||||||
|
// if (fileUrl.result === true) {
|
||||||
|
// createCourseDto.image = fileUrl.url;
|
||||||
|
// } else {
|
||||||
|
// throw new HttpException('فایل یافت نشد', HttpStatus.NOT_FOUND);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
createCourseDto.teacher = teacher._id.toString();
|
||||||
|
|
||||||
|
if (createCourseDto.categories) {
|
||||||
|
const courseCategory =
|
||||||
|
await this.courseCategoryDataAccess.findCourseCategoryByIds(
|
||||||
|
createCourseDto.categories,
|
||||||
|
);
|
||||||
|
if (!courseCategory) {
|
||||||
|
throw new HttpException(
|
||||||
|
'دسته بندی دوره یافت نشد',
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
createCourseDto.categories = courseCategory.map((category) =>
|
||||||
|
category._id ? category._id : category.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const course = await this.courseDataAccess.createCourse(createCourseDto);
|
||||||
|
return { message: 'دوره با موفقیت ثبت شد', course };
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 11000 && error.keyPattern && error.keyPattern.slug) {
|
||||||
|
throw new HttpException(
|
||||||
|
'دوره با این slug قبلا ثبت شده است',
|
||||||
|
HttpStatus.CONFLICT,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(error);
|
||||||
|
throw new HttpException('خطای سرور', HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCourse(
|
||||||
|
id: string,
|
||||||
|
userId?: string,
|
||||||
|
): Promise<{
|
||||||
|
course: Course;
|
||||||
|
comments: Comment[];
|
||||||
|
completionPercentage: number;
|
||||||
|
}> {
|
||||||
|
const course = await this.courseDataAccess.getCourse(id);
|
||||||
|
if (!course) {
|
||||||
|
throw new HttpException('دوره یافت نشد', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
const comments = await this.commentDataAccess.getCommentsByCourseId(id);
|
||||||
|
if (userId) {
|
||||||
|
const user = await this.userDataAccess.findOne(userId);
|
||||||
|
const courseHeadlines =
|
||||||
|
await this.courseHeadlineDataAccess.getCourseHeadlines(id);
|
||||||
|
const totalHeadlines = courseHeadlines.length;
|
||||||
|
let completionPercentage = 0;
|
||||||
|
if (user.courseHeadlineProgress) {
|
||||||
|
console.log(user.courseHeadlineProgress);
|
||||||
|
console.log(courseHeadlines);
|
||||||
|
const completedHeadlines = user.courseHeadlineProgress.filter(
|
||||||
|
(progress) =>
|
||||||
|
courseHeadlines.some(
|
||||||
|
(headline) => headline._id.toString() === progress.toString(),
|
||||||
|
),
|
||||||
|
).length;
|
||||||
|
console.log({ completedHeadlines });
|
||||||
|
|
||||||
|
completionPercentage = (completedHeadlines / totalHeadlines) * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { course, comments, completionPercentage };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { course, comments, completionPercentage: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllCourses(
|
||||||
|
filterCategoryPaginationCourseDto: FilterCategoryPaginationCourseDto,
|
||||||
|
): Promise<{ courses: Course[]; count: number }> {
|
||||||
|
return this.courseDataAccess.getCourses(filterCategoryPaginationCourseDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateCourse(updateCourseDto: UpdateCourseDto): Promise<Course> {
|
||||||
|
if (updateCourseDto.teacher) {
|
||||||
|
const teacher = await this.userDataAccess.findOne(
|
||||||
|
updateCourseDto.teacher,
|
||||||
|
);
|
||||||
|
if (!teacher) {
|
||||||
|
throw new HttpException('استاد یافت نشد', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
updateCourseDto.teacher = teacher._id ? teacher._id : teacher.id;
|
||||||
|
}
|
||||||
|
return this.courseDataAccess.updateCourse(updateCourseDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findCoursesByCategory(
|
||||||
|
categoryId: string,
|
||||||
|
): Promise<{ courses: Course[]; count: number }> {
|
||||||
|
return this.courseDataAccess.findCoursesByCategory(categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createComment(
|
||||||
|
createCommentDto: CreateCommentDto,
|
||||||
|
courseId: string,
|
||||||
|
userId: string,
|
||||||
|
): Promise<Comment> {
|
||||||
|
return this.commentDataAccess.createComment(
|
||||||
|
createCommentDto,
|
||||||
|
courseId,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addRating(
|
||||||
|
createRateDto: CreateRateDto,
|
||||||
|
courseId,
|
||||||
|
userId,
|
||||||
|
): Promise<Rate> {
|
||||||
|
return this.courseDataAccess.addRating(
|
||||||
|
courseId,
|
||||||
|
userId,
|
||||||
|
createRateDto.rating,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRating(courseId: string): Promise<Rate[]> {
|
||||||
|
return this.courseDataAccess.getRating(courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCourseUser(userId: string) {
|
||||||
|
return this.courseDataAccess.getCourseUser(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteCourse(courseId: string, userId: string) {
|
||||||
|
const course = await this.courseDataAccess.getCourse(courseId);
|
||||||
|
// if (course.teacher.toString() !== userId) {
|
||||||
|
if (!course) {
|
||||||
|
throw new HttpException('دوره وجود ندارد', HttpStatus.FORBIDDEN);
|
||||||
|
}
|
||||||
|
return this.courseDataAccess.deleteCourse(courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async changeStatus(statusPostCommentDto): Promise<boolean> {
|
||||||
|
const { id, status } = statusPostCommentDto;
|
||||||
|
const result = await this.courseDataAccess.findByIdCom(id);
|
||||||
|
if (!result) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'COMMENT_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.courseDataAccess.changeStatus(id, status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { Body, Controller, Delete, Get, Param, Post, UseGuards } from '@nestjs/common';
|
||||||
|
import { ApiBearerAuth, ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { CourseCategoryService } from './courseCategory.service';
|
||||||
|
import { CourseCategory } from '../../models/schemas/courseCategory.schema';
|
||||||
|
import { CreateCourseCategoryDto } from '../../DTO/courseCategory.dto';
|
||||||
|
import { AuthGuard } from '../../auth/auth.guard';
|
||||||
|
import { Roles } from '../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../common/eNums/role.enum';
|
||||||
|
import { HttpStatus } from 'aws-sdk/clients/lambda';
|
||||||
|
|
||||||
|
@ApiTags('courseCategory')
|
||||||
|
@Controller('courseCategory')
|
||||||
|
export class CourseCategoryController {
|
||||||
|
constructor(private readonly courseCategoryService: CourseCategoryService) {}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'ایجاد دسته بندی دوره',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateCourseCategoryDto,
|
||||||
|
description: 'get Body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@Post()
|
||||||
|
async createCourseCategory(
|
||||||
|
@Body() createCourseCategoryDto: CreateCourseCategoryDto,
|
||||||
|
): Promise<CourseCategory> {
|
||||||
|
return this.courseCategoryService.createCourseCategory(
|
||||||
|
createCourseCategoryDto,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'دریافت همه دسته بندی ها',
|
||||||
|
})
|
||||||
|
@Get()
|
||||||
|
async getAllCourseCategories(): Promise<CourseCategory[]> {
|
||||||
|
return this.courseCategoryService.getAllCourseCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'دریافت دسته بندی دوره با آیدی',
|
||||||
|
})
|
||||||
|
@Get(':id')
|
||||||
|
async getCourseCategoryById(
|
||||||
|
@Param('id') id: string,
|
||||||
|
): Promise<CourseCategory> {
|
||||||
|
return this.courseCategoryService.getCourseCategoryById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'حذف دسته بندی دوره با آیدی',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@Delete(':id')
|
||||||
|
async deleteCourseCategoryById(@Param('id') id: string): Promise<{
|
||||||
|
status: HttpStatus;
|
||||||
|
message: string;
|
||||||
|
}> {
|
||||||
|
return this.courseCategoryService.deleteCourseCategoryById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { CourseCategoryController } from "./courseCategory.controller";
|
||||||
|
import { CourseCategoryService } from "./courseCategory.service";
|
||||||
|
import { CourseCategoryDataAccess } from "../../dataAccess/courseCategory.dataAccess";
|
||||||
|
import { UserDataAccess } from "../../dataAccess/user.dataAccess";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [CourseCategoryController],
|
||||||
|
providers: [
|
||||||
|
CourseCategoryService,
|
||||||
|
CourseCategoryDataAccess,
|
||||||
|
UserDataAccess
|
||||||
|
],
|
||||||
|
exports: [CourseCategoryService]
|
||||||
|
})
|
||||||
|
export class CourseCategoryModule {}
|
||||||
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from "@nestjs/common";
|
||||||
|
import { CreateCourseCategoryDto } from "../../DTO/courseCategory.dto";
|
||||||
|
import { CourseCategoryDataAccess } from "../../dataAccess/courseCategory.dataAccess";
|
||||||
|
import { CourseCategory } from "../../models/schemas/courseCategory.schema";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CourseCategoryService {
|
||||||
|
constructor(private courseCategoryDataAccess: CourseCategoryDataAccess) {}
|
||||||
|
|
||||||
|
async createCourseCategory(courseCategoryDto: CreateCourseCategoryDto): Promise<CourseCategory> {
|
||||||
|
return this.courseCategoryDataAccess.createCourseCategory(courseCategoryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllCourseCategories(): Promise<CourseCategory[]> {
|
||||||
|
return this.courseCategoryDataAccess.findAllCourseCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCourseCategoryById(id: string): Promise<CourseCategory> {
|
||||||
|
return this.courseCategoryDataAccess.findCourseCategoryById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteCourseCategoryById(id: string) {
|
||||||
|
const courses = await this.courseCategoryDataAccess.findCoursesByCategoryId(id);
|
||||||
|
console.log(courses)
|
||||||
|
if (courses.length > 0) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.BAD_REQUEST,
|
||||||
|
error: 'این دسته بندی نمیتواند حذف شود',
|
||||||
|
},
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.courseCategoryDataAccess.deleteCourseCategoryById(id);
|
||||||
|
return {
|
||||||
|
status: HttpStatus.OK,
|
||||||
|
message: 'دسته بندی با موفقیت حذف شد',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { CourseHeadlineService } from './courseHeadline.service';
|
||||||
|
import {
|
||||||
|
CreateCourseHeadlineDTO,
|
||||||
|
EpisodeDTO,
|
||||||
|
UpdateCourseHeadlineDTO,
|
||||||
|
} from '../../DTO/courseHeadline.dto';
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiBody,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import CourseHeadline from '../../models/courseHeadline.model';
|
||||||
|
import { AuthGuard } from '../../auth/auth.guard';
|
||||||
|
import { Roles } from '../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../common/eNums/role.enum';
|
||||||
|
import { CurrentUser } from '../../decorators/currentUser.decorator';
|
||||||
|
|
||||||
|
@ApiTags('courseHeadline')
|
||||||
|
@Controller('courseHeadline')
|
||||||
|
export class CourseHeadlineController {
|
||||||
|
constructor(private readonly courseHeadlineService: CourseHeadlineService) {}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'ایجاد سرفصل دوره',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateCourseHeadlineDTO,
|
||||||
|
description: 'get Body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@Post()
|
||||||
|
createCourseHeadline(
|
||||||
|
@Body() createCourseHeadlineDto: CreateCourseHeadlineDTO,
|
||||||
|
) {
|
||||||
|
return this.courseHeadlineService.createCourseHeadline(
|
||||||
|
createCourseHeadlineDto,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'دریافت سرفصل دوره با ایدی دوره',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: CourseHeadline,
|
||||||
|
})
|
||||||
|
@Get()
|
||||||
|
getCourseHeadlines(@Query('courseId') courseId: string) {
|
||||||
|
return this.courseHeadlineService.getCourseHeadlines(courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'ویرایش سرفصل دوره',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: UpdateCourseHeadlineDTO,
|
||||||
|
description: 'get Body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@Put(':id')
|
||||||
|
async updateCourseHeadline(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Body() updateCourseHeadlineDto: UpdateCourseHeadlineDTO,
|
||||||
|
) {
|
||||||
|
return this.courseHeadlineService.updateCourseHeadline(
|
||||||
|
id,
|
||||||
|
updateCourseHeadlineDto,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'ثبت پایان سرفصل دوره',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.User)
|
||||||
|
@Post('complete/:id')
|
||||||
|
async completeCourseHeadline(@Param('id') id: string, @CurrentUser() user) {
|
||||||
|
return this.courseHeadlineService.completeCourseHeadline(id, user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'حذف سرفصل دوره',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@Delete(':id')
|
||||||
|
async deleteCourseHeadline(@Param('id') id: string) {
|
||||||
|
return this.courseHeadlineService.deleteCourseHeadline(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'حذف سرفصل دوره از دوره',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@Post('removeHeadlineFromCourse/:courseId/:headlineId')
|
||||||
|
async removeHeadlineFromCourse(
|
||||||
|
@Param('courseId') courseId: string,
|
||||||
|
@Param('headlineId') headlineId: string,
|
||||||
|
) {
|
||||||
|
return this.courseHeadlineService.removeHeadlineFromCourse(
|
||||||
|
courseId,
|
||||||
|
headlineId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { CourseHeadlineController } from './courseHeadline.controller';
|
||||||
|
import { CourseHeadlineService } from './courseHeadline.service';
|
||||||
|
import { CourseDataAccess } from '../../dataAccess/course.dataAccess';
|
||||||
|
import { CourseModule } from '../course/course.module';
|
||||||
|
import { CourseHeadlineCommentModule } from './courseHeadlineComment/courseHeadlineComment.module';
|
||||||
|
import { CourseHeadlineDataAccess } from '../../dataAccess/courseHeadline.dataAccess';
|
||||||
|
import { UserDataAccess } from '../../dataAccess/user.dataAccess';
|
||||||
|
import { Tools } from '../../common/helpers/tools.helpers';
|
||||||
|
import { UploaderService } from '../common/uploader/uploader.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [CourseModule, CourseHeadlineCommentModule],
|
||||||
|
controllers: [CourseHeadlineController],
|
||||||
|
providers: [
|
||||||
|
CourseHeadlineService,
|
||||||
|
CourseHeadlineDataAccess,
|
||||||
|
CourseDataAccess,
|
||||||
|
UserDataAccess,
|
||||||
|
Tools,
|
||||||
|
UploaderService,
|
||||||
|
],
|
||||||
|
exports: [CourseHeadlineService],
|
||||||
|
})
|
||||||
|
export class CourseHeadlineModule {}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { CourseDataAccess } from '../../dataAccess/course.dataAccess';
|
||||||
|
import { CourseHeadlineDataAccess } from '../../dataAccess/courseHeadline.dataAccess';
|
||||||
|
import {
|
||||||
|
CreateCourseHeadlineDTO,
|
||||||
|
EpisodeDTO,
|
||||||
|
UpdateCourseHeadlineDTO,
|
||||||
|
} from '../../DTO/courseHeadline.dto';
|
||||||
|
import { Tools } from '../../common/helpers/tools.helpers';
|
||||||
|
import { UploaderService } from '../common/uploader/uploader.service';
|
||||||
|
import { CourseHeadline } from '../../models/schemas/courseHeadline.schema';
|
||||||
|
import { UserDataAccess } from '../../dataAccess/user.dataAccess';
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CourseHeadlineService {
|
||||||
|
constructor(
|
||||||
|
private courseHeadlineDataAccess: CourseHeadlineDataAccess,
|
||||||
|
private courseDataAccess: CourseDataAccess,
|
||||||
|
private tools: Tools,
|
||||||
|
private uploaderService: UploaderService,
|
||||||
|
private userDataAccess: UserDataAccess,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async createCourseHeadline(
|
||||||
|
createCourseHeadlineDto: CreateCourseHeadlineDTO,
|
||||||
|
): Promise<CourseHeadline> {
|
||||||
|
const course = await this.courseDataAccess.getOneCourse(
|
||||||
|
createCourseHeadlineDto.course,
|
||||||
|
);
|
||||||
|
if (!course) {
|
||||||
|
throw new Error('Course not found');
|
||||||
|
}
|
||||||
|
const files = await this.uploaderService.getFileUrl(
|
||||||
|
createCourseHeadlineDto.videoUrl,
|
||||||
|
);
|
||||||
|
console.log(files);
|
||||||
|
if (files.result == false) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' لینک ویدیو معتبر نمی باشد ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const time = await this.tools.timeVideo(files.url);
|
||||||
|
const result = await this.courseHeadlineDataAccess.createCourseHeadline(
|
||||||
|
createCourseHeadlineDto,
|
||||||
|
time,
|
||||||
|
);
|
||||||
|
course.courseHeadlines.push(result._id ? result._id : result.id);
|
||||||
|
await course.save();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCourseHeadlines(courseId: string): Promise<CourseHeadline[]> {
|
||||||
|
return await this.courseHeadlineDataAccess.getCourseHeadlines(courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addEpisodeToCourseHeadline(
|
||||||
|
courseHeadlineId: string,
|
||||||
|
episodeDto: EpisodeDTO,
|
||||||
|
): Promise<CourseHeadline> {
|
||||||
|
return await this.courseHeadlineDataAccess.addEpisode(
|
||||||
|
courseHeadlineId,
|
||||||
|
episodeDto,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeEpisodeFromCourseHeadline(
|
||||||
|
courseHeadlineId: string,
|
||||||
|
episodeId: string,
|
||||||
|
): Promise<CourseHeadline> {
|
||||||
|
return await this.courseHeadlineDataAccess.removeEpisode(
|
||||||
|
courseHeadlineId,
|
||||||
|
episodeId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateCourseHeadline(
|
||||||
|
id: string,
|
||||||
|
updateCourseHeadlineDto: UpdateCourseHeadlineDTO,
|
||||||
|
): Promise<CourseHeadline> {
|
||||||
|
return await this.courseHeadlineDataAccess.updateCourseHeadline(
|
||||||
|
id,
|
||||||
|
updateCourseHeadlineDto,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteCourseHeadline(
|
||||||
|
id: string,
|
||||||
|
): Promise<{ success: boolean; message: string }> {
|
||||||
|
const courseHeadline = await this.courseHeadlineDataAccess.getHeadline(id);
|
||||||
|
const removeHeadlineFromCourse = await this.removeHeadlineFromCourse(
|
||||||
|
courseHeadline.course.toString(),
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
if (removeHeadlineFromCourse.success) {
|
||||||
|
await this.courseHeadlineDataAccess.deleteCourseHeadline(id);
|
||||||
|
return { success: true, message: 'سرفصل با موفقیت حذف شد' };
|
||||||
|
}
|
||||||
|
return { success: false, message: 'حذف سرفصل با مشکل موجه شد' };
|
||||||
|
}
|
||||||
|
|
||||||
|
async completeCourseHeadline(
|
||||||
|
id: string,
|
||||||
|
userId: string,
|
||||||
|
): Promise<{ success: boolean; message: string }> {
|
||||||
|
const courseHeadline = await this.courseHeadlineDataAccess.getHeadline(id);
|
||||||
|
if (!courseHeadline) {
|
||||||
|
throw new HttpException('سرفصل یافت نشد', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
const user = await this.userDataAccess.findOne(userId);
|
||||||
|
if (
|
||||||
|
user.courseHeadlineProgress.includes(
|
||||||
|
courseHeadline._id as mongoose.Types.ObjectId,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new HttpException('سرفصل فبلا ثبت شده است', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
user.courseHeadlineProgress.push(
|
||||||
|
courseHeadline._id as mongoose.Types.ObjectId,
|
||||||
|
);
|
||||||
|
await user.save();
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'سرفصل با موفقیت ثبت شد',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeHeadlineFromCourse(courseId: string, headlineId: string) {
|
||||||
|
const course = await this.courseDataAccess.getOneCourse(courseId);
|
||||||
|
if (!course) {
|
||||||
|
throw new HttpException('کورس یافت نشد', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
const headline = await this.courseHeadlineDataAccess.getHeadline(
|
||||||
|
headlineId,
|
||||||
|
);
|
||||||
|
if (!headline) {
|
||||||
|
throw new HttpException('سرفصل یافت نشد', HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!course.courseHeadlines.some(
|
||||||
|
(headlineId) => headlineId._id.toString() === headline._id.toString(),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new HttpException(
|
||||||
|
'سرفصل در این دوره یافت نشد',
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.courseDataAccess.removeHeadlineFromCourse(courseId, headlineId);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: 'سرفصل با موفقیت از دوره حذف شد',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiBody,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { AuthGuard } from '../../../auth/auth.guard';
|
||||||
|
import { Role } from '../../../common/eNums/role.enum';
|
||||||
|
import { CurrentUser } from '../../../decorators/currentUser.decorator';
|
||||||
|
import { Roles } from '../../../decorators/role.decorators';
|
||||||
|
import { CourseHeadlineCommentService } from './courseHeadlineComment.service';
|
||||||
|
import {
|
||||||
|
CreateCourseHeadlineCommentDto,
|
||||||
|
StatusCommentDto,
|
||||||
|
} from '../../../DTO/courseHeadlineComment.dto';
|
||||||
|
|
||||||
|
@ApiTags('courseHeadlineComment')
|
||||||
|
@Controller('courseHeadlineComment')
|
||||||
|
export class CourseHeadlineCommentController {
|
||||||
|
constructor(
|
||||||
|
private readonly courseHeadlineCommentService: CourseHeadlineCommentService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// create post comment **********************************************************
|
||||||
|
@ApiOperation({ summary: ' ساخت کامنت ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'post comment Dto',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateCourseHeadlineCommentDto,
|
||||||
|
description: 'create comment Dto',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User, Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('create')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async createPostComment(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() createCourseHeadlineCommentDto: CreateCourseHeadlineCommentDto,
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this.courseHeadlineCommentService.create(
|
||||||
|
user,
|
||||||
|
createCourseHeadlineCommentDto,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reply post comment *********************************************************
|
||||||
|
@ApiOperation({ summary: ' پاسخ دادن کامنت یک پست ' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'PostComment Dto',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateCourseHeadlineCommentDto,
|
||||||
|
description: 'reply post comment',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin, Role.User)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('reply')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async replyPostComment(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() createCourseHeadlineCommentDto: CreateCourseHeadlineCommentDto,
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this.courseHeadlineCommentService.reply(
|
||||||
|
user,
|
||||||
|
createCourseHeadlineCommentDto,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// changeStatus postComment *********************************************************************
|
||||||
|
@ApiOperation({ summary: 'change status' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: 'api status',
|
||||||
|
type: Boolean,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: StatusCommentDto,
|
||||||
|
description: 'change status ',
|
||||||
|
})
|
||||||
|
@Patch('status')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async changeStatus(@Body() statusCommentDto: StatusCommentDto): Promise<any> {
|
||||||
|
try {
|
||||||
|
await this.courseHeadlineCommentService.changeStatus(statusCommentDto);
|
||||||
|
return { status: true };
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { CourseHeadlineCommentController } from './courseHeadlineComment.controller';
|
||||||
|
import { CourseHeadlineCommentService } from './courseHeadlineComment.service';
|
||||||
|
import { CourseHeadlineDataAccess } from '../../../dataAccess/courseHeadline.dataAccess';
|
||||||
|
import { CourseHeadlineCommentDataAccess } from '../../../dataAccess/courseHeadlineComment.dataAccess';
|
||||||
|
import { UserDataAccess } from '../../../dataAccess/user.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [CourseHeadlineCommentController],
|
||||||
|
providers: [
|
||||||
|
CourseHeadlineCommentService,
|
||||||
|
CourseHeadlineCommentDataAccess,
|
||||||
|
CourseHeadlineDataAccess,
|
||||||
|
UserDataAccess,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CourseHeadlineCommentModule {}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { CourseHeadlineDataAccess } from '../../../dataAccess/courseHeadline.dataAccess';
|
||||||
|
import { CourseHeadlineCommentDataAccess } from '../../../dataAccess/courseHeadlineComment.dataAccess';
|
||||||
|
import { ItemStatusCourseHeadline } from '../../../common/eNums/itemStatusCourseHeadline.enum';
|
||||||
|
import { CreateCourseHeadlineCommentDto } from '../../../DTO/courseHeadlineComment.dto';
|
||||||
|
import { ItemStatus } from '../../../common/eNums/itemStatus.enum';
|
||||||
|
@Injectable()
|
||||||
|
export class CourseHeadlineCommentService {
|
||||||
|
constructor(
|
||||||
|
private courseHeadlineDataAccess: CourseHeadlineDataAccess,
|
||||||
|
private courseHeadlineCommentDataAccess: CourseHeadlineCommentDataAccess,
|
||||||
|
) {}
|
||||||
|
// create ***************************************************
|
||||||
|
async create(
|
||||||
|
userInf,
|
||||||
|
createCourseHeadlineCommentDto: CreateCourseHeadlineCommentDto,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const { text, id } = createCourseHeadlineCommentDto;
|
||||||
|
const courseHeadline = await this.courseHeadlineDataAccess.getHeadline(id);
|
||||||
|
if (
|
||||||
|
!courseHeadline ||
|
||||||
|
courseHeadline.status !== ItemStatusCourseHeadline.Completed
|
||||||
|
) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'سرفصل دوره وجود ندارد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const comment = await this.courseHeadlineCommentDataAccess.create(
|
||||||
|
id,
|
||||||
|
userInf.id,
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
courseHeadline.Comment.push(comment._id ? comment._id : comment.id);
|
||||||
|
courseHeadline.save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// replay *******************************************************
|
||||||
|
async reply(
|
||||||
|
userInf,
|
||||||
|
createCourseHeadlineCommentDto: CreateCourseHeadlineCommentDto,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const { text, id } = createCourseHeadlineCommentDto;
|
||||||
|
|
||||||
|
const courseHeadlineComment =
|
||||||
|
await this.courseHeadlineCommentDataAccess.findById(id);
|
||||||
|
if (
|
||||||
|
!courseHeadlineComment ||
|
||||||
|
courseHeadlineComment.status !== ItemStatus.Active
|
||||||
|
) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'کامنت وجود ندارد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const replyComment =
|
||||||
|
await this.courseHeadlineCommentDataAccess.replyComment(
|
||||||
|
courseHeadlineComment.courseHeadline._id,
|
||||||
|
id,
|
||||||
|
userInf.id,
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
courseHeadlineComment.Replies.push(replyComment.id);
|
||||||
|
courseHeadlineComment.save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async changeStatus(statusPostCommentDto): Promise<boolean> {
|
||||||
|
const { id, status } = statusPostCommentDto;
|
||||||
|
const result = await this.courseHeadlineCommentDataAccess.findById(id);
|
||||||
|
if (!result) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'COMMENT_NOT_FOUND',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.courseHeadlineCommentDataAccess.changeStatus(id, status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
DefaultValuePipe,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Param,
|
||||||
|
ParseIntPipe,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
|
UploadedFile,
|
||||||
|
UseGuards,
|
||||||
|
UseInterceptors,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { TicketService } from './ticket.service';
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiBody,
|
||||||
|
ApiOperation,
|
||||||
|
ApiQuery,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { CreateTicketDto, CreateTicketMessageDto } from '../../DTO/ticket.dto';
|
||||||
|
import { CurrentUser } from '../../decorators/currentUser.decorator';
|
||||||
|
import { AuthGuard } from '../../auth/auth.guard';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { UploaderService } from '../common/uploader/uploader.service';
|
||||||
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||||||
|
import { Roles } from '../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../common/eNums/role.enum';
|
||||||
|
@ApiTags('ticket')
|
||||||
|
@Controller('ticket')
|
||||||
|
export class TicketController {
|
||||||
|
constructor(
|
||||||
|
private ticketService: TicketService,
|
||||||
|
private uploaderService: UploaderService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'ایجاد تیکت',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateTicketDto,
|
||||||
|
description: 'get body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.User, Role.Admin)
|
||||||
|
@Post()
|
||||||
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
async create(@Body() createTicketDto: CreateTicketDto, @CurrentUser() user) {
|
||||||
|
return this.ticketService.create(createTicketDto, user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'لیست تیکت ها',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiQuery({ name: 'page', required: false, type: Number })
|
||||||
|
@ApiQuery({ name: 'pageSize', required: false, type: Number })
|
||||||
|
@Roles(Role.User, Role.Admin, Role.Reader, Role.Writer)
|
||||||
|
@Get()
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async findAll(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Query('page', new DefaultValuePipe(1), ParseIntPipe) page = 1,
|
||||||
|
@Query('pageSize', new DefaultValuePipe(10), ParseIntPipe) pageSize = 10,
|
||||||
|
) {
|
||||||
|
return this.ticketService.findAll({ page, pageSize }, user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'دریافت تیکت با آیدی',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.User, Role.Admin, Role.Reader, Role.Writer)
|
||||||
|
@Get(':id')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async findOne(@Param('id') id: string) {
|
||||||
|
return this.ticketService.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'ایجاد پیام تیکت',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: CreateTicketMessageDto,
|
||||||
|
description: 'get body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.User, Role.Admin, Role.Reader, Role.Writer)
|
||||||
|
@Post('message')
|
||||||
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
async createMessage(
|
||||||
|
@Body() createTicketMessageDto: CreateTicketMessageDto,
|
||||||
|
@CurrentUser() user,
|
||||||
|
) {
|
||||||
|
return this.ticketService.createMessage(createTicketMessageDto, user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'بستن تیکت',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Roles(Role.Admin)
|
||||||
|
@Patch('close/:id')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
async closeTicket(@Param('id') id: string) {
|
||||||
|
return this.ticketService.closeTicket(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('upload')
|
||||||
|
@UseInterceptors(FileInterceptor('file'))
|
||||||
|
async uploadFile(@UploadedFile() file: Express.Multer.File) {
|
||||||
|
console.log('file', file);
|
||||||
|
return this.uploaderService.upload(file, 'ticket');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TicketController } from './ticket.controller';
|
||||||
|
import { TicketService } from './ticket.service';
|
||||||
|
import { UserModule } from '../user/user.module';
|
||||||
|
import { UserDataAccess } from '../../dataAccess/user.dataAccess';
|
||||||
|
import { TicketDataAccess } from '../../dataAccess/ticket.dataAccess';
|
||||||
|
import { TicketMessageDataAccess } from '../../dataAccess/ticketMessage.dataAccess';
|
||||||
|
import { UploaderService } from '../common/uploader/uploader.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
UserModule,
|
||||||
|
],
|
||||||
|
controllers: [TicketController],
|
||||||
|
providers: [
|
||||||
|
TicketService,
|
||||||
|
TicketDataAccess,
|
||||||
|
TicketMessageDataAccess,
|
||||||
|
UserDataAccess,
|
||||||
|
UploaderService
|
||||||
|
],
|
||||||
|
exports: [TicketService]
|
||||||
|
})
|
||||||
|
export class TicketModule { }
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { CreateTicketDto, CreateTicketMessageDto } from '../../DTO/ticket.dto';
|
||||||
|
import { TicketDataAccess } from '../../dataAccess/ticket.dataAccess';
|
||||||
|
import { UserDataAccess } from '../../dataAccess/user.dataAccess';
|
||||||
|
import { Ticket } from '../../models/schemas/ticket.schema';
|
||||||
|
import { TicketMessage } from '../../models/schemas/ticketMessage.schema';
|
||||||
|
import { TicketMessageDataAccess } from '../../dataAccess/ticketMessage.dataAccess';
|
||||||
|
import { PaginationDto } from '../../DTO/pagination.dto';
|
||||||
|
import { Role } from '../../common/eNums/role.enum';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TicketService {
|
||||||
|
constructor(
|
||||||
|
private readonly ticketDataAccess: TicketDataAccess,
|
||||||
|
private readonly ticketMessageDataAccess: TicketMessageDataAccess,
|
||||||
|
private readonly userDataAccess: UserDataAccess,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async create(
|
||||||
|
createTicketDto: CreateTicketDto,
|
||||||
|
userId: string,
|
||||||
|
): Promise<{ message: string }> {
|
||||||
|
const user = await this.userDataAccess.findOne(userId);
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'چنین کاربری یافت نشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const result = await this.ticketDataAccess.create(
|
||||||
|
createTicketDto.subject,
|
||||||
|
user._id.toString(),
|
||||||
|
);
|
||||||
|
if (!result) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.BAD_REQUEST,
|
||||||
|
error: 'تیکت با مشکل هایی ثبت نشد',
|
||||||
|
},
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return { message: 'تیکت با موفقیت ثبت شد' };
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: string): Promise<Ticket> {
|
||||||
|
const result = await this.ticketDataAccess.findOne(id);
|
||||||
|
if (!result) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'چنین تیکتی یافت نشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(paginationDto: PaginationDto, userId: string) {
|
||||||
|
const user = await this.userDataAccess.findOne(userId);
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'چنین کاربری یافت نشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (user.roles.includes(Role.Admin)) {
|
||||||
|
const result = await this.ticketDataAccess.findAll(
|
||||||
|
paginationDto.page,
|
||||||
|
paginationDto.pageSize,
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const result = await this.ticketDataAccess.findAllByUser(
|
||||||
|
userId,
|
||||||
|
paginationDto.page,
|
||||||
|
paginationDto.pageSize,
|
||||||
|
);
|
||||||
|
const count = await this.ticketDataAccess.count();
|
||||||
|
|
||||||
|
return { result, count };
|
||||||
|
}
|
||||||
|
|
||||||
|
async createMessage(
|
||||||
|
createTicketMessageDto: CreateTicketMessageDto,
|
||||||
|
userId: string,
|
||||||
|
): Promise<TicketMessage> {
|
||||||
|
const ticket = await this.ticketDataAccess.findOne(
|
||||||
|
createTicketMessageDto.ticketId,
|
||||||
|
);
|
||||||
|
if (!ticket) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'چنین تیکتی یافت نشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (ticket.status === 'closed') {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.BAD_REQUEST,
|
||||||
|
error: 'تیکت بسته شده است و نمیتوان پیام جدیدی ایجاد کرد',
|
||||||
|
},
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const newTicketMessage = await this.ticketMessageDataAccess.createMessage(
|
||||||
|
createTicketMessageDto,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
if (ticket) {
|
||||||
|
ticket.ticketMessages.push(
|
||||||
|
newTicketMessage._id ? newTicketMessage._id : newTicketMessage.id,
|
||||||
|
);
|
||||||
|
await ticket.save();
|
||||||
|
}
|
||||||
|
return newTicketMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeTicket(id: string): Promise<{ message: string }> {
|
||||||
|
await this.ticketDataAccess.closeTicket(id);
|
||||||
|
return { message: 'تیکت با موفقیت بسته شد' };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiBody,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { Roles } from '../../decorators/role.decorators';
|
||||||
|
import { Role } from '../../common/eNums/role.enum';
|
||||||
|
import { AuthGuard } from '../../auth/auth.guard';
|
||||||
|
import {
|
||||||
|
AddBalanceDto,
|
||||||
|
RegisterCourseDto,
|
||||||
|
UserDto,
|
||||||
|
UserUpdateDto,
|
||||||
|
} from '../../DTO/user.dto';
|
||||||
|
import { CurrentUser } from '../../decorators/currentUser.decorator';
|
||||||
|
|
||||||
|
@ApiTags('user')
|
||||||
|
@Controller('user')
|
||||||
|
export class UserController {
|
||||||
|
constructor(private readonly userService: UserService) {}
|
||||||
|
|
||||||
|
// profile user ************************************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' پروفایل کاربر ',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: ' اطلاعات کاربر ',
|
||||||
|
type: UserDto,
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User, Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Get('')
|
||||||
|
async getProfile(@CurrentUser() user) {
|
||||||
|
try {
|
||||||
|
const result = await this.userService.profile(user.id);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update user ************************************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' بروز رسانی پروفایل کاربر ',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: ' UserDto ',
|
||||||
|
type: UserDto,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: UserUpdateDto,
|
||||||
|
description: 'get body',
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User, Role.Admin)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Put('update')
|
||||||
|
async updateUser(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() userUpdateDto: UserUpdateDto,
|
||||||
|
): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const result = await this.userService.update(user.id, userUpdateDto);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update user ************************************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'ثبت نام دوره',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: RegisterCourseDto,
|
||||||
|
description: 'get body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('registerCourse')
|
||||||
|
async registerCourse(
|
||||||
|
@CurrentUser() user,
|
||||||
|
@Body() registerCourseDto: RegisterCourseDto,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const result = await this.userService.registerCourse(
|
||||||
|
user.id,
|
||||||
|
registerCourseDto,
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'افزایش موجودی کیف پول',
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: AddBalanceDto,
|
||||||
|
description: 'get body',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('add')
|
||||||
|
addBalance(@CurrentUser() user, @Body() addBalanceDto: AddBalanceDto) {
|
||||||
|
return this.userService.addBalance(
|
||||||
|
user.id,
|
||||||
|
addBalanceDto.amount,
|
||||||
|
addBalanceDto.paymentNumber,
|
||||||
|
addBalanceDto.reason,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'موجودی کیف پول',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Get('balance')
|
||||||
|
getBalance(@CurrentUser() user) {
|
||||||
|
return this.userService.getBalance(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'لیست تراکنش ها',
|
||||||
|
})
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
@Roles(Role.User)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Get('payments')
|
||||||
|
getPayments(@CurrentUser() user) {
|
||||||
|
return this.userService.getPayments(user.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { UserDataAccess } from '../../dataAccess/user.dataAccess';
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
import { UserController } from './user.controller';
|
||||||
|
import { DiscountDataAccess } from '../../dataAccess/discount.dataAccess';
|
||||||
|
import { UserDiscountDataAccess } from '../../dataAccess/userDiscount.dataAccess';
|
||||||
|
import { DiscountHistoryDataAccess } from '../../dataAccess/discountHistory.dataAccess';
|
||||||
|
import { PaymentDataAccess } from '../../dataAccess/payment.dataAccess';
|
||||||
|
import { CourseHeadlineDataAccess } from '../../dataAccess/courseHeadline.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [],
|
||||||
|
controllers: [UserController],
|
||||||
|
providers: [
|
||||||
|
UserService,
|
||||||
|
UserDataAccess,
|
||||||
|
UserDiscountDataAccess,
|
||||||
|
DiscountHistoryDataAccess,
|
||||||
|
DiscountDataAccess,
|
||||||
|
PaymentDataAccess,
|
||||||
|
CourseHeadlineDataAccess,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class UserModule {}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
RegisterCourseDto,
|
||||||
|
UserDto,
|
||||||
|
userObj,
|
||||||
|
UserUpdateDto,
|
||||||
|
} from '../../DTO/user.dto';
|
||||||
|
import { UserDataAccess } from '../../dataAccess/user.dataAccess';
|
||||||
|
import { DiscountDataAccess } from '../../dataAccess/discount.dataAccess';
|
||||||
|
import { UserDiscountDataAccess } from '../../dataAccess/userDiscount.dataAccess';
|
||||||
|
import { DiscountHistoryDataAccess } from '../../dataAccess/discountHistory.dataAccess';
|
||||||
|
import { PaymentDataAccess } from '../../dataAccess/payment.dataAccess';
|
||||||
|
import { CourseHeadlineDataAccess } from '../../dataAccess/courseHeadline.dataAccess';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserService {
|
||||||
|
constructor(
|
||||||
|
private readonly userDataAccess: UserDataAccess,
|
||||||
|
private readonly courseHeadlineDataAccess: CourseHeadlineDataAccess,
|
||||||
|
private readonly discountDataAccess: DiscountDataAccess,
|
||||||
|
private readonly userDiscountDataAccess: UserDiscountDataAccess,
|
||||||
|
private readonly discountHistoryDataAccess: DiscountHistoryDataAccess,
|
||||||
|
private readonly paymentDataAccess: PaymentDataAccess,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async profile(id) {
|
||||||
|
const result = await this.userDataAccess.findOne(id);
|
||||||
|
let arr = [];
|
||||||
|
await Promise.all(
|
||||||
|
result.courseIds.map(async (item) => {
|
||||||
|
const courseHeadlines =
|
||||||
|
await this.courseHeadlineDataAccess.getCourseHeadlines(item._id);
|
||||||
|
const totalHeadlines = courseHeadlines.length;
|
||||||
|
let completionPercentage = 0;
|
||||||
|
console.log(result.courseHeadlineProgress);
|
||||||
|
if (result.courseHeadlineProgress) {
|
||||||
|
const completedHeadlines = result.courseHeadlineProgress.filter(
|
||||||
|
(progress) =>
|
||||||
|
courseHeadlines.some(
|
||||||
|
(headline) => headline._id.toString() === progress.toString(),
|
||||||
|
),
|
||||||
|
).length;
|
||||||
|
|
||||||
|
completionPercentage = (completedHeadlines / totalHeadlines) * 100;
|
||||||
|
}
|
||||||
|
arr.push({ item, completionPercentage });
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
console.log(arr);
|
||||||
|
|
||||||
|
return { user: userObj(result), com: arr };
|
||||||
|
}
|
||||||
|
// update profile
|
||||||
|
async update(userId, userUpdateDto: UserUpdateDto): Promise<boolean> {
|
||||||
|
const { avatarMedia, name, lastName, userName, email } = userUpdateDto;
|
||||||
|
|
||||||
|
const user = await this.userDataAccess.findOne(userId);
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' کاربر وجود ندارد ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// TODO:cityId check
|
||||||
|
// TODO:proId check
|
||||||
|
// TODO:avatarId check
|
||||||
|
await this.userDataAccess.update(
|
||||||
|
userId,
|
||||||
|
avatarMedia,
|
||||||
|
name,
|
||||||
|
lastName,
|
||||||
|
userName,
|
||||||
|
email,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async registerCourse(userId: string, registerCourseDto: RegisterCourseDto) {
|
||||||
|
const { code } = registerCourseDto;
|
||||||
|
if (code) {
|
||||||
|
const result = await this.discountDataAccess.findCode(
|
||||||
|
registerCourseDto.code,
|
||||||
|
);
|
||||||
|
if (!result) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'تخفیف مورد نظر موجود نمی باشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.allUsers === true) {
|
||||||
|
const userDiscount = await this.userDiscountDataAccess.findOne(
|
||||||
|
result.id,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
if (!userDiscount) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'تخفیف برای شما نمی باشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const discountHistory = await this.discountHistoryDataAccess.findOne(
|
||||||
|
result.id,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
if (discountHistory) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' شما از این تخفیف استفاده کردید ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.discountHistoryDataAccess.create(
|
||||||
|
result,
|
||||||
|
userDiscount,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const discountHistory = await this.discountHistoryDataAccess.findOne(
|
||||||
|
result.id,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
if (discountHistory) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' شما از این تخفیف استفاده کردید ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await this.discountHistoryDataAccess.create(result, null, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.userDataAccess.registerCourse(
|
||||||
|
userId,
|
||||||
|
registerCourseDto.courseId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addBalance(
|
||||||
|
userId: string,
|
||||||
|
amount: number,
|
||||||
|
paymentNumber: string,
|
||||||
|
reason: string,
|
||||||
|
) {
|
||||||
|
const user = await this.userDataAccess.findOne(userId);
|
||||||
|
const payment = await this.paymentDataAccess.createPayment(
|
||||||
|
userId,
|
||||||
|
amount,
|
||||||
|
paymentNumber,
|
||||||
|
reason,
|
||||||
|
);
|
||||||
|
user.walletBalance += amount;
|
||||||
|
return await user.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBalance(userId: string): Promise<number> {
|
||||||
|
const user = await this.userDataAccess.findOne(userId);
|
||||||
|
return user.walletBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPayments(userId: string) {
|
||||||
|
const user = await this.userDataAccess.findOne(userId);
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_FOUND,
|
||||||
|
error: 'کاربر وجود ندارد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_FOUND,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const payments = await this.paymentDataAccess.getPaymentsByUserId(userId);
|
||||||
|
return payments;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { ApiBody, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
LoginDto,
|
||||||
|
LoginRes,
|
||||||
|
LoginStepTwoDto,
|
||||||
|
UserMidDto,
|
||||||
|
} from '../DTO/user.dto';
|
||||||
|
import { IpAddress } from '../decorators/ipAddress.decorator';
|
||||||
|
|
||||||
|
@ApiTags('auth')
|
||||||
|
@Controller('auth')
|
||||||
|
export class AuthController {
|
||||||
|
constructor(private authService: AuthService) {}
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' ورود / ثبت نام مرحله یک ',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: ' ارسال پیام ',
|
||||||
|
type: LoginRes,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: LoginDto,
|
||||||
|
description: 'get body',
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Post('login')
|
||||||
|
signIn(
|
||||||
|
@Body() loginDto: LoginDto,
|
||||||
|
@IpAddress() ip: string,
|
||||||
|
): Promise<LoginRes> {
|
||||||
|
return this.authService.signIn(loginDto, ip);
|
||||||
|
}
|
||||||
|
// login step two ********************
|
||||||
|
@ApiOperation({
|
||||||
|
summary: ' ورود / ثبت نام مرحله دو ',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: ' جزئیات کاربر ',
|
||||||
|
type: UserMidDto,
|
||||||
|
})
|
||||||
|
@ApiBody({
|
||||||
|
type: LoginStepTwoDto,
|
||||||
|
description: 'get body',
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Post('login-step-two')
|
||||||
|
signVerify(@Body() loginStepTwoDto: LoginStepTwoDto): Promise<UserMidDto> {
|
||||||
|
return this.authService.signInStepTwoVerify(loginStepTwoDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import {
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
Injectable,
|
||||||
|
UnauthorizedException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
import { jwtConstants } from './constants';
|
||||||
|
import { Request } from 'express';
|
||||||
|
import { UserDataAccess } from '../dataAccess/user.dataAccess';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
|
import { Role } from '../common/eNums/role.enum';
|
||||||
|
import { ROLES_KEY } from '../decorators/role.decorators';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthGuard implements CanActivate {
|
||||||
|
constructor(
|
||||||
|
private jwtService: JwtService,
|
||||||
|
private reflector: Reflector,
|
||||||
|
private readonly userDataAccess: UserDataAccess,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
|
const request = context.switchToHttp().getRequest();
|
||||||
|
const token = this.extractTokenFromHeader(request);
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = await this.jwtService.verifyAsync(token, {
|
||||||
|
secret: jwtConstants.secret,
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await this.userDataAccess.findOne(payload.id);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(
|
||||||
|
ROLES_KEY,
|
||||||
|
[context.getHandler(), context.getClass()],
|
||||||
|
);
|
||||||
|
if (!requiredRoles) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
request['user'] = payload;
|
||||||
|
return requiredRoles.some((role) => user.roles?.includes(role));
|
||||||
|
} catch {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractTokenFromHeader(request: Request): string | undefined {
|
||||||
|
const [type, token] = request.headers.authorization?.split(' ') ?? [];
|
||||||
|
|
||||||
|
return type === 'Bearer' ? token : undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { UserModule } from '../application/user/user.module';
|
||||||
|
import { UserService } from '../application/user/user.service';
|
||||||
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
import { AuthController } from './auth.controller';
|
||||||
|
import { jwtConstants } from './constants';
|
||||||
|
import { Tools } from '../common/helpers/tools.helpers';
|
||||||
|
import { UserSmsDataAccess } from '../dataAccess/userSms.dataAccess';
|
||||||
|
import { UserDataAccess } from '../dataAccess/user.dataAccess';
|
||||||
|
import { DiscountDataAccess } from '../dataAccess/discount.dataAccess';
|
||||||
|
import { connectionListener } from '../common/listeners/connection.listeners';
|
||||||
|
import { UserDiscountDataAccess } from '../dataAccess/userDiscount.dataAccess';
|
||||||
|
import { DiscountHistoryDataAccess } from '../dataAccess/discountHistory.dataAccess';
|
||||||
|
import { PaymentDataAccess } from '../dataAccess/payment.dataAccess';
|
||||||
|
import { CourseHeadlineDataAccess } from '../dataAccess/courseHeadline.dataAccess';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
UserModule,
|
||||||
|
JwtModule.register({
|
||||||
|
global: true,
|
||||||
|
secret: jwtConstants.secret,
|
||||||
|
signOptions: { expiresIn: '604800s' },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
AuthService,
|
||||||
|
UserService,
|
||||||
|
Tools,
|
||||||
|
UserSmsDataAccess,
|
||||||
|
UserDataAccess,
|
||||||
|
DiscountHistoryDataAccess,
|
||||||
|
connectionListener,
|
||||||
|
UserDiscountDataAccess,
|
||||||
|
DiscountDataAccess,
|
||||||
|
PaymentDataAccess,
|
||||||
|
CourseHeadlineDataAccess,
|
||||||
|
],
|
||||||
|
controllers: [AuthController],
|
||||||
|
exports: [AuthService],
|
||||||
|
})
|
||||||
|
export class AuthModule {}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import { HttpException, Injectable, HttpStatus } from '@nestjs/common';
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
import { Tools } from '../common/helpers/tools.helpers';
|
||||||
|
import {
|
||||||
|
LoginDto,
|
||||||
|
LoginStepTwoDto,
|
||||||
|
UserMidDto,
|
||||||
|
userMidObj,
|
||||||
|
} from '../DTO/user.dto';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { UserSmsDataAccess } from '../dataAccess/userSms.dataAccess';
|
||||||
|
import { UserDataAccess } from '../dataAccess/user.dataAccess';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
import { PhoneEvent } from '../common/events/phone.event';
|
||||||
|
const env = process.env.NODE_ENV;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthService {
|
||||||
|
constructor(
|
||||||
|
private eventEmitter: EventEmitter2,
|
||||||
|
private jwtService: JwtService,
|
||||||
|
private readonly tools: Tools,
|
||||||
|
private readonly userSmsDataAccess: UserSmsDataAccess,
|
||||||
|
private readonly userDataAccess: UserDataAccess,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async signIn(loginDto: LoginDto, ip): Promise<{ message: string }> {
|
||||||
|
const { phone, type } = loginDto;
|
||||||
|
const phoneConfirm = this.tools.removeZero(phone);
|
||||||
|
const validNumber = this.tools.validNumberWithRegex(phoneConfirm);
|
||||||
|
if (validNumber == false) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: 'شماره وارد شده معتبر نیست',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const code = this.tools.codeCreator();
|
||||||
|
const createdEvent = new PhoneEvent();
|
||||||
|
createdEvent.phone = phone;
|
||||||
|
createdEvent.code = code;
|
||||||
|
const smsRq = await this.userSmsDataAccess.findSmsRq(phoneConfirm, ip);
|
||||||
|
if (parseInt(type) === 1 || parseInt(type) === 2) {
|
||||||
|
if (smsRq) {
|
||||||
|
if (smsRq.sendCount > 1000) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: 'شما بیش از حد تلاش کرده اید',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const now = moment(new Date());
|
||||||
|
const secondsDiff = now.diff(moment(smsRq.lastSentTime), 'seconds');
|
||||||
|
if (secondsDiff < 60 && smsRq.phone === phoneConfirm) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: 'برای ارسال کد جدید 1 دقیقه صبر کنید',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const sendCount = smsRq.sendCount + 1;
|
||||||
|
|
||||||
|
if (env !== 'development' && env !== 'test') {
|
||||||
|
if (parseInt(type) === 1) {
|
||||||
|
this.eventEmitter.emit('sms.created', createdEvent);
|
||||||
|
} else {
|
||||||
|
this.eventEmitter.emit('call.created', createdEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.userSmsDataAccess.updateSmsRq(
|
||||||
|
smsRq.id,
|
||||||
|
phoneConfirm,
|
||||||
|
sendCount,
|
||||||
|
ip,
|
||||||
|
code,
|
||||||
|
);
|
||||||
|
return { message: 'کد با موفقیت ارسال شد' };
|
||||||
|
} else {
|
||||||
|
if (env !== 'development' && env !== 'test') {
|
||||||
|
if (parseInt(type) === 1) {
|
||||||
|
this.eventEmitter.emit('sms.created', createdEvent);
|
||||||
|
} else {
|
||||||
|
this.eventEmitter.emit('call.created', createdEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.userSmsDataAccess.createSmsRq(phoneConfirm, ip, code);
|
||||||
|
return {
|
||||||
|
message: 'کد با موفقیت ارسال شد',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: ' نوع ارتباط به درستی وارد نشده است ',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async signInStepTwoVerify(
|
||||||
|
loginStepTwoDto: LoginStepTwoDto,
|
||||||
|
): Promise<UserMidDto> {
|
||||||
|
const { phone, code } = loginStepTwoDto;
|
||||||
|
const smsRq = await this.userSmsDataAccess.validateCode(phone, code);
|
||||||
|
if (!smsRq) {
|
||||||
|
throw new HttpException(
|
||||||
|
{
|
||||||
|
status: HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
error: 'کد اشتباه می باشد',
|
||||||
|
},
|
||||||
|
HttpStatus.NOT_ACCEPTABLE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const userExist = await this.userDataAccess.findByPhone(phone);
|
||||||
|
if (!userExist) {
|
||||||
|
const user = await this.userDataAccess.create(phone);
|
||||||
|
const payload = { id: user.id };
|
||||||
|
const jwtConf = await this.jwtService.signAsync(payload);
|
||||||
|
await this.userDataAccess.updateJwt(user.id, jwtConf);
|
||||||
|
const userSubmit = await this.userDataAccess.findOne(user.id);
|
||||||
|
await this.userSmsDataAccess.deleteSms(smsRq.id);
|
||||||
|
return userMidObj(userSubmit);
|
||||||
|
} else {
|
||||||
|
const payload = { id: userExist.id };
|
||||||
|
const jwtConf = await this.jwtService.signAsync(payload);
|
||||||
|
await this.userDataAccess.updateJwt(userExist.id, jwtConf);
|
||||||
|
const finalUser = await this.userDataAccess.findOne(userExist.id);
|
||||||
|
await this.userSmsDataAccess.deleteSms(smsRq.id);
|
||||||
|
return userMidObj(finalUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const jwtConstants = {
|
||||||
|
secret: '8FWQDF%$ERDFGH#$FDSJ&*#@R1!34Vh',
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const commentType = {
|
||||||
|
Course: 'دوره',
|
||||||
|
BlogPost: 'پست بلاگ',
|
||||||
|
};
|
||||||
|
export default commentType;
|
||||||
|
export enum CommentType {
|
||||||
|
'Course' = 'Course',
|
||||||
|
'BlogPost' = 'BlogPost',
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
const discountStatus = {
|
||||||
|
active: {
|
||||||
|
code: true,
|
||||||
|
text: 'فعال',
|
||||||
|
},
|
||||||
|
deActive: {
|
||||||
|
code: false,
|
||||||
|
text: 'غیرفعال',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default discountStatus;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const discountTypeEnum = {
|
||||||
|
percent: 'درصد',
|
||||||
|
price: 'قیمت واحد',
|
||||||
|
};
|
||||||
|
export default discountTypeEnum;
|
||||||
|
export enum DiscountTypeEnum {
|
||||||
|
'percent' = 'percent',
|
||||||
|
'price' = 'price',
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
const discountUser = {
|
||||||
|
public: {
|
||||||
|
code: true,
|
||||||
|
text: 'عمومی',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
code: false,
|
||||||
|
text: 'خاص',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default discountUser;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const gender = {
|
||||||
|
Men: 'مرد',
|
||||||
|
Women: 'زن',
|
||||||
|
};
|
||||||
|
export default gender;
|
||||||
|
export enum Gender {
|
||||||
|
'Men' = 'Men',
|
||||||
|
'Women' = 'Women',
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const itemStatuses = {
|
||||||
|
Active: 'فعال',
|
||||||
|
DeActivate: 'غیر فعال',
|
||||||
|
Archived: 'بایگانی شده',
|
||||||
|
Pending: 'در حال بررسی',
|
||||||
|
};
|
||||||
|
export default itemStatuses;
|
||||||
|
export enum ItemStatus {
|
||||||
|
'Active' = 'Active',
|
||||||
|
'DeActivate' = 'DeActivate',
|
||||||
|
'Archived' = 'Archived',
|
||||||
|
'Pending' = 'Pending',
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const itemStatusCourseHeadline = {
|
||||||
|
Completed: 'تکمیل شده',
|
||||||
|
DeActivate: 'غیر فعال',
|
||||||
|
Recording: 'درحال ضبط',
|
||||||
|
Soon: 'بزودی',
|
||||||
|
};
|
||||||
|
export default itemStatusCourseHeadline;
|
||||||
|
export enum ItemStatusCourseHeadline {
|
||||||
|
'Completed' = 'Completed',
|
||||||
|
'DeActivate' = 'DeActivate',
|
||||||
|
'Recording' = 'Recording',
|
||||||
|
'Soon' = 'Soon',
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user