Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,700 changes: 2,360 additions & 1,340 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 15 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,29 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^10.0.3",
"@nestjs/common": "^11.1.3",
"@nestjs/config": "^3.0.0",
"@nestjs/core": "^10.0.3",
"@nestjs/core": "^11.1.3",
"@nestjs/jwt": "^10.1.0",
"@nestjs/microservices": "^11.1.3",
"@nestjs/passport": "^10.0.0",
"@nestjs/platform-express": "^10.0.3",
"@nestjs/platform-express": "^11.1.3",
"@nestjs/typeorm": "^11.0.0",
"@nestjs/websockets": "^11.1.3",
"aws-lambda": "^1.0.7",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"helmet": "^7.0.0",
"passport": "^0.6.0",
"passport-http": "^0.3.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1"
"pg": "^8.16.0",
"reflect-metadata": "^0.1.14",
"rxjs": "^7.8.1",
"serverless-http": "^3.2.0",
"tslib": "^2.8.1",
"typeorm": "^0.3.24"
},
"devDependencies": {
"@nestjs/cli": "^10.0.3",
Expand Down
22 changes: 20 additions & 2 deletions src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
/* eslint-disable prettier/prettier */
import { Module } from '@nestjs/common';

import { AppController } from './app.controller';

import { TypeOrmModule } from '@nestjs/typeorm';
import { CartModule } from './cart/cart.module';
import { AuthModule } from './auth/auth.module';
import { OrderModule } from './order/order.module';
import { ConfigModule } from '@nestjs/config';
import { CartItem } from './cart/entity/cart-item.entity';
import { Cart } from './cart/entity/cart.entity';

@Module({
imports: [AuthModule, CartModule, OrderModule, ConfigModule.forRoot()],
imports: [
AuthModule,
CartModule,
OrderModule,
ConfigModule.forRoot(),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
entities: [Cart, CartItem],
synchronize: true, // set to false in production!
}),
],
controllers: [AppController],
providers: [],
})
Expand Down
1 change: 1 addition & 0 deletions src/cart/cart.controller.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable prettier/prettier */
import {
Controller,
Get,
Expand Down
7 changes: 5 additions & 2 deletions src/cart/cart.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
/* eslint-disable prettier/prettier */
import { Module } from '@nestjs/common';

import { OrderModule } from '../order/order.module';

import { TypeOrmModule } from '@nestjs/typeorm';
import { CartController } from './cart.controller';
import { CartService } from './services';
import { Cart } from './entity/cart.entity';
import { CartItem } from './entity/cart-item.entity';

@Module({
imports: [OrderModule],
imports: [OrderModule, TypeOrmModule.forFeature([Cart, CartItem])],
providers: [CartService],
controllers: [CartController],
})
Expand Down
23 changes: 23 additions & 0 deletions src/cart/entity/cart-item.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable prettier/prettier */
import {
Entity, PrimaryGeneratedColumn, Column, ManyToOne
} from 'typeorm';
import { Cart } from './cart.entity';

@Entity({ name: 'cart_items' })
export class CartItem {
@PrimaryGeneratedColumn('uuid')
id: string;

@Column()
product_id: string;

@Column('int')
count: number;

@ManyToOne(() => Cart, (cart) => cart.items, {
onDelete: 'CASCADE',
})
cart: Cart;
}

39 changes: 39 additions & 0 deletions src/cart/entity/cart.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* eslint-disable prettier/prettier */
import {
Entity, PrimaryGeneratedColumn, Column, CreateDateColumn,
UpdateDateColumn, OneToMany
} from 'typeorm';
import { CartItem } from './cart-item.entity';

export enum CartStatuses {
OPEN = 'OPEN',
ORDERED = 'ORDERED',
}

@Entity({ name: 'carts' })
export class Cart {
@PrimaryGeneratedColumn('uuid')
id: string;

@Column()
user_id: string;

@Column({
type: 'enum',
enum: CartStatuses,
default: CartStatuses.OPEN,
})
status: CartStatuses;

@CreateDateColumn()
created_at: Date;

@UpdateDateColumn()
updated_at: Date;

@OneToMany(() => CartItem, (item) => item.cart, {
cascade: true,
eager: true,
})
items: CartItem[];
}
1 change: 1 addition & 0 deletions src/cart/models-rules/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable prettier/prettier */
import { CartItem } from '../models';

export function calculateCartTotal(items: CartItem[]): number {
Expand Down
1 change: 1 addition & 0 deletions src/cart/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable prettier/prettier */
export enum CartStatuses {
OPEN = 'OPEN',
STATUS = 'STATUS',
Expand Down
87 changes: 48 additions & 39 deletions src/cart/services/cart.service.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,71 @@
/* eslint-disable prettier/prettier */
import { Injectable } from '@nestjs/common';
import { randomUUID } from 'node:crypto';
import { Cart, CartStatuses } from '../models';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Cart, CartStatuses } from '../entity/cart.entity';
import { CartItem } from '../entity/cart-item.entity';
import { PutCartPayload } from 'src/order/type';

@Injectable()
export class CartService {
private userCarts: Record<string, Cart> = {};
constructor(
@InjectRepository(Cart)
private readonly cartRepository: Repository<Cart>,

findByUserId(userId: string): Cart {
return this.userCarts[userId];
}
@InjectRepository(CartItem)
private readonly cartItemRepository: Repository<CartItem>,
) {}

createByUserId(user_id: string): Cart {
const timestamp = Date.now();
async findByUserId(userId: string): Promise<Cart> {
return this.cartRepository.findOne({
where: { user_id: userId, status: CartStatuses.OPEN },
});
}

const userCart = {
id: randomUUID(),
async createByUserId(user_id: string): Promise<Cart> {
const newCart = this.cartRepository.create({
user_id,
created_at: timestamp,
updated_at: timestamp,
status: CartStatuses.OPEN,
items: [],
};

this.userCarts[user_id] = userCart;

return userCart;
});
return this.cartRepository.save(newCart);
}

findOrCreateByUserId(userId: string): Cart {
const userCart = this.findByUserId(userId);

if (userCart) {
return userCart;
async findOrCreateByUserId(userId: string): Promise<Cart> {
let cart = await this.findByUserId(userId);
if (!cart) {
cart = await this.createByUserId(userId);
}

return this.createByUserId(userId);
return cart;
}

updateByUserId(userId: string, payload: PutCartPayload): Cart {
const userCart = this.findOrCreateByUserId(userId);
async updateByUserId(userId: string, payload: PutCartPayload): Promise<Cart> {
const cart = await this.findOrCreateByUserId(userId);

const index = userCart.items.findIndex(
({ product }) => product.id === payload.product.id,
);
const existingItem = cart.items.find(item => item.product_id === payload.product.id);

if (index === -1) {
userCart.items.push(payload);
} else if (payload.count === 0) {
userCart.items.splice(index, 1);
} else {
userCart.items[index] = payload;
if (existingItem) {
if (payload.count === 0) {
await this.cartItemRepository.remove(existingItem);
} else {
existingItem.count = payload.count;
await this.cartItemRepository.save(existingItem);
}
} else if (payload.count > 0) {
const newItem = this.cartItemRepository.create({
product_id: payload.product.id,
count: payload.count,
cart,
});
await this.cartItemRepository.save(newItem);
}

return userCart;
return this.findOrCreateByUserId(userId); // to return updated cart with items
}

removeByUserId(userId): void {
this.userCarts[userId] = null;
async removeByUserId(userId: string): Promise<void> {
const cart = await this.findByUserId(userId);
if (cart) {
await this.cartRepository.remove(cart);
}
}
}
23 changes: 17 additions & 6 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { NestFactory } from '@nestjs/core';

import helmet from 'helmet';

import { AppModule } from './app.module';
import { ConfigService } from '@nestjs/config';
import { Handler } from 'aws-lambda';
import serverless from 'serverless-http';

let cachedServer: Handler; // Cache the server for better Lambda warm performance

async function bootstrap() {
const app = await NestFactory.create(AppModule);
Expand All @@ -17,8 +19,17 @@ async function bootstrap() {
});
app.use(helmet());

await app.listen(port, () => {
console.log('App is running on %s port', port);
});
await app.init(); // Ensure the app is initialized before exporting as serverless

console.log('App is ready to be served as a Lambda');
return serverless(app.getHttpAdapter().getInstance()); // Return serverless handler
}
bootstrap();

// Lambda handler function
export const handler: Handler = async (event, context) => {
if (!cachedServer) {
// Only bootstrap the server once (Lambda cold start optimization)
cachedServer = await bootstrap();
}
return cachedServer(event, context); // Proxy event and context to serverless handler
};