تغییرات جدید
This commit is contained in:
parent
6a26ea9b78
commit
cb0cb9106e
|
|
@ -18,8 +18,10 @@
|
|||
"@nestjs/mongoose": "^11.0.3",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@types/kavenegar": "^1.1.3",
|
||||
"@types/passport-google-oauth20": "^2.0.16",
|
||||
"aws-sdk": "^2.1692.0",
|
||||
"axios": "^1.10.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"dotenv": "^17.0.0",
|
||||
|
|
@ -5380,6 +5382,12 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/kavenegar": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/kavenegar/-/kavenegar-1.1.3.tgz",
|
||||
"integrity": "sha512-va7j5bjW+ic1rmTH2kLOItgGflZ/ZI4G7rPQAo+Ysiccb0mFQhK5Dz/HvdNCMZ1TlAQNWb+QCfBsPaktqwYjuA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/methods": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz",
|
||||
|
|
@ -6838,7 +6846,6 @@
|
|||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
|
|
@ -6913,6 +6920,17 @@
|
|||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
|
||||
"integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/b4a": {
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
|
||||
|
|
@ -7818,7 +7836,6 @@
|
|||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
|
|
@ -8162,7 +8179,6 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
|
|
@ -8419,7 +8435,6 @@
|
|||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
|
|
@ -9173,6 +9188,26 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/for-each": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||
|
|
@ -9237,7 +9272,6 @@
|
|||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
|
||||
"integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
|
|
@ -9264,7 +9298,6 @@
|
|||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
|
|
@ -9274,7 +9307,6 @@
|
|||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
|
|
@ -12432,6 +12464,12 @@
|
|||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@
|
|||
"@nestjs/mongoose": "^11.0.3",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@types/kavenegar": "^1.1.3",
|
||||
"@types/passport-google-oauth20": "^2.0.16",
|
||||
"aws-sdk": "^2.1692.0",
|
||||
"axios": "^1.10.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"dotenv": "^17.0.0",
|
||||
|
|
|
|||
|
|
@ -13,13 +13,16 @@ import { ConfigModule } from '@nestjs/config';
|
|||
import { ProductModule } from './product/product.module';
|
||||
import { DiscountModule } from './discount/discount.module';
|
||||
import { DiscountTypeModule } from './discount-type/discount-type.module';
|
||||
import { SmsModule } from './sms/sms.module';
|
||||
import { UserLoginModule } from './user-login/user-login.module';
|
||||
import { UserModule } from './user/user.module';
|
||||
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
CategoryModule,UploadModule,LoginModule,ShopModule,ProductModule,DiscountModule,
|
||||
MongooseModule.forRoot('mongodb://root:FcpaOAxvZd2IiqK5Vl5QRMTE@fartakdatabase:27017/my-app?authSource=admin'),
|
||||
ConfigModule.forRoot({ isGlobal: true }), DiscountTypeModule
|
||||
ConfigModule.forRoot({ isGlobal: true }), DiscountTypeModule, SmsModule, UserLoginModule, UserModule
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common';
|
|||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { Response, Request } from 'express';
|
||||
|
||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
||||
import { AuthenticatedRequest } from 'src/interfaces/request';
|
||||
import { AuthService } from './auth.service';
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private jwtService: JwtService) {}
|
||||
constructor(private jwtService: JwtService,private readonly authService:AuthService) {}
|
||||
|
||||
@Get('google')
|
||||
@UseGuards(AuthGuard('google'))
|
||||
|
|
@ -34,4 +36,17 @@ export class AuthController {
|
|||
// ✅ Option 2: Redirect to frontend with token
|
||||
res.redirect(`http://localhost:4200/login/success?token=${token}`);
|
||||
}
|
||||
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('/get')
|
||||
async getListDiscountShop(
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
const createdShop = await this.authService.verifyauth(req.user.user_ID);
|
||||
console.log(createdShop)
|
||||
return res.status(createdShop.status).json(createdShop.data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
|
|||
import { AuthService } from './auth.service';
|
||||
import { JwtStrategy } from './jwt.strategy';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { SellerModule } from 'src/seller/seller.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule,
|
||||
PassportModule,
|
||||
SellerModule,
|
||||
JwtModule.registerAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
|
|
|
|||
|
|
@ -1,21 +1,23 @@
|
|||
// auth.service.ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { CreateSellerResponse } from 'src/interfaces/request';
|
||||
import { SellerService } from 'src/seller/seller.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(private jwtService: JwtService) {}
|
||||
constructor(private jwtService: JwtService,private readonly sellerService:SellerService) {}
|
||||
|
||||
async generateTokens(payload:any) {
|
||||
console.log(process.env.JWT_SECRET)
|
||||
const accessToken = await this.jwtService.signAsync(payload, {
|
||||
secret: process.env.JWT_SECRET,
|
||||
expiresIn: '15m',
|
||||
expiresIn: '1d',
|
||||
});
|
||||
|
||||
const refreshToken = await this.jwtService.signAsync(payload, {
|
||||
secret: process.env.JWT_REFRESH_SECRET,
|
||||
expiresIn: '7d',
|
||||
expiresIn: '30d',
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -24,9 +26,9 @@ export class AuthService {
|
|||
};
|
||||
}
|
||||
|
||||
async verifyRefreshToken(token: string) {
|
||||
async verifyToken(token: string) {
|
||||
return this.jwtService.verifyAsync(token, {
|
||||
secret: process.env.JWT_REFRESH_SECRET,
|
||||
secret: process.env.JWT_SECRET,
|
||||
});
|
||||
}
|
||||
async loginWithGoogle(user: any) {
|
||||
|
|
@ -37,5 +39,25 @@ export class AuthService {
|
|||
};
|
||||
}
|
||||
|
||||
async verifyauth(user: string):Promise <CreateSellerResponse>{
|
||||
const dataSeller = await this.sellerService.findSellerwidtID(user)
|
||||
console.log("data")
|
||||
console.log(dataSeller)
|
||||
if(dataSeller){
|
||||
return {
|
||||
"message":"فروشنده موجود است",
|
||||
"status":200,
|
||||
"data":dataSeller
|
||||
}
|
||||
}else{
|
||||
return {
|
||||
"message":"فروشنده موجود نیست",
|
||||
"status":404,
|
||||
"data":null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,27 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable,UnauthorizedException } from '@nestjs/common';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
import { UserService } from 'src/user/user.service';
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(private configService: ConfigService) {
|
||||
constructor(private configService: ConfigService,private readonly userService:UserService) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: configService.get<string>('JWT_REFRESH_SECRET'),
|
||||
secretOrKey: configService.get<string>('JWT_SECRET'),
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: any) {
|
||||
console.log("payload")
|
||||
console.log("payload")
|
||||
console.log(payload)
|
||||
return { userId: payload.sellerid};
|
||||
const user = await this.userService.findUserwidtID(payload.userID)
|
||||
console.log("user")
|
||||
console.log(user)
|
||||
if (user.status != 200) {
|
||||
throw new UnauthorizedException('کاربر وارد نشده است');
|
||||
}
|
||||
return { userId: user.data._id,user_ID: user.data.ID};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Body, Controller, Res, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Res, Post, Get } from '@nestjs/common';
|
||||
import { Response } from 'express';
|
||||
import { CategoryService } from './category.service';
|
||||
import { CreatCategoryDto } from './dto/creatCategory.dto';
|
||||
|
|
@ -12,4 +12,10 @@ export class CategoryController {
|
|||
const result = await this.categoryService.creatCategory(dto);
|
||||
return res.status(result.status).json(result);
|
||||
}
|
||||
|
||||
@Get('/all')
|
||||
async findAll(@Res() res: Response) {
|
||||
const result = await this.categoryService.findAllCategory();
|
||||
return res.status(result.status).json(result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ export class CategoryService {
|
|||
const newCategory = new this.categoryModel({
|
||||
Name:creatCategory.Name,
|
||||
Description:creatCategory.Description,
|
||||
ID:newUuid
|
||||
ID:newUuid,
|
||||
Code:creatCategory.Code,
|
||||
})
|
||||
const result = newCategory.save()
|
||||
|
||||
|
|
@ -63,6 +64,14 @@ export class CategoryService {
|
|||
}
|
||||
}
|
||||
}
|
||||
async findAllCategory() :Promise <CreateSellerResponse> {
|
||||
const categories = await this.categoryModel.find().select('-_id')
|
||||
return {
|
||||
"message":"همه دسته بندی ها",
|
||||
"status":200,
|
||||
"data":categories
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
import { IsNotEmpty, IsString,IsNumberString,Length} from "class-validator";
|
||||
|
||||
export class CreatCategoryDto {
|
||||
@IsNotEmpty({message:"نام دسته بندی نباید خالی باشد . "})
|
||||
|
|
@ -9,6 +9,13 @@ export class CreatCategoryDto {
|
|||
@IsString({message:"نام دسته بندی باید متن باشد . "})
|
||||
Description:string
|
||||
|
||||
@IsNotEmpty({ message: 'کد دسته بندی نمیتواند خالی باشد.' })
|
||||
@IsNumberString({},{ message: 'کد دسته بندی باید فقط شامل اعداد باشد.' })
|
||||
Code: number;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Body, Controller, Res, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Res, Post,Get} from '@nestjs/common';
|
||||
import { Response } from 'express';
|
||||
import { DiscountTypeService } from './discount-type.service';
|
||||
import { CreatDiscountTypeDto } from './dto/discountType.dto';
|
||||
|
|
@ -12,4 +12,10 @@ export class DiscountTypeController {
|
|||
const result = await this.discountTypeService.creatDiscountType(dto);
|
||||
return res.status(result.status).json(result);
|
||||
}
|
||||
|
||||
@Get('/all')
|
||||
async findAll(@Res() res: Response) {
|
||||
const result = await this.discountTypeService.findAllDiscountType();
|
||||
return res.status(result.status).json(result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ export class DiscountTypeService {
|
|||
const newDiscountType = new this.discountTypeModel({
|
||||
Name:creatDiscountType.Name,
|
||||
Description:creatDiscountType.Description,
|
||||
ID:newUuid
|
||||
ID:newUuid,
|
||||
Code:creatDiscountType.Code
|
||||
})
|
||||
const result = newDiscountType.save()
|
||||
|
||||
|
|
@ -59,5 +60,15 @@ export class DiscountTypeService {
|
|||
}
|
||||
}
|
||||
|
||||
async findAllDiscountType() :Promise <CreateSellerResponse> {
|
||||
const categories = await this.discountTypeModel.find().select('-_id')
|
||||
return {
|
||||
"message":"همه دسته بندی های تخفیف",
|
||||
"status":200,
|
||||
"data":categories
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
import { IsNotEmpty, IsString,IsNumberString } from "class-validator";
|
||||
|
||||
export class CreatDiscountTypeDto {
|
||||
@IsNotEmpty({message:"نام دسته بندی نباید خالی باشد . "})
|
||||
@IsString({message:"نام دسته بندی باید متن باشد . "})
|
||||
@IsNotEmpty({message:"نوع تخفیف نباید خالی باشد . "})
|
||||
@IsString({message:"نوع تخفیف باید متن باشد . "})
|
||||
Name:string;
|
||||
|
||||
@IsNotEmpty({message:"نام دسته بندی نباید خالی باشد . "})
|
||||
@IsString({message:"نام دسته بندی باید متن باشد . "})
|
||||
@IsNotEmpty({message:"نوع تخفیف نباید خالی باشد . "})
|
||||
@IsString({message:"نوع تخفیف باید متن باشد . "})
|
||||
Description:string
|
||||
|
||||
@IsNotEmpty({ message: 'نوع تخفیف نمیتواند خالی باشد.' })
|
||||
@IsNumberString({},{ message: 'نوع تخفیف باید فقط شامل اعداد باشد.' })
|
||||
Code: number;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,28 +5,139 @@ import {
|
|||
UseGuards,
|
||||
Res,
|
||||
Req,
|
||||
|
||||
UploadedFiles,
|
||||
BadRequestException,
|
||||
UseInterceptors,
|
||||
Param,
|
||||
Get,
|
||||
Query
|
||||
} from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { FilesInterceptor } from '@nestjs/platform-express';
|
||||
import { DiscountService } from './discount.service';
|
||||
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
|
||||
import { AuthenticatedRequest } from 'src/interfaces/request';
|
||||
import { CreateDiscountDto } from './dto/discount.dto';
|
||||
import { EditDiscountDto } from './dto/editDiscount.dto';
|
||||
import { UploadService } from 'src/upload/upload.service';
|
||||
import { PaginationQueryDiscountDto } from './dto/discountfind.dto';
|
||||
import { Request } from 'express';
|
||||
import { Response } from 'express';
|
||||
@Controller('discount')
|
||||
export class DiscountController {
|
||||
constructor(
|
||||
private readonly upload: UploadService,
|
||||
private readonly discountService: DiscountService,
|
||||
) {}
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('/add')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('/add')
|
||||
@UseInterceptors(
|
||||
FilesInterceptor('Images', 3, {
|
||||
limits: {
|
||||
fileSize: 2 * 1024 * 1024, // 2MB
|
||||
},
|
||||
fileFilter: (req, file, cb) => {
|
||||
const allowedMimeTypes = ['image/png', 'image/jpeg', 'image/jpg'];
|
||||
cb(null, true);
|
||||
},
|
||||
}))
|
||||
async create(
|
||||
@Body() dto:CreateDiscountDto,
|
||||
@UploadedFiles() files: Express.Multer.File[],
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
const creatDiscount = await this.discountService.creatDiscount(dto,req.user.userId);
|
||||
let fileIds: string[] = [];
|
||||
|
||||
if (files && files.length > 0) {
|
||||
for (const file of files) {
|
||||
const result = await this.upload.uploadFileSingle(file);
|
||||
if (result.status !== 200) {
|
||||
return res.status(400).json({
|
||||
message: 'آپلود فایل با خطا مواجه شد',
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
fileIds.push(result.data._id);
|
||||
}
|
||||
}
|
||||
|
||||
const creatDiscount = await this.discountService.creatDiscount(dto,fileIds,req.user.userId);
|
||||
return res.status(creatDiscount.status).json(creatDiscount);
|
||||
}
|
||||
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('/get')
|
||||
async getListDiscountShop(
|
||||
@Query() query :PaginationQueryDiscountDto,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
const createdShop = await this.discountService.findAlldiscount(req.user.userId,query);
|
||||
return res.status(createdShop.status).json(createdShop);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('/get/:id')
|
||||
async getSingleDiscountwithID(
|
||||
@Param('id') id: string,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
const createdShop = await this.discountService.findDiscountWithID(id);
|
||||
return res.status(createdShop.status).json(createdShop);
|
||||
}
|
||||
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('/delete/:id')
|
||||
async deleteSingleDiscountwithID(
|
||||
@Param('id') id: string,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
const createdShop = await this.discountService.deleteDiscountWithID(id);
|
||||
return res.status(createdShop.status).json(createdShop);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('/edit/:id')
|
||||
@UseInterceptors(
|
||||
FilesInterceptor('Images', 3, {
|
||||
limits: {
|
||||
fileSize: 2 * 1024 * 1024, // 2MB
|
||||
},
|
||||
fileFilter: (req, file, cb) => {
|
||||
const allowedMimeTypes = ['image/png', 'image/jpeg', 'image/jpg'];
|
||||
cb(null, true);
|
||||
},
|
||||
}))
|
||||
async editDiscountWithID(
|
||||
@Param('id') id: string,
|
||||
@Body() dto:EditDiscountDto,
|
||||
@UploadedFiles() files: Express.Multer.File[],
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
let fileIds: string[] = [];
|
||||
|
||||
if (files && files.length > 0) {
|
||||
for (const file of files) {
|
||||
const result = await this.upload.uploadFileSingle(file);
|
||||
if (result.status !== 200) {
|
||||
return res.status(400).json({
|
||||
message: 'آپلود فایل با خطا مواجه شد',
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
fileIds.push(result.data._id);
|
||||
}
|
||||
}
|
||||
const createdShop = await this.discountService.editDiscount(id,dto,fileIds,req.user.userId);
|
||||
return res.status(createdShop.status).json(createdShop);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import { DiscountSchema } from 'src/schemas/discount.schema';
|
|||
import { ProductModule } from 'src/product/product.module';
|
||||
import { ShopModule } from 'src/shop/shop.module';
|
||||
import { DiscountTypeModule } from 'src/discount-type/discount-type.module';
|
||||
import { UploadModule } from 'src/upload/upload.module';
|
||||
import { UploadService } from 'src/upload/upload.service';
|
||||
@Module({
|
||||
imports:[
|
||||
MongooseModule.forFeature([
|
||||
{ name: 'Discount', schema: DiscountSchema },
|
||||
]),ProductModule,DiscountTypeModule,ShopModule
|
||||
]),UploadModule,DiscountTypeModule,ShopModule
|
||||
|
||||
|
||||
],
|
||||
|
|
|
|||
|
|
@ -9,26 +9,32 @@ import { CreateDiscountDto } from './dto/discount.dto';
|
|||
import { CreateSellerResponse } from 'src/interfaces/request';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { ShopService } from 'src/shop/shop.service';
|
||||
|
||||
import { UploadService } from 'src/upload/upload.service';
|
||||
import { PaginationQueryDiscountDto } from './dto/discountfind.dto';
|
||||
import { EditDiscountDto } from './dto/editDiscount.dto';
|
||||
@Injectable()
|
||||
export class DiscountService {
|
||||
constructor(
|
||||
@InjectModel(Discount.name)
|
||||
private readonly discountModel: Model<DiscountDocument>,
|
||||
private readonly shopService: ShopService,
|
||||
private readonly productService: ProductService,
|
||||
private readonly discountTypeService: DiscountTypeService,
|
||||
) {}
|
||||
|
||||
private readonly upload: UploadService
|
||||
) { }
|
||||
private convertToMinutes(time: string): number {
|
||||
const [hours, minutes] = time.split(':').map(Number);
|
||||
return hours * 60 + minutes;
|
||||
}
|
||||
async creatDiscount(
|
||||
creatDiscountDto: CreateDiscountDto,
|
||||
images: (string[] | null),
|
||||
user: any,
|
||||
): Promise<CreateSellerResponse> {
|
||||
const newUuid: string = uuidv4();
|
||||
|
||||
|
||||
console.log("creatDiscountDto")
|
||||
console.log(creatDiscountDto)
|
||||
console.log(creatDiscountDto)
|
||||
const existCategory = await this.discountTypeService.findDiscountType(creatDiscountDto.Type);
|
||||
if (existCategory.status !== 200 || !existCategory.data) {
|
||||
return {
|
||||
|
|
@ -38,7 +44,7 @@ export class DiscountService {
|
|||
};
|
||||
}
|
||||
|
||||
const existShop = await this.shopService.findShopSingle(creatDiscountDto.Shop);
|
||||
const existShop = await this.shopService.findShopWithSeller(user);
|
||||
if (existShop.status !== 200 || !existShop.data) {
|
||||
return {
|
||||
message: 'فروشگاه ثبت نشده است',
|
||||
|
|
@ -46,50 +52,43 @@ export class DiscountService {
|
|||
data: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (!existShop.data.Seller) {
|
||||
return {
|
||||
message: 'اطلاعات فروشنده فروشگاه ناقص است',
|
||||
status: 500,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
|
||||
const sellerId = new Types.ObjectId(user);
|
||||
const shopSellerId = existShop.data.Seller;
|
||||
|
||||
if (!sellerId.equals(shopSellerId)) {
|
||||
const start = this.convertToMinutes(creatDiscountDto.StartTime);
|
||||
const end = this.convertToMinutes(creatDiscountDto.EndTime);
|
||||
if (end <= start) {
|
||||
return {
|
||||
message: 'این فروشنده مجاز به ثبت تخفیف برای این فروشگاه نیست',
|
||||
status: 403,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
|
||||
const existProduct = await this.productService.findProductSingle(creatDiscountDto.Product);
|
||||
if (existProduct.status !== 200 || !existProduct.data) {
|
||||
return {
|
||||
message: 'محصول ثبت نشده است',
|
||||
message: 'تایم شروع نباید از تایم پایان بزرگتر باشد',
|
||||
status: 404,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (!existProduct.data.Shop.equals(existShop.data._id)) {
|
||||
|
||||
const sellerId = new Types.ObjectId(user);
|
||||
const shopSellerId = existShop.data.Seller._id;
|
||||
|
||||
console.log("shopSellerId")
|
||||
console.log(existShop)
|
||||
/*if (!sellerId.equals(shopSellerId)) {
|
||||
return {
|
||||
message: 'این محصول در این فروشگاه ثبت نشده است',
|
||||
status: 400,
|
||||
message: 'این فروشنده مجاز به ثبت تخفیف برای این فروشگاه نیست',
|
||||
status: 403,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
||||
try {
|
||||
const newDiscount = new this.discountModel({
|
||||
Name: creatDiscountDto.Name,
|
||||
Shop: existShop.data._id,
|
||||
Product: existProduct.data._id,
|
||||
Images: images,
|
||||
Type: existCategory.data._id,
|
||||
ProductDescription: "محصول جدید",
|
||||
Description: creatDiscountDto.Description,
|
||||
Price: creatDiscountDto.Price,
|
||||
NPrice: creatDiscountDto.NPrice,
|
||||
StartDate: creatDiscountDto.Start,
|
||||
EndDate: creatDiscountDto.End,
|
||||
StartTime: creatDiscountDto.StartTime,
|
||||
|
|
@ -116,4 +115,204 @@ export class DiscountService {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
async findAlldiscount(
|
||||
user: any,
|
||||
query: PaginationQueryDiscountDto,
|
||||
): Promise<CreateSellerResponse> {
|
||||
const existShop = await this.shopService.findShopWithSeller(user);
|
||||
|
||||
if (existShop.status !== 200 || !existShop.data) {
|
||||
return {
|
||||
message: 'فروشگاهی ثبت نشده است',
|
||||
status: 404,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
|
||||
const filter: any = {};
|
||||
const now = new Date();
|
||||
|
||||
|
||||
filter.Shop = existShop.data._id;
|
||||
|
||||
|
||||
if (query.search) {
|
||||
filter.Name = { $regex: query.search, $options: 'i' };
|
||||
}
|
||||
|
||||
|
||||
if (Number(query.status) === 1) {
|
||||
|
||||
filter.StartDate = { $lte: now };
|
||||
filter.EndDate = { $gte: now };
|
||||
} else if (Number(query.status) === 0) {
|
||||
filter.$or = [
|
||||
{ StartDate: { $gt: now } },
|
||||
{ EndDate: { $lt: now } },
|
||||
];
|
||||
}
|
||||
|
||||
const page = Number(query.page) || 1;
|
||||
const limit = Number(query.limit) || 10;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
console.log("query1111")
|
||||
console.log(query)
|
||||
|
||||
|
||||
const [discounts, total] = await Promise.all([
|
||||
this.discountModel
|
||||
.find(filter)
|
||||
.skip(skip)
|
||||
.limit(limit)
|
||||
.populate({ path: 'Images' })
|
||||
.populate({ path: 'Shop', select: 'ID Name' })
|
||||
.populate({ path: 'Type' }),
|
||||
this.discountModel.countDocuments(filter),
|
||||
]);
|
||||
|
||||
console.log("discounts")
|
||||
console.log(discounts)
|
||||
return {
|
||||
message: 'لیست تخفیفات',
|
||||
status: 200,
|
||||
data: {
|
||||
discounts,
|
||||
total,
|
||||
page,
|
||||
lastPage: Math.ceil(total / limit),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async findDiscountWithID(id: string): Promise<CreateSellerResponse> {
|
||||
|
||||
|
||||
const discount = await this.discountModel.findOne({ ID: id }).populate({ path: 'Images' }).populate({ path: 'Shop', select: 'Name ID' }).populate({ path: 'Type' })
|
||||
if (discount) {
|
||||
return {
|
||||
"message": "تخفیف موجود می باشد",
|
||||
"status": 200,
|
||||
"data": discount
|
||||
}
|
||||
|
||||
} else {
|
||||
return {
|
||||
"message": "تخفیف موجود نمی باشد",
|
||||
"status": 401,
|
||||
"data": null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async deleteDiscountWithID(id: string): Promise<CreateSellerResponse> {
|
||||
const discount = await this.discountModel.findOne({ ID: id })
|
||||
if (!discount) {
|
||||
return {
|
||||
message: 'تخفیف موجود نمیباشد',
|
||||
status: 404,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
|
||||
const deleteResult = await this.discountModel.deleteOne({ _id: discount._id });
|
||||
|
||||
if (deleteResult.deletedCount !== 1) {
|
||||
return {
|
||||
message: 'خطا در حذف تخفیف',
|
||||
status: 500,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
return {
|
||||
message: 'تخفیف با موفقیت حذف شد',
|
||||
status: 200,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async editDiscount(
|
||||
id: string,
|
||||
editDiscountDto: EditDiscountDto,
|
||||
images: (string[] | null),
|
||||
user: string,
|
||||
): Promise<CreateSellerResponse> {
|
||||
|
||||
const discount = await this.findDiscountWithID(id)
|
||||
if (discount.status !== 200) {
|
||||
return {
|
||||
"data": "",
|
||||
"message": "تخفیف با این شناسه ثبت نشده است",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
|
||||
const existCategory = await this.discountTypeService.findDiscountType(editDiscountDto.Type);
|
||||
if (existCategory.status !== 200 || !existCategory.data) {
|
||||
return {
|
||||
message: 'نوع تخفیف ثبت نشده است',
|
||||
status: 404,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
const start = this.convertToMinutes(editDiscountDto.StartTime);
|
||||
const end = this.convertToMinutes(editDiscountDto.EndTime);
|
||||
if (end <= start) {
|
||||
return {
|
||||
message: 'تایم شروع نباید از تایم پایان بزرگتر باشد',
|
||||
status: 404,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
|
||||
var discountImages = discount.data.Images;
|
||||
if (images && images.length > 0) {
|
||||
var changeImages = discountImages.filter(item => !editDiscountDto.Image.includes(item));
|
||||
|
||||
images.forEach(item => {
|
||||
if (!changeImages.includes(item)) {
|
||||
changeImages.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const updateDiscount = await this.discountModel.updateOne(
|
||||
{_id:discount.data._id},
|
||||
{$set: {
|
||||
"Name":editDiscountDto.Name,
|
||||
"Images":changeImages,
|
||||
"Type":editDiscountDto.Type,
|
||||
"Description":editDiscountDto.Description,
|
||||
"Price":editDiscountDto.Price,
|
||||
"NPrice":editDiscountDto.NPrice,
|
||||
"StartDate":editDiscountDto.Start,
|
||||
"EndDate":editDiscountDto.End,
|
||||
"StartTime":editDiscountDto.StartTime,
|
||||
"EndTime":editDiscountDto.EndTime,
|
||||
"Radius":editDiscountDto.Radius
|
||||
}}
|
||||
)
|
||||
if (updateDiscount.modifiedCount === 0) {
|
||||
return {
|
||||
message: 'آپدیت تخفیف با شکست مواجه شد',
|
||||
data: null,
|
||||
status: 500,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
message: 'تخفیف با موفقیت آپدیت شد',
|
||||
data: null,
|
||||
status: 200,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,24 +3,26 @@ import { Type } from 'class-transformer';
|
|||
|
||||
export class CreateDiscountDto {
|
||||
|
||||
@IsNotEmpty({ message: 'شناسه فروشگاه نباید خالی باشد' })
|
||||
@IsString({ message: 'شناسه فروشگاه باید شامل متن باشد' })
|
||||
Shop: string;
|
||||
@IsNotEmpty({ message: 'نام تخفیف نباید خالی باشد' })
|
||||
@IsString({ message: 'نام تخفیف باید شامل متن باشد' })
|
||||
Name: string;
|
||||
|
||||
@IsNotEmpty({ message: 'نوع تخفیف نباید خالی باشد' })
|
||||
@IsString({ message: 'نوع تخفیف باید شامل متن باشد' })
|
||||
Type: string;
|
||||
|
||||
@IsNotEmpty({ message: 'شناسه محصول نباید خالی باشد' })
|
||||
@IsString({ message: 'شناسه محصول باید شامل متن باشد' })
|
||||
Product: string;
|
||||
|
||||
@IsNotEmpty({ message: 'توضیحات تخفیف نباید خالی باشد' })
|
||||
@IsNotEmpty({ message: 'توضیحات تخفیف نباید خالی باشد' })
|
||||
@IsString({ message: 'توضیحات تخفیف باید شامل متن باشد' })
|
||||
@Length(0, 250, { message: 'طول توضیحات تخفیف حداکثر 250 حرف می تواند باشد' })
|
||||
Description: string;
|
||||
|
||||
@IsNotEmpty({ message: 'قیمت محصول نباید خالی باشد' })
|
||||
@IsNumberString({},{ message: 'قیمت محصول باید شامل عدد باشد' })
|
||||
Price: number;
|
||||
|
||||
@IsNotEmpty({ message: 'قیمت محصول با احتساب تخفیف نباید خالی باشد' })
|
||||
@IsNumberString({},{ message: 'قیمت محصول با احتساب تخفیف باید شامل عدد باشد' })
|
||||
NPrice: number;
|
||||
|
||||
@IsNotEmpty({ message: 'تاریخ شروع نباید خالی باشد' })
|
||||
@IsDateString({},{ message: 'تاریخ شروع باید به فرمت تاریخ باشد' })
|
||||
|
|
@ -30,12 +32,11 @@ export class CreateDiscountDto {
|
|||
@IsDateString({},{ message: 'تاریخ پایان باید به فرمت تاریخ باشد' })
|
||||
End: string;
|
||||
|
||||
|
||||
@IsNotEmpty({ message: 'ساعت شروع نباید خالی باشد' })
|
||||
@Matches(/^([01]\d|2[0-3]):([0-5]\d)$/, {
|
||||
message: 'فرمت تایم شروع صحیح نیست',
|
||||
})
|
||||
StartTime: string;
|
||||
StartTime: string;
|
||||
|
||||
@IsNotEmpty({ message: 'ساعت پایان نباید خالی باشد' })
|
||||
@Matches(/^([01]\d|2[0-3]):([0-5]\d)$/, {
|
||||
|
|
@ -43,7 +44,6 @@ export class CreateDiscountDto {
|
|||
})
|
||||
EndTime: string;
|
||||
|
||||
|
||||
@IsNotEmpty({ message: 'شعاع ارسال اعلان نباید خالی باشد' })
|
||||
@IsNumberString({},{ message: 'شعاع ارسال اعلان باید شامل عدد باشد' })
|
||||
Radius: number;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
// dto/pagination-query.dto.ts
|
||||
import { IsOptional, IsEnum, IsString, IsNumberString, IsNumber } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class PaginationQueryDiscountDto {
|
||||
@IsOptional()
|
||||
@IsNumberString()
|
||||
page: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumberString()
|
||||
limit: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
search: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumberString()
|
||||
status: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
import { IsNotEmpty,IsDateString, IsNumberString, IsString,ArrayMinSize, Matches,Length,ArrayNotEmpty, IsOptional,IsArray,ValidateNested,Validate, IsDate} from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class EditDiscountDto {
|
||||
|
||||
@IsNotEmpty({ message: 'نام تخفیف نباید خالی باشد' })
|
||||
@IsString({ message: 'نام تخفیف باید شامل متن باشد' })
|
||||
Name: string;
|
||||
|
||||
@IsNotEmpty({ message: 'نوع تخفیف نباید خالی باشد' })
|
||||
@IsString({ message: 'نوع تخفیف باید شامل متن باشد' })
|
||||
Type: string;
|
||||
|
||||
@IsNotEmpty({ message: 'توضیحات تخفیف نباید خالی باشد' })
|
||||
@IsString({ message: 'توضیحات تخفیف باید شامل متن باشد' })
|
||||
@Length(0, 250, { message: 'طول توضیحات تخفیف حداکثر 250 حرف می تواند باشد' })
|
||||
Description: string;
|
||||
|
||||
@IsNotEmpty({ message: 'قیمت محصول نباید خالی باشد' })
|
||||
@IsNumberString({},{ message: 'قیمت محصول باید شامل عدد باشد' })
|
||||
Price: number;
|
||||
|
||||
@IsNotEmpty({ message: 'قیمت محصول با احتساب تخفیف نباید خالی باشد' })
|
||||
@IsNumberString({},{ message: 'قیمت محصول با احتساب تخفیف باید شامل عدد باشد' })
|
||||
NPrice: number;
|
||||
|
||||
@IsArray({ message: 'تصاویر باید به صورت آرایه ارسال شوند' })
|
||||
@IsString({ each: true, message: 'هر تصویر باید به صورت رشته باشد' })
|
||||
Image: string[];
|
||||
|
||||
|
||||
@IsNotEmpty({ message: 'تاریخ شروع نباید خالی باشد' })
|
||||
@IsDateString({},{ message: 'تاریخ شروع باید به فرمت تاریخ باشد' })
|
||||
Start: string;
|
||||
|
||||
@IsNotEmpty({ message: 'تاریخ پایان نباید خالی باشد' })
|
||||
@IsDateString({},{ message: 'تاریخ پایان باید به فرمت تاریخ باشد' })
|
||||
End: string;
|
||||
|
||||
@IsNotEmpty({ message: 'ساعت شروع نباید خالی باشد' })
|
||||
@Matches(/^([01]\d|2[0-3]):([0-5]\d)$/, {
|
||||
message: 'فرمت تایم شروع صحیح نیست',
|
||||
})
|
||||
StartTime: string;
|
||||
|
||||
@IsNotEmpty({ message: 'ساعت پایان نباید خالی باشد' })
|
||||
@Matches(/^([01]\d|2[0-3]):([0-5]\d)$/, {
|
||||
message: 'فرمت تایم پایان صحیح نیست',
|
||||
})
|
||||
EndTime: string;
|
||||
|
||||
@IsNotEmpty({ message: 'شعاع ارسال اعلان نباید خالی باشد' })
|
||||
@IsNumberString({},{ message: 'شعاع ارسال اعلان باید شامل عدد باشد' })
|
||||
Radius: number;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import { Request } from "express";
|
|||
export interface AuthenticatedRequest extends Request {
|
||||
user: {
|
||||
userId: string;
|
||||
user_ID:string;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,4 @@ export class SendOTPCodeDto {
|
|||
@IsNumberString({},{ message: 'کد ارسالی باید فقط شامل اعداد باشد.' })
|
||||
@Length(5, 5, { message: 'عدد باید دقیقاً ۵ رقم باشد' })
|
||||
OTP: number;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export class LoginController {
|
|||
|
||||
constructor(private readonly loginService:LoginService){}
|
||||
|
||||
@Post('/sendcode')
|
||||
@Post('/sendcode')
|
||||
async sendCode(@Body() dto: CreateSellerDto,@Res() res: Response) {
|
||||
const result = await this.loginService.sendCode(dto);
|
||||
return res.status(result.status).json(result);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import { LoginService } from './login.service';
|
|||
import { SellerService } from 'src/seller/seller.service';
|
||||
import { AuthModule } from 'src/auth/auth.module';
|
||||
import { SellerModule } from 'src/seller/seller.module';
|
||||
import { SmsModule } from 'src/sms/sms.module';
|
||||
@Module({
|
||||
imports:[SellerModule,AuthModule],
|
||||
imports:[SellerModule,AuthModule,SmsModule],
|
||||
controllers: [LoginController],
|
||||
providers: [LoginService]
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import redis from '../redis/redis.provider';
|
|||
import { SellerService } from 'src/seller/seller.service';
|
||||
import {SendOTPCodeDto} from './dto/sendcode.dto'
|
||||
import { AuthService } from '../auth/auth.service'
|
||||
import { SmsService } from 'src/sms/sms.service';
|
||||
|
||||
interface CreateSellerResponse {
|
||||
message: string;
|
||||
|
|
@ -16,15 +17,19 @@ interface CreateSellerResponse {
|
|||
export class LoginService {
|
||||
|
||||
|
||||
constructor(private readonly sellerService: SellerService,private readonly authService: AuthService) {}
|
||||
constructor(private readonly sellerService: SellerService,private readonly authService: AuthService,private readonly smsService:SmsService) {}
|
||||
|
||||
async sendCode(createSellerDto: CreateSellerDto): Promise<CreateSellerResponse> {
|
||||
async sendCode(createSellerDto: CreateSellerDto): Promise<CreateSellerResponse> {
|
||||
|
||||
const code = await generateCode();
|
||||
console.log(`verified code is : ${code}`);
|
||||
const getSeller = await this.sellerService.findSellerwidtPhone(createSellerDto.Code,createSellerDto.Phone);
|
||||
if (!getSeller) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
const sendSms = await this.smsService.sendMessage(`0${createSellerDto.Phone}`, Number(code));
|
||||
console.log("sendSms")
|
||||
console.log(sendSms)
|
||||
await redis.set(`login-code:${createSellerDto.Phone}`, Number(code), 'EX', 60);
|
||||
return {
|
||||
message: 'کد یکبار مصرف ارسال شد',
|
||||
|
|
@ -55,9 +60,6 @@ async getcode(sendOTPCodeDto: SendOTPCodeDto): Promise<CreateSellerResponse> {
|
|||
|
||||
console.log("getSeller")
|
||||
console.log(getSeller)
|
||||
if (!getSeller) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
if(getSeller.status == 404){
|
||||
const creatSeller = await this.sellerService.createWithPhone({Phone:sendOTPCodeDto.Phone,Code:sendOTPCodeDto.Code})
|
||||
if(creatSeller.status == 200){
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Redis from 'ioredis';
|
||||
|
||||
const redis = new Redis(process.env.REDIS_URL || 'redis://:eCwxwRr7YX9q0ynso4uR6HNf@fartakredis:6379/0');
|
||||
const redis = new Redis('redis://:eCwxwRr7YX9q0ynso4uR6HNf@fartakredis:6379/0');
|
||||
|
||||
export default redis;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ export class Category {
|
|||
|
||||
@Prop({required:true,unique: true})
|
||||
ID: string;
|
||||
|
||||
@Prop()
|
||||
Code:number;
|
||||
}
|
||||
|
||||
export const CategorySchema = SchemaFactory.createForClass(Category);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import mongoose, { HydratedDocument } from 'mongoose';
|
||||
|
||||
|
||||
export type CommentDocument = HydratedDocument<Comment>;
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
|
||||
export class Comment {
|
||||
@Prop({ type: { type: mongoose.Schema.Types.ObjectId, ref: 'Shop' } })
|
||||
Shops: mongoose.Schema.Types.ObjectId;
|
||||
|
||||
@Prop({ type: { type: mongoose.Schema.Types.ObjectId, ref: 'Discount' } })
|
||||
Discount: mongoose.Schema.Types.ObjectId;
|
||||
|
||||
|
||||
@Prop({ type: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } })
|
||||
User: mongoose.Schema.Types.ObjectId;
|
||||
|
||||
@Prop({ required: true })
|
||||
Text: string;
|
||||
|
||||
@Prop({ default: null })
|
||||
Score: number;
|
||||
@Prop({ required: true, unique: true })
|
||||
ID: string
|
||||
|
||||
}
|
||||
|
||||
export const commentSchema = SchemaFactory.createForClass(Comment);
|
||||
commentSchema.index({ ID: 1 });
|
||||
|
|
@ -7,18 +7,31 @@ export type DiscountDocument = HydratedDocument<Discount>;
|
|||
@Schema({ timestamps: true })
|
||||
export class Discount {
|
||||
|
||||
@Prop({required:true})
|
||||
Name: string;
|
||||
|
||||
@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'Shop', default: null,required:true})
|
||||
Shop: mongoose.Schema.Types.ObjectId;
|
||||
|
||||
@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'Product', default: null,required:true })
|
||||
Product: mongoose.Schema.Types.ObjectId;
|
||||
@Prop({ type: [mongoose.Schema.Types.ObjectId], ref: 'File', default: [] })
|
||||
Images: mongoose.Schema.Types.ObjectId[];
|
||||
|
||||
@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'DiscountType', default: null,required:true })
|
||||
Type: mongoose.Schema.Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
ProductDescription: string
|
||||
|
||||
@Prop({})
|
||||
Description: string;
|
||||
|
||||
@Prop()
|
||||
Price: number;
|
||||
|
||||
@Prop()
|
||||
NPrice: number;
|
||||
|
||||
|
||||
@Prop({required:true})
|
||||
StartDate:Date
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ export class DiscountType {
|
|||
|
||||
@Prop({required:true,unique: true})
|
||||
ID: string;
|
||||
|
||||
|
||||
@Prop()
|
||||
Code:number;
|
||||
}
|
||||
|
||||
export const DiscountTypeSchema = SchemaFactory.createForClass(DiscountType);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ export type FileDocument = HydratedDocument<File>;
|
|||
@Schema({ timestamps: true })
|
||||
export class File {
|
||||
|
||||
@Prop({ required: true,enum:['image','video','document']})
|
||||
FileType: string;
|
||||
@Prop()
|
||||
FileType: string;
|
||||
|
||||
@Prop({required:false})
|
||||
Title:string
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export type SellerDocument = HydratedDocument<Seller>;
|
|||
|
||||
@Schema({ timestamps: true })
|
||||
export class Seller {
|
||||
@Prop({ unique: true })
|
||||
@Prop()
|
||||
Email: string;
|
||||
|
||||
@Prop({ unique: true })
|
||||
|
|
@ -35,6 +35,9 @@ export class Seller {
|
|||
|
||||
@Prop()
|
||||
CountryCode: number;
|
||||
|
||||
@Prop({ default: null })
|
||||
TokenFireBase: string;
|
||||
}
|
||||
|
||||
export const SellerSchema = SchemaFactory.createForClass(Seller);
|
||||
|
|
@ -19,6 +19,16 @@ class ShopSchedule {
|
|||
Status: boolean;
|
||||
}
|
||||
|
||||
@Schema()
|
||||
class UserInfo {
|
||||
@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true })
|
||||
User: mongoose.Schema.Types.ObjectId;
|
||||
|
||||
@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'File' }], default: [] })
|
||||
Images: mongoose.Schema.Types.ObjectId[];
|
||||
}
|
||||
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class Shop {
|
||||
@Prop({required:true})
|
||||
|
|
@ -75,8 +85,11 @@ export class Shop {
|
|||
@Prop()
|
||||
ShopNumber: number;
|
||||
|
||||
@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'File' }], default: [] })
|
||||
Images: mongoose.Schema.Types.ObjectId[];
|
||||
@Prop()
|
||||
Phone: number;
|
||||
|
||||
@Prop({ type: [UserInfo], default: [] })
|
||||
Images: UserInfo[];
|
||||
|
||||
@Prop({ type: [ShopSchedule], default: [] })
|
||||
Schedule: ShopSchedule[];
|
||||
|
|
@ -86,6 +99,10 @@ export class Shop {
|
|||
|
||||
@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Order' }], default: [] })
|
||||
Orders: mongoose.Schema.Types.ObjectId[];
|
||||
|
||||
@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }], default: [] })
|
||||
Comments: mongoose.Schema.Types.ObjectId[];
|
||||
|
||||
|
||||
@Prop({required:true,unique:true})
|
||||
ID:string
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import mongoose, { HydratedDocument } from 'mongoose';
|
||||
|
||||
export enum Gender {
|
||||
Male = 'male',
|
||||
Female = 'female',
|
||||
None = 'none',
|
||||
}
|
||||
|
||||
export type UserDocument = HydratedDocument<User>;
|
||||
|
||||
|
||||
@Schema()
|
||||
export class GpsPoint {
|
||||
@Prop({ type: String, enum: ['Point'], required: true, default: 'Point' })
|
||||
type: 'Point';
|
||||
|
||||
@Prop({ type: [Number], required: true })
|
||||
coordinates: [number, number];
|
||||
}
|
||||
export const GpsPointSchema = SchemaFactory.createForClass(GpsPoint);
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class User {
|
||||
|
||||
@Prop()
|
||||
Name: string;
|
||||
|
||||
@Prop({ type: String, enum: Gender })
|
||||
Gender: Gender;
|
||||
|
||||
@Prop()
|
||||
Email: string;
|
||||
|
||||
@Prop({ unique: true })
|
||||
ID: string;
|
||||
|
||||
@Prop({ required: true, unique: true })
|
||||
Phone: number;
|
||||
|
||||
@Prop({ required: false })
|
||||
verify: boolean;
|
||||
|
||||
|
||||
@Prop({ default: null })
|
||||
LastLogin: Date;
|
||||
|
||||
@Prop({ default: null })
|
||||
IpAddress: string;
|
||||
|
||||
|
||||
@Prop({ type: [GpsPointSchema] })
|
||||
Gps: GpsPoint[];
|
||||
|
||||
@Prop({ type: GpsPointSchema })
|
||||
CurrentGps: GpsPoint;
|
||||
|
||||
@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Category' }], default: [] })
|
||||
FCategory: mongoose.Schema.Types.ObjectId[];
|
||||
|
||||
|
||||
@Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Category' }], default: [] })
|
||||
NotifCategory: mongoose.Schema.Types.ObjectId[];
|
||||
|
||||
@Prop()
|
||||
CountryCode: number;
|
||||
|
||||
@Prop({ default: null })
|
||||
TokenFireBase: string;
|
||||
}
|
||||
|
||||
export const UserSchema = SchemaFactory.createForClass(User);
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { IsNotEmpty, IsString,IsNumberString,Length} from "class-validator";
|
||||
|
||||
export class CreatTokenFireBase {
|
||||
@IsNotEmpty({message:"توکن نباید خالی باشد. "})
|
||||
@IsString({message:"توکن باید متن باشد . "})
|
||||
Token:string;
|
||||
}
|
||||
|
|
@ -1,4 +1,35 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { Controller,
|
||||
Post,
|
||||
UploadedFile,
|
||||
UseInterceptors,
|
||||
Body,
|
||||
UseGuards,
|
||||
Res,
|
||||
Req,
|
||||
BadRequestException,
|
||||
Get } from '@nestjs/common';
|
||||
import { SellerService } from './seller.service';
|
||||
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
|
||||
import { AuthenticatedRequest } from 'src/interfaces/request';
|
||||
import { CreatTokenFireBase } from './dto/token-firebase.dto';
|
||||
import { Response } from 'express';
|
||||
|
||||
@Controller('seller')
|
||||
export class SellerController {}
|
||||
export class SellerController {
|
||||
|
||||
constructor (private readonly sellerService:SellerService){}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('/firebaseUpdate')
|
||||
async updateFireBaseToken (
|
||||
@Body() body:CreatTokenFireBase,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
){
|
||||
const updateToken = await this.sellerService.updateTokenFireBase(body.Token,req.user.userId)
|
||||
return res.status(updateToken.status).json(updateToken.data)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ export class SellerService {
|
|||
"LastLogin": null,
|
||||
"IpAddress": null,
|
||||
"Shops": [],
|
||||
"CountryCode": 98
|
||||
"CountryCode": 98,
|
||||
"TokenFireBase":""
|
||||
});
|
||||
const savedSeller = await createdSeller.save();
|
||||
|
||||
|
|
@ -107,6 +108,35 @@ export class SellerService {
|
|||
}
|
||||
}
|
||||
|
||||
async updateTokenFireBase (token:string,user:string) : Promise <CreateSellerResponse> {
|
||||
|
||||
const seller = await this.findSellerwidtID(user)
|
||||
if(!seller){
|
||||
return {
|
||||
"data":null,
|
||||
"status": 404,
|
||||
"message": "فروشنده موجود نیست"
|
||||
}
|
||||
}
|
||||
const updateSeller = await this.sellerModel.updateOne({_id:seller.data._id},{$set:{TokenFireBase:token}})
|
||||
if (updateSeller.modifiedCount === 0) {
|
||||
return {
|
||||
"data":null,
|
||||
"status": 500,
|
||||
"message": "آپدیت با شکست مواجه شد"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
data: null,
|
||||
status: 200,
|
||||
message: "بهروزرسانی انجام شد"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export class CreateShopDto {
|
|||
//@Validate(IsCategoryExists)
|
||||
Category: string;
|
||||
|
||||
@IsNotEmpty({ message: 'نام استان نباید خالی باشد' })
|
||||
@IsNotEmpty({ message: 'نام استان نباید خالی باشد' })
|
||||
@IsString({ message: 'نام استان باید شامل متن باشد' })
|
||||
@Length(0, 20, { message: 'طول نام استان حداکثر 20 حرف می تواند باشد' })
|
||||
Province: string;
|
||||
|
|
@ -43,9 +43,14 @@ export class CreateShopDto {
|
|||
|
||||
|
||||
@IsOptional()
|
||||
@IsNumberString({},{ message: 'شماره تلفن باید فقط شامل اعداد باشد.' })
|
||||
@Length(1, 12, { message: 'شماره تلفن باید حداکثر 12 رقم باشد.' })
|
||||
ShopNumber: string;
|
||||
@IsNumberString({},{ message: 'تلفن فروشگاه باید فقط شامل اعداد باشد.' })
|
||||
@Length(1, 12, { message: 'تلفن فروشگاه باید حداکثر 12 رقم باشد.' })
|
||||
Phone: string;
|
||||
|
||||
|
||||
@IsOptional()
|
||||
@IsNumberString({},{ message: 'پلاک باید فقط شامل اعداد باشد.' })
|
||||
ShopNumber: number;
|
||||
|
||||
@IsNotEmpty({message: 'کد پستی نباید خالی باشد.'})
|
||||
@IsNumberString({},{ message: 'کد پستی باید فقط شامل اعداد باشد.' })
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
Res,
|
||||
Req,
|
||||
BadRequestException,
|
||||
Get,
|
||||
} from '@nestjs/common';
|
||||
import { Response } from 'express';
|
||||
import { UploadService } from '../upload/upload.service';
|
||||
|
|
@ -31,11 +32,7 @@ export class ShopController {
|
|||
},
|
||||
fileFilter: (req, file, cb) => {
|
||||
const allowedMimeTypes = ['image/png', 'image/jpeg', 'image/jpg'];
|
||||
if (!allowedMimeTypes.includes(file.mimetype)) {
|
||||
cb(new BadRequestException('فقط فرمت PNG یا JPG مجاز است'), false);
|
||||
} else {
|
||||
cb(null, true);
|
||||
}
|
||||
cb(null, true);
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
|
@ -58,6 +55,7 @@ export class ShopController {
|
|||
|
||||
if (file) {
|
||||
const result = await this.upload.uploadFileSingle(file);
|
||||
console.log(result)
|
||||
if (result.status !== 200) {
|
||||
return res.status(400).json({
|
||||
message: 'آپلود لوگو با خطا مواجه شد',
|
||||
|
|
@ -70,4 +68,14 @@ export class ShopController {
|
|||
const createdShop = await this.shopService.creatshop(shopData, fileId, req.user.userId);
|
||||
return res.status(createdShop.status).json(createdShop);
|
||||
}
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('/get')
|
||||
async getDataShopSeller(
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
) {
|
||||
const createdShop = await this.shopService.findShopWithSeller(req.user.userId);
|
||||
return res.status(createdShop.status).json(createdShop);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,14 @@ export class ShopService {
|
|||
"data":null
|
||||
}
|
||||
}
|
||||
const existShop = await this.shopModel.findOne({Seller:user}).select('-_id')
|
||||
if(existShop){
|
||||
return {
|
||||
"message":"فروشگاهی قبلا ثبت شده است",
|
||||
"status":409,
|
||||
"data":existShop
|
||||
}
|
||||
}
|
||||
try {
|
||||
const newShop = new this.shopModel({
|
||||
"Name":creatshopDto.Name,
|
||||
|
|
@ -32,7 +40,7 @@ export class ShopService {
|
|||
"Address":creatshopDto.Address,
|
||||
"Map":{
|
||||
"type":'Point',
|
||||
"coordinates":[creatshopDto.Coordinates.latitude,creatshopDto.Coordinates.latitude]
|
||||
"coordinates":[creatshopDto.Coordinates.latitude,creatshopDto.Coordinates.longitude]
|
||||
},
|
||||
"Property":creatshopDto.Property,
|
||||
"BusinessLicense":creatshopDto.BusinessLicense,
|
||||
|
|
@ -41,6 +49,7 @@ export class ShopService {
|
|||
"Province":creatshopDto.Province,
|
||||
"PostalCode":creatshopDto.PostalCode,
|
||||
"ShopNumber":creatshopDto.ShopNumber,
|
||||
"Phone":creatshopDto.Phone,
|
||||
"Images":null,
|
||||
"Schedule":creatshopDto.Schedule,
|
||||
"Discounts":[],
|
||||
|
|
@ -101,7 +110,27 @@ export class ShopService {
|
|||
|
||||
async findShopSingle(id:string) : Promise<CreateSellerResponse>{
|
||||
|
||||
const shop = await this.shopModel.findOne({ID:id})
|
||||
const shop = await this.shopModel.findOne({ID:id}).select('-_id')
|
||||
if(shop){
|
||||
return {
|
||||
"message":"فروشگاه ثبت شده است",
|
||||
"status":200,
|
||||
"data":shop
|
||||
}
|
||||
}else {
|
||||
return {
|
||||
"message":"فروشگاهی با این شناسه موجود نیست",
|
||||
"status":404,
|
||||
"data":null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
async findShopWithSeller(id:string) : Promise<CreateSellerResponse>{
|
||||
|
||||
const shop = await this.shopModel.findOne({Seller:id}).populate({path:'Seller',select:'-_id'}).populate({path:'Category',select:'-_id'})
|
||||
if(shop){
|
||||
return {
|
||||
"message":"فروشگاه ثبت شده است",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { SmsController } from './sms.controller';
|
||||
|
||||
describe('SmsController', () => {
|
||||
let controller: SmsController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [SmsController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<SmsController>(SmsController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
|
||||
@Controller('sms')
|
||||
export class SmsController {}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { SmsController } from './sms.controller';
|
||||
import { SmsService } from './sms.service';
|
||||
|
||||
@Module({
|
||||
providers: [SmsService],
|
||||
exports: [SmsService],
|
||||
})
|
||||
|
||||
export class SmsModule {}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { SmsService } from './sms.service';
|
||||
|
||||
describe('SmsService', () => {
|
||||
let service: SmsService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [SmsService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<SmsService>(SmsService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import axios from 'axios';
|
||||
|
||||
@Injectable()
|
||||
export class SmsService {
|
||||
private readonly logger = new Logger(SmsService.name);
|
||||
|
||||
private readonly API_KEY = '346446725341592F2F4554514846714A366A6349327A376671316779412F63474F48684D6348372B326A553D';
|
||||
private readonly TEMPLATE = 'verify';
|
||||
|
||||
/**
|
||||
* ارسال پیامک تاییدیه با قالب کاوهنگار
|
||||
* @param receptor شماره گیرنده (مثلاً: "09123456789")
|
||||
* @param token1 مقدار توکن (مثلاً: 123456)
|
||||
*/
|
||||
async sendMessage(receptor: string, token1: number): Promise<void> {
|
||||
const token2 = receptor;
|
||||
const url = `https://api.kavenegar.com/v1/${this.API_KEY}/verify/lookup.json`;
|
||||
|
||||
try {
|
||||
const response = await axios.post(url, null, {
|
||||
params: {
|
||||
receptor,
|
||||
template: this.TEMPLATE,
|
||||
token: token1,
|
||||
token2,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`پیام با موفقیت برای ${receptor} ارسال شد.`);
|
||||
console.log(response.data);
|
||||
} catch (error: any) {
|
||||
console.error(`خطا در ارسال پیام برای ${receptor}:`, error?.response?.data || error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,8 +61,10 @@ const keyWithExt = `${key}${extension}`;
|
|||
ContentType: file.mimetype,
|
||||
});
|
||||
|
||||
await this.s3.send(command);
|
||||
const dataupload = await this.s3.send(command);
|
||||
const mimeType = file.mimetype;
|
||||
console.log("dataupload")
|
||||
console.log(dataupload)
|
||||
|
||||
let fileType: 'image' | 'video' | 'document' | 'other' = 'other';
|
||||
|
||||
|
|
@ -72,7 +74,7 @@ const keyWithExt = `${key}${extension}`;
|
|||
fileType = 'video';
|
||||
} else if (
|
||||
mimeType === 'application/pdf' ||
|
||||
mimeType === 'application/msword' ||
|
||||
mimeType === 'application/msword' || 'application/octet-stream' ||
|
||||
mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||
) {
|
||||
fileType = 'document';
|
||||
|
|
@ -80,6 +82,8 @@ const keyWithExt = `${key}${extension}`;
|
|||
|
||||
|
||||
try {
|
||||
console.log("file")
|
||||
console.log(file)
|
||||
const newFile = new this.fileModel({
|
||||
"FileType":fileType,
|
||||
"Title":"avatar",
|
||||
|
|
@ -88,6 +92,8 @@ try {
|
|||
"Url":urlFile,
|
||||
"ID":key
|
||||
})
|
||||
console.log("newFile")
|
||||
console.log(newFile)
|
||||
const savedSeller = await newFile.save();
|
||||
return {
|
||||
"message":"فایل با موفقیت آپلود شد .",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { IsNotEmpty, IsNumberString, Length, Matches } from 'class-validator';
|
||||
|
||||
export class CreateUserDto {
|
||||
@IsNotEmpty({ message: 'کد کشور نمیتواند خالی باشد.' })
|
||||
@IsNumberString({},{ message: 'کد کشور باید فقط شامل اعداد باشد.' })
|
||||
@Length(1, 4, { message: 'کد کشور باید بین 1 تا 4 رقم باشد.' })
|
||||
Code: number;
|
||||
|
||||
@IsNotEmpty({ message: 'شماره تلفن نمیتواند خالی باشد.' })
|
||||
@IsNumberString({},{ message: 'شماره تلفن باید فقط شامل اعداد باشد.' })
|
||||
@Length(10, 10, { message: 'شماره تلفن باید دقیقاً 10 رقم باشد.' })
|
||||
Phone: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { IsNotEmpty, IsString,IsNumberString,Length} from "class-validator";
|
||||
|
||||
export class CreatTokenFireBase {
|
||||
@IsNotEmpty({message:"توکن نباید خالی باشد. "})
|
||||
@IsString({message:"توکن باید متن باشد . "})
|
||||
Token:string;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { SellerController } from './user.controller';
|
||||
|
||||
describe('SellerController', () => {
|
||||
let controller: SellerController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [SellerController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<SellerController>(SellerController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { Controller,
|
||||
Post,
|
||||
UploadedFile,
|
||||
UseInterceptors,
|
||||
Body,
|
||||
UseGuards,
|
||||
Res,
|
||||
Req,
|
||||
BadRequestException,
|
||||
Get } from '@nestjs/common';
|
||||
import { UserService } from './user.service';
|
||||
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
|
||||
import { AuthenticatedRequest } from 'src/interfaces/request';
|
||||
import { CreatTokenFireBase } from './dto/token-firebase.dto';
|
||||
import { Response } from 'express';
|
||||
|
||||
@Controller('seller')
|
||||
export class UserController {
|
||||
|
||||
constructor (private readonly userService:UserService){}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('/firebaseUpdate')
|
||||
async updateFireBaseToken (
|
||||
@Body() body:CreatTokenFireBase,
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Res() res: Response,
|
||||
){
|
||||
const updateToken = await this.userService.updateTokenFireBase(body.Token,req.user.userId)
|
||||
return res.status(updateToken.status).json(updateToken.data)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { UserController } from './user.controller';
|
||||
import { UserService } from './user.service';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { UserSchema } from 'src/schemas/user.schema';
|
||||
|
||||
@Module({
|
||||
imports:[
|
||||
MongooseModule.forFeature([
|
||||
{ name:'User', schema: UserSchema },
|
||||
])],
|
||||
controllers: [UserController],
|
||||
providers: [UserService],
|
||||
exports:[UserService]
|
||||
})
|
||||
export class UserModule {}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { SellerService } from './user.service';
|
||||
|
||||
describe('SellerService', () => {
|
||||
let service: SellerService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [SellerService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<SellerService>(SellerService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
|
||||
import { Injectable, NotFoundException, InternalServerErrorException } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { User, UserDocument } from '../schemas/user.schema';
|
||||
import { CreateUserDto } from './dto/creat-seller.dto';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
interface CreateSellerResponse {
|
||||
message: string;
|
||||
status: number;
|
||||
data: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
constructor(
|
||||
@InjectModel(User.name) private userModel: Model<UserDocument>,
|
||||
) {}
|
||||
async createWithPhone(createUserDto: CreateUserDto): Promise<CreateSellerResponse> {
|
||||
const newUuid: string = uuidv4();
|
||||
try {
|
||||
const createdUser = new this.userModel({
|
||||
"Email": null,
|
||||
"ID": newUuid,
|
||||
"Phone": createUserDto.Phone,
|
||||
"verify": false,
|
||||
"LastLogin":new Date(),
|
||||
"IpAddress": null,
|
||||
"CountryCode": 98,
|
||||
"TokenFireBase":""
|
||||
});
|
||||
const savedUser = await createdUser.save();
|
||||
|
||||
return {
|
||||
"message": "کاربر با موفقیت ساخته شد",
|
||||
"status": 200,
|
||||
"data": {
|
||||
"_id":createdUser._id,
|
||||
"ID":createdUser.ID
|
||||
}
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Error saving seller:", e);
|
||||
throw new InternalServerErrorException("ذخیره دیتا با شکست مواجه شد");
|
||||
|
||||
}
|
||||
}
|
||||
async findUserwidtID(id: string): Promise<CreateSellerResponse> {
|
||||
try {
|
||||
const user = await this.userModel.findOne({ ID: id }).exec();
|
||||
if (!user) {
|
||||
return {
|
||||
"message" : "کاربری ای با این شناسه ایدی موجود نیست",
|
||||
"status":404,
|
||||
"data":null
|
||||
}
|
||||
}
|
||||
return {
|
||||
"message":"اطلاعات کاربر",
|
||||
"status":200,
|
||||
"data":{
|
||||
"ID":user.ID,
|
||||
"Phone":user.Phone
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error("Error finding user:", e);
|
||||
return {
|
||||
"message":"خطا در دریافت اطلاعات کاربر",
|
||||
"status":502,
|
||||
"data":null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findUserwidtPhone(code:number,phone:number): Promise<CreateSellerResponse> {
|
||||
try {
|
||||
const user = await this.userModel.findOne({CountryCode: code,Phone:phone }).exec();
|
||||
if (!user) {
|
||||
return {
|
||||
"message" : "کاربری ای با این شناسه ایدی موجود نیست",
|
||||
"status":404,
|
||||
"data":null
|
||||
}
|
||||
}
|
||||
return {
|
||||
"message":"اطلاعات کاربر",
|
||||
"status":200,
|
||||
"data":{
|
||||
"ID":user.ID,
|
||||
"Phone":user.Phone,
|
||||
"_id":user._id
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error("Error finding seller:", e);
|
||||
return {
|
||||
"message":"خطا در دریافت اطلاعات کاربر",
|
||||
"status":502,
|
||||
"data":null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateTokenFireBase (token:string,user:string) : Promise <CreateSellerResponse> {
|
||||
|
||||
const userfind = await this.findUserwidtID(user)
|
||||
if(!userfind){
|
||||
return {
|
||||
"data":null,
|
||||
"status": 404,
|
||||
"message": "کاربر موجود نیست"
|
||||
}
|
||||
}
|
||||
const updateUser = await this.userModel.updateOne({_id:userfind.data._id},{$set:{TokenFireBase:token}})
|
||||
if (updateUser.modifiedCount === 0) {
|
||||
return {
|
||||
"data":null,
|
||||
"status": 500,
|
||||
"message": "آپدیت با شکست مواجه شد"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
data: null,
|
||||
status: 200,
|
||||
message: "بهروزرسانی انجام شد"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue