update:add ci cd files ==> do not edit those file
This commit is contained in:
@@ -1,18 +1,27 @@
|
||||
PORT=6060
|
||||
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://api-delearn.iran.liara.run
|
||||
MONGO_CONNECTION_HOST=127.0.0.1 mongodb://127.0.0.1:27017/delearn
|
||||
MONGO_CONNECTION_PORT=27017
|
||||
MONGO_CONNECTION_NAME=delearn
|
||||
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=127.0.0.1
|
||||
MONGO_CONNECTION_PORT=27017
|
||||
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 = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
|
||||
rules: {
|
||||
'linebreak-style': 0,
|
||||
strict: 0,
|
||||
'max-len': ['error', { code: 120 }],
|
||||
'quote-props': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'func-names': ['error', 'as-needed'],
|
||||
'prefer-const': 'warn',
|
||||
radix: 'off',
|
||||
'no-restricted-syntax': ['error', "BinaryExpression[operator='at']"],
|
||||
'no-await-in-loop': 'off',
|
||||
'no-trailing-spaces': 'off',
|
||||
'object-curly-spacing': 'off',
|
||||
'object-curly-newline': [
|
||||
'error',
|
||||
{
|
||||
ObjectExpression: {
|
||||
multiline: true,
|
||||
minProperties: 6,
|
||||
consistent: true,
|
||||
},
|
||||
ObjectPattern: { multiline: true, minProperties: 6, consistent: true },
|
||||
ImportDeclaration: {
|
||||
multiline: true,
|
||||
minProperties: 6,
|
||||
consistent: true,
|
||||
},
|
||||
ExportDeclaration: {
|
||||
multiline: true,
|
||||
minProperties: 6,
|
||||
consistent: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
'global-require': 'off',
|
||||
'import/no-dynamic-require': 'off',
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
endOfLine: 'auto',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
|
||||
rules: {
|
||||
'linebreak-style': 0,
|
||||
strict: 0,
|
||||
'max-len': ['error', { code: 120 }],
|
||||
'quote-props': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'func-names': ['error', 'as-needed'],
|
||||
'prefer-const': 'warn',
|
||||
radix: 'off',
|
||||
'no-restricted-syntax': ['error', "BinaryExpression[operator='at']"],
|
||||
'no-await-in-loop': 'off',
|
||||
'no-trailing-spaces': 'off',
|
||||
'object-curly-spacing': 'off',
|
||||
'object-curly-newline': [
|
||||
'error',
|
||||
{
|
||||
ObjectExpression: {
|
||||
multiline: true,
|
||||
minProperties: 6,
|
||||
consistent: true,
|
||||
},
|
||||
ObjectPattern: { multiline: true, minProperties: 6, consistent: true },
|
||||
ImportDeclaration: {
|
||||
multiline: true,
|
||||
minProperties: 6,
|
||||
consistent: true,
|
||||
},
|
||||
ExportDeclaration: {
|
||||
multiline: true,
|
||||
minProperties: 6,
|
||||
consistent: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
'global-require': 'off',
|
||||
'import/no-dynamic-require': 'off',
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
endOfLine: 'auto',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# package json
|
||||
package-lock.json
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# Environments
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# package json
|
||||
package-lock.json
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# Environments
|
||||
.env
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
{
|
||||
"singleQuote": true,
|
||||
"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",
|
||||
"sourceRoot": "src"
|
||||
}
|
||||
{
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src"
|
||||
}
|
||||
|
||||
+128
-117
@@ -1,117 +1,128 @@
|
||||
{
|
||||
"name": "DLearn",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"author": "daniel.3380",
|
||||
"dependencies": {
|
||||
"@nestjs/axios": "^2.0.0",
|
||||
"@nestjs/common": "^10.0.5",
|
||||
"@nestjs/core": "^10.0.5",
|
||||
"@nestjs/mongoose": "^10.0.6",
|
||||
"@nestjs/platform-express": "^10.0.5",
|
||||
"@nestjs/platform-socket.io": "^10.0.5",
|
||||
"@nestjs/serve-static": "^4.0.0",
|
||||
"@nestjs/swagger": "^7.1.1",
|
||||
"@nestjs/testing": "^9.4.0",
|
||||
"@nestjs/throttler": "^4.0.0",
|
||||
"@types/axios": "^0.14.0",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/express-session": "1.17.0",
|
||||
"@types/moment": "^2.13.0",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/request": "^2.48.8",
|
||||
"@types/request-ip": "^0.0.37",
|
||||
"axios": "^1.3.6",
|
||||
"base64-async": "^2.1.3",
|
||||
"bluebird": "^3.7.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"ejs": "^3.1.8",
|
||||
"express-session": "^1.17.2",
|
||||
"fs": "^0.0.1-security",
|
||||
"jalali-moment": "^3.3.11",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"md5": "^2.3.0",
|
||||
"mongoose": "^8.3.2",
|
||||
"mysql": "^2.18.1",
|
||||
"mysql2": "^3.2.3",
|
||||
"path": "^0.12.7",
|
||||
"pg": "^8.11.1",
|
||||
"pm2": "^5.2.0",
|
||||
"qrcode": "^1.5.0",
|
||||
"random-number": "^0.0.9",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"request": "^2.88.2",
|
||||
"request-ip": "^2.1.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.5.5",
|
||||
"sequelize": "^6.32.1",
|
||||
"sequelize-typescript": "^2.1.5",
|
||||
"shelljs": "^0.8.5",
|
||||
"soap": "^1.0.0",
|
||||
"swagger-ui-express": "^4.6.3",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^8.2.5",
|
||||
"@nestjs/schematics": "^8.0.11",
|
||||
"@types/bluebird": "^3.5.36",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/md5": "^2.3.2",
|
||||
"@types/node": "^16.11.33",
|
||||
"@types/qrcode": "^1.4.2",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@typescript-eslint/eslint-plugin": "^5.22.0",
|
||||
"@typescript-eslint/parser": "^5.22.0",
|
||||
"eslint": "^8.15.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.5.1",
|
||||
"prettier": "^2.6.2",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.2.3",
|
||||
"ts-jest": "^27.1.4",
|
||||
"ts-node": "^10.7.0",
|
||||
"tsconfig-paths": "^3.14.1"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "DLearn",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "node dist/main",
|
||||
"start:dev": "cross-env NODE_ENV=development nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"author": "daniel.3380",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.574.0",
|
||||
"@ffprobe-installer/ffprobe": "^2.1.2",
|
||||
"@liara/cli": "^6.0.3",
|
||||
"@nestjs/axios": "^2.0.0",
|
||||
"@nestjs/common": "^10.0.5",
|
||||
"@nestjs/config": "^3.2.2",
|
||||
"@nestjs/core": "^10.0.5",
|
||||
"@nestjs/event-emitter": "^2.0.4",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
"@nestjs/mongoose": "^10.0.6",
|
||||
"@nestjs/platform-express": "^10.0.5",
|
||||
"@nestjs/platform-socket.io": "^10.0.5",
|
||||
"@nestjs/serve-static": "^4.0.0",
|
||||
"@nestjs/swagger": "^7.1.1",
|
||||
"@nestjs/testing": "^9.4.0",
|
||||
"@nestjs/throttler": "^4.0.0",
|
||||
"@types/axios": "^0.14.0",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/express-session": "1.17.0",
|
||||
"@types/moment": "^2.13.0",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/request": "^2.48.8",
|
||||
"@types/request-ip": "^0.0.37",
|
||||
"aws-sdk": "^2.1618.0",
|
||||
"axios": "^1.3.6",
|
||||
"base64-async": "^2.1.3",
|
||||
"bluebird": "^3.7.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cross-env": "^7.0.3",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"ejs": "^3.1.8",
|
||||
"express-session": "^1.17.2",
|
||||
"fs": "^0.0.1-security",
|
||||
"get-video-duration": "^4.1.0",
|
||||
"jalali-moment": "^3.3.11",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"md5": "^2.3.0",
|
||||
"moment": "^2.29.4",
|
||||
"mongoose": "^8.3.2",
|
||||
"mysql": "^2.18.1",
|
||||
"mysql2": "^3.2.3",
|
||||
"path": "^0.12.7",
|
||||
"pg": "^8.11.1",
|
||||
"pm2": "^5.2.0",
|
||||
"qrcode": "^1.5.0",
|
||||
"random-number": "^0.0.9",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"request": "^2.88.2",
|
||||
"request-ip": "^3.3.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.5.5",
|
||||
"sequelize": "^6.32.1",
|
||||
"sequelize-typescript": "^2.1.5",
|
||||
"shelljs": "^0.8.5",
|
||||
"soap": "^1.0.0",
|
||||
"soft-delete-plugin-mongoose": "^1.0.17",
|
||||
"swagger-ui-express": "^4.6.3",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.3.2",
|
||||
"@nestjs/schematics": "^8.0.11",
|
||||
"@types/bluebird": "^3.5.36",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/md5": "^2.3.2",
|
||||
"@types/node": "^16.11.33",
|
||||
"@types/qrcode": "^1.4.2",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@typescript-eslint/eslint-plugin": "^5.22.0",
|
||||
"@typescript-eslint/parser": "^5.22.0",
|
||||
"eslint": "^8.15.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.5.1",
|
||||
"prettier": "^2.6.2",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.2.3",
|
||||
"ts-jest": "^27.1.4",
|
||||
"ts-node": "^10.7.0",
|
||||
"tsconfig-paths": "^3.14.1"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 { ThrottlerModule } from '@nestjs/throttler';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
// ServeStaticModule.forRoot({
|
||||
// rootPat h: join(__dirname, '..', 'public'),
|
||||
// }),
|
||||
// ConfigModule.forRoot({
|
||||
// isGlobal: true,
|
||||
// }),
|
||||
ThrottlerModule.forRoot({
|
||||
ttl: 60,
|
||||
limit: 10,
|
||||
}),
|
||||
],
|
||||
controllers: [],
|
||||
providers: [],
|
||||
})
|
||||
export class AppModule {}
|
||||
import { AdminModule } from './application/admin/admin.module';
|
||||
import { CommonModule } from './application/common/common.module';
|
||||
import { TicketModule } from './application/ticket/ticket.module';
|
||||
import { UserModule } from './application/user/user.module';
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ThrottlerModule } from '@nestjs/throttler';
|
||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { UploaderService } from './application/common/uploader/uploader.service';
|
||||
import { CourseModule } from './application/course/course.module';
|
||||
import { UploaderModule } from './application/common/uploader/uploader.module';
|
||||
import { CourseHeadlineModule } from './application/courseHeadline/courseHeadline.module';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { CourseCategoryModule } from './application/courseCategory/courseCategory.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
AdminModule,
|
||||
// EventEmitterModule.forRoot({
|
||||
// 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