├── src ├── cart │ ├── create-cart.dto.ts │ ├── dto │ │ └── create-cart-item.dto.ts │ ├── cart-item.entity.ts │ ├── cart.entity.ts │ ├── cart.module.ts │ ├── cart.controller.ts │ └── cart.service.ts ├── auth │ ├── role.ts │ ├── jwt-payload.interface.ts │ ├── auth.constants.ts │ ├── dto │ │ └── auth-credentials.dto.ts │ ├── get-user.decorator.ts │ ├── jwt.strategy.ts │ ├── auth.module.ts │ ├── user.entity.ts │ ├── auth.controller.ts │ ├── auth.service.ts │ └── user.repository.ts ├── payment │ ├── payment.controller.ts │ ├── payment-methods.enum.ts │ ├── payment.repository.ts │ ├── dto │ │ └── create-payment.dto.ts │ ├── payment.module.ts │ ├── payment.entity.ts │ └── payment.service.ts ├── order │ ├── order-status.enum.ts │ ├── order.repository.ts │ ├── dto │ │ └── create-order.dto.ts │ ├── order.module.ts │ ├── order_item.entity.ts │ ├── order.controller.ts │ ├── order.entity.ts │ └── order.service.ts ├── contact │ ├── create-contact.dto.ts │ ├── contact.module.ts │ ├── contact.controller.ts │ └── contact.service.ts ├── app.service.ts ├── product │ ├── dto │ │ ├── get-products-filter.dto.ts │ │ └── create-product.dto.ts │ ├── product.repository.ts │ ├── product.module.ts │ ├── product.controller.ts │ ├── product.entity.ts │ └── product.service.ts ├── category │ ├── category-types.enum.ts │ ├── dto │ │ ├── update-category.dto.ts │ │ └── create-category.dto.ts │ ├── category.repository.ts │ ├── category.entity.ts │ ├── category.module.ts │ ├── category.controller.ts │ └── category.service.ts ├── test │ └── jest-e2e.json ├── invoice │ ├── invoice.repository.ts │ ├── dto │ │ └── create-invoice.dto.ts │ ├── invoice.controller.ts │ ├── invoice.module.ts │ ├── invoice.entity.ts │ └── invoice.service.ts ├── ormconfig.json ├── profile │ ├── profile.repository.ts │ ├── dto │ │ └── create-profile.dto.ts │ ├── profile.module.ts │ ├── profile.entity.ts │ ├── profile.controller.ts │ └── profile.service.ts ├── shared │ └── aws │ │ ├── aws.controller.ts │ │ ├── aws.module.ts │ │ └── aws.service.ts ├── app.e2e-spec.ts ├── helpers │ └── upload-file.helper.ts ├── main.ts ├── config.ts ├── app.controller.ts └── app.module.ts ├── .prettierrc ├── nest-cli.json ├── tsconfig.build.json ├── files ├── product-images │ ├── c1-beb1.jpg │ ├── c2-e855.jpg │ ├── c5-f868.png │ ├── c6-5e7e.jpg │ ├── c7-1910.jpg │ ├── pc1-6f4c.jpg │ ├── pc2-4df1.jpg │ ├── pc4-2d9b.jpg │ ├── pc5-c4a8.jpg │ ├── pc7-77f8.jpg │ ├── ps2-d2af.jpg │ ├── ps5-e366.jpg │ ├── gtx1-ef94.jpg │ ├── gtx2-1754.jpg │ ├── gtx3-c0910.jpg │ ├── head1-eb2d.jpg │ ├── head2-4e91.jpg │ ├── head5-c1e6.jpg │ ├── ps1-af10e.jpg │ ├── ps4-104fe.jpg │ ├── hard-drive1-c0e3.jpg │ ├── jack-audio-5d1b.jpg │ ├── jack-audio2-9fc0.jpg │ ├── power-cable1-9272.jpg │ ├── printer-cable-de4c.jpg │ └── extension-table-1-0137.jpg └── profile-images │ ├── myPic1-4cd7.jpg │ └── computer-81f8.jpg ├── ormconfig.json ├── tsconfig.json ├── .gitignore ├── tslint.json ├── package.json └── README.md /src/cart/create-cart.dto.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /src/auth/role.ts: -------------------------------------------------------------------------------- 1 | export enum Role { 2 | ADMIN = "ADMIN", 3 | USER = "USER" 4 | } 5 | -------------------------------------------------------------------------------- /src/auth/jwt-payload.interface.ts: -------------------------------------------------------------------------------- 1 | export interface JwtPayload { 2 | username: string; 3 | } -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "ts", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src" 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /files/product-images/c1-beb1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/c1-beb1.jpg -------------------------------------------------------------------------------- /files/product-images/c2-e855.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/c2-e855.jpg -------------------------------------------------------------------------------- /files/product-images/c5-f868.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/c5-f868.png -------------------------------------------------------------------------------- /files/product-images/c6-5e7e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/c6-5e7e.jpg -------------------------------------------------------------------------------- /files/product-images/c7-1910.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/c7-1910.jpg -------------------------------------------------------------------------------- /files/product-images/pc1-6f4c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/pc1-6f4c.jpg -------------------------------------------------------------------------------- /files/product-images/pc2-4df1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/pc2-4df1.jpg -------------------------------------------------------------------------------- /files/product-images/pc4-2d9b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/pc4-2d9b.jpg -------------------------------------------------------------------------------- /files/product-images/pc5-c4a8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/pc5-c4a8.jpg -------------------------------------------------------------------------------- /files/product-images/pc7-77f8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/pc7-77f8.jpg -------------------------------------------------------------------------------- /files/product-images/ps2-d2af.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/ps2-d2af.jpg -------------------------------------------------------------------------------- /files/product-images/ps5-e366.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/ps5-e366.jpg -------------------------------------------------------------------------------- /src/auth/auth.constants.ts: -------------------------------------------------------------------------------- 1 | export const jwtConstants = { 2 | secret: 'secretKey', 3 | strategy: 'jwt', 4 | expiresIn: '10hr' 5 | }; 6 | -------------------------------------------------------------------------------- /files/product-images/gtx1-ef94.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/gtx1-ef94.jpg -------------------------------------------------------------------------------- /files/product-images/gtx2-1754.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/gtx2-1754.jpg -------------------------------------------------------------------------------- /files/product-images/gtx3-c0910.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/gtx3-c0910.jpg -------------------------------------------------------------------------------- /files/product-images/head1-eb2d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/head1-eb2d.jpg -------------------------------------------------------------------------------- /files/product-images/head2-4e91.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/head2-4e91.jpg -------------------------------------------------------------------------------- /files/product-images/head5-c1e6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/head5-c1e6.jpg -------------------------------------------------------------------------------- /files/product-images/ps1-af10e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/ps1-af10e.jpg -------------------------------------------------------------------------------- /files/product-images/ps4-104fe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/ps4-104fe.jpg -------------------------------------------------------------------------------- /files/profile-images/myPic1-4cd7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/profile-images/myPic1-4cd7.jpg -------------------------------------------------------------------------------- /files/profile-images/computer-81f8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/profile-images/computer-81f8.jpg -------------------------------------------------------------------------------- /src/payment/payment.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common'; 2 | 3 | @Controller('payment') 4 | export class PaymentController {} 5 | -------------------------------------------------------------------------------- /files/product-images/hard-drive1-c0e3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/hard-drive1-c0e3.jpg -------------------------------------------------------------------------------- /files/product-images/jack-audio-5d1b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/jack-audio-5d1b.jpg -------------------------------------------------------------------------------- /files/product-images/jack-audio2-9fc0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/jack-audio2-9fc0.jpg -------------------------------------------------------------------------------- /src/order/order-status.enum.ts: -------------------------------------------------------------------------------- 1 | export enum OrderStatus { 2 | PROCESSED = 'PROCESSED', 3 | SHIPPED = 'SHIPPED', 4 | DELIVERED = 'DELIVERED' 5 | } -------------------------------------------------------------------------------- /files/product-images/power-cable1-9272.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/power-cable1-9272.jpg -------------------------------------------------------------------------------- /files/product-images/printer-cable-de4c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/printer-cable-de4c.jpg -------------------------------------------------------------------------------- /files/product-images/extension-table-1-0137.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammadqaderi/Ecommerce-Nestjs-Back-End/HEAD/files/product-images/extension-table-1-0137.jpg -------------------------------------------------------------------------------- /src/contact/create-contact.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateContactDto { 2 | name: string; 3 | phone: string; 4 | email: string; 5 | title: string; 6 | message: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/cart/dto/create-cart-item.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNumber, IsNotEmpty } from "class-validator"; 2 | 3 | export class CreateCartItemDto { 4 | @IsNotEmpty() 5 | @IsNumber() 6 | total_items: number; 7 | } -------------------------------------------------------------------------------- /src/payment/payment-methods.enum.ts: -------------------------------------------------------------------------------- 1 | export enum PaymentMethod { 2 | VISA = 'VISA', 3 | PAYPAL = 'PAYPAL', 4 | CASH_ON_DELIVERY = 'CASH_ON_DELIVERY', 5 | MASTERCARD = "MASTERCARD", 6 | PURCHASE_ORDER = "PURCHASE_ORDER" 7 | } -------------------------------------------------------------------------------- /src/product/dto/get-products-filter.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsOptional, IsNotEmpty } from 'class-validator'; 2 | 3 | export class GetProductsFilterDto { 4 | name: string; 5 | description: string; 6 | price: number; 7 | } 8 | -------------------------------------------------------------------------------- /src/order/order.repository.ts: -------------------------------------------------------------------------------- 1 | import { EntityRepository, Repository } from "typeorm"; 2 | import { Order } from "./order.entity"; 3 | 4 | @EntityRepository(Order) 5 | export class OrderRepository extends Repository { 6 | 7 | } -------------------------------------------------------------------------------- /src/payment/payment.repository.ts: -------------------------------------------------------------------------------- 1 | import { EntityRepository, Repository } from "typeorm"; 2 | import { Payment } from "./payment.entity"; 3 | 4 | @EntityRepository(Payment) 5 | export class PaymentRepository extends Repository { 6 | 7 | } -------------------------------------------------------------------------------- /src/category/category-types.enum.ts: -------------------------------------------------------------------------------- 1 | export enum CategoryTypes { 2 | PCs = 'PCs', 3 | LAPTOPS = 'LAPTOPS', 4 | PLAY_STATIONS = 'PLAY_STATIONS', 5 | ACCESSORIES = 'ACCESSORIES', 6 | OTHERS = 'OTHERS', 7 | HEAD_PHONES = 'HEAD_PHONES' 8 | } 9 | -------------------------------------------------------------------------------- /src/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/auth/dto/auth-credentials.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, MinLength, MaxLength, Matches, IsNotEmpty } from 'class-validator'; 2 | 3 | export class AuthCredentialsDto { 4 | 5 | 6 | username: string; 7 | 8 | 9 | password: string; 10 | } 11 | -------------------------------------------------------------------------------- /src/invoice/invoice.repository.ts: -------------------------------------------------------------------------------- 1 | import { Repository, EntityRepository } from 'typeorm'; 2 | import { Invoice } from './invoice.entity'; 3 | 4 | @EntityRepository(Invoice) 5 | export class InvoiceRepository extends Repository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/auth/get-user.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator } from '@nestjs/common'; 2 | import { User } from './user.entity'; 3 | 4 | export const GetAuthenticatedUser = createParamDecorator( 5 | (data, req): User => { 6 | return req.user; 7 | }, 8 | ); 9 | -------------------------------------------------------------------------------- /ormconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "postgres", 3 | "host": "localhost", 4 | "port": 5432, 5 | "username": "postgres", 6 | "password": "password", 7 | "database": "database", 8 | "entities": "[__dirname + '/**/*.entity{.ts,.js}']", 9 | "synchronize": true 10 | } -------------------------------------------------------------------------------- /src/ormconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "postgres", 3 | "host": "localhost", 4 | "port": 5432, 5 | "username": "postgres", 6 | "password": "password", 7 | "database": "database", 8 | "entities": ["__dirname + '/**/*.entity{.ts,.js}'"], 9 | "synchronize": true 10 | } -------------------------------------------------------------------------------- /src/invoice/dto/create-invoice.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNumberString, IsNotEmpty, IsDate } from "class-validator"; 2 | 3 | export class CreateInvoiceDto { 4 | 5 | @IsNumberString() 6 | @IsNotEmpty() 7 | number: string; 8 | 9 | 10 | @IsDate() 11 | @IsNotEmpty() 12 | due_date: Date; 13 | } -------------------------------------------------------------------------------- /src/profile/profile.repository.ts: -------------------------------------------------------------------------------- 1 | import { Repository, EntityRepository } from 'typeorm'; 2 | import { Profile } from './profile.entity'; 3 | import { CreateProfileDto } from './dto/create-profile.dto'; 4 | 5 | @EntityRepository(Profile) 6 | export class ProfileRepository extends Repository { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/contact/contact.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ContactController } from './contact.controller'; 3 | import { ContactService } from './contact.service'; 4 | 5 | @Module({ 6 | controllers: [ 7 | ContactController, 8 | ], 9 | providers: [ 10 | ContactService, 11 | ] 12 | }) 13 | export class ContactModule { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/order/dto/create-order.dto.ts: -------------------------------------------------------------------------------- 1 | import { OrderStatus } from '../order-status.enum'; 2 | import { IsIn, IsNotEmpty, IsString } from 'class-validator'; 3 | 4 | export class CreateOrderDto { 5 | @IsNotEmpty() 6 | @IsIn([OrderStatus.PROCESSED, OrderStatus.SHIPPED, OrderStatus.DELIVERED]) 7 | status: OrderStatus; 8 | 9 | @IsNotEmpty() 10 | @IsString() 11 | comments: string; 12 | } 13 | -------------------------------------------------------------------------------- /src/category/dto/update-category.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsIn, IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator'; 2 | import { CategoryTypes } from '../category-types.enum'; 3 | 4 | export class UpdateCategoryDto { 5 | @IsString() 6 | @MinLength(5) 7 | @MaxLength(20) 8 | name: string; 9 | 10 | @IsString() 11 | @MinLength(5) 12 | @MaxLength(100) 13 | description: string; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es2017", 9 | "sourceMap": true, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "incremental": true 13 | }, 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /src/payment/dto/create-payment.dto.ts: -------------------------------------------------------------------------------- 1 | import { PaymentMethod } from '../payment-methods.enum'; 2 | import { IsIn } from 'class-validator'; 3 | 4 | export class CreatePaymentDto { 5 | @IsIn([ 6 | PaymentMethod.VISA, 7 | PaymentMethod.PAYPAL, 8 | PaymentMethod.CASH_ON_DELIVERY, 9 | PaymentMethod.MASTERCARD, 10 | PaymentMethod.PURCHASE_ORDER 11 | ]) 12 | payment_method: PaymentMethod; 13 | } 14 | -------------------------------------------------------------------------------- /src/shared/aws/aws.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Post, Req, Res} from '@nestjs/common'; 2 | import { AwsService } from './aws.service'; 3 | 4 | @Controller('aws') 5 | export class AwsController { 6 | constructor(private service: AwsService) {} 7 | // @Post('file-upload') 8 | // uploadFile(@Req() request, @Res() response) { 9 | // return this.service.fileupload(request, response); 10 | // } 11 | } 12 | -------------------------------------------------------------------------------- /src/shared/aws/aws.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AwsService } from './aws.service'; 3 | import { AwsController } from './aws.controller'; 4 | 5 | @Module({ 6 | providers: [AwsService], 7 | controllers: [AwsController], 8 | exports: [AwsService] 9 | }) 10 | export class AwsModule {} 11 | // Access Key ID: 12 | // AKIAI74L3YS6JQG6LWBQ 13 | // Secret Access Key: 14 | // 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 -------------------------------------------------------------------------------- /src/profile/dto/create-profile.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString, IsEmail, IsNumber, IsNumberString } from "class-validator"; 2 | 3 | export class CreateProfileDto { 4 | 5 | firstname: string; 6 | 7 | 8 | lastname: string; 9 | 10 | 11 | email: string; 12 | 13 | gender: string; 14 | 15 | 16 | age: string; 17 | 18 | country: string; 19 | 20 | 21 | city: string; 22 | 23 | 24 | address: string; 25 | 26 | 27 | phone: string; 28 | 29 | } -------------------------------------------------------------------------------- /src/product/product.repository.ts: -------------------------------------------------------------------------------- 1 | import { EntityRepository, Repository } from 'typeorm'; 2 | import { Product } from './product.entity'; 3 | import { GetProductsFilterDto } from './dto/get-products-filter.dto'; 4 | 5 | @EntityRepository(Product) 6 | export class ProductRepository extends Repository { 7 | async getProducts( 8 | ): Promise { 9 | try { 10 | return await this.find(); 11 | } catch (error) { 12 | console.error(error); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json -------------------------------------------------------------------------------- /src/payment/payment.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { PaymentService } from './payment.service'; 3 | import { PaymentController } from './payment.controller'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { PaymentRepository } from './payment.repository'; 6 | 7 | @Module({ 8 | imports: [TypeOrmModule.forFeature([PaymentRepository])], 9 | controllers: [PaymentController], 10 | providers: [PaymentService], 11 | exports: [ 12 | PaymentService 13 | ] 14 | }) 15 | export class PaymentModule {} 16 | -------------------------------------------------------------------------------- /src/category/dto/create-category.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsIn, IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator'; 2 | import { CategoryTypes } from '../category-types.enum'; 3 | 4 | export class CreateCategoryDto { 5 | @IsString() 6 | @IsNotEmpty() 7 | @MinLength(5) 8 | @MaxLength(20) 9 | name: string; 10 | 11 | @IsString() 12 | @IsNotEmpty() 13 | @MinLength(5) 14 | @MaxLength(100) 15 | description: string; 16 | 17 | @IsIn([CategoryTypes.PCs, CategoryTypes.PLAY_STATIONS, CategoryTypes.LAPTOPS, CategoryTypes.ACCESSORIES,CategoryTypes.HEAD_PHONES, CategoryTypes.OTHERS]) 18 | type: CategoryTypes; 19 | } 20 | -------------------------------------------------------------------------------- /src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import * as request from 'supertest'; 3 | import { AppModule } from './app.module'; 4 | 5 | describe('AppController (e2e)', () => { 6 | let app; 7 | 8 | beforeEach(async () => { 9 | const moduleFixture: TestingModule = await Test.createTestingModule({ 10 | imports: [AppModule], 11 | }).compile(); 12 | 13 | app = moduleFixture.createNestApplication(); 14 | await app.init(); 15 | }); 16 | 17 | it('/ (GET)', () => { 18 | return request(app.getHttpServer()) 19 | .get('/') 20 | .expect(200) 21 | .expect('Hello World!'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/contact/contact.controller.ts: -------------------------------------------------------------------------------- 1 | import { Body, Controller, Post } from '@nestjs/common'; 2 | import { CreateContactDto } from './create-contact.dto'; 3 | import { ContactService } from './contact.service'; 4 | 5 | @Controller('contacts') 6 | export class ContactController { 7 | constructor(private contactService: ContactService){} 8 | @Post('/new-mail') 9 | newMail 10 | ( 11 | @Body('name') name: string, 12 | @Body('phone') phone: string, 13 | @Body('email') email: string, 14 | @Body('title') title: string, 15 | @Body('message') message: string, 16 | ) { 17 | this.contactService.mailing(name, phone, email, title, message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/helpers/upload-file.helper.ts: -------------------------------------------------------------------------------- 1 | import { extname } from 'path'; 2 | 3 | export const imageFileFilter = (req, file, callback) => { 4 | if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) { 5 | return callback(new Error('Only image files are allowed!'), false); 6 | } 7 | callback(null, true); 8 | }; 9 | 10 | export const editFileName = (req, file, callback) => { 11 | const name = file.originalname.split('.')[0]; 12 | const fileExtName = extname(file.originalname); 13 | const randomName = Array(4) 14 | .fill(null) 15 | .map(() => Math.round(Math.random() * 16).toString(16)) 16 | .join(''); 17 | callback(null, `${name}-${randomName}${fileExtName}`); 18 | }; 19 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import { join } from 'path'; 4 | import { NestExpressApplication } from '@nestjs/platform-express'; 5 | 6 | async function bootstrap() { 7 | const app = await NestFactory.create(AppModule, { 8 | cors: true, 9 | bodyParser: true, 10 | }); 11 | app.enableCors(); 12 | app.useStaticAssets(join(__dirname, '..', 'files')); 13 | app.useStaticAssets(join(__dirname, '../files/profile-images')); 14 | app.useStaticAssets(join(__dirname, '../files/product-images')); 15 | 16 | await app.listen(process.env.PORT || 3000); 17 | } 18 | 19 | bootstrap(); 20 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | mail: { 3 | email: 'email', 4 | password:'password', 5 | host: 'smtp.gmail.com', 6 | port: 465, 7 | secure: true, 8 | rejectUnauthorized: false 9 | }, 10 | dbConfig: { 11 | type: 'postgres', 12 | host: 'localhost', 13 | port: 5432, 14 | username: 'postgres', 15 | password: 'password', 16 | database: 'database', 17 | entities: [__dirname + '/**/*.entity{.ts,.js}'], 18 | synchronize: true, 19 | }, 20 | 21 | aws: { 22 | AWS_S3_BUCKET_NAME: 'AWS_S3_BUCKET_NAME', 23 | Access_Key_ID: 'Access_Key_ID', 24 | Secret_Access_Key:'Secret_Access_Key', 25 | cdnUrl: 'cdnUrl' 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/cart/cart-item.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PrimaryGeneratedColumn, 3 | OneToOne, 4 | Entity, 5 | BaseEntity, 6 | Column, 7 | OneToMany, 8 | } from 'typeorm'; 9 | import { Cart } from './cart.entity'; 10 | import { Product } from '../product/product.entity'; 11 | 12 | @Entity('cart_items') 13 | export class CartItem extends BaseEntity { 14 | @PrimaryGeneratedColumn() 15 | id: number; 16 | 17 | @Column() 18 | total_products: number; 19 | 20 | @OneToMany(type => Product, product => product.cartItem, { 21 | eager: true, 22 | }) 23 | products: Product[]; 24 | 25 | @OneToOne(type => Cart, cart => cart.cart_item, { 26 | onDelete: 'CASCADE', 27 | onUpdate: 'CASCADE' 28 | }) 29 | cart: Cart; 30 | } 31 | -------------------------------------------------------------------------------- /src/invoice/invoice.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Param, ParseIntPipe, UseGuards } from '@nestjs/common'; 2 | import { InvoiceService } from './invoice.service'; 3 | import { AuthGuard } from '@nestjs/passport'; 4 | import { GetAuthenticatedUser } from '../auth/get-user.decorator'; 5 | import { User } from '../auth/user.entity'; 6 | 7 | @Controller('invoices') 8 | export class InvoiceController { 9 | constructor(private invoiceService: InvoiceService) { 10 | } 11 | 12 | @UseGuards(AuthGuard()) 13 | @Get(':id') 14 | getUserInvoice(@GetAuthenticatedUser() user: User, 15 | @Param('id', ParseIntPipe) invoiceId: number) { 16 | return this.invoiceService.findInvoice(user, invoiceId); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/product/dto/create-product.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | IsNotEmpty, 4 | MinLength, 5 | MaxLength, 6 | IsNumber, 7 | IsPositive, 8 | Min, 9 | Max, 10 | IsDate, 11 | IsDateString, 12 | MinDate, 13 | } from 'class-validator'; 14 | 15 | export class CreateProductDto { 16 | @IsString() 17 | @IsNotEmpty() 18 | @MinLength(5) 19 | @MaxLength(20) 20 | name: string; 21 | 22 | @IsString() 23 | @IsNotEmpty() 24 | @MinLength(5) 25 | @MaxLength(100) 26 | description: string; 27 | 28 | @IsNumber() 29 | @IsNotEmpty() 30 | @IsPositive() 31 | @Min(5) 32 | @Max(300) 33 | price: number; 34 | 35 | 36 | @IsNumber() 37 | @IsNotEmpty() 38 | @IsPositive() 39 | @Min(1) 40 | @Max(99999999) 41 | quantity: number; 42 | } 43 | -------------------------------------------------------------------------------- /src/category/category.repository.ts: -------------------------------------------------------------------------------- 1 | import { Category } from './category.entity'; 2 | import { EntityRepository, Repository } from 'typeorm'; 3 | import { CreateCategoryDto } from './dto/create-category.dto'; 4 | 5 | @EntityRepository(Category) 6 | export class CategoryRepository extends Repository { 7 | async createCategory(createCategoryDto: CreateCategoryDto): Promise { 8 | const { name, description, type } = createCategoryDto; 9 | const category = new Category(); 10 | category.name = name; 11 | category.description = description; 12 | category.type = type; 13 | try { 14 | await category.save(); 15 | return category; 16 | 17 | } catch (error) { 18 | console.log(error); 19 | } 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/category/category.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | OneToMany, 6 | BaseEntity, 7 | Unique, 8 | } from 'typeorm'; 9 | import { Product } from '../product/product.entity'; 10 | import { CategoryTypes } from './category-types.enum'; 11 | 12 | @Entity('categories') 13 | @Unique(['type']) 14 | export class Category extends BaseEntity { 15 | @PrimaryGeneratedColumn() 16 | id: number; 17 | 18 | @Column() 19 | name: string; 20 | 21 | @Column() 22 | description: string; 23 | 24 | @OneToMany(type => Product, product => product.category, { 25 | eager: true, 26 | onDelete: 'CASCADE', 27 | onUpdate: 'CASCADE' 28 | }) 29 | products: Product[]; 30 | 31 | @Column() 32 | type: CategoryTypes; 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/cart/cart.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseEntity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | Entity, 6 | OneToOne, 7 | JoinColumn, 8 | } from 'typeorm'; 9 | import { CartItem } from './cart-item.entity'; 10 | import { Profile } from '../profile/profile.entity'; 11 | 12 | @Entity('carts') 13 | export class Cart extends BaseEntity { 14 | @PrimaryGeneratedColumn() 15 | id: number; 16 | 17 | @OneToOne(type => Profile, profile => profile.cart, { 18 | onDelete: 'CASCADE', 19 | onUpdate: 'CASCADE', 20 | }) 21 | profile: Profile; 22 | 23 | @OneToOne(type => CartItem, cart_item => cart_item.cart, { 24 | onDelete: 'CASCADE', 25 | onUpdate: 'CASCADE' 26 | }) 27 | @JoinColumn() 28 | cart_item: CartItem; 29 | 30 | @Column() 31 | cartItemId: number; 32 | } 33 | -------------------------------------------------------------------------------- /src/category/category.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from '@nestjs/common'; 2 | import { CategoryService } from './category.service'; 3 | import { CategoryController } from './category.controller'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { CategoryRepository } from './category.repository'; 6 | import { AuthModule } from '../auth/auth.module'; 7 | import { ProductModule } from '../product/product.module'; 8 | import { AwsModule } from '../shared/aws/aws.module'; 9 | 10 | @Module({ 11 | imports: [ 12 | TypeOrmModule.forFeature([CategoryRepository]), 13 | AuthModule, 14 | ProductModule, 15 | AwsModule 16 | ], 17 | providers: [CategoryService], 18 | controllers: [CategoryController], 19 | exports: [CategoryService] 20 | }) 21 | export class CategoryModule {} 22 | -------------------------------------------------------------------------------- /src/profile/profile.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ProfileService } from './profile.service'; 3 | import { ProfileController } from './profile.controller'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { ProfileRepository } from './profile.repository'; 6 | import { PassportModule } from '@nestjs/passport'; 7 | import { jwtConstants } from '../auth/auth.constants'; 8 | import { AwsModule } from '../shared/aws/aws.module'; 9 | 10 | @Module({ 11 | imports: [TypeOrmModule.forFeature([ProfileRepository]), 12 | PassportModule.register({ 13 | defaultStrategy: jwtConstants.strategy 14 | }), 15 | AwsModule], 16 | providers: [ProfileService], 17 | controllers: [ProfileController], 18 | exports: [ProfileService], 19 | }) 20 | export class ProfileModule {} 21 | -------------------------------------------------------------------------------- /src/product/product.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ProductController } from './product.controller'; 3 | import { ProductService } from './product.service'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { ProductRepository } from './product.repository'; 6 | import { CartModule } from '../cart/cart.module'; 7 | import { PassportModule } from '@nestjs/passport'; 8 | import { jwtConstants } from '../auth/auth.constants'; 9 | 10 | @Module({ 11 | imports: [ 12 | CartModule, 13 | TypeOrmModule.forFeature([ProductRepository]), 14 | PassportModule.register({ 15 | defaultStrategy: jwtConstants.strategy 16 | }), 17 | ], 18 | controllers: [ProductController], 19 | providers: [ProductService], 20 | exports: [ProductService], 21 | }) 22 | export class ProductModule {} 23 | -------------------------------------------------------------------------------- /src/invoice/invoice.module.ts: -------------------------------------------------------------------------------- 1 | import { forwardRef, Module } from '@nestjs/common'; 2 | import { InvoiceController } from './invoice.controller'; 3 | import { InvoiceService } from './invoice.service'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { InvoiceRepository } from './invoice.repository'; 6 | import { AuthModule } from '../auth/auth.module'; 7 | import { PassportModule } from '@nestjs/passport'; 8 | import { jwtConstants } from '../auth/auth.constants'; 9 | 10 | @Module({ 11 | imports: [TypeOrmModule.forFeature([InvoiceRepository]), 12 | forwardRef(() => AuthModule), 13 | PassportModule.register({ 14 | defaultStrategy: jwtConstants.strategy, 15 | })], 16 | 17 | controllers: [InvoiceController], 18 | providers: [InvoiceService], 19 | exports: [InvoiceService], 20 | }) 21 | export class InvoiceModule { 22 | } 23 | -------------------------------------------------------------------------------- /src/order/order.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from '@nestjs/common'; 2 | import { OrderController } from './order.controller'; 3 | import { OrderService } from './order.service'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { OrderRepository } from './order.repository'; 6 | import { AuthModule } from '../auth/auth.module'; 7 | import { OrderItem } from './order_item.entity'; 8 | import { PassportModule } from '@nestjs/passport'; 9 | import { jwtConstants } from '../auth/auth.constants'; 10 | 11 | @Module({ 12 | imports: [ 13 | TypeOrmModule.forFeature([OrderRepository, OrderItem]), 14 | forwardRef(() => AuthModule), 15 | PassportModule.register({ 16 | defaultStrategy: jwtConstants.strategy 17 | }), 18 | ], 19 | controllers: [OrderController], 20 | providers: [OrderService], 21 | exports: [OrderService], 22 | 23 | }) 24 | export class OrderModule {} 25 | -------------------------------------------------------------------------------- /src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Res, Param } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | import { Response } from 'express'; 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | getHello(): string { 10 | return this.appService.getHello(); 11 | } 12 | 13 | @Get('/files/:filename') 14 | getImage(@Param('filename') filename: string, @Res() res: Response) { 15 | res.sendFile(filename, { root: 'files' }); 16 | } 17 | @Get('/files/profile-images/:filename') 18 | getProfileImage(@Param('filename') filename: string, @Res() res: Response) { 19 | res.sendFile(filename, { root: 'files/profile-images' }); 20 | } 21 | 22 | @Get('/files/product-images/:filename') 23 | getProductImage(@Param('filename') filename: string, @Res() res: Response) { 24 | res.sendFile(filename, { root: 'files/product-images' }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/payment/payment.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseEntity, 3 | Entity, 4 | PrimaryGeneratedColumn, 5 | ManyToOne, 6 | Column, 7 | OneToOne, 8 | JoinColumn, 9 | } from 'typeorm'; 10 | import { User } from '../auth/user.entity'; 11 | import { PaymentMethod } from './payment-methods.enum'; 12 | import { Invoice } from '../invoice/invoice.entity'; 13 | 14 | @Entity('payments') 15 | export class Payment extends BaseEntity { 16 | @PrimaryGeneratedColumn() 17 | id: number; 18 | 19 | @ManyToOne(type => User, user => user.payments, { 20 | eager: false, 21 | onDelete: 'CASCADE', 22 | onUpdate: 'CASCADE' 23 | }) 24 | client: User; 25 | 26 | @Column() 27 | date: Date; 28 | 29 | @Column() 30 | amount: number; 31 | 32 | @Column() 33 | payment_method: PaymentMethod; 34 | 35 | @OneToOne(type => Invoice, invoice => invoice.payment, { 36 | onDelete: 'CASCADE', 37 | onUpdate: 'CASCADE' 38 | }) 39 | @JoinColumn() 40 | invoice: Invoice; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/product/product.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Param, 5 | ParseIntPipe, 6 | Query, 7 | Post, 8 | Patch, 9 | UseGuards, 10 | } from '@nestjs/common'; 11 | 12 | import { ProductService } from './product.service'; 13 | import { AuthGuard } from '@nestjs/passport'; 14 | @Controller('products') 15 | export class ProductController { 16 | constructor(private readonly productService: ProductService) {} 17 | @Get() 18 | getProducts( 19 | ) { 20 | return this.productService.getProducts(); 21 | } 22 | 23 | @Get(':id') 24 | getProduct(@Param('id', ParseIntPipe) productId) { 25 | return this.productService.getProduct(productId); 26 | } 27 | 28 | @UseGuards(AuthGuard()) 29 | @Post(':productId/addtocart/:cart_item_id') 30 | insertToCart( 31 | @Param('productId', ParseIntPipe) productId: number, 32 | @Param('cart_item_id', ParseIntPipe) cartId: number 33 | ) { 34 | return this.productService.insertToCart(productId, cartId); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/order/order_item.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PrimaryGeneratedColumn, 3 | Entity, 4 | BaseEntity, 5 | Column, 6 | ManyToOne, 7 | } from 'typeorm'; 8 | import { Product } from '../product/product.entity'; 9 | import { Order } from './order.entity'; 10 | 11 | @Entity('order_items') 12 | export class OrderItem extends BaseEntity { 13 | @PrimaryGeneratedColumn() 14 | id: number; 15 | 16 | @Column('float') 17 | unit_price: number; 18 | 19 | @Column({ 20 | nullable: true 21 | }) 22 | quantity: number; 23 | 24 | @Column('float') 25 | totalPrice: number 26 | 27 | @ManyToOne(type => Product, product => product.order_items, { 28 | eager: false, 29 | onDelete: 'CASCADE', 30 | onUpdate: 'CASCADE' 31 | }) 32 | product: Product; 33 | 34 | @ManyToOne(type => Order, order => order.order_items, { 35 | eager: false, 36 | onDelete: 'CASCADE', 37 | onUpdate: 'CASCADE' 38 | }) 39 | order: Order; 40 | 41 | @Column() 42 | productId: number; 43 | 44 | @Column() 45 | orderId: number; 46 | } 47 | -------------------------------------------------------------------------------- /src/contact/contact.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { CreateContactDto } from './create-contact.dto'; 3 | import { Nodemailer, NodemailerDrivers } from '@crowdlinker/nestjs-mailer'; 4 | import * as config from '../config'; 5 | 6 | @Injectable() 7 | export class ContactService { 8 | constructor(private readonly nodeMailerService: Nodemailer) { 9 | } 10 | 11 | mailing( 12 | name: string, 13 | phone: string, 14 | email: string, 15 | title: string, 16 | message: string, 17 | ) { 18 | this.nodeMailerService.sendMail({ 19 | to: config.default.mail.email, 20 | from: email, 21 | subject: title, 22 | text: message, 23 | html: `

hey ${name} i want to greet to you

24 |
25 |

check your phone number: ${phone} for any additional details

`, 26 | }).then(value => { 27 | console.log(value); 28 | }).catch(err => { 29 | console.error(err); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/cart/cart.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from '@nestjs/common'; 2 | import { CartService } from './cart.service'; 3 | import { CartController, CartItemController } from './cart.controller'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { AuthModule } from '../auth/auth.module'; 6 | import { Cart } from './cart.entity'; 7 | import { ProfileModule } from '../profile/profile.module'; 8 | import { CartItem } from './cart-item.entity'; 9 | import { PassportModule } from '@nestjs/passport'; 10 | import { jwtConstants } from '../auth/auth.constants'; 11 | import { OrderModule } from '../order/order.module'; 12 | 13 | @Module({ 14 | imports: [ 15 | TypeOrmModule.forFeature([Cart, CartItem]), 16 | forwardRef(() => AuthModule), 17 | forwardRef(() => OrderModule), 18 | ProfileModule, 19 | PassportModule.register({ 20 | defaultStrategy: jwtConstants.strategy 21 | }), 22 | ], 23 | providers: [CartService], 24 | controllers: [CartController, CartItemController], 25 | exports: [CartService], 26 | }) 27 | export class CartModule {} 28 | -------------------------------------------------------------------------------- /src/auth/jwt.strategy.ts: -------------------------------------------------------------------------------- 1 | import { Strategy, ExtractJwt } from 'passport-jwt'; 2 | import { PassportStrategy } from '@nestjs/passport'; 3 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 4 | import { JwtPayload } from './jwt-payload.interface'; 5 | import { UserRepository } from './user.repository'; 6 | import { InjectRepository } from '@nestjs/typeorm'; 7 | import { User } from './user.entity'; 8 | import { jwtConstants } from './auth.constants'; 9 | @Injectable() 10 | export class JwtStrategy extends PassportStrategy(Strategy) { 11 | constructor( 12 | @InjectRepository(User) private readonly userRepository: UserRepository, 13 | ) { 14 | super({ 15 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 16 | ignoreExpiration: false, 17 | secretOrKey: jwtConstants.secret, 18 | }); 19 | } 20 | 21 | async validate(payload: JwtPayload): Promise { 22 | const { username } = payload; 23 | const user = await this.userRepository.findOne({ username }); 24 | if (!user) { 25 | throw new UnauthorizedException(); 26 | } 27 | return user; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended"], 4 | "jsRules": { 5 | "no-unused-expression": true 6 | }, 7 | "rules": { 8 | "quotemark": [false, "single"], 9 | "member-access": [false], 10 | "ordered-imports": [false], 11 | "whitespace":false, 12 | "variable-name":false, 13 | "semicolon":false, 14 | "typedef":false, 15 | "no-shadowed-variable": false, 16 | "typedef-whitespace":false, 17 | "max-classes-per-file":false, 18 | "no-use-before-declare":false, 19 | "align":false, 20 | "prefer-for-of":false, 21 | "newline-before-return":false, 22 | "one-line":false, 23 | "trailing-comma":false, 24 | "space-within-parens":false, 25 | "max-line-length": [true, 150], 26 | "member-ordering": [false], 27 | "interface-name": [false], 28 | "eofline":false, 29 | "no-trailing-whitespace": false, 30 | "arrow-parens": false, 31 | "no-console": false, 32 | "no-consecutive-blank-lines":false, 33 | "no-empty": false, 34 | "object-literal-sort-keys": false 35 | }, 36 | "rulesDirectory": [] 37 | } 38 | -------------------------------------------------------------------------------- /src/invoice/invoice.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | BaseEntity, 4 | PrimaryGeneratedColumn, 5 | Column, 6 | Unique, 7 | OneToOne, 8 | ManyToOne, 9 | } from 'typeorm'; 10 | import { User } from '../auth/user.entity'; 11 | import { Payment } from '../payment/payment.entity'; 12 | import { Order } from '../order/order.entity'; 13 | 14 | @Entity('invoices') 15 | @Unique(['number']) 16 | export class Invoice extends BaseEntity { 17 | @PrimaryGeneratedColumn() 18 | id: number; 19 | 20 | @Column() 21 | number: string; 22 | 23 | @Column() 24 | invoice_total: number; 25 | 26 | @Column() 27 | invoice_date: Date; 28 | 29 | @Column() 30 | due_date: Date; 31 | 32 | @Column() 33 | payment_date: Date; 34 | 35 | @ManyToOne(type => User, user => user.invoices, { 36 | eager: false, 37 | onDelete: 'CASCADE', 38 | onUpdate: 'CASCADE' 39 | }) 40 | client: User; 41 | 42 | @OneToOne(type => Payment, payment => payment.invoice, { 43 | onDelete: 'CASCADE', 44 | onUpdate: 'CASCADE' 45 | }) 46 | payment: Payment; 47 | 48 | @OneToOne(type => Order, order => order.invoice, { 49 | onDelete: 'CASCADE', 50 | onUpdate: 'CASCADE' 51 | }) 52 | order: Order; 53 | } 54 | -------------------------------------------------------------------------------- /src/order/order.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Delete, 4 | Param, 5 | ParseIntPipe, 6 | UseGuards, 7 | Get, 8 | } from '@nestjs/common'; 9 | import { OrderService } from './order.service'; 10 | import { AuthGuard } from '@nestjs/passport'; 11 | import { GetAuthenticatedUser } from '../auth/get-user.decorator'; 12 | import { User } from '../auth/user.entity'; 13 | 14 | @Controller('orders') 15 | export class OrderController { 16 | constructor(private orderService: OrderService) { 17 | } 18 | 19 | @UseGuards(AuthGuard()) 20 | @Delete(':id/cancel-order') 21 | cancelOrder( 22 | @GetAuthenticatedUser() user: User, 23 | @Param('id', ParseIntPipe) orderId: number, 24 | ) { 25 | return this.orderService.deleteOrder(user, orderId); 26 | } 27 | 28 | @UseGuards(AuthGuard()) 29 | @Get('/user-orders') 30 | getUserOrders(@GetAuthenticatedUser() user: User) { 31 | return this.orderService.findUserOrders(user); 32 | } 33 | 34 | @UseGuards(AuthGuard()) 35 | @Get('/user-orders/:id') 36 | getUserOrder(@GetAuthenticatedUser() user: User, 37 | @Param('id', ParseIntPipe) orderId: number) { 38 | return this.getUserOrder(user, orderId); 39 | } 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/profile/profile.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | BaseEntity, 6 | OneToOne, 7 | JoinColumn, 8 | } from 'typeorm'; 9 | import { Cart } from '../cart/cart.entity'; 10 | import { User } from '../auth/user.entity'; 11 | 12 | @Entity('profiles') 13 | export class Profile extends BaseEntity { 14 | @PrimaryGeneratedColumn() 15 | id: number; 16 | 17 | @Column() 18 | firstname: string; 19 | 20 | @Column() 21 | lastname: string; 22 | 23 | @Column() 24 | email: string; 25 | 26 | @Column() 27 | gender: string; 28 | 29 | @Column() 30 | age: string; 31 | 32 | @Column() 33 | country: string; 34 | 35 | @Column() 36 | city: string; 37 | 38 | @Column() 39 | address: string; 40 | 41 | @Column({ nullable: true }) 42 | image: string; 43 | @Column({ 44 | nullable: true, 45 | }) 46 | phone: string; 47 | 48 | @OneToOne(type => Cart, cart => cart.profile, { 49 | onDelete: 'CASCADE', 50 | onUpdate: 'CASCADE', 51 | }) 52 | @JoinColumn() 53 | cart: Cart; 54 | 55 | @OneToOne(type => User, user => user.profile, { 56 | onDelete: 'CASCADE', 57 | onUpdate: 'CASCADE', 58 | }) 59 | user: User; 60 | 61 | @Column() 62 | cartId: number; 63 | } 64 | -------------------------------------------------------------------------------- /src/payment/payment.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException } from '@nestjs/common'; 2 | import { PaymentRepository } from './payment.repository'; 3 | import { User } from '../auth/user.entity'; 4 | import { Payment } from './payment.entity'; 5 | 6 | @Injectable() 7 | export class PaymentService { 8 | constructor(private paymentRepository: PaymentRepository) {} 9 | 10 | 11 | async findPayments(client: User): Promise { 12 | const payments = await this.paymentRepository.find({ 13 | where: { 14 | client 15 | } 16 | }); 17 | return payments; 18 | } 19 | async findPayment(client: User, id: number): Promise { 20 | const payment = await this.paymentRepository.findOne({ 21 | where: { 22 | client, 23 | id 24 | }, 25 | }); 26 | if (!payment) { 27 | throw new NotFoundException(`Payment with id ${id} not found!!`); 28 | } 29 | return payment; 30 | } 31 | async deletePayment(client: User, paymentId: number): Promise { 32 | const payment = await this.findPayment(client, paymentId); 33 | const result = await this.paymentRepository.delete(payment); 34 | if (result.affected === 0) { 35 | throw new NotFoundException(`Payment with id ${paymentId} not found!!`); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/invoice/invoice.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException } from '@nestjs/common'; 2 | import { InvoiceRepository } from './invoice.repository'; 3 | import { User } from '../auth/user.entity'; 4 | import { Invoice } from './invoice.entity'; 5 | 6 | @Injectable() 7 | export class InvoiceService { 8 | constructor(private invoiceRepository: InvoiceRepository) {} 9 | 10 | 11 | async findInvoices(): Promise { 12 | const invoices = await this.invoiceRepository.find(); 13 | return invoices; 14 | } 15 | async findInvoice(user: User, id: number): Promise { 16 | const invoice = await this.invoiceRepository.findOne({ 17 | where: { 18 | client: user, 19 | id, 20 | 21 | }, 22 | }); 23 | if (!invoice) { 24 | InvoiceService.throwError(id); 25 | } 26 | return invoice; 27 | } 28 | async deleteInvoice(client: User, invoiceId: number): Promise { 29 | const invoice = await this.findInvoice(client, invoiceId); 30 | const result = await this.invoiceRepository.delete(invoice); 31 | if (result.affected === 0) { 32 | InvoiceService.throwError(invoiceId); 33 | } 34 | } 35 | 36 | static throwError(invoiceId: number) { 37 | throw new NotFoundException(`Invoice with id ${invoiceId} not found!!`); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/product/product.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | ManyToOne, 6 | BaseEntity, 7 | OneToMany, 8 | } from 'typeorm'; 9 | import { Category } from '../category/category.entity'; 10 | import { CartItem } from '../cart/cart-item.entity'; 11 | import { OrderItem } from '../order/order_item.entity'; 12 | 13 | @Entity('products') 14 | export class Product extends BaseEntity { 15 | @PrimaryGeneratedColumn() 16 | id: number; 17 | 18 | @Column() 19 | name: string; 20 | 21 | @Column() 22 | description: string; 23 | 24 | @Column('float',{ 25 | default: 0.0 26 | }) 27 | price: number; 28 | 29 | @Column({ 30 | default: new Date() 31 | }) 32 | publishedIn: Date; 33 | 34 | 35 | @Column({ 36 | default: 1 37 | }) 38 | quantity: number; 39 | 40 | @Column({ 41 | nullable: true 42 | }) 43 | image: string; 44 | @ManyToOne(type => Category, category => category.products, { 45 | eager: false, 46 | onDelete: 'CASCADE', 47 | onUpdate: 'CASCADE' 48 | }) 49 | category: Category; 50 | 51 | @ManyToOne(type => CartItem, cartItem => cartItem.products, { 52 | eager: false, 53 | onDelete: 'CASCADE', 54 | onUpdate: 'CASCADE' 55 | }) 56 | cartItem: CartItem; 57 | 58 | @OneToMany(type => OrderItem, orderItem => orderItem.product, { 59 | eager: true, 60 | 61 | }) 62 | order_items: OrderItem[]; 63 | } 64 | -------------------------------------------------------------------------------- /src/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from '@nestjs/common'; 2 | import { AuthController } from './auth.controller'; 3 | import { AuthService } from './auth.service'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | import { UserRepository } from './user.repository'; 6 | import { JwtModule } from '@nestjs/jwt'; 7 | import { PassportModule } from '@nestjs/passport'; 8 | import { JwtStrategy } from './jwt.strategy'; 9 | import { jwtConstants } from './auth.constants'; 10 | import { OrderModule } from '../order/order.module'; 11 | import { InvoiceModule } from '../invoice/invoice.module'; 12 | import { CartModule } from '../cart/cart.module'; 13 | import { PaymentModule } from '../payment/payment.module'; 14 | import { ProfileModule } from '../profile/profile.module'; 15 | @Module({ 16 | imports: [ 17 | PassportModule.register({ 18 | defaultStrategy: jwtConstants.strategy 19 | }), 20 | JwtModule.register({ 21 | secret: jwtConstants.secret, 22 | signOptions: { 23 | expiresIn: jwtConstants.expiresIn, 24 | }, 25 | }), 26 | TypeOrmModule.forFeature([UserRepository]), 27 | forwardRef(() => OrderModule), 28 | forwardRef(() => CartModule), 29 | PaymentModule, 30 | ProfileModule, 31 | InvoiceModule, 32 | ], 33 | controllers: [AuthController], 34 | providers: [AuthService, JwtStrategy], 35 | exports: [JwtStrategy, PassportModule], 36 | }) 37 | export class AuthModule {} 38 | -------------------------------------------------------------------------------- /src/order/order.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | BaseEntity, 4 | PrimaryGeneratedColumn, 5 | Column, 6 | ManyToOne, 7 | ManyToMany, 8 | JoinTable, 9 | OneToMany, OneToOne, JoinColumn, 10 | } from 'typeorm'; 11 | import { OrderStatus } from './order-status.enum'; 12 | import { User } from '../auth/user.entity'; 13 | import { OrderItem } from './order_item.entity'; 14 | import { Invoice } from '../invoice/invoice.entity'; 15 | 16 | @Entity('orders') 17 | export class Order extends BaseEntity { 18 | @PrimaryGeneratedColumn() 19 | id: number; 20 | 21 | @Column({ 22 | default: new Date(), 23 | }) 24 | order_date: Date; 25 | 26 | @Column({ 27 | default: 'PROCESSED' 28 | }) 29 | status: OrderStatus; 30 | 31 | @Column({ 32 | nullable: true, 33 | }) 34 | shipmentDate: Date; 35 | 36 | @Column() 37 | comments: string; 38 | 39 | @Column() 40 | shippedTo: string; 41 | 42 | @ManyToOne(type => User, user => user.orders, { 43 | eager: false, 44 | onDelete: 'CASCADE', 45 | onUpdate: 'CASCADE' 46 | }) 47 | user: User; 48 | 49 | 50 | @OneToMany(type => OrderItem, orderItem => orderItem.order, { 51 | eager: true, 52 | onDelete: 'CASCADE', 53 | onUpdate: 'CASCADE' 54 | }) 55 | order_items: OrderItem[]; 56 | 57 | @OneToOne(type => Invoice, invoice => invoice.order, { 58 | onDelete: 'CASCADE', 59 | onUpdate: 'CASCADE' 60 | }) 61 | @JoinColumn() 62 | invoice: Invoice; 63 | 64 | @Column({ 65 | nullable: true 66 | }) 67 | invoiceId: number; 68 | } 69 | -------------------------------------------------------------------------------- /src/shared/aws/aws.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Req, Res } from '@nestjs/common'; 2 | import * as AWS from 'aws-sdk'; 3 | import * as config from '../../config'; 4 | import { extname } from 'path'; 5 | const s3 = new AWS.S3(); 6 | AWS.config.update({ 7 | accessKeyId: config.default.aws.Access_Key_ID, 8 | secretAccessKey: config.default.aws.Secret_Access_Key, 9 | }); 10 | @Injectable() 11 | export class AwsService { 12 | async fileupload(file: any): Promise { 13 | return new Promise((resolve, reject) => { 14 | const name = file.originalname.split('.')[0]; 15 | const fileExtName = extname(file.originalname); 16 | const randomName = Array(4) 17 | .fill(null) 18 | .map(() => Math.round(Math.random() * 16).toString(16)) 19 | .join(''); 20 | const params: AWS.S3.Types.PutObjectRequest = { 21 | Bucket: 'e-commerce-products', 22 | Key: `${name}-${randomName}${fileExtName}`, 23 | Body: file.buffer, 24 | ACL: 'public-read', 25 | 26 | }; 27 | s3.upload(params, (err, data: AWS.S3.ManagedUpload.SendData) => { 28 | if (err) { 29 | return reject(err); 30 | } 31 | resolve(`${config.default.aws.cdnUrl}/${data.Key}`) 32 | }); 33 | }); 34 | } 35 | async fileDelete(filename: string): Promise { 36 | return new Promise((resolve, reject) => { 37 | const params: AWS.S3.DeleteObjectRequest = { 38 | Bucket: 'e-commerce-products', 39 | Key: filename.substring(55), 40 | }; 41 | s3.deleteObject(params, (err, data) => { 42 | if (err) { 43 | return reject(err); 44 | } 45 | resolve(); 46 | }) 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/auth/user.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseEntity, 3 | Entity, 4 | PrimaryGeneratedColumn, 5 | Column, 6 | Unique, 7 | OneToOne, 8 | JoinColumn, 9 | OneToMany, 10 | } from 'typeorm'; 11 | import * as bcrypt from 'bcryptjs'; 12 | import { Profile } from '../profile/profile.entity'; 13 | import { Order } from '../order/order.entity'; 14 | import { Invoice } from '../invoice/invoice.entity'; 15 | import { Payment } from '../payment/payment.entity'; 16 | import { Role } from './role'; 17 | 18 | @Entity('users') 19 | @Unique(['username']) 20 | export class User extends BaseEntity { 21 | @PrimaryGeneratedColumn() 22 | id: number; 23 | 24 | @Column() 25 | username: string; 26 | 27 | @Column() 28 | password: string; 29 | 30 | @Column() 31 | salt: string; 32 | 33 | @Column({ 34 | default: false 35 | }) 36 | isAdmin: boolean; 37 | 38 | @OneToOne(type => Profile, profile => profile.user, { 39 | onDelete: 'CASCADE', 40 | onUpdate: 'CASCADE', 41 | }) 42 | @JoinColumn() 43 | profile: Profile; 44 | 45 | @OneToMany(type => Order, order => order.user, { 46 | eager: true, 47 | onDelete: 'CASCADE', 48 | onUpdate: 'CASCADE', 49 | }) 50 | orders: Order[]; 51 | 52 | @OneToMany(type => Invoice, invoice => invoice.client, { 53 | eager: true, 54 | onDelete: 'CASCADE', 55 | onUpdate: 'CASCADE', 56 | }) 57 | invoices: Invoice[]; 58 | 59 | @OneToMany(type => Payment, payment => payment.client, { 60 | eager: true, 61 | onDelete: 'CASCADE', 62 | onUpdate: 'CASCADE', 63 | }) 64 | payments: Payment[]; 65 | 66 | 67 | 68 | 69 | async validatePassword(password: string): Promise { 70 | const hash = await bcrypt.hash(password, this.salt); 71 | return hash === this.password; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/profile/profile.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Put, 4 | Get, 5 | UseGuards, 6 | Body, 7 | ValidationPipe, 8 | Post, 9 | UseInterceptors, 10 | UploadedFile, 11 | Delete, 12 | Patch, 13 | } from '@nestjs/common'; 14 | import { AuthGuard } from '@nestjs/passport'; 15 | import { GetAuthenticatedUser } from '../auth/get-user.decorator'; 16 | import { User } from '../auth/user.entity'; 17 | import { CreateProfileDto } from './dto/create-profile.dto'; 18 | import { ProfileService } from './profile.service'; 19 | import { FileInterceptor } from '@nestjs/platform-express'; 20 | 21 | @Controller('profile') 22 | export class ProfileController { 23 | constructor(private profileService: ProfileService) { 24 | } 25 | 26 | @UseGuards(AuthGuard()) 27 | @Put('userprofile/edit') 28 | editProfile( 29 | @GetAuthenticatedUser() user: User, 30 | @Body(ValidationPipe) createProfileDto: CreateProfileDto, 31 | ) { 32 | return this.profileService.editUserProfile(user, createProfileDto); 33 | } 34 | 35 | @UseGuards(AuthGuard()) 36 | @Get() 37 | getUserProfile(@GetAuthenticatedUser() user: User) { 38 | return this.profileService.getProfileData(user); 39 | } 40 | 41 | @UseGuards(AuthGuard()) 42 | @Post('userprofile/setprofileimage') 43 | @UseInterceptors( 44 | FileInterceptor('image') 45 | ) 46 | setImage(@GetAuthenticatedUser() user: User, @UploadedFile() file) { 47 | return this.profileService.setProfileImage(user, file); 48 | } 49 | 50 | @UseGuards(AuthGuard()) 51 | @Delete('userprofile/deleteprofileimage') 52 | deleteImage(@GetAuthenticatedUser() user: User) { 53 | return this.profileService.deleteProfileImage(user); 54 | } 55 | 56 | @UseGuards(AuthGuard()) 57 | @Patch('userprofile/changeprofileimage') 58 | @UseInterceptors( 59 | FileInterceptor('image') 60 | ) 61 | changeProfileImage(@GetAuthenticatedUser() user: User, @UploadedFile() file) { 62 | return this.profileService.changeProfileImage(user, file); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/auth/auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Post, 4 | Body, 5 | Get, 6 | ValidationPipe, 7 | Delete, 8 | UseGuards, Scope, 9 | } from '@nestjs/common'; 10 | import { AuthCredentialsDto } from './dto/auth-credentials.dto'; 11 | import { AuthService } from './auth.service'; 12 | import { UnauthorizedException } from '@nestjs/common'; 13 | import { CreateProfileDto } from '../profile/dto/create-profile.dto'; 14 | import { GetAuthenticatedUser } from './get-user.decorator'; 15 | import { User } from './user.entity'; 16 | import { AuthGuard } from '@nestjs/passport'; 17 | 18 | @Controller('auth') 19 | export class AuthController { 20 | constructor(private readonly authService: AuthService) { 21 | } 22 | 23 | @Post('/register') 24 | signUp( 25 | @Body('authCredentialsDto', ValidationPipe) 26 | authCredentialsDto: AuthCredentialsDto, 27 | @Body('createProfileDto', ValidationPipe) 28 | createProfileDto: CreateProfileDto, 29 | ) { 30 | return this.authService.signUp( 31 | authCredentialsDto, 32 | createProfileDto, 33 | ); 34 | } 35 | 36 | @Post('/login') 37 | signIn( 38 | @Body(ValidationPipe) authCredentialsDto: AuthCredentialsDto, 39 | ): Promise<{ accessToken: string }> { 40 | return this.authService.signIn(authCredentialsDto); 41 | } 42 | 43 | @UseGuards(AuthGuard('local')) 44 | @Delete('/delete-user') 45 | deleteUser(@GetAuthenticatedUser() user: User) { 46 | return this.authService.deleteUser(user); 47 | } 48 | @UseGuards(AuthGuard()) 49 | @Get('system-users') 50 | getAllUsers(): Promise { 51 | return this.authService.getAllUsers(); 52 | } 53 | 54 | @UseGuards(AuthGuard()) 55 | @Get('exist-data') 56 | getExistData(): Promise { 57 | return this.authService.existData(); 58 | } 59 | 60 | 61 | @UseGuards(AuthGuard()) 62 | @Get('/current-user') 63 | getCurrentUser(@GetAuthenticatedUser() user: User) { 64 | return this.authService.getAuthenticatedUser(user.id); 65 | } 66 | 67 | @UseGuards(AuthGuard()) 68 | @Get('/user-main-data') 69 | getUserData(@GetAuthenticatedUser() user: User) { 70 | return this.authService.getUserData(user.id); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { AuthModule } from './auth/auth.module'; 5 | import { TypeOrmModule } from '@nestjs/typeorm'; 6 | import { CategoryModule } from './category/category.module'; 7 | import { ProductModule } from './product/product.module'; 8 | import { ProfileModule } from './profile/profile.module'; 9 | import { OrderModule } from './order/order.module'; 10 | import { PaymentModule } from './payment/payment.module'; 11 | import { CartModule } from './cart/cart.module'; 12 | import { InvoiceModule } from './invoice/invoice.module'; 13 | import { MulterModule } from '@nestjs/platform-express'; 14 | 15 | import { ContactModule } from './contact/contact.module'; 16 | import { NodemailerModule } from '@crowdlinker/nestjs-mailer'; 17 | import { NodemailerDrivers } from '@crowdlinker/nestjs-mailer'; 18 | import { NodemailerOptions } from '@crowdlinker/nestjs-mailer'; 19 | import { AwsModule } from './shared/aws/aws.module'; 20 | import * as config from './config'; 21 | 22 | @Module({ 23 | imports: [ 24 | TypeOrmModule.forRoot({ 25 | type: 'postgres', 26 | host: 'localhost', 27 | port: 5432, 28 | username: 'postgres', 29 | password: 'password', 30 | database: 'database', 31 | entities: [__dirname + '/**/*.entity{.ts,.js}'], 32 | synchronize: true, 33 | }), 34 | MulterModule.register({ 35 | dest: './files', 36 | }), 37 | NodemailerModule.forRoot({ 38 | transport: { 39 | host: 'smtp.gmail.com', 40 | port: 465, 41 | secure: true, 42 | auth: { 43 | user: 'user', 44 | pass:'pass', 45 | }, 46 | tls: { 47 | // do not fail on invalid certs 48 | rejectUnauthorized: false 49 | } 50 | }, 51 | } as NodemailerOptions), 52 | AuthModule, 53 | AwsModule, 54 | CategoryModule, 55 | ProductModule, 56 | ProfileModule, 57 | OrderModule, 58 | PaymentModule, 59 | CartModule, 60 | InvoiceModule, 61 | ContactModule, 62 | ], 63 | controllers: [AppController], 64 | providers: [AppService] 65 | }) 66 | export class AppModule { 67 | } 68 | -------------------------------------------------------------------------------- /src/product/product.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | NotFoundException, 4 | ConflictException, 5 | } from '@nestjs/common'; 6 | import { InjectRepository } from '@nestjs/typeorm'; 7 | import { ProductRepository } from './product.repository'; 8 | import { Product } from './product.entity'; 9 | import { CartService } from '../cart/cart.service'; 10 | import {CartItem} from '../cart/cart-item.entity'; 11 | @Injectable() 12 | export class ProductService { 13 | constructor( 14 | @InjectRepository(ProductRepository) 15 | private productRepository: ProductRepository, 16 | private cartService: CartService, 17 | ) {} 18 | 19 | async getProducts( 20 | ): Promise { 21 | return await this.productRepository.getProducts(); 22 | } 23 | 24 | async getProduct(productId: number): Promise { 25 | const product = await this.productRepository.findOne({ 26 | where: { id: productId }, 27 | }); 28 | if (!product) { 29 | throw new NotFoundException( 30 | `Product with id: ${productId} is not found `, 31 | ); 32 | } 33 | return product; 34 | } 35 | 36 | async deleteProduct(id: number): Promise { 37 | const product = await this.getProduct(id); 38 | const result = await this.productRepository.delete(product); 39 | if (result.affected === 0) { 40 | throw new NotFoundException(`Product with id: ${id} is not found `); 41 | } 42 | } 43 | 44 | async insertToCart( 45 | productId: number, 46 | cartId: number 47 | ): Promise { 48 | const cart_item = await this.cartService.getCartItem(cartId); 49 | const product = await this.getProduct(productId); 50 | product.quantity = product.quantity - 1; 51 | cart_item.products.push(product); 52 | cart_item.total_products += 1; 53 | await cart_item.save(); 54 | await product.save(); 55 | return cart_item; 56 | } 57 | 58 | 59 | async removeFromCart(productId: number, cartId: number): Promise { 60 | const cart_item = await this.cartService.getCartItem(cartId); 61 | const product = await this.getProduct(productId); 62 | cart_item.products.splice(0, 1, product); 63 | product.quantity = product.quantity + 1; 64 | cart_item.total_products -= 1; 65 | await cart_item.save(); 66 | await product.save(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecommerce-siq", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "license": "MIT", 7 | "scripts": { 8 | "build": "rimraf dist && tsc -p tsconfig.build.json", 9 | "format": "prettier --write \"src/**/*.ts\"", 10 | "start": "ts-node -r tsconfig-paths/register src/main.ts", 11 | "start:dev": "tsc-watch -p tsconfig.build.json --onSuccess \"node dist/main.js\"", 12 | "start:debug": "tsc-watch -p tsconfig.build.json --onSuccess \"node --inspect-brk dist/main.js\"", 13 | "start:prod": "node dist/main.js", 14 | "lint": "tslint -p tsconfig.json -c tslint.json", 15 | "test": "jest", 16 | "test:watch": "jest --watch", 17 | "test:cov": "jest --coverage", 18 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 19 | "test:e2e": "jest --config ./test/jest-e2e.json" 20 | }, 21 | "dependencies": { 22 | "@crowdlinker/nestjs-mailer": "0.0.3", 23 | "@nest-modules/mailer": "^1.1.3", 24 | "@nestjs/common": "^6.0.0", 25 | "@nestjs/core": "^6.0.0", 26 | "@nestjs/jwt": "^6.1.1", 27 | "@nestjs/passport": "^6.1.1", 28 | "@nestjs/platform-express": "^6.0.0", 29 | "@nestjs/typeorm": "^6.2.0", 30 | "@types/passport-local": "^1.0.33", 31 | "aws-sdk": "^2.635.0", 32 | "bcryptjs": "^2.4.3", 33 | "class-transformer": "^0.2.3", 34 | "class-validator": "^0.11.0", 35 | "multer": "^1.4.2", 36 | "multer-s3": "^2.9.0", 37 | "nestjs-multer-extended": "^1.1.0", 38 | "passport": "^0.4.1", 39 | "passport-jwt": "^4.0.0", 40 | "passport-local": "^1.0.0", 41 | "pg": "^7.15.0", 42 | "reflect-metadata": "^0.1.12", 43 | "rimraf": "^2.6.2", 44 | "rxjs": "^6.3.3", 45 | "typeorm": "^0.2.21" 46 | }, 47 | "devDependencies": { 48 | "@nestjs/testing": "^6.0.0", 49 | "@types/express": "4.16.1", 50 | "@types/jest": "24.0.11", 51 | "@types/node": "11.13.4", 52 | "@types/supertest": "2.0.7", 53 | "jest": "^25.1.0", 54 | "prettier": "1.17.0", 55 | "supertest": "4.0.2", 56 | "ts-jest": "24.0.2", 57 | "ts-node": "8.1.0", 58 | "tsc-watch": "2.2.1", 59 | "tsconfig-paths": "3.8.0", 60 | "tslint": "5.16.0", 61 | "typescript": "3.4.3" 62 | }, 63 | "jest": { 64 | "moduleFileExtensions": [ 65 | "js", 66 | "json", 67 | "ts" 68 | ], 69 | "rootDir": "src", 70 | "testRegex": ".spec.ts$", 71 | "transform": { 72 | "^.+\\.(t|j)s$": "ts-jest" 73 | }, 74 | "coverageDirectory": "../coverage", 75 | "testEnvironment": "node" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/cart/cart.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Post, 4 | Get, 5 | Param, 6 | ParseIntPipe, 7 | Body, 8 | Delete, 9 | Query, 10 | UseGuards, 11 | } from '@nestjs/common'; 12 | import { CartService } from './cart.service'; 13 | import { GetAuthenticatedUser } from '../auth/get-user.decorator'; 14 | import { User } from '../auth/user.entity'; 15 | import { AuthGuard } from '@nestjs/passport'; 16 | import { CreateOrderDto } from '../order/dto/create-order.dto'; 17 | import { Profile } from '../profile/profile.entity'; 18 | import { Cart } from './cart.entity'; 19 | import { Product } from '../product/product.entity'; 20 | import { CreatePaymentDto } from '../payment/dto/create-payment.dto'; 21 | 22 | 23 | @Controller('cart') 24 | export class CartController { 25 | constructor(private readonly cartService: CartService) { 26 | } 27 | 28 | @UseGuards(AuthGuard()) 29 | @Get(':id') 30 | getCart(@Param('id', ParseIntPipe) cartId: number) { 31 | return this.cartService.getCart(cartId); 32 | } 33 | 34 | } 35 | 36 | @Controller('cart_items') 37 | export class CartItemController { 38 | constructor(private readonly cartService: CartService) { 39 | } 40 | 41 | @UseGuards(AuthGuard()) 42 | @Get(':cart_item_id') 43 | getCartItem(@Param('cart_item_id', ParseIntPipe) cart_item_id: number) { 44 | return this.cartService.getCartItem(cart_item_id); 45 | } 46 | 47 | @UseGuards(AuthGuard()) 48 | @Delete(':cart_item_id/products/clear-products') 49 | clearProducts(@Param('cart_item_id', ParseIntPipe) cart_item_id: number) { 50 | return this.cartService.clearCartItemProducts(cart_item_id); 51 | } 52 | 53 | @UseGuards(AuthGuard()) 54 | @Delete(':cart_item_id/products/:id/remove-from-cart') 55 | removeFromCart(@Param('cart_item_id', ParseIntPipe) cart_item_id: number, 56 | @Param('id', ParseIntPipe) productId: number) { 57 | return this.cartService.removeFromCart(cart_item_id, productId); 58 | } 59 | 60 | @UseGuards(AuthGuard()) 61 | @Post(':cart_item_id/products/:id/checkout') 62 | productCheckout( 63 | @GetAuthenticatedUser() user: User, 64 | @Param('cart_item_id', ParseIntPipe) cart_item_id: number, 65 | @Param('id', ParseIntPipe) productId: number, 66 | @Body('createOrderDto') createOrderDto: CreateOrderDto, 67 | @Body('createPaymentDto') createPaymentDto: CreatePaymentDto, 68 | @Query('quantity', ParseIntPipe) quantity: number, 69 | ) { 70 | return this.cartService.productCheckout( 71 | user, 72 | cart_item_id, 73 | productId, 74 | createOrderDto, 75 | createPaymentDto, 76 | quantity 77 | ); 78 | } 79 | 80 | @UseGuards(AuthGuard()) 81 | @Post(':cart_item_id/checkout') 82 | cartCheckout( 83 | @GetAuthenticatedUser() user: User, 84 | @Param('cart_item_id', ParseIntPipe) cart_item_id: number, 85 | @Body('createOrderDto') createOrderDto: CreateOrderDto, 86 | @Body('createPaymentDto') createPaymentDto: CreatePaymentDto, 87 | @Body('cartProductsQuantity') cartProductsQuantity: any 88 | ) { 89 | return this.cartService.cartCheckout( 90 | user, 91 | cart_item_id, 92 | createOrderDto, 93 | createPaymentDto, 94 | cartProductsQuantity 95 | ); 96 | } 97 | 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/category/category.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Param, 5 | ParseIntPipe, 6 | Post, 7 | Body, 8 | Delete, 9 | UseGuards, 10 | Put, 11 | UseInterceptors, 12 | UploadedFile, 13 | } from '@nestjs/common'; 14 | import { CategoryService } from './category.service'; 15 | import { CreateCategoryDto } from './dto/create-category.dto'; 16 | import { AuthGuard } from '@nestjs/passport'; 17 | import { UpdateCategoryDto } from './dto/update-category.dto'; 18 | import { FileInterceptor } from '@nestjs/platform-express'; 19 | 20 | @Controller('categories') 21 | export class CategoryController { 22 | constructor(private readonly categoryService: CategoryService) {} 23 | 24 | @Get() 25 | getCategories() { 26 | return this.categoryService.getCategories(); 27 | } 28 | 29 | @Get(':id') 30 | getCategory(@Param('id', ParseIntPipe) id: number) { 31 | return this.categoryService.getCategory(id); 32 | } 33 | 34 | @UseGuards(AuthGuard()) 35 | @Post(':id/products') 36 | @UseInterceptors( 37 | FileInterceptor('image') 38 | ) 39 | createProduct( 40 | @Param('id', ParseIntPipe) categoryId: number, 41 | @Body('name') name: string, 42 | @Body('description') description: string, 43 | @Body('price') price: number, 44 | @Body('quantity') quantity: number, 45 | @UploadedFile() image, 46 | ) { 47 | return this.categoryService.createProduct( 48 | categoryId, 49 | name, 50 | description, 51 | price, 52 | quantity, 53 | image 54 | ); 55 | } 56 | 57 | @UseGuards(AuthGuard()) 58 | @Put(':id/products/:productId') 59 | @UseInterceptors( 60 | FileInterceptor('image') 61 | ) 62 | updateProduct( 63 | @Param('id', ParseIntPipe) categoryId: number, 64 | @Param('productId', ParseIntPipe) productId: number, 65 | @Body('name') name: string, 66 | @Body('description') description: string, 67 | @Body('price') price: number, 68 | @Body('quantity') quantity: number, 69 | @UploadedFile() image, 70 | ) { 71 | return this.categoryService.updateProduct( 72 | categoryId, 73 | productId, 74 | name, 75 | description, 76 | price, 77 | quantity, 78 | image, 79 | ); 80 | } 81 | @UseGuards(AuthGuard()) 82 | @Delete(':id/products/:productId') 83 | deleteProduct( 84 | @Param('id', ParseIntPipe) categoryId: number, 85 | @Param('productId', ParseIntPipe) productId: number, 86 | ) { 87 | return this.categoryService.deleteProduct( 88 | categoryId, 89 | productId, 90 | ); 91 | } 92 | 93 | @Get(':id/products') 94 | getProducts(@Param('id', ParseIntPipe) id: number) { 95 | return this.categoryService.getProducts(id); 96 | } 97 | 98 | @UseGuards(AuthGuard()) 99 | @Post() 100 | createCategory(@Body() createCategoryDto: CreateCategoryDto) { 101 | return this.categoryService.createCategory(createCategoryDto); 102 | } 103 | 104 | @UseGuards(AuthGuard()) 105 | @Put(':id') 106 | updateCategory( 107 | @Param('id', ParseIntPipe) id: number, 108 | @Body() updateCategoryDto: UpdateCategoryDto, 109 | ) { 110 | return this.categoryService.updateCategory(id, updateCategoryDto); 111 | } 112 | 113 | @UseGuards(AuthGuard()) 114 | @Delete(':id') 115 | deleteCategory(@Param('id', ParseIntPipe) id: number) { 116 | return this.categoryService.deleteCategory(id); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master 6 | [travis-url]: https://travis-ci.org/nestjs/nest 7 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux 8 | [linux-url]: https://travis-ci.org/nestjs/nest 9 | 10 |

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

11 |

12 | NPM Version 13 | Package License 14 | NPM Downloads 15 | Travis 16 | Linux 17 | Coverage 18 | Gitter 19 | Backers on Open Collective 20 | Sponsors on Open Collective 21 | 22 | 23 |

24 | 26 | 27 | ## Description 28 | 29 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 30 | 31 | ## Installation 32 | 33 | ```bash 34 | $ npm install 35 | ``` 36 | 37 | ## Running the app 38 | 39 | ```bash 40 | # development 41 | $ npm run start 42 | 43 | # watch mode 44 | $ npm run start:dev 45 | 46 | # production mode 47 | $ npm run start:prod 48 | ``` 49 | 50 | ## Test 51 | 52 | ```bash 53 | # unit tests 54 | $ npm run test 55 | 56 | # e2e tests 57 | $ npm run test:e2e 58 | 59 | # test coverage 60 | $ npm run test:cov 61 | ``` 62 | 63 | ## Support 64 | 65 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). 66 | 67 | ## Stay in touch 68 | 69 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) 70 | - Website - [https://nestjs.com](https://nestjs.com/) 71 | - Twitter - [@nestframework](https://twitter.com/nestframework) 72 | 73 | ## License 74 | 75 | Nest is [MIT licensed](LICENSE). 76 | -------------------------------------------------------------------------------- /src/profile/profile.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | NotFoundException, 4 | ConflictException, 5 | } from '@nestjs/common'; 6 | import { ProfileRepository } from './profile.repository'; 7 | import { InjectRepository } from '@nestjs/typeorm'; 8 | import { Profile } from './profile.entity'; 9 | import { User } from '../auth/user.entity'; 10 | import { CreateProfileDto } from './dto/create-profile.dto'; 11 | import { AwsService } from '../shared/aws/aws.service'; 12 | 13 | @Injectable() 14 | export class ProfileService { 15 | constructor( 16 | @InjectRepository(ProfileRepository) 17 | private profileRepository: ProfileRepository, 18 | private readonly awsService: AwsService 19 | ) {} 20 | 21 | async getProfileData(user: User): Promise { 22 | const profile = await this.profileRepository.findOne(user); 23 | if (!profile) { 24 | throw new NotFoundException(`profile does not exist`); 25 | } 26 | return profile; 27 | } 28 | 29 | async deleteProfile(profile: Profile): Promise { 30 | if(profile.image) { 31 | await this.awsService.fileDelete(profile.image); 32 | } 33 | await this.profileRepository.delete(profile); 34 | } 35 | async editUserProfile(user: User, createProfileDto: CreateProfileDto): Promise { 36 | const profile = await this.getProfileData(user); 37 | const { 38 | firstname, 39 | lastname, 40 | email, 41 | age, 42 | gender, 43 | address, 44 | country, 45 | city, 46 | phone, 47 | } = createProfileDto; 48 | 49 | if (firstname) { 50 | profile.firstname = firstname; 51 | } 52 | if (lastname) { 53 | profile.lastname = lastname; 54 | } 55 | if (email) { 56 | profile.email = email; 57 | } 58 | if (gender) { 59 | profile.gender = gender; 60 | } 61 | if (age) { 62 | profile.age = age; 63 | } 64 | if (country) { 65 | profile.country = country; 66 | } 67 | if (city) { 68 | profile.city = city; 69 | } 70 | if (address) { 71 | profile.address = address; 72 | } 73 | if (phone) { 74 | profile.phone = phone; 75 | } 76 | try { 77 | return await profile.save(); 78 | } catch (error) { 79 | console.error(error); 80 | } 81 | } 82 | 83 | async setProfileImage(user: User, image: any): Promise { 84 | const profile = await this.getProfileData(user); 85 | profile.image = await this.awsService.fileupload(image); 86 | try { 87 | await profile.save(); 88 | return profile; 89 | } catch (error) { 90 | console.error(error); 91 | } 92 | } 93 | 94 | async deleteProfileImage(user: User): Promise { 95 | const profile = await this.getProfileData(user); 96 | if (profile.image === null) { 97 | throw new ConflictException(`Profile Image is already set to null!!`); 98 | } 99 | await this.awsService.fileDelete(profile.image); 100 | profile.image = null; 101 | try { 102 | await profile.save(); 103 | } catch (error) { 104 | console.error(error); 105 | } 106 | } 107 | async changeProfileImage(user: User, image: any): Promise { 108 | const profile = await this.getProfileData(user); 109 | if (profile.image) { 110 | await this.deleteProfileImage(user); 111 | profile.image = await this.awsService.fileupload(image); 112 | try { 113 | await profile.save(); 114 | return profile; 115 | } catch (error) { 116 | console.error(error); 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { User } from './user.entity'; 4 | import { UserRepository } from './user.repository'; 5 | import { AuthCredentialsDto } from './dto/auth-credentials.dto'; 6 | import { JwtService } from '@nestjs/jwt'; 7 | import { JwtPayload } from './jwt-payload.interface'; 8 | import { CreateProfileDto } from '../profile/dto/create-profile.dto'; 9 | import { ProfileService } from '../profile/profile.service'; 10 | import { PaymentService } from '../payment/payment.service'; 11 | import { InvoiceService } from '../invoice/invoice.service'; 12 | import { CartService } from '../cart/cart.service'; 13 | import { OrderService } from '../order/order.service'; 14 | 15 | @Injectable() 16 | export class AuthService { 17 | constructor( 18 | private jwtService: JwtService, 19 | @InjectRepository(User) private readonly userRepository: UserRepository, 20 | private profileService: ProfileService, 21 | private paymentService: PaymentService, 22 | private invoiceService: InvoiceService, 23 | private cartService: CartService, 24 | private orderService: OrderService, 25 | ) { 26 | } 27 | 28 | async signUp( 29 | authCredentialsDto: AuthCredentialsDto, 30 | createProfileDto: CreateProfileDto, 31 | ): Promise { 32 | return this.userRepository.signUp(authCredentialsDto, createProfileDto); 33 | } 34 | 35 | async signIn( 36 | authCredentialsDto: AuthCredentialsDto, 37 | ): Promise<{ accessToken: string }> { 38 | const username = await this.userRepository.validateUserPassword( 39 | authCredentialsDto, 40 | ); 41 | if (!username) { 42 | throw new UnauthorizedException('Invalid Credentials'); 43 | } 44 | const payload: JwtPayload = { username }; 45 | const accessToken = this.jwtService.sign(payload); 46 | return { accessToken }; 47 | } 48 | 49 | async getAuthenticatedUser(id: number): Promise { 50 | const currentUser = await this.userRepository.findOne({ 51 | where: { 52 | id, 53 | }, 54 | }); 55 | if (!currentUser) { 56 | throw new UnauthorizedException(); 57 | } 58 | return currentUser; 59 | } 60 | 61 | async getUserData(id: number): Promise{ 62 | const user = await this.getAuthenticatedUser(id); 63 | const profile = await this.profileService.getProfileData(user); 64 | const cart = await this.cartService.getCart(profile.cartId); 65 | const cartItem = await this.cartService.getCartItem(cart.cartItemId); 66 | return { 67 | profile, 68 | cart, 69 | cartItem 70 | } 71 | } 72 | async getAllUsers(): Promise { 73 | const users = await this.userRepository.find(); 74 | return users; 75 | } 76 | 77 | async existData(){ 78 | return this.userRepository.existData(); 79 | } 80 | 81 | async deleteUser(user: User): Promise { 82 | const orders = await this.orderService.findUserOrders(user); 83 | for (let i = 0; i < orders.length; i++) { 84 | for (let j = 0; j < orders[i].order_items.length; j++) { 85 | await this.orderService.deleteOrderItem(orders[i].order_items[j].id); 86 | } 87 | } 88 | for (let i = 0; i < orders.length; i++) { 89 | await this.orderService.deleteOrder(user, orders[i].id); 90 | } 91 | 92 | const profile = await this.profileService.getProfileData(user); 93 | await this.profileService.deleteProfileImage(user); 94 | const cart = await this.cartService.getCart(null, profile); 95 | const cart_item = await this.cartService.getCartItem(null, cart); 96 | 97 | await this.cartService.clearCartItemProducts(cart_item.id); 98 | await this.cartService.deleteCartItem(cart_item); 99 | await this.cartService.deleteCart(cart); 100 | await this.profileService.deleteProfile(profile); 101 | 102 | const payments = await this.paymentService.findPayments(user); 103 | for (let i = 0; i < payments.length; i++) { 104 | await this.paymentService.deletePayment(user, payments[i].id); 105 | } 106 | 107 | const invoices = await this.invoiceService.findInvoices(); 108 | for (let i = 0; i < invoices.length; i++) { 109 | await this.invoiceService.deleteInvoice(user, invoices[i].id); 110 | } 111 | try { 112 | await this.userRepository.delete(user); 113 | } catch (error) { 114 | console.error(error); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/auth/user.repository.ts: -------------------------------------------------------------------------------- 1 | import { Repository, EntityRepository } from 'typeorm'; 2 | import { User } from './user.entity'; 3 | import { AuthCredentialsDto } from './dto/auth-credentials.dto'; 4 | import { ConflictException } from '@nestjs/common'; 5 | import * as bcrypt from 'bcryptjs'; 6 | import { CreateProfileDto } from '../profile/dto/create-profile.dto'; 7 | import { Profile } from '../profile/profile.entity'; 8 | import { Cart } from '../cart/cart.entity'; 9 | import { CartItem } from '../cart/cart-item.entity'; 10 | 11 | @EntityRepository(User) 12 | export class UserRepository extends Repository { 13 | async signUp( 14 | authCredentialsDto: AuthCredentialsDto, 15 | createProfileDto: CreateProfileDto, 16 | ): Promise { 17 | const { username, password } = authCredentialsDto; 18 | const user = new User(); 19 | user.salt = await bcrypt.genSalt(); 20 | user.username = username; 21 | user.password = await this.hashPassword(password, user.salt); 22 | const query = this.createQueryBuilder('user'); 23 | const users = await query 24 | .select('username') 25 | .where('user.username LIKE :username', { username }); 26 | if (await users.getCount()) { 27 | throw new ConflictException('username is already exist in the database'); 28 | } else { 29 | const profile = await this.createProfile(user, createProfileDto); 30 | user.profile = profile; 31 | user.orders = []; 32 | user.invoices = []; 33 | user.payments = []; 34 | await user.save(); 35 | } 36 | } 37 | 38 | async existData() { 39 | const availableData = { 40 | totalUsers: 0, 41 | totalOrders: 0, 42 | totalPayments: 0, 43 | totalInvoices: 0, 44 | }; 45 | const users = await this.find(); 46 | for (let i = 0; i < users.length; i++) { 47 | availableData.totalUsers++; 48 | if (users[i].invoices) { 49 | for (let j = 0; j < users[i].invoices.length; j++) { 50 | availableData.totalInvoices++; 51 | } 52 | } 53 | if (users[i].orders) { 54 | for (let j = 0; j < users[i].orders.length; j++) { 55 | availableData.totalOrders++; 56 | } 57 | } 58 | if (users[i].payments) { 59 | for (let j = 0; j < users[i].payments.length; j++) { 60 | availableData.totalPayments++; 61 | } 62 | } 63 | } 64 | return availableData; 65 | } 66 | 67 | async validateUserPassword( 68 | authCredentialsDto: AuthCredentialsDto, 69 | ): Promise { 70 | const { username, password } = authCredentialsDto; 71 | const user = await this.findOne({ username }); 72 | if (user && (await user.validatePassword(password))) { 73 | return username; 74 | } else { 75 | return null; 76 | } 77 | } 78 | 79 | async createProfile( 80 | user: User, 81 | createProfileDto: CreateProfileDto, 82 | ): Promise { 83 | const { 84 | firstname, 85 | lastname, 86 | phone, 87 | email, 88 | gender, 89 | age, 90 | country, 91 | city, 92 | address, 93 | } = createProfileDto; 94 | const profile = new Profile(); 95 | profile.firstname = firstname; 96 | profile.lastname = lastname; 97 | profile.email = email; 98 | profile.gender = gender; 99 | profile.age = age; 100 | profile.country = country; 101 | profile.city = city; 102 | profile.address = address; 103 | profile.phone = phone; 104 | profile.user = user; 105 | const cart = await this.createCart(profile); 106 | profile.cart = cart; 107 | try { 108 | await profile.save(); 109 | return profile; 110 | } catch (err) { 111 | console.error(err); 112 | } 113 | } 114 | 115 | async createCart(profile: Profile): Promise { 116 | const cart = new Cart(); 117 | const cart_item = await this.createCartItem(cart); 118 | cart.cart_item = cart_item; 119 | cart.profile = profile; 120 | try { 121 | await cart.save(); 122 | return cart; 123 | } catch (err) { 124 | console.error(err); 125 | } 126 | } 127 | 128 | async createCartItem(cart: Cart): Promise { 129 | const cart_item = new CartItem(); 130 | cart_item.total_products = 0; 131 | cart_item.cart = cart; 132 | cart_item.products = []; 133 | try { 134 | await cart_item.save(); 135 | return cart_item; 136 | } catch (error) { 137 | console.error(error); 138 | } 139 | } 140 | 141 | private async hashPassword(password: string, salt: string): Promise { 142 | return bcrypt.hash(password, salt); 143 | } 144 | 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/category/category.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException, Req, Res } from '@nestjs/common'; 2 | import { CategoryRepository } from './category.repository'; 3 | import { InjectRepository } from '@nestjs/typeorm'; 4 | import { Category } from './category.entity'; 5 | import { CreateCategoryDto } from './dto/create-category.dto'; 6 | import { Product } from '../product/product.entity'; 7 | import { ProductService } from '../product/product.service'; 8 | import { DeleteResult } from 'typeorm'; 9 | import { UpdateCategoryDto } from './dto/update-category.dto'; 10 | import * as fs from 'fs'; 11 | import { AwsService } from '../shared/aws/aws.service'; 12 | @Injectable() 13 | export class CategoryService { 14 | constructor( 15 | @InjectRepository(CategoryRepository) 16 | private categoryRepository: CategoryRepository, 17 | private readonly productService: ProductService, 18 | private readonly awsService: AwsService, 19 | ) {} 20 | 21 | async getCategories(): Promise { 22 | return await this.categoryRepository.find(); 23 | } 24 | 25 | async getCategory(id: number): Promise { 26 | const category: Category = await this.categoryRepository.findOne({ 27 | where: { id }, 28 | }); 29 | if (!category) { 30 | throw new NotFoundException(`Category with id: ${id} not found `); 31 | } 32 | return category; 33 | } 34 | 35 | async createCategory( 36 | createCategoryDto: CreateCategoryDto, 37 | ): Promise { 38 | return await this.categoryRepository.createCategory(createCategoryDto); 39 | } 40 | 41 | async createProduct( 42 | id: number, 43 | name: string, 44 | description: string, 45 | price: number, 46 | quantity: number, 47 | image: any, 48 | ) { 49 | const category: Category = await this.getCategory(id); 50 | const product = new Product(); 51 | product.name = name; 52 | product.description = description; 53 | product.price = price; 54 | product.quantity = quantity; 55 | product.publishedIn = new Date(); 56 | product.category = category; 57 | product.order_items = []; 58 | product.image = await this.awsService.fileupload(image); 59 | console.log(product.image); 60 | category.products.push(await product.save()); 61 | } 62 | 63 | async updateProduct( 64 | categoryId: number, 65 | productId: number, 66 | name: string, 67 | description: string, 68 | price: number, 69 | quantity: number, 70 | image: any, 71 | ): Promise { 72 | const category: Category = await this.getCategory(categoryId); 73 | const product = category.products.find(prod => prod.id === productId); 74 | if (name) { 75 | product.name = name; 76 | } 77 | if (description) { 78 | product.description = description; 79 | } 80 | if (price) { 81 | product.price = price; 82 | } 83 | if (quantity) { 84 | product.quantity = quantity; 85 | } 86 | if (image) { 87 | await this.awsService.fileDelete(product.image); 88 | product.image = await this.awsService.fileupload(image); 89 | } 90 | await product.save(); 91 | } 92 | async updateCategory( 93 | id: number, 94 | updateCategoryDto: UpdateCategoryDto, 95 | ): Promise { 96 | const category = await this.getCategory(id); 97 | const { name, description } = updateCategoryDto; 98 | if (name) { 99 | category.name = name; 100 | } 101 | if (description) { 102 | category.description = description; 103 | } 104 | await category.save(); 105 | } 106 | 107 | async getProducts(categoryId: number) { 108 | const category = await this.getCategory(categoryId); 109 | const products = category.products; 110 | products.forEach(prod => { 111 | if (prod.id === 81) { 112 | console.log(prod.image.substring(55)); 113 | } 114 | }); 115 | return products; 116 | } 117 | 118 | async deleteCategory(categoryId: number): Promise { 119 | const result = await this.categoryRepository.delete(categoryId); 120 | if (result.affected === 0) { 121 | throw new NotFoundException( 122 | `Category with id: ${categoryId} is not found `, 123 | ); 124 | } 125 | return result; 126 | } 127 | 128 | async deleteProduct(categoryId: number, productId: number) { 129 | const category: Category = await this.getCategory(categoryId); 130 | const product = category.products.find(prod => prod.id === productId); 131 | if (product.image) { 132 | await this.awsService.fileDelete(product.image); 133 | } 134 | await this.productService.deleteProduct(productId); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/order/order.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException } from '@nestjs/common'; 2 | import { OrderRepository } from './order.repository'; 3 | import { DeleteResult, Repository } from 'typeorm'; 4 | import { Order } from './order.entity'; 5 | import { User } from '../auth/user.entity'; 6 | import { CreatePaymentDto } from '../payment/dto/create-payment.dto'; 7 | import { Payment } from '../payment/payment.entity'; 8 | import { Invoice } from '../invoice/invoice.entity'; 9 | import { OrderItem } from './order_item.entity'; 10 | import { InjectRepository } from '@nestjs/typeorm'; 11 | import {OrderStatus} from './order-status.enum' 12 | @Injectable() 13 | export class OrderService { 14 | constructor( 15 | private orderRepository: OrderRepository, 16 | @InjectRepository(OrderItem) 17 | private orderItemRepository: Repository, 18 | ) { 19 | } 20 | 21 | async findUserOrders(user: User): Promise { 22 | const orders = await this.orderRepository.find({ 23 | where: { 24 | user, 25 | }, 26 | }); 27 | if (!orders) { 28 | throw new NotFoundException(`User has no orders`); 29 | } 30 | 31 | let today = new Date(); 32 | for(let i=0; i < orders.length; i++) { 33 | 34 | let date = orders[i].order_date; 35 | const threeDaysLater = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 3); 36 | const eightDaysLater = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 7); 37 | 38 | if(threeDaysLater.getDate() === today.getDate()){ 39 | orders[i].status = OrderStatus.SHIPPED; 40 | await orders[i].save(); 41 | } 42 | else if(eightDaysLater.getDate() === today.getDate()){ 43 | orders[i].status = OrderStatus.DELIVERED; 44 | await orders[i].save(); 45 | } 46 | else { 47 | orders[i].status = OrderStatus.PROCESSED 48 | } 49 | } 50 | return orders; 51 | } 52 | 53 | async findOrder(user: User, id: number): Promise { 54 | const order = this.orderRepository.findOne({ 55 | where: { 56 | user, 57 | id, 58 | }, 59 | }); 60 | if (!order) { 61 | throw new NotFoundException(`Order with id ${id} not found`); 62 | } 63 | return order; 64 | } 65 | 66 | async deleteOrder(user: User, orderId: number): Promise { 67 | const order = await this.findOrder(user, orderId); 68 | const result = await this.orderRepository.delete(order); 69 | if (result.affected === 0) { 70 | throw new NotFoundException(`Order with id ${orderId} not found`); 71 | } 72 | } 73 | 74 | async findOrderItem(id: number): Promise { 75 | const order_item = this.orderItemRepository.findOne({ 76 | where: { 77 | id, 78 | }, 79 | }); 80 | if (!order_item) { 81 | throw new NotFoundException(`Order Item with id ${id} not found`); 82 | } 83 | return order_item; 84 | } 85 | 86 | async deleteOrderItem(id: number): Promise { 87 | const order_item = await this.findOrderItem(id); 88 | const result = await this.orderItemRepository.delete(order_item); 89 | if (result.affected === 0) { 90 | throw new NotFoundException(`Order Item with id ${id} not found`); 91 | } 92 | } 93 | 94 | async completeOrder( 95 | user: User, 96 | orderId: number, 97 | createPaymentDto: CreatePaymentDto, 98 | ): Promise { 99 | const order = user.orders.find(order => order.id === orderId); 100 | const { payment_method } = createPaymentDto; 101 | const payment = new Payment(); 102 | payment.date = new Date(); 103 | payment.client = user; 104 | payment.payment_method = payment_method; 105 | const invoice = await this.createInvoice(user, payment, order); 106 | order.invoice = invoice; 107 | payment.invoice = invoice; 108 | payment.amount = invoice.invoice_total; 109 | try { 110 | user.payments.push(await payment.save()); 111 | await order.save(); 112 | } catch (error) { 113 | console.error(error); 114 | } 115 | } 116 | 117 | async createInvoice( 118 | user: User, 119 | payment: Payment, 120 | order: Order, 121 | ): Promise { 122 | const today = new Date(); 123 | const invoice = new Invoice(); 124 | invoice.client = user; 125 | invoice.order = order; 126 | invoice.invoice_date = new Date(today.getFullYear(), today.getMonth(), today.getDate()); 127 | const nextWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7); 128 | invoice.due_date = nextWeek; 129 | invoice.payment = payment; 130 | let total_amount = 0; 131 | 132 | for (let i = 0; i < order.order_items.length; i++) { 133 | total_amount += order.order_items[i].totalPrice; 134 | } 135 | 136 | invoice.invoice_total = total_amount; 137 | invoice.payment_date = payment.date; 138 | invoice.number = `${Math.random() + '-' + Date.now() + '.'}`; 139 | 140 | try { 141 | user.invoices.push(await invoice.save()); 142 | return invoice; 143 | } catch (error) { 144 | console.error(error); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/cart/cart.service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | NotFoundException, 4 | ConflictException, 5 | } from '@nestjs/common'; 6 | import { Cart } from './cart.entity'; 7 | import { InjectRepository } from '@nestjs/typeorm'; 8 | import { Repository } from 'typeorm'; 9 | import { User } from '../auth/user.entity'; 10 | import { Order } from '../order/order.entity'; 11 | import { CreateOrderDto } from '../order/dto/create-order.dto'; 12 | import { ProfileService } from '../profile/profile.service'; 13 | import { CartItem } from './cart-item.entity'; 14 | import { OrderItem } from '../order/order_item.entity'; 15 | import { Product } from '../product/product.entity'; 16 | import { Profile } from '../profile/profile.entity'; 17 | import { OrderService } from '../order/order.service'; 18 | import { CreatePaymentDto } from '../payment/dto/create-payment.dto'; 19 | 20 | @Injectable() 21 | export class CartService { 22 | constructor( 23 | @InjectRepository(Cart) private readonly cartRepository: Repository, 24 | @InjectRepository(CartItem) 25 | private readonly cartItemRepository: Repository, 26 | private readonly profileService: ProfileService, 27 | private readonly orderService: OrderService, 28 | ) {} 29 | 30 | async getCart(id: number, profile?: Profile): Promise { 31 | let cart = null; 32 | if (id) { 33 | cart = this.cartRepository.findOne({ 34 | id, 35 | }); 36 | } 37 | if (profile) { 38 | cart = this.cartRepository.findOne({ 39 | profile, 40 | }); 41 | } 42 | 43 | if (!cart) { 44 | throw new NotFoundException(`Cart with id: ${id} not found`); 45 | } 46 | return cart; 47 | } 48 | 49 | async getCartItem(id: number, cart?: Cart): Promise { 50 | let cart_item = null; 51 | if (id) { 52 | cart_item = this.cartItemRepository.findOne({ 53 | id, 54 | }); 55 | } 56 | if (cart) { 57 | cart_item = this.cartItemRepository.findOne({ 58 | cart, 59 | }); 60 | } 61 | 62 | if (!cart_item) { 63 | throw new NotFoundException(`CartItem with id: ${id} not found`); 64 | } 65 | return cart_item; 66 | } 67 | 68 | async clearCartItemProducts(cart_item_id: number): Promise { 69 | const cart_item = await this.getCartItem(cart_item_id); 70 | cart_item.total_products = 0; 71 | cart_item.products = []; 72 | await cart_item.save(); 73 | return cart_item; 74 | } 75 | 76 | async removeFromCart(cart_item_id: number, productId: number): Promise { 77 | const cart_item = await this.getCartItem(cart_item_id); 78 | if (cart_item) { 79 | const array = cart_item.products; 80 | for (let i = 0; i < array.length; i = i + 1) { 81 | if (array[i].id === productId) { 82 | const product = array.find(prod => { 83 | return prod.id === productId; 84 | }); 85 | await product.save(); 86 | array.splice(i, 1); 87 | cart_item.total_products = cart_item.total_products - 1; 88 | await cart_item.save(); 89 | } 90 | } 91 | return cart_item; 92 | } 93 | } 94 | 95 | async deleteCart(cart: Cart): Promise { 96 | await this.cartRepository.delete(cart); 97 | } 98 | 99 | async deleteCartItem(cart_item: CartItem): Promise { 100 | await this.cartItemRepository.remove(cart_item); 101 | } 102 | 103 | async productCheckout( 104 | user: User, 105 | cartItemId: number, 106 | productId: number, 107 | createOrderDto: CreateOrderDto, 108 | createPaymentDto: CreatePaymentDto, 109 | quantity: number 110 | ): Promise { 111 | const cart_item = await this.getCartItem(cartItemId); 112 | const product = cart_item.products.find( 113 | product => product.id === productId, 114 | ); 115 | if (!product) { 116 | throw new NotFoundException( 117 | `Product with id: ${productId} not found in Cart`, 118 | ); 119 | } 120 | 121 | const profile = await this.profileService.getProfileData(user); 122 | const order = await this.createOrder(user, createOrderDto, profile); 123 | const order_item = await this.createOrderItem(order, product, quantity); 124 | order.order_items.push(order_item); 125 | product.order_items.push(order_item); 126 | try { 127 | const savedOrder = await order.save(); 128 | user.orders.push(savedOrder); 129 | await product.save(); 130 | await this.removeFromCart(cartItemId, productId); 131 | await this.orderService.completeOrder(user, order.id, createPaymentDto); 132 | } catch (error) { 133 | console.error(error); 134 | } 135 | } 136 | 137 | async cartCheckout( 138 | user: User, 139 | cart_item_id: number, 140 | createOrderDto: CreateOrderDto, 141 | createPaymentDto: CreatePaymentDto, 142 | cartProductsQuantity: any 143 | ): Promise { 144 | const profile = await this.profileService.getProfileData(user); 145 | const cart_item = await this.getCartItem(cart_item_id); 146 | const order = await this.createOrder(user, createOrderDto, profile); 147 | for (let i = 0; i < cart_item.products.length; i++) { 148 | const product = cart_item.products[i]; 149 | if (!product) { 150 | throw new NotFoundException(`Product is not found in this Cart`); 151 | } 152 | const productQuantity = cartProductsQuantity[i]; 153 | const order_item = await this.createOrderItem(order, product, productQuantity); 154 | order.order_items.push(order_item); 155 | product.order_items.push(order_item); 156 | await product.save(); 157 | } 158 | try { 159 | const savedOrder = await order.save(); 160 | user.orders.push(savedOrder); 161 | await this.clearCartItemProducts(cart_item_id); 162 | await this.orderService.completeOrder(user, order.id, createPaymentDto); 163 | } catch (error) { 164 | console.error(error); 165 | } 166 | } 167 | 168 | async createOrder( 169 | user: User, 170 | createOrderDto: CreateOrderDto, 171 | profile: Profile, 172 | ): Promise { 173 | const order = new Order(); 174 | const { comments } = createOrderDto; 175 | order.comments = comments; 176 | order.user = user; 177 | const today = new Date(); 178 | const threeDaysLater = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 3); 179 | order.shipmentDate = threeDaysLater; 180 | order.shippedTo = profile.address; 181 | order.order_date = new Date(); 182 | order.order_items = []; 183 | 184 | try { 185 | await order.save(); 186 | return order; 187 | } catch (error) { 188 | console.error(error); 189 | } 190 | } 191 | 192 | async createOrderItem(order: Order, product: Product, productQuantity: any): Promise { 193 | const order_item = new OrderItem(); 194 | order_item.order = order; 195 | order_item.product = product; 196 | order_item.quantity = productQuantity; 197 | order_item.unit_price = product.price; 198 | order_item.totalPrice = order_item.unit_price * order_item.quantity; 199 | 200 | try { 201 | await order_item.save(); 202 | return order_item; 203 | } catch (error) { 204 | console.error(error); 205 | } 206 | } 207 | } 208 | --------------------------------------------------------------------------------