upload
This commit is contained in:
commit
d2b6c0bb2b
|
|
@ -0,0 +1,13 @@
|
|||
# Editor configuration, see http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
MONGODB_URI=mongodb://root:ct4k7miShBc3VDw2wzVaDQRq@fitz-roy.liara.cloud:32207/my-app?authSource=admin
|
||||
LIARA_ENDPOINT=https://storage.iran.liara.space
|
||||
LIARA_BUCKET_NAME=development-bucket
|
||||
LIARA_ACCESS_KEY=cc510kvmpiu861r2
|
||||
LIARA_SECRET_KEY=13ed8542-c0e6-48cb-b3b4-ff8af6d12364
|
||||
LIARA_REGION=ir
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
dist
|
||||
tmp
|
||||
out-tsc
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
.cursor/rules/nx-rules.mdc
|
||||
.github/instructions/nx.instructions.md
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Add files here to ignore them from prettier formatting
|
||||
/dist
|
||||
/coverage
|
||||
/.nx/cache
|
||||
/.nx/workspace-data
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"nrwl.angular-console",
|
||||
"esbenp.prettier-vscode",
|
||||
"firsttris.vscode-jest-runner"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
# EcommerceMicroservices
|
||||
|
||||
<a alt="Nx logo" href="https://nx.dev" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.png" width="45"></a>
|
||||
|
||||
✨ Your new, shiny [Nx workspace](https://nx.dev) is almost ready ✨.
|
||||
|
||||
Run `npx nx graph` to visually explore what got created. Now, let's get you up to speed!
|
||||
|
||||
## Finish your remote caching setup
|
||||
|
||||
[Click here to finish setting up your workspace!](https://cloud.nx.app/connect/LFsmU67JYN)
|
||||
|
||||
|
||||
## Run tasks
|
||||
|
||||
To run tasks with Nx use:
|
||||
|
||||
```sh
|
||||
npx nx <target> <project-name>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```sh
|
||||
npx nx build myproject
|
||||
```
|
||||
|
||||
These targets are either [inferred automatically](https://nx.dev/concepts/inferred-tasks?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) or defined in the `project.json` or `package.json` files.
|
||||
|
||||
[More about running tasks in the docs »](https://nx.dev/features/run-tasks?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
|
||||
|
||||
## Add new projects
|
||||
|
||||
While you could add new projects to your workspace manually, you might want to leverage [Nx plugins](https://nx.dev/concepts/nx-plugins?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) and their [code generation](https://nx.dev/features/generate-code?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) feature.
|
||||
|
||||
To install a new plugin you can use the `nx add` command. Here's an example of adding the React plugin:
|
||||
```sh
|
||||
npx nx add @nx/react
|
||||
```
|
||||
|
||||
Use the plugin's generator to create new projects. For example, to create a new React app or library:
|
||||
|
||||
```sh
|
||||
# Generate an app
|
||||
npx nx g @nx/react:app demo
|
||||
|
||||
# Generate a library
|
||||
npx nx g @nx/react:lib some-lib
|
||||
```
|
||||
|
||||
You can use `npx nx list` to get a list of installed plugins. Then, run `npx nx list <plugin-name>` to learn about more specific capabilities of a particular plugin. Alternatively, [install Nx Console](https://nx.dev/getting-started/editor-setup?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) to browse plugins and generators in your IDE.
|
||||
|
||||
[Learn more about Nx plugins »](https://nx.dev/concepts/nx-plugins?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects) | [Browse the plugin registry »](https://nx.dev/plugin-registry?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
|
||||
|
||||
|
||||
[Learn more about Nx on CI](https://nx.dev/ci/intro/ci-with-nx#ready-get-started-with-your-provider?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
|
||||
|
||||
## Install Nx Console
|
||||
|
||||
Nx Console is an editor extension that enriches your developer experience. It lets you run tasks, generate code, and improves code autocompletion in your IDE. It is available for VSCode and IntelliJ.
|
||||
|
||||
[Install Nx Console »](https://nx.dev/getting-started/editor-setup?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
|
||||
|
||||
## Useful links
|
||||
|
||||
Learn more:
|
||||
|
||||
- [Learn about Nx on CI](https://nx.dev/ci/intro/ci-with-nx?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
|
||||
- [Releasing Packages with Nx release](https://nx.dev/features/manage-releases?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
|
||||
- [What are Nx plugins?](https://nx.dev/concepts/nx-plugins?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
|
||||
|
||||
And join the Nx community:
|
||||
- [Discord](https://go.nx.dev/community)
|
||||
- [Follow us on X](https://twitter.com/nxdevtools) or [LinkedIn](https://www.linkedin.com/company/nrwl)
|
||||
- [Our Youtube channel](https://www.youtube.com/@nxdevtools)
|
||||
- [Our blog](https://nx.dev/blog?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects)
|
||||
|
||||
|
||||
/*
|
||||
User Service (Handles user profiles, authentication, notifications):
|
||||
|
||||
Collections: User, Address, AddressDocument, PaymentMethod, Invitation, Support, Comment.
|
||||
Services to Implement:
|
||||
|
||||
User registration/login (with roles: seller/user).
|
||||
Profile management (update name, language, nationality).
|
||||
Notification system (send based on categories/frequency).
|
||||
Address CRUD (create/read/update/delete).
|
||||
Payment method management.
|
||||
Support ticket creation/resolution.
|
||||
Comment/review posting (with likes/dislikes).
|
||||
|
||||
|
||||
|
||||
Catalog Service (Manages products, categories, properties):
|
||||
|
||||
Collections: ProductCategory, ProductCategoryDocument, ShopCategory, ShopCategoryDocument, Property, PropertyDocument, Tag, Product, ProductDocument, Brand.
|
||||
Services to Implement:
|
||||
|
||||
Category CRUD (hierarchical trees for shops/products).
|
||||
Product listing/create/update (with properties, tags, images).
|
||||
Brand management.
|
||||
Search/filtering (by tags, categories, properties).
|
||||
Property definition (user/system types).
|
||||
|
||||
|
||||
Why? Core catalog domain; scales independently for search-heavy ops.
|
||||
|
||||
|
||||
Vendor Service (Handles shops, branches, regions):
|
||||
|
||||
Collections: Shop, ShopDocument, Branch, Region, RegionDocument.
|
||||
Services to Implement:
|
||||
|
||||
Shop/branch creation (with addresses, schedules, facilities).
|
||||
Region management (polygons for geo-queries).
|
||||
Vendor approval (adminStatus updates).
|
||||
Scoring/facilities updates.
|
||||
|
||||
|
||||
Why? Vendor-specific; allows separate scaling for multi-vendor growth.
|
||||
|
||||
|
||||
Discount Service (Manages promotions and discounts):
|
||||
|
||||
Collections: ItemDiscount, Discount.
|
||||
Services to Implement:
|
||||
|
||||
Discount creation (types, percentages, expirations, limits).
|
||||
Apply discounts to items/products/shops.
|
||||
Validation (e.g., check expiry, max usage).
|
||||
Event emission for discount applications.
|
||||
|
||||
|
||||
Why? Isolated for complex rules; easy to update without affecting core e-commerce.
|
||||
|
||||
|
||||
Order Service (Handles orders, reservations, transactions):
|
||||
|
||||
Collections: Order, OrderDetail, Transaction, Reservation.
|
||||
Services to Implement:
|
||||
|
||||
Order creation/processing (apply discounts, calculate final price).
|
||||
Payment integration (via PaymentMethod refs).
|
||||
Transaction logging (increases/decreases).
|
||||
Status updates (e.g., delivery time, process).
|
||||
Reservation handling.
|
||||
|
||||
|
||||
Why? Transactional core; use sagas for distributed transactions (e.g., with discounts).
|
||||
|
||||
|
||||
Advertisement Service (Handles ads and banners):
|
||||
|
||||
Collections: Advertisement.
|
||||
Services to Implement:
|
||||
|
||||
Ad creation (banners, costs, dates).
|
||||
Targeting (by location, tags, user).
|
||||
Transaction linking for ad payments.
|
||||
|
||||
|
||||
Why? Non-core; can be scaled separately for marketing teams.
|
||||
|
||||
|
||||
API Gateway / Orchestration Service (Cross-cutting):
|
||||
|
||||
No specific collections; aggregates from others.
|
||||
Services: Authentication, rate-limiting, routing to other services.
|
||||
Why? Entry point; handles multi-language responses.
|
||||
|
||||
*/
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
const baseConfig = require('../eslint.config.js');
|
||||
|
||||
module.exports = [...baseConfig];
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
export default {
|
||||
displayName: 'advertisement-service',
|
||||
preset: '../jest.preset.js',
|
||||
testEnvironment: 'node',
|
||||
transform: {
|
||||
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageDirectory: '../coverage/advertisement-service',
|
||||
};
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "advertisement-service",
|
||||
"$schema": "../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "advertisement-service/src",
|
||||
"projectType": "application",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"serve": {
|
||||
"executor": "@nx/js:node",
|
||||
"defaultConfiguration": "development",
|
||||
"dependsOn": ["build"],
|
||||
"options": {
|
||||
"buildTarget": "advertisement-service:build",
|
||||
"runBuildTargetDependencies": false
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "advertisement-service:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "advertisement-service:build:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let app: TestingModule;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
});
|
||||
|
||||
describe('getData', () => {
|
||||
it('should return "Hello API"', () => {
|
||||
const appController = app.get<AppController>(AppController);
|
||||
expect(appController.getData()).toEqual({ message: 'Hello API' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { Controller, Get } from '@nestjs/common';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get()
|
||||
getData() {
|
||||
return this.appService.getData();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { Test } from '@nestjs/testing';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppService', () => {
|
||||
let service: AppService;
|
||||
|
||||
beforeAll(async () => {
|
||||
const app = await Test.createTestingModule({
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
service = app.get<AppService>(AppService);
|
||||
});
|
||||
|
||||
describe('getData', () => {
|
||||
it('should return "Hello API"', () => {
|
||||
expect(service.getData()).toEqual({ message: 'Hello API' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getData(): { message: string } {
|
||||
return { message: 'Hello API' };
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* This is not a production server yet!
|
||||
* This is only a minimal backend to get started.
|
||||
*/
|
||||
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { Transport } from '@nestjs/microservices';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.createMicroservice(AppModule, {
|
||||
transport: Transport.TCP,
|
||||
options: { port: 4006 },
|
||||
});
|
||||
await app.listen();
|
||||
console.log('User Service is running on port 4006');
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["node"],
|
||||
"emitDecoratorMetadata": true,
|
||||
"target": "es2021"
|
||||
},
|
||||
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
|
||||
const { join } = require('path');
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
path: join(__dirname, '../dist/advertisement-service'),
|
||||
},
|
||||
plugins: [
|
||||
new NxAppWebpackPlugin({
|
||||
target: 'node',
|
||||
compiler: 'tsc',
|
||||
main: './src/main.ts',
|
||||
tsConfig: './tsconfig.app.json',
|
||||
assets: ['./src/assets'],
|
||||
optimization: false,
|
||||
outputHashing: 'none',
|
||||
generatePackageJson: true,
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
const baseConfig = require('../eslint.config.js');
|
||||
|
||||
module.exports = [...baseConfig];
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
export default {
|
||||
displayName: 'api-gateway',
|
||||
preset: '../jest.preset.js',
|
||||
testEnvironment: 'node',
|
||||
transform: {
|
||||
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageDirectory: '../coverage/api-gateway',
|
||||
};
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "api-gateway",
|
||||
"$schema": "../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "api-gateway/src",
|
||||
"projectType": "application",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"serve": {
|
||||
"executor": "@nx/js:node",
|
||||
"defaultConfiguration": "development",
|
||||
"dependsOn": ["build"],
|
||||
"options": {
|
||||
"buildTarget": "api-gateway:build",
|
||||
"runBuildTargetDependencies": false
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "api-gateway:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "api-gateway:build:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AdvertisementController } from './advertisement.controller';
|
||||
|
||||
describe('AdvertisementController', () => {
|
||||
let controller: AdvertisementController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AdvertisementController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<AdvertisementController>(AdvertisementController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { Client, ClientProxy, Transport } from '@nestjs/microservices';
|
||||
|
||||
@Controller('advertisement')
|
||||
export class AdvertisementController {
|
||||
|
||||
|
||||
@Client({ transport: Transport.TCP, options: { host: '127.0.0.1', port: 4006 } })
|
||||
private advertismentService: ClientProxy;
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let app: TestingModule;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
});
|
||||
|
||||
describe('getData', () => {
|
||||
it('should return "Hello API"', () => {
|
||||
const appController = app.get<AppController>(AppController);
|
||||
expect(appController.getData()).toEqual({ message: 'Hello API' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { Controller, All, Req, Res } from '@nestjs/common';
|
||||
import { Request, Response } from 'express';
|
||||
import { ClientProxyFactory, Transport, ClientProxy } from '@nestjs/microservices';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Controller()
|
||||
export class ApiGatewayController {
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { ClientsModule, Transport } from '@nestjs/microservices';
|
||||
import { ApiGatewayController } from './app.controller';
|
||||
import { CatalogController } from './catalog/catalog.controller';
|
||||
import { UserController } from './user/user.controller';
|
||||
import { VendorController } from './vendor/vendor.controller';
|
||||
import { OrderController } from './order/order.controller';
|
||||
import { AdvertisementController } from './advertisement/advertisement.controller';
|
||||
import { DiscountController } from './discount/discount.controller';
|
||||
@Module({
|
||||
imports: [
|
||||
ClientsModule.register([
|
||||
{
|
||||
name: 'USER_SERVICE',
|
||||
transport: Transport.TCP,
|
||||
options: { host: 'localhost', port: 4001 },
|
||||
},
|
||||
{
|
||||
name: 'VENDOR_SERVICE',
|
||||
transport: Transport.TCP,
|
||||
options: { host: 'localhost', port: 4002 },
|
||||
},
|
||||
{
|
||||
name: 'ORDER_SERVICE',
|
||||
transport: Transport.TCP,
|
||||
options: { host: 'localhost', port: 4003 },
|
||||
},
|
||||
{
|
||||
name: 'DISCOUNT_SERVICE',
|
||||
transport: Transport.TCP,
|
||||
options: { host: 'localhost', port: 4004 },
|
||||
},
|
||||
{
|
||||
name: 'CATALOG_SERVICE',
|
||||
transport: Transport.TCP,
|
||||
options: { host: 'localhost', port: 4005 },
|
||||
},
|
||||
{
|
||||
name: 'ADVERTISEMENT_SERVICE',
|
||||
transport: Transport.TCP,
|
||||
options: { host: 'localhost', port: 4006 },
|
||||
},
|
||||
]),
|
||||
],
|
||||
controllers: [
|
||||
ApiGatewayController,
|
||||
CatalogController,
|
||||
UserController,
|
||||
VendorController,
|
||||
OrderController,
|
||||
AdvertisementController,
|
||||
DiscountController,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { Test } from '@nestjs/testing';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppService', () => {
|
||||
let service: AppService;
|
||||
|
||||
beforeAll(async () => {
|
||||
const app = await Test.createTestingModule({
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
service = app.get<AppService>(AppService);
|
||||
});
|
||||
|
||||
describe('getData', () => {
|
||||
it('should return "Hello API"', () => {
|
||||
expect(service.getData()).toEqual({ message: 'Hello API' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getData(): { message: string } {
|
||||
return { message: 'Hello API' };
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import { Controller, Post, Body, Inject, Query, Headers } from '@nestjs/common';
|
||||
import { ClientProxy } from '@nestjs/microservices';
|
||||
|
||||
interface CreateLanguageDto {
|
||||
lang?: string; // زبان (اختیاری)
|
||||
// سایر فیلدها
|
||||
}
|
||||
|
||||
@Controller('catalog')
|
||||
export class CatalogController {
|
||||
constructor(@Inject('CATALOG_SERVICE') private readonly catalogService: ClientProxy) {}
|
||||
|
||||
@Post('/language/create')
|
||||
async getCatalog(
|
||||
@Body() body: CreateLanguageDto,
|
||||
@Query('lang') queryLang: string,
|
||||
@Headers('accept-language') acceptLanguage: string,
|
||||
) {
|
||||
// اولویتبندی: body > query > header > پیشفرض
|
||||
const lang = body.lang || queryLang || acceptLanguage?.split(',')[0] || 'en';
|
||||
|
||||
// ارسال پیام به catalog-service
|
||||
return this.catalogService
|
||||
.send({ cmd: 'POST-language/create' }, { body, lang })
|
||||
.toPromise();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { DiscountController } from './discount.controller';
|
||||
|
||||
describe('DiscountController', () => {
|
||||
let controller: DiscountController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [DiscountController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<DiscountController>(DiscountController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { Client, ClientProxy, Transport } from '@nestjs/microservices';
|
||||
|
||||
@Controller('discount')
|
||||
export class DiscountController {
|
||||
|
||||
@Client({ transport: Transport.TCP, options: { host: '127.0.0.1', port: 4004 } })
|
||||
private discountService: ClientProxy;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { OrderController } from './order.controller';
|
||||
|
||||
describe('OrderController', () => {
|
||||
let controller: OrderController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [OrderController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<OrderController>(OrderController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { Client, ClientProxy, Transport } from '@nestjs/microservices';
|
||||
|
||||
@Controller('order')
|
||||
export class OrderController {
|
||||
|
||||
@Client({ transport: Transport.TCP, options: { host: '127.0.0.1', port: 4003 } })
|
||||
private orderService: ClientProxy;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UserController } from './user.controller';
|
||||
|
||||
describe('UserController', () => {
|
||||
let controller: UserController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [UserController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<UserController>(UserController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { Client, ClientProxy, Transport } from '@nestjs/microservices';
|
||||
|
||||
@Controller('user')
|
||||
export class UserController {
|
||||
|
||||
@Client({ transport: Transport.TCP, options: { host: '127.0.0.1', port: 4001 } })
|
||||
private userService: ClientProxy;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { VendorController } from './vendor.controller';
|
||||
|
||||
describe('VendorController', () => {
|
||||
let controller: VendorController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [VendorController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<VendorController>(VendorController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { Client, ClientProxy, Transport } from '@nestjs/microservices';
|
||||
|
||||
@Controller('vendor')
|
||||
export class VendorController {
|
||||
|
||||
@Client({ transport: Transport.TCP, options: { host: '127.0.0.1', port: 4002 } })
|
||||
private vendorService: ClientProxy;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* This is not a production server yet!
|
||||
* This is only a minimal backend to get started.
|
||||
*/
|
||||
|
||||
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { Transport, MicroserviceOptions } from '@nestjs/microservices';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
await app.listen(3000);
|
||||
console.log('API Gateway is running on port 3000');
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["node"],
|
||||
"emitDecoratorMetadata": true,
|
||||
"target": "es2021"
|
||||
},
|
||||
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
|
||||
const { join } = require('path');
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
path: join(__dirname, '../dist/api-gateway'),
|
||||
},
|
||||
plugins: [
|
||||
new NxAppWebpackPlugin({
|
||||
target: 'node',
|
||||
compiler: 'tsc',
|
||||
main: './src/main.ts',
|
||||
tsConfig: './tsconfig.app.json',
|
||||
assets: ['./src/assets'],
|
||||
optimization: false,
|
||||
outputHashing: 'none',
|
||||
generatePackageJson: true,
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
const baseConfig = require('../eslint.config.js');
|
||||
|
||||
module.exports = [...baseConfig];
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
export default {
|
||||
displayName: 'catalog-service',
|
||||
preset: '../jest.preset.js',
|
||||
testEnvironment: 'node',
|
||||
transform: {
|
||||
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageDirectory: '../coverage/catalog-service',
|
||||
};
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "catalog-service",
|
||||
"$schema": "../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "catalog-service/src",
|
||||
"projectType": "application",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"serve": {
|
||||
"executor": "@nx/js:node",
|
||||
"defaultConfiguration": "development",
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"options": {
|
||||
"buildTarget": "catalog-service:build",
|
||||
"runBuildTargetDependencies": false
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "catalog-service:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "catalog-service:build:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let app: TestingModule;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
});
|
||||
|
||||
describe('getData', () => {
|
||||
it('should return "Hello API"', () => {
|
||||
const appController = app.get<AppController>(AppController);
|
||||
expect(appController.getData()).toEqual({ message: 'Hello API' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { Controller, Get } from '@nestjs/common';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
import { MessagePattern } from '@nestjs/microservices';
|
||||
import { CreateLanguageDto } from './language/dto/language.dto';
|
||||
import { LanguageService } from './language/language.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly languageService: LanguageService) { }
|
||||
|
||||
|
||||
|
||||
@MessagePattern({ cmd: 'get-catalog' })
|
||||
getCatalog() {
|
||||
return [
|
||||
{ id: 1, name: 'Laptop' },
|
||||
{ id: 2, name: 'Phone' },
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { I18nJsonLoader, I18nModule } from 'nestjs-i18n';
|
||||
import * as path from 'path';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { UploadModule } from './upload/upload.module';
|
||||
import { ShopCategoryModule } from './shop-category/shop-category.module';
|
||||
import { LanguageModule } from './language/language.module';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { LanguageController } from './language/language.controller';
|
||||
import { I18nextModule } from './i18next/i18next.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath: '.env',
|
||||
}),
|
||||
MongooseModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
uri: configService.get<string>('MONGODB_URI'),
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
UploadModule,
|
||||
ShopCategoryModule,
|
||||
LanguageModule,
|
||||
I18nextModule,
|
||||
],
|
||||
controllers: [AppController, LanguageController],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { Test } from '@nestjs/testing';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppService', () => {
|
||||
let service: AppService;
|
||||
|
||||
beforeAll(async () => {
|
||||
const app = await Test.createTestingModule({
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
service = app.get<AppService>(AppService);
|
||||
});
|
||||
|
||||
describe('getData', () => {
|
||||
it('should return "Hello API"', () => {
|
||||
expect(service.getData()).toEqual({ message: 'Hello API' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getData(): { message: string } {
|
||||
return { message: 'Hello API' };
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import * as i18next from 'i18next';
|
||||
import Backend from 'i18next-fs-backend';
|
||||
import { join } from 'path';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: 'I18NEXT',
|
||||
useFactory: async () => {
|
||||
await i18next.use(Backend).init({
|
||||
lng: 'en', // زبان پیشفرض
|
||||
fallbackLng: 'en', // زبان جایگزین
|
||||
backend: {
|
||||
loadPath: join(__dirname, './locales/{{lng}}/{{ns}}.json'), // مسیر فایلهای ترجمه
|
||||
},
|
||||
ns: ['language', 'validation','translation'], // فضاهای نام
|
||||
defaultNS: 'translation', // فضای نام پیشفرض
|
||||
debug: true, // برای دیباگ
|
||||
});
|
||||
return i18next;
|
||||
},
|
||||
},
|
||||
],
|
||||
exports: ['I18NEXT'],
|
||||
})
|
||||
export class I18nextModule {}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import { IsString, IsNotEmpty, IsOptional, IsEnum, IsUUID } from 'class-validator';
|
||||
|
||||
export enum LanguageDirection {
|
||||
LTR = 'ltr',
|
||||
RTL = 'rtl',
|
||||
}
|
||||
|
||||
export class CreateLanguageDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
Name: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
Symbol?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
Icon?: string;
|
||||
|
||||
@IsEnum(LanguageDirection)
|
||||
@IsOptional()
|
||||
Direction?: LanguageDirection;
|
||||
|
||||
}
|
||||
|
||||
export class UpdateLanguageDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
Name?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
Symbol?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
Icon?: string;
|
||||
|
||||
@IsEnum(LanguageDirection)
|
||||
@IsOptional()
|
||||
Direction?: LanguageDirection;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { LanguageController } from './language.controller';
|
||||
|
||||
describe('LanguageController', () => {
|
||||
let controller: LanguageController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [LanguageController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<LanguageController>(LanguageController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { Controller, Get, Post, Put, Delete, Body, Param, HttpStatus } from '@nestjs/common';
|
||||
import { LanguageService } from './language.service';
|
||||
import { CreateLanguageDto, UpdateLanguageDto } from 'user-service/src/app/language/dto/language.dto';
|
||||
import { Language } from 'user-service/src/schemas/language.schema';
|
||||
import { I18n, I18nContext, I18nService } from 'nestjs-i18n';
|
||||
|
||||
import { MessagePattern, Payload } from '@nestjs/microservices';
|
||||
|
||||
@Controller()
|
||||
export class LanguageController {
|
||||
constructor(private readonly languageService: LanguageService,
|
||||
) { }
|
||||
|
||||
|
||||
@MessagePattern({cmd:'POST-language/create'})
|
||||
async create(payload: any): Promise<any> {
|
||||
console.log("---------------------",payload)
|
||||
const createLanguageDto: CreateLanguageDto = payload.body;
|
||||
const lang = payload.body.lang || 'en'
|
||||
return this.languageService.create(createLanguageDto,lang);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { LanguageController } from './language.controller';
|
||||
import { LanguageService } from './language.service';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { Language,LanguageSchema } from 'user-service/src/schemas/language.schema';
|
||||
import { I18nModule } from 'nestjs-i18n';
|
||||
import { I18nextModule } from '../i18next/i18next.module';
|
||||
|
||||
@Module({
|
||||
imports: [ MongooseModule.forFeature([{ name: Language.name, schema: LanguageSchema }]),I18nextModule],
|
||||
controllers: [LanguageController],
|
||||
providers: [LanguageService],
|
||||
exports:[LanguageService]
|
||||
})
|
||||
export class LanguageModule {}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { LanguageService } from './language.service';
|
||||
|
||||
describe('LanguageService', () => {
|
||||
let service: LanguageService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [LanguageService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<LanguageService>(LanguageService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import { Inject, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { Language } from 'user-service/src/schemas/language.schema';
|
||||
import { CreateLanguageDto, UpdateLanguageDto } from 'user-service/src/app/language/dto/language.dto';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { I18nContext, I18nService } from 'nestjs-i18n';
|
||||
import i18next from 'i18next';
|
||||
|
||||
@Injectable()
|
||||
export class LanguageService {
|
||||
constructor(@InjectModel(Language.name) private languageModel: Model<Language>,
|
||||
@Inject('I18NEXT') private readonly i18n: typeof i18next) {}
|
||||
|
||||
async create(
|
||||
createLanguageDto: CreateLanguageDto,lang:string): Promise<any> {
|
||||
const createdLanguage = new this.languageModel({
|
||||
...createLanguageDto,
|
||||
ID: uuidv4(),
|
||||
Status: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
const result = await createdLanguage.save();
|
||||
console.log("language -----------------", await this.i18n.t('created', { lng: lang ,ns: 'language' }))
|
||||
return {
|
||||
success: true,
|
||||
message: await this.i18n.t('created', { lng:'fa' }),
|
||||
data: result,
|
||||
};
|
||||
}
|
||||
|
||||
async findAll(): Promise<Language[]> {
|
||||
return this.languageModel.find().exec();
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<Language> {
|
||||
const language = await this.languageModel.findOne({ ID: id }).exec();
|
||||
if (!language) {
|
||||
throw new NotFoundException(`Language with ID ${id} not found`);
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
async update(id: string, updateLanguageDto: UpdateLanguageDto): Promise<Language> {
|
||||
const language = await this.languageModel
|
||||
.findOneAndUpdate(
|
||||
{ ID: id },
|
||||
{ ...updateLanguageDto, updatedAt: new Date() },
|
||||
{ new: true },
|
||||
)
|
||||
.exec();
|
||||
|
||||
if (!language) {
|
||||
throw new NotFoundException(`Language with ID ${id} not found`);
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
const result = await this.languageModel.deleteOne({ ID: id }).exec();
|
||||
if (result.deletedCount === 0) {
|
||||
throw new NotFoundException(`Language with ID ${id} not found`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import { IsString, IsOptional, IsNumber, IsNotEmpty } from 'class-validator';
|
||||
import { Types } from 'mongoose';
|
||||
import { i18nValidationMessage } from 'nestjs-i18n';
|
||||
import { IsMongoId } from 'class-validator';
|
||||
|
||||
export class CreateShopCategoryDto {
|
||||
@IsString({ message: i18nValidationMessage('validation.isString') })
|
||||
@IsOptional()
|
||||
Name?: string;
|
||||
|
||||
@IsString({ message: i18nValidationMessage('validation.isString') })
|
||||
@IsOptional()
|
||||
Description?: string;
|
||||
|
||||
@IsString({ message: i18nValidationMessage('validation.isString') })
|
||||
@IsOptional()
|
||||
Icon?: string;
|
||||
|
||||
@IsNumber({}, { message: i18nValidationMessage('validation.isNumber') })
|
||||
@IsOptional()
|
||||
Level?: number;
|
||||
|
||||
@IsMongoId({ message: i18nValidationMessage('validation.isMongoId') })
|
||||
@IsOptional()
|
||||
ParentCategory?: Types.ObjectId;
|
||||
|
||||
@IsMongoId({ message: i18nValidationMessage('validation.isMongoId') })
|
||||
@IsOptional()
|
||||
Language?: Types.ObjectId;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ShopCategoryController } from './shop-category.controller';
|
||||
|
||||
describe('ShopCategoryController', () => {
|
||||
let controller: ShopCategoryController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [ShopCategoryController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<ShopCategoryController>(ShopCategoryController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
|
||||
@Controller('shop-category')
|
||||
export class ShopCategoryController {}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { ShopCategoryController } from './shop-category.controller';
|
||||
import { ShopCategoryService } from './shop-category.service';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { ShopCategory, ShopCategorySchema } from 'catalog-service/src/schemas/shopCategory.schema';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
MongooseModule.forFeature([{ name: ShopCategory.name, schema: ShopCategorySchema }]),
|
||||
],
|
||||
controllers: [ShopCategoryController],
|
||||
providers: [ShopCategoryService],
|
||||
})
|
||||
export class ShopCategoryModule { }
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ShopCategoryService } from './shop-category.service';
|
||||
|
||||
describe('ShopCategoryService', () => {
|
||||
let service: ShopCategoryService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [ShopCategoryService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ShopCategoryService>(ShopCategoryService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose';
|
||||
import { ShopCategory,ShopCategoryDoc } from '../../schemas/shopCategory.schema';
|
||||
import { CreateShopCategoryDto } from './dto/create-shop-category.dto';
|
||||
import { I18n, I18nContext } from 'nestjs-i18n';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class ShopCategoryService {
|
||||
constructor(
|
||||
@InjectModel(ShopCategory.name) private shopCategoryModel: Model<ShopCategoryDoc>,
|
||||
) {}
|
||||
|
||||
async create(createShopCategoryDto: CreateShopCategoryDto): Promise<any> {
|
||||
|
||||
|
||||
}
|
||||
|
||||
async findAll(): Promise<ShopCategory[]> {
|
||||
return this.shopCategoryModel.find()
|
||||
.populate('Tags')
|
||||
.populate('ParentCategory')
|
||||
.populate('Document')
|
||||
.exec();
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<ShopCategory> {
|
||||
const category = await this.shopCategoryModel.findOne({ ID: id })
|
||||
.populate('Tags')
|
||||
.populate('ParentCategory')
|
||||
.populate('Document')
|
||||
.exec();
|
||||
if (!category) {
|
||||
throw new NotFoundException(`ShopCategory with ID ${id} not found`);
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
const result = await this.shopCategoryModel.deleteOne({ ID: id }).exec();
|
||||
if (result.deletedCount === 0) {
|
||||
throw new NotFoundException(`ShopCategory with ID ${id} not found`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UploadController } from './upload.controller';
|
||||
|
||||
describe('UploadController', () => {
|
||||
let controller: UploadController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [UploadController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<UploadController>(UploadController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { Body, Post, Controller, Res, UseInterceptors, UploadedFile } from '@nestjs/common';
|
||||
import { UploadService } from './upload.service';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { Response } from 'express';
|
||||
|
||||
interface MulterFile {
|
||||
fieldname: string;
|
||||
originalname: string;
|
||||
encoding: string;
|
||||
mimetype: string;
|
||||
size: number;
|
||||
destination?: string;
|
||||
filename?: string;
|
||||
path?: string;
|
||||
buffer?: Buffer;
|
||||
}
|
||||
|
||||
@Controller('upload')
|
||||
export class UploadController {
|
||||
constructor(private readonly uploadService: UploadService) {}
|
||||
|
||||
@Post()
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async uploadfiles(@UploadedFile() file: MulterFile, @Res() res: Response) {
|
||||
const result = await this.uploadService.uploadFileSingle(file);
|
||||
return res.status(result.status).json(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { UploadService } from './upload.service';
|
||||
import { UploadController } from './upload.controller';
|
||||
import { FileSchema } from 'catalog-service/src/schemas/file.schema';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
@Module({
|
||||
imports:[
|
||||
ConfigModule,
|
||||
MongooseModule.forFeature([
|
||||
{ name: 'File', schema: FileSchema },
|
||||
])],
|
||||
providers: [UploadService,ConfigService],
|
||||
controllers: [UploadController],
|
||||
exports: [UploadService],
|
||||
})
|
||||
export class UploadModule {}
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UploadService } from './upload.service';
|
||||
|
||||
describe('UploadService', () => {
|
||||
let service: UploadService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UploadService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<UploadService>(UploadService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
// src/s3/s3.service.ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { extname } from 'path';
|
||||
import { File, FileDocument } from 'catalog-service/src/schemas/file.schema'
|
||||
import { InjectModel } from '@nestjs/mongoose';
|
||||
import { Model } from 'mongoose'
|
||||
import { Express } from 'express';
|
||||
|
||||
interface CreateSellerResponse {
|
||||
message: string;
|
||||
status: number;
|
||||
data: any;
|
||||
}
|
||||
@Injectable()
|
||||
export class UploadService {
|
||||
private s3: S3Client;
|
||||
private bucket: string;
|
||||
private region: string;
|
||||
private endpoint: string;
|
||||
|
||||
constructor(private configService: ConfigService,
|
||||
@InjectModel(File.name) private fileModel: Model<FileDocument>,
|
||||
) {
|
||||
const accessKeyId = configService.get<string>('LIARA_ACCESS_KEY');
|
||||
const secretAccessKey = configService.get<string>('LIARA_SECRET_KEY');
|
||||
const region = configService.get<string>('LIARA_REGION') || 'default';
|
||||
const endpoint = configService.get<string>('LIARA_ENDPOINT');
|
||||
const bucket = configService.get<string>('LIARA_BUCKET_NAME');
|
||||
|
||||
if (!accessKeyId || !secretAccessKey || !bucket || !endpoint) {
|
||||
throw new Error('Missing Liara S3 configuration');
|
||||
}
|
||||
|
||||
this.bucket = bucket;
|
||||
this.region = region;
|
||||
this.endpoint = endpoint;
|
||||
|
||||
this.s3 = new S3Client({
|
||||
region: this.region,
|
||||
endpoint: this.endpoint,
|
||||
forcePathStyle: true,
|
||||
credentials: {
|
||||
accessKeyId,
|
||||
secretAccessKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async uploadFileSingle(file: any): Promise<CreateSellerResponse> {
|
||||
const key: string = uuidv4();
|
||||
const extension = extname(file.originalname);
|
||||
|
||||
const urlFile = `${this.endpoint}/${this.bucket}/${key}${extension}`;
|
||||
const keyWithExt = `${key}${extension}`;
|
||||
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: this.bucket,
|
||||
Key: keyWithExt,
|
||||
Body: file.buffer,
|
||||
ContentType: file.mimetype,
|
||||
});
|
||||
|
||||
const dataupload = await this.s3.send(command);
|
||||
const mimeType = file.mimetype;
|
||||
console.log("dataupload", dataupload);
|
||||
|
||||
let fileType: 'image' | 'video' | 'document' | 'other' = 'other';
|
||||
|
||||
if (mimeType.startsWith('image/')) {
|
||||
fileType = 'image';
|
||||
} else if (mimeType.startsWith('video/')) {
|
||||
fileType = 'video';
|
||||
} else if (
|
||||
mimeType === 'application/pdf' ||
|
||||
mimeType === 'application/msword' ||
|
||||
mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||
) {
|
||||
fileType = 'document';
|
||||
}
|
||||
|
||||
try {
|
||||
const newFile = new this.fileModel({
|
||||
FileType: fileType,
|
||||
Title: "avatar",
|
||||
Description: "avatar",
|
||||
Format: mimeType,
|
||||
Url: urlFile,
|
||||
ID: key
|
||||
});
|
||||
|
||||
const savedSeller = await newFile.save();
|
||||
return {
|
||||
message: "فایل با موفقیت آپلود شد.",
|
||||
status: 200,
|
||||
data: {
|
||||
ID: key,
|
||||
Url: urlFile,
|
||||
_id: newFile._id
|
||||
}
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
message: "آپلود فایل با شکست مواجه شد",
|
||||
status: 502,
|
||||
data: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"created": "Language created successfully"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"isString": "The field $property must be a string",
|
||||
"isNumber": "The field $property must be a number",
|
||||
"isMongoId": "The field $property must be a valid MongoDB ObjectId",
|
||||
"isNotEmpty": "The field $property should not be empty",
|
||||
"isEnum": "The field $property must be one of the allowed values"
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"created": "زبان با موفقیت ایجاد شد"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"isString": "فیلد $property باید رشته باشد",
|
||||
"isNumber": "فیلد $property باید یک عدد باشد",
|
||||
"isMongoId": "فیلد $property باید یک شناسه معتبر مونگو باشد",
|
||||
"isNotEmpty": "فیلد $property نباید خالی باشد",
|
||||
"isEnum": "فیلد $property باید یکی از مقادیر مجاز باشد"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app/app.module';
|
||||
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
|
||||
transport: Transport.TCP,
|
||||
options: {
|
||||
host: 'localhost',
|
||||
port: 4005, // پورت میکروسرویس
|
||||
},
|
||||
});
|
||||
await app.listen();
|
||||
console.log('Catalog Microservice is listening on port 4005');
|
||||
}
|
||||
bootstrap();
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// src/schemas/album.schema.ts
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Types } from 'mongoose';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
export type AlbumDocument = Album & Document;
|
||||
|
||||
@Schema()
|
||||
export class Album {
|
||||
@Prop({ default: () => uuidv4(), unique: true })
|
||||
ID: string; // UUID
|
||||
|
||||
@Prop()
|
||||
Name: string;
|
||||
|
||||
@Prop()
|
||||
Description: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'Album', default: null })
|
||||
Parent: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'User' })
|
||||
Owner: Types.ObjectId;
|
||||
|
||||
@Prop({ default: 'active' })
|
||||
Status: string;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export const AlbumSchema = SchemaFactory.createForClass(Album);
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// src/schemas/file.schema.ts
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { Document, Types } from 'mongoose';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export type FileDocument = File & Document;
|
||||
|
||||
/*
|
||||
@Schema()
|
||||
export class File {
|
||||
@Prop({ default: () => uuidv4(), unique: true })
|
||||
ID: string; // UUID
|
||||
|
||||
@Prop()
|
||||
Title: string;
|
||||
|
||||
@Prop()
|
||||
Description: string;
|
||||
|
||||
@Prop()
|
||||
Format: string;
|
||||
|
||||
@Prop()
|
||||
Url: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'Album' })
|
||||
Album: Types.ObjectId; // آلبوم مربوطه
|
||||
|
||||
@Prop({ default: 'active' })
|
||||
Status: string;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
*/
|
||||
@Schema({ timestamps: true })
|
||||
export class File {
|
||||
|
||||
@Prop()
|
||||
FileType: string;
|
||||
|
||||
@Prop({required:false})
|
||||
Title:string
|
||||
|
||||
@Prop({required:false})
|
||||
Description:string
|
||||
|
||||
@Prop({required:false})
|
||||
Format:string
|
||||
|
||||
@Prop({required:false})
|
||||
Url:string
|
||||
|
||||
@Prop({required:true,unique:true})
|
||||
ID:string
|
||||
|
||||
|
||||
}
|
||||
export const FileSchema = SchemaFactory.createForClass(File);
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { HydratedDocument, Types } from 'mongoose';
|
||||
|
||||
@Schema()
|
||||
export class ProductCategory {
|
||||
|
||||
@Prop({ required: true, unique: true })
|
||||
ID: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'ProductCategoryDocument' })
|
||||
Document: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
Name: string;
|
||||
|
||||
@Prop()
|
||||
Description: string;
|
||||
|
||||
@Prop()
|
||||
Icon: string;
|
||||
|
||||
@Prop()
|
||||
Level: number;
|
||||
|
||||
@Prop({ type: [{ type: Types.ObjectId, ref: 'Tag' }] })
|
||||
Tags: Types.ObjectId[];
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'ProductCategory' })
|
||||
ParentCategory: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
Status: string;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
export const ProductCategorySchema = SchemaFactory.createForClass(ProductCategory);
|
||||
ProductCategorySchema.index({ ID: 1 });
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { HydratedDocument, Types } from 'mongoose';
|
||||
|
||||
@Schema()
|
||||
export class ProductCategoryDocument {
|
||||
|
||||
@Prop({ required: true, unique: true })
|
||||
ID: string;
|
||||
|
||||
@Prop()
|
||||
Name: string;
|
||||
|
||||
@Prop()
|
||||
Description: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'ProductCategory' })
|
||||
ProductCategory: Types.ObjectId;
|
||||
|
||||
@Prop({ type: [{ type: Types.ObjectId, ref: 'Tag' }] })
|
||||
Tags: Types.ObjectId[];
|
||||
|
||||
@Prop()
|
||||
Status: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'Language' })
|
||||
Language: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
export const ProductCategoryDocumentSchema = SchemaFactory.createForClass(ProductCategoryDocument);
|
||||
ProductCategoryDocumentSchema.index({ID:1})
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { HydratedDocument, Types } from 'mongoose';
|
||||
|
||||
|
||||
@Schema()
|
||||
export class Property {
|
||||
|
||||
@Prop({ required: true, unique: true })
|
||||
ID: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'PropertyDocument' })
|
||||
Document: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
Name: string;
|
||||
|
||||
@Prop()
|
||||
Description: string;
|
||||
|
||||
@Prop()
|
||||
Data: string;
|
||||
|
||||
@Prop({ enum: ['user', 'system'] })
|
||||
Type: string;
|
||||
|
||||
@Prop()
|
||||
Status: string;
|
||||
|
||||
@Prop()
|
||||
AdminStatus: string;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
export const PropertySchema = SchemaFactory.createForClass(Property);
|
||||
PropertySchema.index({ ID: 1 })
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { HydratedDocument, Types } from 'mongoose';
|
||||
|
||||
|
||||
@Schema()
|
||||
export class PropertyDocument {
|
||||
|
||||
@Prop({ required: true, unique: true })
|
||||
ID: string;
|
||||
|
||||
@Prop()
|
||||
Name: string;
|
||||
|
||||
@Prop()
|
||||
Description: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'Property' })
|
||||
Property: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
Data: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'Language' })
|
||||
Language: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
export const PropertyDocumentSchema = SchemaFactory.createForClass(PropertyDocument);
|
||||
PropertyDocumentSchema.index({ ID: 1 })
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { HydratedDocument, Types } from 'mongoose';
|
||||
|
||||
@Schema()
|
||||
export class Region {
|
||||
|
||||
@Prop({ required: true, unique: true })
|
||||
ID: string;
|
||||
|
||||
@Prop()
|
||||
Name: string;
|
||||
|
||||
@Prop()
|
||||
Description: string;
|
||||
|
||||
@Prop({ type: { type: String, enum: ['Polygon'] }, coordinates: [[[Number]]] })
|
||||
Location: { type: 'Polygon', coordinates: number[][][] };
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'RegionDocument' })
|
||||
Document: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
Status: string;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
export const RegionSchema = SchemaFactory.createForClass(Region);
|
||||
RegionSchema.index({ ID: 1 })
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { HydratedDocument, Types } from 'mongoose';
|
||||
|
||||
@Schema()
|
||||
export class RegionDocument {
|
||||
|
||||
@Prop({ required: true, unique: true })
|
||||
ID: string;
|
||||
|
||||
@Prop()
|
||||
Name: string;
|
||||
|
||||
@Prop()
|
||||
Description: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'Region' })
|
||||
Region: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
Status: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'Language' })
|
||||
Language: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export const RegionDocumentSchema = SchemaFactory.createForClass(RegionDocument);
|
||||
RegionDocumentSchema.index({ ID: 1 })
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { HydratedDocument, Types } from 'mongoose';
|
||||
|
||||
export type ShopCategoryDoc = HydratedDocument<ShopCategory>;
|
||||
|
||||
@Schema()
|
||||
export class ShopCategory {
|
||||
|
||||
@Prop({ required: true, unique: true })
|
||||
ID: string;
|
||||
|
||||
@Prop()
|
||||
Name: string;
|
||||
|
||||
@Prop()
|
||||
Description: string;
|
||||
|
||||
@Prop()
|
||||
Icon: string;
|
||||
|
||||
@Prop()
|
||||
Level: number;
|
||||
|
||||
@Prop({ type: [{ type: Types.ObjectId, ref: 'Tag' }] })
|
||||
Tags: Types.ObjectId[];
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'ShopCategory' })
|
||||
ParentCategory: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'ShopCategoryDocument' })
|
||||
Document: Types.ObjectId;
|
||||
|
||||
@Prop()
|
||||
Status: string;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
export const ShopCategorySchema = SchemaFactory.createForClass(ShopCategory);
|
||||
ShopCategorySchema.index({ ID: 1 });
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
|
||||
import { HydratedDocument, Types } from 'mongoose';
|
||||
|
||||
|
||||
@Schema()
|
||||
export class ShopCategoryDocument {
|
||||
|
||||
@Prop({ required: true, unique: true })
|
||||
ID: string;
|
||||
|
||||
@Prop()
|
||||
Name: string;
|
||||
|
||||
@Prop()
|
||||
Description: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'ShopCategory' })
|
||||
ShopCategory: Types.ObjectId;
|
||||
|
||||
@Prop({ type: [{ type: Types.ObjectId, ref: 'Tag' }] })
|
||||
Tags: Types.ObjectId[];
|
||||
|
||||
@Prop()
|
||||
Status: string;
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: 'Language' })
|
||||
Language: Types.ObjectId;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
createdAt: Date;
|
||||
|
||||
@Prop({ type: Date, default: Date.now })
|
||||
updatedAt: Date;
|
||||
}
|
||||
export const ShopCategoryDocumentSchema = SchemaFactory.createForClass(ShopCategoryDocument);
|
||||
ShopCategoryDocumentSchema.index({ID:1})
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["node"],
|
||||
"emitDecoratorMetadata": true,
|
||||
"target": "es2021",
|
||||
|
||||
},
|
||||
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
|
||||
const { join } = require('path');
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
path: join(__dirname, '../dist/catalog-service'),
|
||||
},
|
||||
plugins: [
|
||||
new NxAppWebpackPlugin({
|
||||
target: 'node',
|
||||
compiler: 'tsc',
|
||||
main: './src/main.ts',
|
||||
tsConfig: './tsconfig.app.json',
|
||||
assets: ['./src/assets'],
|
||||
optimization: false,
|
||||
outputHashing: 'none',
|
||||
generatePackageJson: true,
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
const baseConfig = require('../eslint.config.js');
|
||||
|
||||
module.exports = [...baseConfig];
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
export default {
|
||||
displayName: 'discount-service',
|
||||
preset: '../jest.preset.js',
|
||||
testEnvironment: 'node',
|
||||
transform: {
|
||||
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageDirectory: '../coverage/discount-service',
|
||||
};
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "discount-service",
|
||||
"$schema": "../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "discount-service/src",
|
||||
"projectType": "application",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"serve": {
|
||||
"executor": "@nx/js:node",
|
||||
"defaultConfiguration": "development",
|
||||
"dependsOn": ["build"],
|
||||
"options": {
|
||||
"buildTarget": "discount-service:build",
|
||||
"runBuildTargetDependencies": false
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "discount-service:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "discount-service:build:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppController', () => {
|
||||
let app: TestingModule;
|
||||
|
||||
beforeAll(async () => {
|
||||
app = await Test.createTestingModule({
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
});
|
||||
|
||||
describe('getData', () => {
|
||||
it('should return "Hello API"', () => {
|
||||
const appController = app.get<AppController>(AppController);
|
||||
expect(appController.getData()).toEqual({ message: 'Hello API' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { Controller, Get } from '@nestjs/common';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get()
|
||||
getData() {
|
||||
return this.appService.getData();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { Test } from '@nestjs/testing';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppService', () => {
|
||||
let service: AppService;
|
||||
|
||||
beforeAll(async () => {
|
||||
const app = await Test.createTestingModule({
|
||||
providers: [AppService],
|
||||
}).compile();
|
||||
|
||||
service = app.get<AppService>(AppService);
|
||||
});
|
||||
|
||||
describe('getData', () => {
|
||||
it('should return "Hello API"', () => {
|
||||
expect(service.getData()).toEqual({ message: 'Hello API' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getData(): { message: string } {
|
||||
return { message: 'Hello API' };
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue