chore: add danak services and admin login
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import { join } from "path";
|
||||
|
||||
import { config } from "dotenv";
|
||||
import { DataSource } from "typeorm";
|
||||
config();
|
||||
|
||||
const { DB_HOST, DB_NAME, DB_PASS, DB_PORT, DB_USER, NODE_ENV } = process.env;
|
||||
const IS_PROD = NODE_ENV === "production";
|
||||
const PATH = IS_PROD ? "dist" : "src";
|
||||
|
||||
export const connectionSource = new DataSource({
|
||||
type: "postgres",
|
||||
host: DB_HOST as string,
|
||||
port: parseInt(DB_PORT as string),
|
||||
username: DB_USER,
|
||||
password: DB_PASS,
|
||||
database: DB_NAME,
|
||||
logging: true,
|
||||
entities: [join(process.cwd(), PATH, "modules/**/*.entity{.ts,.js}")],
|
||||
migrations: [join(process.cwd(), "database/migrations/*{.ts,.js}")],
|
||||
synchronize: false,
|
||||
migrationsTableName: "typeorm_migrations",
|
||||
migrationsRun: false,
|
||||
});
|
||||
console.log(join(process.cwd(), PATH, "modules/**/*.entity{.ts,.js}"));
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Logger } from "@nestjs/common";
|
||||
|
||||
import { connectionSource } from "./connection";
|
||||
import { seedAdmin } from "./seeders/admin.seeder";
|
||||
import { seedRole } from "./seeders/role.seeder";
|
||||
|
||||
const logger = new Logger("Seeder");
|
||||
|
||||
export const runSeeder = async () => {
|
||||
await connectionSource.initialize();
|
||||
logger.log("start seeding database");
|
||||
await seedRole(connectionSource, logger);
|
||||
await seedAdmin(connectionSource, logger);
|
||||
|
||||
logger.log("seeding completed");
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
runSeeder();
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Logger } from "@nestjs/common";
|
||||
import { DataSource, DeepPartial } from "typeorm";
|
||||
|
||||
import { Role } from "../../src/modules/users/entities/role.entity";
|
||||
import { User } from "../../src/modules/users/entities/user.entity";
|
||||
import { RoleEnum } from "../../src/modules/users/enums/role.enum";
|
||||
|
||||
const defaultAdmin: DeepPartial<User> = {
|
||||
email: "admin-dsc@gmail.com",
|
||||
phone: "09922320740",
|
||||
userName: "admin-default",
|
||||
password: "admin123",
|
||||
firstName: "DSC",
|
||||
lastName: "Admin",
|
||||
nationalCode: "1234567890",
|
||||
};
|
||||
|
||||
export const seedAdmin = async (dataSource: DataSource, logger: Logger) => {
|
||||
try {
|
||||
const roleRepo = dataSource.getRepository(Role);
|
||||
const userRepo = dataSource.getRepository(User);
|
||||
|
||||
const adminRole = await roleRepo.findOneBy({ name: RoleEnum.ADMIN });
|
||||
if (!adminRole) throw new Error("Role not found");
|
||||
|
||||
const admin = userRepo.create({ ...defaultAdmin, role: adminRole });
|
||||
await userRepo.save(admin);
|
||||
logger.log("admin created successfully");
|
||||
} catch (error) {
|
||||
logger.error("Error in seeding admin", error);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
@@ -1,3 +1,21 @@
|
||||
import { Logger } from "@nestjs/common";
|
||||
import { DataSource } from "typeorm";
|
||||
|
||||
import { Role } from "../../src/modules/users/entities/role.entity";
|
||||
import { RoleEnum } from "../../src/modules/users/enums/role.enum";
|
||||
|
||||
export const roles = [{ name: RoleEnum.SUPER_ADMIN }, { name: RoleEnum.ADMIN }, { name: RoleEnum.VISITOR }, { name: RoleEnum.USER }];
|
||||
const roles = [{ name: RoleEnum.SUPER_ADMIN }, { name: RoleEnum.ADMIN }, { name: RoleEnum.VISITOR }, { name: RoleEnum.USER }];
|
||||
|
||||
export const seedRole = async (dataSource: DataSource, logger: Logger) => {
|
||||
try {
|
||||
const roleRepo = dataSource.getRepository(Role);
|
||||
for (const role of roles) {
|
||||
const newRole = roleRepo.create(role);
|
||||
await roleRepo.save(newRole);
|
||||
}
|
||||
logger.log("Role seeded successfully");
|
||||
} catch (error) {
|
||||
logger.error("Error in seeding role", error);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ services:
|
||||
restart: always
|
||||
container_name: postgres_dsc
|
||||
ports:
|
||||
- 54320:5432
|
||||
- ${DB_PORT}:5432
|
||||
# networks:
|
||||
# - app_network
|
||||
volumes:
|
||||
|
||||
+6
-3
@@ -8,10 +8,10 @@
|
||||
"scripts": {
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:nest": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "NODE_ENV=production node --trace-warnings dist/main",
|
||||
"start": "NODE_ENV=production node --trace-warnings dist/main",
|
||||
"lint:fix": "eslint . --ext .ts --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
@@ -19,12 +19,14 @@
|
||||
"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",
|
||||
"typeorm": "node -r ts-node/register ./node_modules/typeorm/cli",
|
||||
"schema:drop": "npx typeorm-ts-node-commonjs schema:drop -d ./database/connection.ts",
|
||||
"migration:run": "npx typeorm-ts-node-commonjs migration:run -d ./database/connection.ts",
|
||||
"migration:generate": "npx typeorm-ts-node-commonjs migration:generate -d ./database/connection.ts ./database/migrations/$npm_config_name",
|
||||
"migration:create": "npx typeorm-ts-node-commonjs migration:create ./database/migrations/$npm_config_name",
|
||||
"migration:revert": "npx typeorm-ts-node-commonjs migration:revert -d ./database/connection.ts",
|
||||
"migration:show": "npx typeorm-ts-node-commonjs migration:show -d ./database/connection.ts ",
|
||||
"prepare": "husky"
|
||||
"seed:ts": "npx ts-node -r tsconfig-paths/register ./database/seeder.runner.ts",
|
||||
"prepare": "husky || true"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/static": "^7.0.4",
|
||||
@@ -48,6 +50,7 @@
|
||||
"cache-manager": "^6.3.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"dotenv": "^16.4.7",
|
||||
"fastify": "^5.2.1",
|
||||
"handlebars": "^4.7.8",
|
||||
"nodemailer": "^6.9.16",
|
||||
|
||||
Generated
+3
@@ -71,6 +71,9 @@ importers:
|
||||
class-validator:
|
||||
specifier: ^0.14.1
|
||||
version: 0.14.1
|
||||
dotenv:
|
||||
specifier: ^16.4.7
|
||||
version: 16.4.7
|
||||
fastify:
|
||||
specifier: ^5.2.1
|
||||
version: 5.2.1
|
||||
|
||||
@@ -9,6 +9,7 @@ import { rateLimitConfig } from "./configs/rateLimit.config";
|
||||
import { databaseConfigs } from "./configs/typeorm.config";
|
||||
import { HTTPLogger } from "./core/middlewares/logger.middleware";
|
||||
import { AuthModule } from "./modules/auth/auth.module";
|
||||
import { DanakServicesModule } from "./modules/danak-services/danak-services.module";
|
||||
import { TicketsModule } from "./modules/tickets/tickets.module";
|
||||
import { UsersModule } from "./modules/users/users.module";
|
||||
|
||||
@@ -21,6 +22,7 @@ import { UsersModule } from "./modules/users/users.module";
|
||||
UsersModule,
|
||||
TicketsModule,
|
||||
AuthModule,
|
||||
DanakServicesModule,
|
||||
],
|
||||
controllers: [],
|
||||
providers: [],
|
||||
|
||||
@@ -49,6 +49,7 @@ export const enum AuthMessage {
|
||||
NATIONAL_NOT_EMPTY = "کد ملی نمیتواند خالی باشد",
|
||||
OTP_ALREADY_SENT = "کد یکبار مصرف قبلا ارسال شده است",
|
||||
TOO_MANY_REQUESTS = "تعداد درخواست های شما بیش از حد مجاز است",
|
||||
NOT_ADMIN = "شما دسترسی به بخش ادمین ندارید",
|
||||
}
|
||||
|
||||
export const enum UserMessage {
|
||||
@@ -66,4 +67,16 @@ export const enum CommonMessage {
|
||||
THIS_FILED_IS_REQUIRED = "این فیلد الزامی است",
|
||||
VALID_FOR_CHOOSE = "معتبر برای انتخاب",
|
||||
UPDATE_SUCCESS = "با موفقیت به روز رسانی شد",
|
||||
CREATED = "با موفقیت ایجاد شد",
|
||||
}
|
||||
|
||||
export const enum CategoryMessage {
|
||||
TITLE_REQUIRED = "نام دسته بندی مورد نیاز است",
|
||||
TITLE_STRING = "نام دسته بندی باید یک رشته باشد",
|
||||
TITLE_LENGTH = "نام دسته بندی باید بین ۳ تا ۵۰ کاراکتر باشد",
|
||||
ICON_REQUIRED = "آیکون دسته بندی مورد نیاز است",
|
||||
ICON_SHOULD_BE_URL = "آیکون باید یک آدرس یو آر ال باشد",
|
||||
PARENT_ID_REQUIRED = "شناسه والد دسته بندی مورد نیاز است",
|
||||
PARENT_ID_SHOULD_BE_UUID = "شناسه والد باید یک یو یو آی دی باشد",
|
||||
TITLE_EXIST = "دسته بندی با این نام قبلا ثبت شده است",
|
||||
}
|
||||
|
||||
@@ -57,6 +57,22 @@ export class AuthController {
|
||||
return this.authService.loginWithPassword(loginPasswordDto);
|
||||
}
|
||||
|
||||
//***************************** */
|
||||
@ApiOperation({ summary: "login with password ==> admin" })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post("login/password/admin")
|
||||
adminLoginWithPassword(@Body() loginPasswordDto: LoginPasswordDTO) {
|
||||
return this.authService.adminLoginWithPassword(loginPasswordDto);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "verify otp for login" })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post("otp/verify/admin")
|
||||
adminVerifyOtp(@Body() verifyOtpDto: VerifyOtpDto) {
|
||||
return this.authService.adminVerifyLoginOtp(verifyOtpDto);
|
||||
}
|
||||
//***************************** */
|
||||
|
||||
// @Post("forgot-password")
|
||||
// async forgotPassword(@Body() forgotPasswordDto: ForgotPasswordDto) {
|
||||
// return this.authService.forgotPassword(forgotPasswordDto);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||
|
||||
import { TokensService } from "./tokens.service";
|
||||
import { AuthMessage, UserMessage } from "../../../common/enums/message.enum";
|
||||
import { RoleEnum } from "../../users/enums/role.enum";
|
||||
import { UsersService } from "../../users/providers/users.service";
|
||||
import { OTPService } from "../../utils/providers/otp.service";
|
||||
import { PasswordService } from "../../utils/providers/password.service";
|
||||
@@ -64,11 +65,7 @@ export class AuthService {
|
||||
async loginWithPassword(loginDto: LoginPasswordDTO) {
|
||||
const { email, password } = loginDto;
|
||||
|
||||
const user = await this.usersService.findOneWithEmail(email);
|
||||
if (!user) throw new BadRequestException(AuthMessage.INVALID_PASSWORD);
|
||||
|
||||
const passCompare = await this.passwordService.comparePassword(password, user.password);
|
||||
if (!passCompare) throw new BadRequestException(AuthMessage.INVALID_PASSWORD);
|
||||
const user = await this.checkUserLoginCredentialWithEmail(email, password);
|
||||
|
||||
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
||||
|
||||
@@ -79,6 +76,21 @@ export class AuthService {
|
||||
}
|
||||
//****************** */
|
||||
|
||||
async adminLoginWithPassword(loginDto: LoginPasswordDTO) {
|
||||
const { email, password } = loginDto;
|
||||
|
||||
const user = await this.checkUserLoginCredentialWithEmail(email, password);
|
||||
|
||||
if (user.role.name !== RoleEnum.ADMIN) throw new BadRequestException(AuthMessage.NOT_ADMIN);
|
||||
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
||||
|
||||
return {
|
||||
message: AuthMessage.PASSWORD_LOGIN_SUCCESS,
|
||||
...tokens,
|
||||
};
|
||||
}
|
||||
|
||||
//****************** */
|
||||
async checkUserExist(checkUserExistDto: CheckUserExistDto) {
|
||||
const user = await this.usersService.findOneWithEmail(checkUserExistDto.email);
|
||||
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
@@ -86,7 +98,6 @@ export class AuthService {
|
||||
}
|
||||
|
||||
//****************** */
|
||||
|
||||
async requestLoginOtp(requestOtpDto: RequestOtpDto) {
|
||||
const { phone } = requestOtpDto;
|
||||
const user = await this.usersService.findOneWithPhone(phone);
|
||||
@@ -114,13 +125,22 @@ export class AuthService {
|
||||
async verifyLoginOtp(verifyOtpDto: VerifyOtpDto) {
|
||||
const { code, phone } = verifyOtpDto;
|
||||
|
||||
const isValid = await this.otpService.verifyOtp(phone, code, "LOGIN");
|
||||
if (!isValid) throw new BadRequestException(AuthMessage.INVALID_OTP);
|
||||
const user = await this.checkUserLoginCredentialWithPhone(phone, code);
|
||||
|
||||
await this.otpService.delOtpFormCache(phone, "LOGIN");
|
||||
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
||||
return {
|
||||
message: AuthMessage.LOGIN_SUCCESS,
|
||||
...tokens,
|
||||
};
|
||||
}
|
||||
|
||||
const user = await this.usersService.findOneWithPhone(phone);
|
||||
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
//****************** */
|
||||
async adminVerifyLoginOtp(verifyOtpDto: VerifyOtpDto) {
|
||||
const { code, phone } = verifyOtpDto;
|
||||
|
||||
const user = await this.checkUserLoginCredentialWithPhone(phone, code);
|
||||
|
||||
if (user.role.name !== RoleEnum.ADMIN) throw new BadRequestException(AuthMessage.NOT_ADMIN);
|
||||
|
||||
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
||||
return {
|
||||
@@ -129,4 +149,29 @@ export class AuthService {
|
||||
};
|
||||
}
|
||||
//****************** */
|
||||
//****************** */
|
||||
|
||||
private async checkUserLoginCredentialWithEmail(email: string, password: string) {
|
||||
const user = await this.usersService.findOneWithEmail(email);
|
||||
if (!user) throw new BadRequestException(AuthMessage.INVALID_PASSWORD);
|
||||
|
||||
const passCompare = await this.passwordService.comparePassword(password, user.password);
|
||||
if (!passCompare) throw new BadRequestException(AuthMessage.INVALID_PASSWORD);
|
||||
|
||||
return user;
|
||||
}
|
||||
//****************** */
|
||||
//****************** */
|
||||
|
||||
private async checkUserLoginCredentialWithPhone(phone: string, otpCode: string) {
|
||||
const isValid = await this.otpService.verifyOtp(phone, otpCode, "LOGIN");
|
||||
if (!isValid) throw new BadRequestException(AuthMessage.INVALID_OTP);
|
||||
|
||||
await this.otpService.delOtpFormCache(phone, "LOGIN");
|
||||
|
||||
const user = await this.usersService.findOneWithPhone(phone);
|
||||
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator";
|
||||
|
||||
import { CategoryMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class CreateCategoryDto {
|
||||
@IsNotEmpty({ message: CategoryMessage.TITLE_REQUIRED })
|
||||
@IsString({ message: CategoryMessage.TITLE_STRING })
|
||||
@Length(3, 50, { message: CategoryMessage.TITLE_LENGTH })
|
||||
@ApiProperty({ description: "Category title", example: "business" })
|
||||
title: string;
|
||||
|
||||
@IsNotEmpty({ message: CategoryMessage.ICON_REQUIRED })
|
||||
@IsUrl({ protocols: ["http", "https"], require_protocol: true }, { message: CategoryMessage.ICON_SHOULD_BE_URL })
|
||||
@ApiProperty({ description: "Category icon", example: "https://example.com/icon.png" })
|
||||
icon: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNotEmpty({ message: CategoryMessage.PARENT_ID_REQUIRED })
|
||||
@IsUUID("4", { message: CategoryMessage.PARENT_ID_SHOULD_BE_UUID })
|
||||
@ApiPropertyOptional({ description: "Parent category id", example: "8b1e8b1e-8b1e-8b1e-8b1e-8b1e8b1e8b1e" })
|
||||
parentId?: string;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Body, Controller, Post } from "@nestjs/common";
|
||||
import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||
|
||||
import { CreateCategoryDto } from "./DTO/create-category.dto";
|
||||
import { DanakServicesService } from "./providers/danak-services.service";
|
||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
|
||||
@Controller("services")
|
||||
@ApiTags("Danak-Services")
|
||||
export class DanakServicesController {
|
||||
constructor(private readonly danakServicesService: DanakServicesService) {}
|
||||
@AuthGuards()
|
||||
@ApiOperation({ summary: "Create a new service category" })
|
||||
@Post("category")
|
||||
createCategory(@Body() createDto: CreateCategoryDto) {
|
||||
return this.danakServicesService.createCategory(createDto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
|
||||
import { DanakServicesController } from "./danak-services.controller";
|
||||
import { DanakServiceCategory } from "./entities/danak-service-category.entity";
|
||||
import { DanakServiceImage } from "./entities/danak-service-image.entity";
|
||||
import { DanakService } from "./entities/danak-service.entity";
|
||||
import { DanakServicesService } from "./providers/danak-services.service";
|
||||
import { DanakServicesCategoryRepository } from "./repositories/danak-services-category.repository";
|
||||
import { DanakServicesImageRepository } from "./repositories/danak-services-image.repository";
|
||||
import { DanakServicesRepository } from "./repositories/danak-services.repository";
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([DanakService, DanakServiceImage, DanakServiceCategory])],
|
||||
providers: [DanakServicesService, DanakServicesRepository, DanakServicesCategoryRepository, DanakServicesImageRepository],
|
||||
controllers: [DanakServicesController],
|
||||
exports: [DanakServicesService, TypeOrmModule],
|
||||
})
|
||||
export class DanakServicesModule {}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from "typeorm";
|
||||
|
||||
import { DanakService } from "./danak-service.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
|
||||
@Entity()
|
||||
export class DanakServiceCategory extends BaseEntity {
|
||||
@Column({ type: "varchar", length: 100, nullable: false, unique: true })
|
||||
title: string;
|
||||
|
||||
@Column({ type: "varchar", length: 150, nullable: false })
|
||||
icon: string;
|
||||
|
||||
@ManyToOne(() => DanakServiceCategory, (category) => category.children)
|
||||
@JoinColumn({ name: "parentId" })
|
||||
parent: DanakServiceCategory;
|
||||
|
||||
@OneToMany(() => DanakServiceCategory, (category) => category.parent)
|
||||
children: DanakServiceCategory[];
|
||||
|
||||
@Column({ nullable: true })
|
||||
parentId: string;
|
||||
|
||||
@OneToMany(() => DanakService, (danakService) => danakService.category)
|
||||
danakService: DanakService[];
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Column, Entity, ManyToOne } from "typeorm";
|
||||
|
||||
import { DanakService } from "./danak-service.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
|
||||
@Entity()
|
||||
export class DanakServiceImage extends BaseEntity {
|
||||
@Column({ type: "varchar", length: 255, nullable: false })
|
||||
imageUrl: string;
|
||||
|
||||
@ManyToOne(() => DanakService, (danakService) => danakService.images, { nullable: false, onDelete: "CASCADE" })
|
||||
danakService: DanakService;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Column, Entity, ManyToOne, OneToMany } from "typeorm";
|
||||
|
||||
import { DanakServiceCategory } from "./danak-service-category.entity";
|
||||
import { DanakServiceImage } from "./danak-service-image.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { ServiceStatus } from "../enums/service-status.enum";
|
||||
import { ServicesLanguage } from "../enums/services-language.enum";
|
||||
|
||||
@Entity()
|
||||
export class DanakService extends BaseEntity {
|
||||
@Column({ type: "varchar", length: 255, nullable: false })
|
||||
name: string;
|
||||
|
||||
@Column({ type: "text", nullable: false })
|
||||
description: string;
|
||||
|
||||
@Column({ type: "varchar", length: 255, nullable: false })
|
||||
author: string;
|
||||
|
||||
@Column({ type: "timestamp", nullable: false, default: () => "CURRENT_TIMESTAMP" })
|
||||
createDate: Date;
|
||||
|
||||
@Column({ type: "int", nullable: false, default: 0 })
|
||||
userCount: number;
|
||||
|
||||
@Column({ type: "enum", enum: ServicesLanguage, nullable: false })
|
||||
serviceLanguage: ServicesLanguage;
|
||||
|
||||
@Column({ type: "varchar", length: 150, nullable: false })
|
||||
softwareLanguage: string;
|
||||
|
||||
@Column({ type: "text", nullable: false })
|
||||
metaDescription: string;
|
||||
|
||||
@Column({ type: "enum", enum: ServiceStatus, nullable: false, default: ServiceStatus.IN_ACTIVE })
|
||||
status: ServiceStatus;
|
||||
|
||||
@Column({ type: "varchar", length: 255, nullable: false })
|
||||
link: string;
|
||||
|
||||
@Column({ type: "varchar", length: 255, nullable: false })
|
||||
icon: string;
|
||||
|
||||
@OneToMany(() => DanakServiceImage, (danakServiceImage) => danakServiceImage.danakService, { eager: true })
|
||||
images: DanakServiceImage[];
|
||||
|
||||
@ManyToOne(() => DanakServiceCategory, (danakServiceCategory) => danakServiceCategory.danakService, {
|
||||
nullable: false,
|
||||
onDelete: "RESTRICT",
|
||||
})
|
||||
category: DanakServiceCategory;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum ServiceStatus {
|
||||
ACTIVE = "ACTIVE",
|
||||
IN_ACTIVE = "IN_ACTIVE",
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum ServicesLanguage {
|
||||
ENGLISH = "en",
|
||||
GERMAN = "de",
|
||||
PERSIAN = "fa",
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||
|
||||
import { CategoryMessage, CommonMessage } from "../../../common/enums/message.enum";
|
||||
import { CreateCategoryDto } from "../DTO/create-category.dto";
|
||||
import { DanakServicesCategoryRepository } from "../repositories/danak-services-category.repository";
|
||||
|
||||
@Injectable()
|
||||
export class DanakServicesService {
|
||||
private readonly logger = new Logger(DanakServicesService.name);
|
||||
constructor(private readonly danakServicesCategoryRepository: DanakServicesCategoryRepository) {}
|
||||
/********** */
|
||||
async createCategory(createDto: CreateCategoryDto) {
|
||||
this.logger.log(`Creating a new category with title: ${createDto.title}`);
|
||||
const existCategory = await this.danakServicesCategoryRepository.findOneBy({ title: createDto.title });
|
||||
if (existCategory) throw new BadRequestException(CategoryMessage.TITLE_EXIST);
|
||||
const category = this.danakServicesCategoryRepository.create(createDto);
|
||||
await this.danakServicesCategoryRepository.save(category);
|
||||
return {
|
||||
message: CommonMessage.CREATED,
|
||||
category,
|
||||
};
|
||||
}
|
||||
/********** */
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
|
||||
import { DanakServiceCategory } from "../entities/danak-service-category.entity";
|
||||
|
||||
@Injectable()
|
||||
export class DanakServicesCategoryRepository extends Repository<DanakServiceCategory> {
|
||||
constructor(@InjectRepository(DanakServiceCategory) danakServicesCategoryRepository: Repository<DanakServiceCategory>) {
|
||||
super(danakServicesCategoryRepository.target, danakServicesCategoryRepository.manager, danakServicesCategoryRepository.queryRunner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
|
||||
import { DanakServiceImage } from "../entities/danak-service-image.entity";
|
||||
|
||||
@Injectable()
|
||||
export class DanakServicesImageRepository extends Repository<DanakServiceImage> {
|
||||
constructor(@InjectRepository(DanakServiceImage) danakServicesImageRepository: Repository<DanakServiceImage>) {
|
||||
super(danakServicesImageRepository.target, danakServicesImageRepository.manager, danakServicesImageRepository.queryRunner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
|
||||
import { DanakService } from "../entities/danak-service.entity";
|
||||
|
||||
@Injectable()
|
||||
export class DanakServicesRepository extends Repository<DanakService> {
|
||||
constructor(@InjectRepository(DanakService) danakServicesRepository: Repository<DanakService>) {
|
||||
super(danakServicesRepository.target, danakServicesRepository.manager, danakServicesRepository.queryRunner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne } from "typeorm";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { Ticket } from "./ticket.entity";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
|
||||
@Entity()
|
||||
export class TicketMessage extends BaseEntity {
|
||||
@Column({ type: "text", nullable: false })
|
||||
content: string;
|
||||
|
||||
@Column({ type: "varchar", length: 150, nullable: true, default: null })
|
||||
attachmentUrl: string;
|
||||
|
||||
@JoinColumn()
|
||||
@ManyToOne(() => Ticket, (ticket) => ticket.messages, { onDelete: "CASCADE", nullable: false })
|
||||
ticket: Ticket;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.ticketMessage, { onDelete: "SET NULL", nullable: true })
|
||||
@JoinColumn()
|
||||
author: User;
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne } from "typeorm";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from "typeorm";
|
||||
|
||||
import { TicketCategory } from "./ticket-category.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { TicketPriority } from "../enums/ticket-priority.enum";
|
||||
import { TicketStatus } from "../enums/ticket-status.enum";
|
||||
import { TicketMessage } from "./ticket-message.entity";
|
||||
|
||||
@Entity()
|
||||
export class Ticket extends BaseEntity {
|
||||
@Column({ type: "int", generated: "identity", insert: false })
|
||||
numericId: number;
|
||||
|
||||
@Column({ type: "varchar", length: 150, nullable: false })
|
||||
title: string;
|
||||
|
||||
@@ -33,4 +37,7 @@ export class Ticket extends BaseEntity {
|
||||
@JoinColumn()
|
||||
@ManyToOne(() => TicketCategory, (category) => category.tickets, { onDelete: "RESTRICT", nullable: false })
|
||||
category: TicketCategory;
|
||||
|
||||
@OneToMany(() => TicketMessage, (message) => message.ticket)
|
||||
messages: TicketMessage[];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
import { TicketCategory } from "../entities/ticket-category.entity";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
@Injectable()
|
||||
export class TicketCategoryRepository extends Repository<TicketCategory> {
|
||||
constructor(@InjectRepository(TicketCategory) ticketCategoryRepository: Repository<TicketCategory>) {
|
||||
super(ticketCategoryRepository.target, ticketCategoryRepository.manager, ticketCategoryRepository.queryRunner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
import { TicketMessage } from "../entities/ticket-message.entity";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
@Injectable()
|
||||
export class TicketMessagesRepository extends Repository<TicketMessage> {
|
||||
constructor(@InjectRepository(TicketMessage) ticketMessagesRepository: Repository<TicketMessage>) {
|
||||
super(ticketMessagesRepository.target, ticketMessagesRepository.manager, ticketMessagesRepository.queryRunner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Repository } from "typeorm";
|
||||
import { Ticket } from "../entities/ticket.entity";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
|
||||
@Injectable()
|
||||
export class TicketsRepository extends Repository<Ticket> {
|
||||
constructor(@InjectRepository(Ticket) ticketsRepository: Repository<Ticket>) {
|
||||
super(ticketsRepository.target, ticketsRepository.manager, ticketsRepository.queryRunner);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Controller } from "@nestjs/common";
|
||||
import { ApiTags } from "@nestjs/swagger";
|
||||
|
||||
// import { TicketsService } from "./providers/tickets.service";
|
||||
|
||||
@Controller("tickets")
|
||||
@ApiTags("users")
|
||||
export class TicketsController {}
|
||||
@ApiTags("Tickets")
|
||||
export class TicketsController {
|
||||
// constructor(private readonly ticketsService: TicketsService) {}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,16 @@ import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import { TicketCategory } from "./entities/ticket-category.entity";
|
||||
import { Ticket } from "./entities/ticket.entity";
|
||||
import { TicketsController } from "./tickets.controller";
|
||||
import { TicketsService } from "./tickets.service";
|
||||
import { TicketsService } from "./providers/tickets.service";
|
||||
import { TicketMessage } from "./entities/ticket-message.entity";
|
||||
import { TicketsRepository } from "./repositories/tickets.repository";
|
||||
import { TicketCategoryRepository } from "./repositories/tickets-category.repository";
|
||||
import { TicketMessagesRepository } from "./repositories/tickets-message.repository";
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Ticket, TicketCategory])],
|
||||
providers: [TicketsService],
|
||||
imports: [TypeOrmModule.forFeature([Ticket, TicketCategory, TicketMessage])],
|
||||
providers: [TicketsService, TicketsRepository, TicketCategoryRepository, TicketMessagesRepository],
|
||||
controllers: [TicketsController],
|
||||
exports: [TypeOrmModule, TicketsService, TicketsRepository, TicketCategoryRepository, TicketMessagesRepository],
|
||||
})
|
||||
export class TicketsModule {}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from "typeorm";
|
||||
import { Column, Entity, ManyToOne, OneToMany } from "typeorm";
|
||||
|
||||
import { Role } from "./role.entity";
|
||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||
import { TicketMessage } from "../../tickets/entities/ticket-message.entity";
|
||||
import { Ticket } from "../../tickets/entities/ticket.entity";
|
||||
|
||||
@Entity()
|
||||
@@ -30,10 +31,12 @@ export class User extends BaseEntity {
|
||||
@Column({ type: "varchar", length: 100, unique: true, nullable: false })
|
||||
nationalCode: string;
|
||||
|
||||
@JoinColumn()
|
||||
@ManyToOne(() => Role, { eager: true, onDelete: "RESTRICT", nullable: false })
|
||||
role: Role;
|
||||
|
||||
@OneToMany(() => Ticket, (ticket) => ticket.user)
|
||||
tickets: Ticket[];
|
||||
|
||||
@OneToMany(() => TicketMessage, (ticketMessage) => ticketMessage.author)
|
||||
ticketMessage: TicketMessage[];
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import slugify from "slugify";
|
||||
import { Not } from "typeorm";
|
||||
|
||||
import { RoleRepository } from "./roles.repository";
|
||||
import { UserRepository } from "./users.repository";
|
||||
import { roles } from "../../../../database/seeders/role.seeder";
|
||||
import { UserRepository } from "../repositories/users.repository";
|
||||
// import { roles } from "../../../../database/seeders/role.seeder";
|
||||
import { CommonMessage, UserMessage } from "../../../common/enums/message.enum";
|
||||
import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto";
|
||||
import { CheckValidityDTO } from "../DTO/check-validity.dto";
|
||||
@@ -38,6 +38,7 @@ export class UsersService {
|
||||
/************************************************************ */
|
||||
|
||||
async getMe(userId: string) {
|
||||
this.logger.debug("sss");
|
||||
const user = await this.userRepository.findOne({ where: { id: userId }, select: this.userSelect });
|
||||
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
return { user };
|
||||
@@ -105,11 +106,11 @@ export class UsersService {
|
||||
}
|
||||
|
||||
///****************************************************** */
|
||||
async roleSeeder() {
|
||||
const countRole = await this.roleRepository.count();
|
||||
if (countRole > 0) return;
|
||||
const createdRoles = await this.roleRepository.insert(roles);
|
||||
this.logger.log({ createdRoles });
|
||||
return createdRoles;
|
||||
}
|
||||
// async roleSeeder() {
|
||||
// const countRole = await this.roleRepository.count();
|
||||
// if (countRole > 0) return;
|
||||
// const createdRoles = await this.roleRepository.insert(roles);
|
||||
// this.logger.log({ createdRoles });
|
||||
// return createdRoles;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import { Role } from "./entities/role.entity";
|
||||
import { User } from "./entities/user.entity";
|
||||
import { RoleRepository } from "./providers/roles.repository";
|
||||
import { UserRepository } from "./providers/users.repository";
|
||||
import { UsersService } from "./providers/users.service";
|
||||
import { UserRepository } from "./repositories/users.repository";
|
||||
import { UsersController } from "./users.controller";
|
||||
|
||||
@Module({
|
||||
|
||||
Reference in New Issue
Block a user