├── gateway ├── src │ ├── apps │ │ ├── dto │ │ │ ├── create-app.dto.ts │ │ │ └── update-app.dto.ts │ │ ├── apps.service.ts │ │ ├── apps.module.ts │ │ ├── apps.service.spec.ts │ │ ├── apps.controller.spec.ts │ │ ├── entities │ │ │ └── app.entity.ts │ │ └── apps.controller.ts │ ├── forms │ │ ├── dto │ │ │ ├── create-form.dto.ts │ │ │ └── update-form.dto.ts │ │ ├── entities │ │ │ └── form.entity.ts │ │ ├── forms.service.ts │ │ ├── forms.module.ts │ │ ├── forms.service.spec.ts │ │ ├── forms.controller.spec.ts │ │ └── forms.controller.ts │ ├── plans │ │ ├── dto │ │ │ ├── create-plan.dto.ts │ │ │ └── update-plan.dto.ts │ │ ├── entities │ │ │ └── plan.entity.ts │ │ ├── plans.service.ts │ │ ├── apps.module.ts │ │ ├── apps.service.spec.ts │ │ ├── plans.controller.spec.ts │ │ └── plans.controller.ts │ ├── roles │ │ ├── dto │ │ │ ├── create-role.dto.ts │ │ │ └── update-role.dto.ts │ │ ├── roles.service.ts │ │ ├── entities │ │ │ └── role.entity.ts │ │ ├── roles.module.ts │ │ ├── roles.service.spec.ts │ │ ├── roles.controller.spec.ts │ │ └── roles.controller.ts │ ├── accounts │ │ ├── dto │ │ │ ├── create-account.dto.ts │ │ │ ├── create-accountRef.dto.ts │ │ │ └── update-account.dto.ts │ │ ├── accounts.service.ts │ │ ├── accounts.module.ts │ │ ├── accounts.service.spec.ts │ │ ├── accounts.controller.spec.ts │ │ ├── entities │ │ │ └── account.entity.ts │ │ └── accounts.controller.ts │ ├── features │ │ ├── dto │ │ │ ├── create-feature.dto.ts │ │ │ └── update-feature.dto.ts │ │ ├── entities │ │ │ └── feature.entity.ts │ │ ├── features.service.ts │ │ ├── features.module.ts │ │ ├── features.service.spec.ts │ │ ├── features.controller.spec.ts │ │ └── features.controller.ts │ ├── userRoles │ │ ├── dto │ │ │ ├── create-userRole.dto.ts │ │ │ └── update-userRoles.dto.ts │ │ ├── userRoles.service.ts │ │ ├── userRoles.module.ts │ │ ├── userRoles.service.spec.ts │ │ ├── entities │ │ │ └── userRole.entity.ts │ │ ├── userRoles.controller.spec.ts │ │ └── userRoles.controller.ts │ ├── auth │ │ ├── dto │ │ │ └── register.dto.ts │ │ ├── guards │ │ │ ├── jwt-auth.guard.ts │ │ │ ├── local-auth.guard.ts │ │ │ ├── email-confirmation.guard.ts │ │ │ └── auth.guard.ts │ │ ├── roles.decorator.ts │ │ ├── strategies │ │ │ ├── local.strategy.ts │ │ │ └── jwt.strategy.ts │ │ ├── roles.guard.ts │ │ ├── auth.module.ts │ │ ├── auth.service.spec.ts │ │ ├── auth.controller.ts │ │ └── auth.service.ts │ ├── app.service.ts │ ├── users │ │ ├── dto │ │ │ └── user-create.dto.ts │ │ ├── users.module.ts │ │ ├── users.service.spec.ts │ │ ├── users.controller.ts │ │ ├── users.service.ts │ │ ├── user.subscriber.ts │ │ └── entities │ │ │ └── user.entity.ts │ ├── admin │ │ ├── navigation.ts │ │ ├── resources │ │ │ ├── account.resource.ts │ │ │ ├── webhook.resource.ts │ │ │ ├── apps.resource.ts │ │ │ └── user.resource.ts │ │ ├── frontend │ │ │ └── home.tsx │ │ └── admin.module.ts │ ├── webhooks │ │ ├── dto │ │ │ └── create-webhook.dto.ts │ │ ├── events │ │ │ └── webhook.event.ts │ │ ├── webhooks.service.spec.ts │ │ ├── webhooks.module.ts │ │ ├── webhooks.controller.spec.ts │ │ ├── entities │ │ │ └── webhook.entity.ts │ │ ├── webhooks.service.ts │ │ └── webhooks.controller.ts │ ├── app.controller.ts │ ├── health │ │ ├── health.module.ts │ │ └── health.controller.ts │ ├── chat │ │ └── chat.gateway.ts │ ├── app.controller.spec.ts │ ├── sentiment │ │ ├── sentiment.controller.ts │ │ ├── sentiment.module.ts │ │ └── dto │ │ │ └── sentiment.dto.ts │ ├── database │ │ └── database.module.ts │ ├── main.ts │ └── app.module.ts ├── .prettierrc ├── tsconfig.build.json ├── Dockerfile ├── test │ ├── jest-e2e.json │ └── app.e2e-spec.ts ├── nest-cli.json ├── tsconfig.json ├── .gitignore ├── .eslintrc ├── views │ └── health.hbs ├── README.md └── package.json ├── mailer ├── .prettierrc ├── tsconfig.build.json ├── Dockerfile ├── test │ ├── jest-e2e.json │ └── app.e2e-spec.ts ├── nest-cli.json ├── tsconfig.json ├── .gitignore ├── src │ ├── main.ts │ ├── mailer.controller.spec.ts │ ├── mailer.module.ts │ └── mailer.controller.ts ├── .eslintrc ├── templates │ └── confirm-account.mjml ├── package.json └── README.md ├── scheduler ├── .prettierrc ├── nest-cli.json ├── tsconfig.build.json ├── Dockerfile ├── test │ ├── jest-e2e.json │ └── app.e2e-spec.ts ├── src │ ├── scheduler.controller.ts │ ├── scheduler.module.ts │ ├── main.ts │ └── scheduler.controller.spec.ts ├── tsconfig.json ├── .gitignore ├── .eslintrc ├── package.json └── README.md ├── sentiment ├── .prettierrc ├── nest-cli.json ├── tsconfig.build.json ├── Dockerfile ├── test │ ├── jest-e2e.json │ └── app.e2e-spec.ts ├── src │ ├── sentiment.service.ts │ ├── sentiment.controller.ts │ ├── sentiment.module.ts │ ├── main.ts │ └── sentiment.controller.spec.ts ├── tsconfig.json ├── .gitignore ├── .eslintrc ├── package.json └── README.md ├── workspace.code-workspace ├── README.md ├── docker-compose.yaml └── .env-template /gateway/src/apps/dto/create-app.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateAppDto {} 2 | -------------------------------------------------------------------------------- /gateway/src/forms/dto/create-form.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateFormDto {} 2 | -------------------------------------------------------------------------------- /gateway/src/plans/dto/create-plan.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreatePlanDto {} 2 | -------------------------------------------------------------------------------- /gateway/src/roles/dto/create-role.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateRoleDto {} 2 | -------------------------------------------------------------------------------- /gateway/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /gateway/src/accounts/dto/create-account.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateAccountDto {} 2 | -------------------------------------------------------------------------------- /gateway/src/features/dto/create-feature.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateFeatureDto {} 2 | -------------------------------------------------------------------------------- /gateway/src/userRoles/dto/create-userRole.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateRoleDto {} 2 | -------------------------------------------------------------------------------- /mailer/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /scheduler/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /sentiment/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /scheduler/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /sentiment/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /gateway/src/auth/dto/register.dto.ts: -------------------------------------------------------------------------------- 1 | export class RegisterDto { 2 | email: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /gateway/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService {} 5 | -------------------------------------------------------------------------------- /gateway/src/accounts/dto/create-accountRef.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateAccountRefDto { 2 | name: string; 3 | description: string; 4 | } 5 | -------------------------------------------------------------------------------- /gateway/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /mailer/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /scheduler/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /sentiment/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /mailer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine3.11 2 | RUN mkdir -p /mailer 3 | WORKDIR /mailer 4 | ADD . /mailer 5 | RUN yarn 6 | CMD yarn start:dev 7 | -------------------------------------------------------------------------------- /scheduler/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine3.11 2 | RUN mkdir -p /mailer 3 | WORKDIR /mailer 4 | ADD . /mailer 5 | RUN yarn 6 | CMD yarn start:dev 7 | -------------------------------------------------------------------------------- /sentiment/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine3.11 2 | RUN mkdir -p /mailer 3 | WORKDIR /mailer 4 | ADD . /mailer 5 | RUN yarn 6 | CMD yarn start:dev 7 | -------------------------------------------------------------------------------- /gateway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine3.11 2 | RUN mkdir -p /gateway 3 | WORKDIR /gateway 4 | ADD . /gateway 5 | RUN yarn 6 | CMD yarn start:dev 7 | -------------------------------------------------------------------------------- /gateway/src/users/dto/user-create.dto.ts: -------------------------------------------------------------------------------- 1 | export class UserCreateDto { 2 | email: string; 3 | password: string; 4 | username?: string; 5 | } 6 | -------------------------------------------------------------------------------- /gateway/src/admin/navigation.ts: -------------------------------------------------------------------------------- 1 | export const mainNav = { 2 | name: 'Global', 3 | }; 4 | 5 | export const devNav = { 6 | name: 'Developers', 7 | icon: 'Code', 8 | }; 9 | -------------------------------------------------------------------------------- /gateway/src/webhooks/dto/create-webhook.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateWebhookDto { 2 | id?: number; 3 | eventName: string; 4 | callbackUrl: string; 5 | accountId: number; 6 | } 7 | -------------------------------------------------------------------------------- /gateway/src/auth/guards/jwt-auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { AuthGuard } from '@nestjs/passport'; 3 | 4 | @Injectable() 5 | export class JwtAuthGuard extends AuthGuard('jwt') {} 6 | -------------------------------------------------------------------------------- /gateway/src/apps/dto/update-app.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | 3 | import { CreateAppDto } from './create-app.dto'; 4 | 5 | export class UpdateAppDto extends PartialType(CreateAppDto) {} 6 | -------------------------------------------------------------------------------- /gateway/src/auth/guards/local-auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { AuthGuard } from '@nestjs/passport'; 3 | 4 | @Injectable() 5 | export class LocalAuthGuard extends AuthGuard('local') {} 6 | -------------------------------------------------------------------------------- /gateway/src/roles/dto/update-role.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/swagger'; 2 | 3 | import { CreateRoleDto } from './create-role.dto'; 4 | 5 | export class UpdateRoleDto extends PartialType(CreateRoleDto) {} 6 | -------------------------------------------------------------------------------- /gateway/src/forms/dto/update-form.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | 3 | import { CreateFormDto } from './create-form.dto'; 4 | 5 | export class UpdateFormDto extends PartialType(CreateFormDto) {} 6 | -------------------------------------------------------------------------------- /gateway/src/plans/dto/update-plan.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | 3 | import { CreatePlanDto } from './create-plan.dto'; 4 | 5 | export class UpdatePlanDto extends PartialType(CreatePlanDto) {} 6 | -------------------------------------------------------------------------------- /gateway/src/userRoles/dto/update-userRoles.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/swagger'; 2 | 3 | import { CreateRoleDto } from './create-userRole.dto'; 4 | 5 | export class UpdateRoleDto extends PartialType(CreateRoleDto) {} 6 | -------------------------------------------------------------------------------- /gateway/src/features/entities/feature.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, BaseEntity } from 'typeorm'; 2 | 3 | @Entity() 4 | export class Feature extends BaseEntity { 5 | @PrimaryGeneratedColumn() 6 | id: number; 7 | } 8 | -------------------------------------------------------------------------------- /gateway/src/accounts/dto/update-account.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | 3 | import { CreateAccountDto } from './create-account.dto'; 4 | 5 | export class UpdateAccountDto extends PartialType(CreateAccountDto) {} 6 | -------------------------------------------------------------------------------- /gateway/src/features/dto/update-feature.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | 3 | import { CreateFeatureDto } from './create-feature.dto'; 4 | 5 | export class UpdateFeatureDto extends PartialType(CreateFeatureDto) {} 6 | -------------------------------------------------------------------------------- /gateway/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common'; 2 | 3 | import { AppService } from './app.service'; 4 | 5 | @Controller() 6 | export class AppController { 7 | constructor(private readonly appService: AppService) {} 8 | } 9 | -------------------------------------------------------------------------------- /gateway/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 | -------------------------------------------------------------------------------- /mailer/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 | -------------------------------------------------------------------------------- /scheduler/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 | -------------------------------------------------------------------------------- /sentiment/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 | -------------------------------------------------------------------------------- /workspace.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "gateway" 5 | }, 6 | { 7 | "path": "mailer" 8 | }, 9 | { 10 | "path": "scheduler" 11 | }, 12 | { 13 | "path": "sentiment" 14 | } 15 | ], 16 | "settings": {} 17 | } 18 | -------------------------------------------------------------------------------- /mailer/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src", 4 | "compilerOptions": { 5 | "assets": [{ 6 | "include": "templates/**/*.hbs", 7 | "outDir": "dist" 8 | }], 9 | "watchAssets": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /gateway/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src", 4 | "compilerOptions": { 5 | "plugins": [{ 6 | "name": "@nestjs/swagger", 7 | "options": { 8 | "introspectComments": true 9 | } 10 | }] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /sentiment/src/sentiment.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import * as sentiment from 'multilang-sentiment'; 3 | 4 | @Injectable() 5 | export class SentimentService { 6 | analyse(sentence: string, lang = 'en') { 7 | return sentiment(sentence, lang); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /gateway/src/webhooks/events/webhook.event.ts: -------------------------------------------------------------------------------- 1 | export class WebhookEvent { 2 | name: string; 3 | accountId: number; 4 | payload: Record; 5 | 6 | constructor(params: WebhookEvent) { 7 | this.name = params.name; 8 | this.accountId = params.accountId; 9 | this.payload = params.payload; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scheduler/src/scheduler.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common'; 2 | import { MessagePattern } from '@nestjs/microservices'; 3 | 4 | @Controller() 5 | export class SchedulerController { 6 | constructor( 7 | 8 | ) {} 9 | 10 | @MessagePattern('scheduler.execTask') 11 | execTask() { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /gateway/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TerminusModule } from '@nestjs/terminus'; 3 | 4 | import { HealthController } from './health.controller'; 5 | 6 | @Module({ 7 | imports: [TerminusModule], 8 | controllers: [HealthController], 9 | providers: [], 10 | }) 11 | export class HealthModule {} 12 | -------------------------------------------------------------------------------- /gateway/src/plans/entities/plan.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, Column, PrimaryGeneratedColumn, BaseEntity } from 'typeorm'; 2 | 3 | @Entity() 4 | export class Plan extends BaseEntity { 5 | @PrimaryGeneratedColumn() 6 | id: number; 7 | 8 | @Column() 9 | name: string; 10 | 11 | // @OneToMany(() => Account, account => account.plan) 12 | // account: number; 13 | } 14 | -------------------------------------------------------------------------------- /gateway/src/admin/resources/account.resource.ts: -------------------------------------------------------------------------------- 1 | import { ResourceWithOptions } from 'adminjs'; 2 | 3 | import { Account } from '@/accounts/entities/account.entity'; 4 | 5 | import { mainNav } from '../navigation'; 6 | 7 | const AccountResource: ResourceWithOptions = { 8 | resource: Account, 9 | options: { 10 | navigation: mainNav, 11 | }, 12 | }; 13 | 14 | export default AccountResource; 15 | -------------------------------------------------------------------------------- /gateway/src/admin/resources/webhook.resource.ts: -------------------------------------------------------------------------------- 1 | import { ResourceWithOptions } from 'adminjs'; 2 | 3 | import { Webhook } from '@/webhooks/entities/webhook.entity'; 4 | 5 | import { devNav } from '../navigation'; 6 | 7 | const WebhookResource: ResourceWithOptions = { 8 | resource: Webhook, 9 | options: { 10 | navigation: devNav, 11 | }, 12 | }; 13 | 14 | export default WebhookResource; 15 | -------------------------------------------------------------------------------- /gateway/src/auth/roles.decorator.ts: -------------------------------------------------------------------------------- 1 | import { SetMetadata, applyDecorators, UseGuards } from '@nestjs/common'; 2 | 3 | import { JwtAuthGuard } from './guards/jwt-auth.guard'; 4 | import { RolesGuard } from './roles.guard'; 5 | 6 | export function Roles(...roles: string[]) { 7 | return applyDecorators( 8 | SetMetadata('roles', roles), 9 | UseGuards(JwtAuthGuard, RolesGuard), 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /gateway/src/apps/apps.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { TypeOrmCrudService } from '@nestjsx/crud-typeorm'; 4 | 5 | import { App } from './entities/app.entity'; 6 | 7 | @Injectable() 8 | export class AppsService extends TypeOrmCrudService { 9 | constructor(@InjectRepository(App) repo) { 10 | super(repo); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gateway/src/forms/entities/form.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | Column, 4 | PrimaryGeneratedColumn, 5 | BaseEntity, 6 | } from 'typeorm'; 7 | 8 | @Entity() 9 | export class Form extends BaseEntity { 10 | @PrimaryGeneratedColumn() 11 | id: number; 12 | 13 | @Column() 14 | name: string; 15 | 16 | @Column() 17 | submitUrl: string; 18 | 19 | @Column({ type: 'jsonb' }) 20 | schema: any; 21 | } 22 | -------------------------------------------------------------------------------- /mailer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /scheduler/src/scheduler.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule } from '@nestjs/config'; 3 | import { SchedulerController } from './scheduler.controller'; 4 | 5 | @Module({ 6 | imports: [ 7 | ConfigModule.forRoot({ 8 | isGlobal: true, 9 | envFilePath: `../.env`, 10 | }), 11 | ], 12 | controllers: [SchedulerController], 13 | }) 14 | export class AppModule {} 15 | -------------------------------------------------------------------------------- /scheduler/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sentiment/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /gateway/src/forms/forms.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { TypeOrmCrudService } from '@nestjsx/crud-typeorm'; 4 | 5 | import { Form } from './entities/form.entity'; 6 | 7 | @Injectable() 8 | export class FormsService extends TypeOrmCrudService
{ 9 | constructor(@InjectRepository(Form) repo) { 10 | super(repo); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gateway/src/plans/plans.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { TypeOrmCrudService } from '@nestjsx/crud-typeorm'; 4 | 5 | import { Plan } from './entities/plan.entity'; 6 | 7 | @Injectable() 8 | export class PlansService extends TypeOrmCrudService { 9 | constructor(@InjectRepository(Plan) repo) { 10 | super(repo); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gateway/src/roles/roles.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { TypeOrmCrudService } from '@nestjsx/crud-typeorm'; 4 | 5 | import { Role } from '@/roles/entities/Role.entity'; 6 | 7 | @Injectable() 8 | export class RolesService extends TypeOrmCrudService { 9 | constructor(@InjectRepository(Role) repo) { 10 | super(repo); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gateway/src/accounts/accounts.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { TypeOrmCrudService } from '@nestjsx/crud-typeorm'; 4 | 5 | import { Account } from './entities/account.entity'; 6 | 7 | @Injectable() 8 | export class AccountsService extends TypeOrmCrudService { 9 | constructor(@InjectRepository(Account) repo) { 10 | super(repo); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gateway/src/features/features.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { TypeOrmCrudService } from '@nestjsx/crud-typeorm'; 4 | 5 | import { Feature } from './entities/feature.entity'; 6 | 7 | @Injectable() 8 | export class FeaturesService extends TypeOrmCrudService { 9 | constructor(@InjectRepository(Feature) repo) { 10 | super(repo); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gateway/src/userRoles/userRoles.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { TypeOrmCrudService } from '@nestjsx/crud-typeorm'; 4 | 5 | import { UserRole } from './entities/userRole.entity'; 6 | 7 | @Injectable() 8 | export class UserRolesService extends TypeOrmCrudService { 9 | constructor(@InjectRepository(UserRole) repo) { 10 | super(repo); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gateway/src/admin/resources/apps.resource.ts: -------------------------------------------------------------------------------- 1 | import { ResourceWithOptions } from 'adminjs'; 2 | 3 | import { App } from '@/apps/entities/app.entity'; 4 | 5 | import { devNav } from '../navigation'; 6 | 7 | const AppsResource: ResourceWithOptions = { 8 | resource: App, 9 | options: { 10 | navigation: devNav, 11 | properties: { 12 | clientId: { 13 | isVisible: false, 14 | }, 15 | }, 16 | }, 17 | }; 18 | 19 | export default AppsResource; 20 | -------------------------------------------------------------------------------- /gateway/src/chat/chat.gateway.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MessageBody, 3 | SubscribeMessage, 4 | WebSocketGateway, 5 | WebSocketServer, 6 | } from '@nestjs/websockets'; 7 | import { Server } from 'socket.io'; 8 | 9 | @WebSocketGateway() 10 | export class ChatGateway { 11 | @WebSocketServer() 12 | server: Server; 13 | 14 | @SubscribeMessage('send_message') 15 | listenForMessages(@MessageBody() data: string) { 16 | this.server.sockets.emit('receive_message', data); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sentiment/src/sentiment.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common'; 2 | import { MessagePattern } from '@nestjs/microservices'; 3 | import { SentimentService } from './sentiment.service'; 4 | 5 | @Controller() 6 | export class SentimentController { 7 | constructor(private readonly sentimentService: SentimentService) {} 8 | 9 | @MessagePattern('sentiment.analyse') 10 | analyse({ sentence, lang }) { 11 | return this.sentimentService.analyse(sentence, lang); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gateway/src/roles/entities/role.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseEntity, 3 | Column, 4 | Entity, 5 | OneToMany, 6 | PrimaryGeneratedColumn, 7 | } from 'typeorm'; 8 | 9 | import { UserRole } from '@/userRoles/entities/userRole.entity'; 10 | 11 | @Entity() 12 | export class Role extends BaseEntity { 13 | @PrimaryGeneratedColumn() 14 | id: number; 15 | 16 | @Column() 17 | name: string; 18 | 19 | @OneToMany(() => UserRole, (userRole) => userRole.role) 20 | userRole: UserRole[]; 21 | } 22 | -------------------------------------------------------------------------------- /gateway/src/accounts/accounts.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | 4 | import { AccountsController } from './accounts.controller'; 5 | import { AccountsService } from './accounts.service'; 6 | import { Account } from './entities/account.entity'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([Account])], 10 | controllers: [AccountsController], 11 | providers: [AccountsService], 12 | }) 13 | export class AccountsModule {} 14 | -------------------------------------------------------------------------------- /gateway/src/apps/apps.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | 4 | import { AppsController } from './apps.controller'; 5 | import { AppsService } from './apps.service'; 6 | import { App } from './entities/app.entity'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([App])], 10 | controllers: [AppsController], 11 | providers: [AppsService], 12 | exports: [TypeOrmModule, AppsService], 13 | }) 14 | export class AppsModule {} 15 | -------------------------------------------------------------------------------- /gateway/src/roles/roles.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | 4 | import { Role } from './entities/role.entity'; 5 | import { RolesController } from './roles.controller'; 6 | import { RolesService } from './roles.service'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([Role])], 10 | providers: [RolesService], 11 | exports: [TypeOrmModule], 12 | controllers: [RolesController], 13 | }) 14 | export class RolesModule {} 15 | -------------------------------------------------------------------------------- /gateway/src/forms/forms.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | 4 | import { FormsController } from './forms.controller'; 5 | import { FormsService } from './forms.service'; 6 | import { Form } from './entities/form.entity'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([Form])], 10 | controllers: [FormsController], 11 | providers: [FormsService], 12 | exports: [TypeOrmModule, FormsService], 13 | }) 14 | export class FormsModule {} 15 | -------------------------------------------------------------------------------- /gateway/src/plans/apps.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | 4 | import { Plan } from './entities/plan.entity'; 5 | import { PlansController } from './plans.controller'; 6 | import { PlansService } from './plans.service'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([Plan])], 10 | controllers: [PlansController], 11 | providers: [PlansService], 12 | exports: [TypeOrmModule, PlansService], 13 | }) 14 | export class AppsModule {} 15 | -------------------------------------------------------------------------------- /sentiment/src/sentiment.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule } from '@nestjs/config'; 3 | import { SentimentController } from './sentiment.controller'; 4 | import { SentimentService } from './sentiment.service'; 5 | 6 | @Module({ 7 | imports: [ 8 | ConfigModule.forRoot({ 9 | isGlobal: true, 10 | envFilePath: `../.env`, 11 | }), 12 | ], 13 | controllers: [SentimentController], 14 | providers: [SentimentService], 15 | }) 16 | export class AppModule {} 17 | -------------------------------------------------------------------------------- /gateway/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "jsx": "preserve", 16 | "paths": { 17 | "@/*": ["src/*"] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /mailer/.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 -------------------------------------------------------------------------------- /gateway/.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 -------------------------------------------------------------------------------- /scheduler/.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 -------------------------------------------------------------------------------- /sentiment/.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 -------------------------------------------------------------------------------- /gateway/src/auth/guards/email-confirmation.guard.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | CanActivate, 4 | ExecutionContext, 5 | UnauthorizedException, 6 | } from '@nestjs/common'; 7 | 8 | @Injectable() 9 | export class EmailConfirmationGuard implements CanActivate { 10 | canActivate(context: ExecutionContext) { 11 | const request = context.switchToHttp().getRequest(); 12 | 13 | if (!request.user?.isEmailConfirmed) { 14 | throw new UnauthorizedException('Confirm your email first'); 15 | } 16 | 17 | return true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gateway/src/userRoles/userRoles.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | 4 | import { UserRole } from './entities/userRole.entity'; 5 | import { UserRolesController } from './userRoles.controller'; 6 | import { UserRolesService } from './userRoles.service'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([UserRole])], 10 | providers: [UserRolesService], 11 | exports: [TypeOrmModule], 12 | controllers: [UserRolesController], 13 | }) 14 | export class UserRolesModule {} 15 | -------------------------------------------------------------------------------- /gateway/src/features/features.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | 4 | import { Feature } from './entities/feature.entity'; 5 | import { FeaturesController } from './features.controller'; 6 | import { FeaturesService } from './features.service'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([Feature])], 10 | controllers: [FeaturesController], 11 | providers: [FeaturesService], 12 | exports: [TypeOrmModule, FeaturesService], 13 | }) 14 | export class FeaturesModule {} 15 | -------------------------------------------------------------------------------- /gateway/src/apps/apps.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { AppsService } from './apps.service'; 4 | 5 | describe('AppsService', () => { 6 | let service: AppsService; 7 | 8 | beforeEach(async () => { 9 | const module: TestingModule = await Test.createTestingModule({ 10 | providers: [AppsService], 11 | }).compile(); 12 | 13 | service = module.get(AppsService); 14 | }); 15 | 16 | it('should be defined', () => { 17 | expect(service).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /mailer/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { RmqOptions, Transport } from '@nestjs/microservices'; 3 | import { AppModule } from './mailer.module'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.createMicroservice(AppModule, { 7 | transport: Transport.RMQ, 8 | options: { 9 | urls: [process.env.RMQ_URL], 10 | queue: 'mailer_queue', 11 | queueOptions: { 12 | durable: false, 13 | }, 14 | }, 15 | }); 16 | 17 | app.listen(); 18 | } 19 | bootstrap(); 20 | -------------------------------------------------------------------------------- /gateway/src/plans/apps.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { PlansService } from './plans.service'; 4 | 5 | describe('PlansService', () => { 6 | let service: PlansService; 7 | 8 | beforeEach(async () => { 9 | const module: TestingModule = await Test.createTestingModule({ 10 | providers: [PlansService], 11 | }).compile(); 12 | 13 | service = module.get(PlansService); 14 | }); 15 | 16 | it('should be defined', () => { 17 | expect(service).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /gateway/src/forms/forms.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { FormsService } from './forms.service'; 4 | 5 | describe('FormsService', () => { 6 | let service: FormsService; 7 | 8 | beforeEach(async () => { 9 | const module: TestingModule = await Test.createTestingModule({ 10 | providers: [FormsService], 11 | }).compile(); 12 | 13 | service = module.get(FormsService); 14 | }); 15 | 16 | it('should be defined', () => { 17 | expect(service).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /gateway/src/roles/roles.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { RolesService } from './roles.service'; 4 | 5 | describe('RolesService', () => { 6 | let service: RolesService; 7 | 8 | beforeEach(async () => { 9 | const module: TestingModule = await Test.createTestingModule({ 10 | providers: [RolesService], 11 | }).compile(); 12 | 13 | service = module.get(RolesService); 14 | }); 15 | 16 | it('should be defined', () => { 17 | expect(service).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /scheduler/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { RmqOptions, Transport } from '@nestjs/microservices'; 3 | import { AppModule } from './scheduler.module'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.createMicroservice(AppModule, { 7 | transport: Transport.RMQ, 8 | options: { 9 | urls: [process.env.RMQ_URL], 10 | queue: 'scheduler_queue', 11 | queueOptions: { 12 | durable: false, 13 | }, 14 | }, 15 | }); 16 | 17 | app.listen(); 18 | } 19 | bootstrap(); 20 | -------------------------------------------------------------------------------- /sentiment/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { RmqOptions, Transport } from '@nestjs/microservices'; 3 | import { AppModule } from './sentiment.module'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.createMicroservice(AppModule, { 7 | transport: Transport.RMQ, 8 | options: { 9 | urls: [process.env.RMQ_URL], 10 | queue: 'sentiment_queue', 11 | queueOptions: { 12 | durable: false, 13 | }, 14 | }, 15 | }); 16 | 17 | app.listen(); 18 | } 19 | bootstrap(); 20 | -------------------------------------------------------------------------------- /gateway/src/users/users.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | 4 | import { User } from './entities/user.entity'; 5 | import { UserSubscriber } from './user.subscriber'; 6 | import { UsersController } from './users.controller'; 7 | import { UsersService } from './users.service'; 8 | 9 | @Module({ 10 | imports: [TypeOrmModule.forFeature([User])], 11 | providers: [UsersService, UserSubscriber], 12 | exports: [TypeOrmModule, UsersService], 13 | controllers: [UsersController], 14 | }) 15 | export class UsersModule {} 16 | -------------------------------------------------------------------------------- /gateway/src/accounts/accounts.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { AccountsService } from './accounts.service'; 4 | 5 | describe('AccountsService', () => { 6 | let service: AccountsService; 7 | 8 | beforeEach(async () => { 9 | const module: TestingModule = await Test.createTestingModule({ 10 | providers: [AccountsService], 11 | }).compile(); 12 | 13 | service = module.get(AccountsService); 14 | }); 15 | 16 | it('should be defined', () => { 17 | expect(service).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /gateway/src/features/features.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { FeaturesService } from './features.service'; 4 | 5 | describe('FeaturesService', () => { 6 | let service: FeaturesService; 7 | 8 | beforeEach(async () => { 9 | const module: TestingModule = await Test.createTestingModule({ 10 | providers: [FeaturesService], 11 | }).compile(); 12 | 13 | service = module.get(FeaturesService); 14 | }); 15 | 16 | it('should be defined', () => { 17 | expect(service).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /gateway/src/webhooks/webhooks.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { WebhooksService } from './webhooks.service'; 4 | 5 | describe('WebhooksService', () => { 6 | let service: WebhooksService; 7 | 8 | beforeEach(async () => { 9 | const module: TestingModule = await Test.createTestingModule({ 10 | providers: [WebhooksService], 11 | }).compile(); 12 | 13 | service = module.get(WebhooksService); 14 | }); 15 | 16 | it('should be defined', () => { 17 | expect(service).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /gateway/src/userRoles/userRoles.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { UserRolesService } from './userRoles.service'; 4 | 5 | describe('UserRolesService', () => { 6 | let service: UserRolesService; 7 | 8 | beforeEach(async () => { 9 | const module: TestingModule = await Test.createTestingModule({ 10 | providers: [UserRolesService], 11 | }).compile(); 12 | 13 | service = module.get(UserRolesService); 14 | }); 15 | 16 | it('should be defined', () => { 17 | expect(service).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /gateway/src/webhooks/webhooks.module.ts: -------------------------------------------------------------------------------- 1 | import { HttpModule } from '@nestjs/axios'; 2 | import { Module } from '@nestjs/common'; 3 | import { TypeOrmModule } from '@nestjs/typeorm'; 4 | 5 | import { Webhook } from './entities/webhook.entity'; 6 | import { WebhooksController } from './webhooks.controller'; 7 | import { WebhooksService } from './webhooks.service'; 8 | 9 | @Module({ 10 | imports: [HttpModule, TypeOrmModule.forFeature([Webhook])], 11 | exports: [TypeOrmModule, WebhooksService], 12 | controllers: [WebhooksController], 13 | providers: [WebhooksService], 14 | }) 15 | export class WebhooksModule {} 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NestJS Microservices Boilerplate 2 | 3 | ## Dev Env 4 | 5 | Duplicate `.env-template` and rename it `.env` 6 | 7 | `yarn` in each folder then `yarn start:dev` in each folder. 8 | 9 | - API gateway: `localhost:3000` 10 | - API documentation: `localhost:3000/api` 11 | - Mailer microservice: `localhost:8004` 12 | - Scheduler microservice: `localhost:8005` 13 | 14 | For a correct lint and typecheck, open `workspace.code-workspace` in VSCode. 15 | 16 | ## Gateway 17 | 18 | - Auth 19 | - User 20 | - Account 21 | - Features 22 | - Plans 23 | - Roles 24 | - Chat 25 | - Notification 26 | - App 27 | - Webhook 28 | - Health 29 | -------------------------------------------------------------------------------- /gateway/src/admin/frontend/home.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@adminjs/design-system'; 2 | import { ApiClient } from 'adminjs'; 3 | import React, { useState, useEffect } from 'react'; 4 | 5 | const api = new ApiClient(); 6 | 7 | const Dashboard = () => { 8 | const [data, setData] = useState({}); 9 | 10 | useEffect(() => { 11 | api.getDashboard().then((response) => { 12 | console.log(response.data); 13 | setData(response.data); 14 | }); 15 | }, []); 16 | 17 | return ( 18 | 19 | some dashboard 20 | 21 | ); 22 | }; 23 | 24 | export default Dashboard; 25 | -------------------------------------------------------------------------------- /gateway/src/userRoles/entities/userRole.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseEntity, 3 | ManyToOne, 4 | PrimaryGeneratedColumn, 5 | OneToOne, 6 | Entity, 7 | Column, 8 | JoinColumn, 9 | } from 'typeorm'; 10 | 11 | import { Role } from '@/roles/entities/role.entity'; 12 | import { User } from '@/users/entities/user.entity'; 13 | 14 | @Entity() 15 | export class UserRole extends BaseEntity { 16 | @PrimaryGeneratedColumn() 17 | id: number; 18 | 19 | // @Column() 20 | // roleId: number; 21 | 22 | @ManyToOne(() => Role, (role) => role.userRole) 23 | role: Role; 24 | 25 | @ManyToOne(() => User, (user) => user.roles) 26 | user: User; 27 | } 28 | -------------------------------------------------------------------------------- /gateway/src/apps/apps.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { AppsController } from './apps.controller'; 4 | import { AppsService } from './apps.service'; 5 | 6 | describe('AppsController', () => { 7 | let controller: AppsController; 8 | 9 | beforeEach(async () => { 10 | const module: TestingModule = await Test.createTestingModule({ 11 | controllers: [AppsController], 12 | providers: [AppsService], 13 | }).compile(); 14 | 15 | controller = module.get(AppsController); 16 | }); 17 | 18 | it('should be defined', () => { 19 | expect(controller).toBeDefined(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | redis: 5 | container_name: redis 6 | image: redis:5 7 | networks: 8 | - webnet 9 | ports: 10 | - 6379:6379 11 | postgres: 12 | container_name: postgres 13 | image: postgres:12 14 | networks: 15 | - webnet 16 | environment: 17 | POSTGRES_PASSWORD: ${DB_PASSWORD} 18 | POSTGRES_USER: ${DB_USERNAME} 19 | POSTGRES_DB: ${DB_DATABASE_NAME} 20 | PG_DATA: /var/lib/postgresql/data 21 | ports: 22 | - 5432:5432 23 | volumes: 24 | - pgdata:/var/lib/postgresql/data 25 | networks: 26 | webnet: 27 | volumes: 28 | pgdata: 29 | node_modules: 30 | -------------------------------------------------------------------------------- /gateway/src/forms/forms.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { FormsController } from './forms.controller'; 4 | import { FormsService } from './forms.service'; 5 | 6 | describe('FormsController', () => { 7 | let controller: FormsController; 8 | 9 | beforeEach(async () => { 10 | const module: TestingModule = await Test.createTestingModule({ 11 | controllers: [FormsController], 12 | providers: [FormsService], 13 | }).compile(); 14 | 15 | controller = module.get(FormsController); 16 | }); 17 | 18 | it('should be defined', () => { 19 | expect(controller).toBeDefined(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /gateway/src/plans/plans.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { PlansController } from './plans.controller'; 4 | import { PlansService } from './plans.service'; 5 | 6 | describe('PlansController', () => { 7 | let controller: PlansController; 8 | 9 | beforeEach(async () => { 10 | const module: TestingModule = await Test.createTestingModule({ 11 | controllers: [PlansController], 12 | providers: [PlansService], 13 | }).compile(); 14 | 15 | controller = module.get(PlansController); 16 | }); 17 | 18 | it('should be defined', () => { 19 | expect(controller).toBeDefined(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /gateway/src/roles/roles.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { RolesController } from './roles.controller'; 4 | import { RolesService } from './roles.service'; 5 | 6 | describe('RolesController', () => { 7 | let controller: RolesController; 8 | 9 | beforeEach(async () => { 10 | const module: TestingModule = await Test.createTestingModule({ 11 | controllers: [RolesController], 12 | providers: [RolesService], 13 | }).compile(); 14 | 15 | controller = module.get(RolesController); 16 | }); 17 | 18 | it('should be defined', () => { 19 | expect(controller).toBeDefined(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /scheduler/src/scheduler.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { MailerController } from './scheduler.controller'; 3 | 4 | describe('MailerController', () => { 5 | let mailerController: MailerController; 6 | 7 | beforeEach(async () => { 8 | const app: TestingModule = await Test.createTestingModule({ 9 | controllers: [MailerController], 10 | }).compile(); 11 | 12 | mailerController = app.get(MailerController); 13 | }); 14 | 15 | describe('root', () => { 16 | it('should return "Hello World!"', () => { 17 | expect(mailerController.getHello()).toBe('Hello World!'); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /sentiment/src/sentiment.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { SentimentController } from './sentiment.controller'; 3 | 4 | describe('sentimentController', () => { 5 | let sentimentController: SentimentController; 6 | 7 | beforeEach(async () => { 8 | const app: TestingModule = await Test.createTestingModule({ 9 | controllers: [SentimentController], 10 | }).compile(); 11 | 12 | sentimentController = app.get(SentimentController); 13 | }); 14 | 15 | describe('root', () => { 16 | it('should return "Hello World!"', () => { 17 | expect(sentimentController.getHello()).toBe('Hello World!'); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /gateway/src/accounts/accounts.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { AccountsController } from './accounts.controller'; 4 | import { AccountsService } from './accounts.service'; 5 | 6 | describe('AccountsController', () => { 7 | let controller: AccountsController; 8 | 9 | beforeEach(async () => { 10 | const module: TestingModule = await Test.createTestingModule({ 11 | controllers: [AccountsController], 12 | providers: [AccountsService], 13 | }).compile(); 14 | 15 | controller = module.get(AccountsController); 16 | }); 17 | 18 | it('should be defined', () => { 19 | expect(controller).toBeDefined(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /gateway/src/features/features.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { FeaturesController } from './features.controller'; 4 | import { FeaturesService } from './features.service'; 5 | 6 | describe('FeaturesController', () => { 7 | let controller: FeaturesController; 8 | 9 | beforeEach(async () => { 10 | const module: TestingModule = await Test.createTestingModule({ 11 | controllers: [FeaturesController], 12 | providers: [FeaturesService], 13 | }).compile(); 14 | 15 | controller = module.get(FeaturesController); 16 | }); 17 | 18 | it('should be defined', () => { 19 | expect(controller).toBeDefined(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /gateway/src/webhooks/webhooks.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { WebhooksController } from './webhooks.controller'; 4 | import { WebhooksService } from './webhooks.service'; 5 | 6 | describe('WebhooksController', () => { 7 | let controller: WebhooksController; 8 | 9 | beforeEach(async () => { 10 | const module: TestingModule = await Test.createTestingModule({ 11 | controllers: [WebhooksController], 12 | providers: [WebhooksService], 13 | }).compile(); 14 | 15 | controller = module.get(WebhooksController); 16 | }); 17 | 18 | it('should be defined', () => { 19 | expect(controller).toBeDefined(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /gateway/src/userRoles/userRoles.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { UserRolesController } from './userRoles.controller'; 4 | import { UserRolesService } from './userRoles.service'; 5 | 6 | describe('UserRolesController', () => { 7 | let controller: UserRolesController; 8 | 9 | beforeEach(async () => { 10 | const module: TestingModule = await Test.createTestingModule({ 11 | controllers: [UserRolesController], 12 | providers: [UserRolesService], 13 | }).compile(); 14 | 15 | controller = module.get(UserRolesController); 16 | }); 17 | 18 | it('should be defined', () => { 19 | expect(controller).toBeDefined(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /gateway/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { AppController } from './app.controller'; 4 | import { AppService } from './app.service'; 5 | 6 | describe('AppController', () => { 7 | let appController: AppController; 8 | 9 | beforeEach(async () => { 10 | const app: TestingModule = await Test.createTestingModule({ 11 | controllers: [AppController], 12 | providers: [AppService], 13 | }).compile(); 14 | 15 | appController = app.get(AppController); 16 | }); 17 | 18 | describe('root', () => { 19 | it('should return "Hello World!"', () => { 20 | expect(appController.getHello()).toBe('Hello World!'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /gateway/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from '../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /mailer/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /gateway/src/auth/strategies/local.strategy.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 2 | import { PassportStrategy } from '@nestjs/passport'; 3 | import { Strategy } from 'passport-local'; 4 | 5 | import { AuthService } from '@/auth/auth.service'; 6 | 7 | @Injectable() 8 | export class LocalStrategy extends PassportStrategy(Strategy) { 9 | constructor(private readonly authService: AuthService) { 10 | super({ usernameField: 'email' }); 11 | } 12 | 13 | async validate(email: string, password: string): Promise { 14 | const user = await this.authService.validateUser(email, password); 15 | 16 | if (!user) { 17 | throw new UnauthorizedException(); 18 | } 19 | 20 | return user; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /scheduler/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /sentiment/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /mailer/src/mailer.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { MailerController } from './mailer.controller'; 3 | import { AppService } from './mailer.service'; 4 | 5 | describe('MailerController', () => { 6 | let mailerController: MailerController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [MailerController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | mailerController = app.get(MailerController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(mailerController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /gateway/src/apps/entities/app.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | Column, 4 | PrimaryGeneratedColumn, 5 | BaseEntity, 6 | RelationId, 7 | ManyToOne, 8 | Generated, 9 | } from 'typeorm'; 10 | 11 | import { Account } from '@/accounts/entities/account.entity'; 12 | 13 | @Entity() 14 | export class App extends BaseEntity { 15 | @PrimaryGeneratedColumn() 16 | id: number; 17 | 18 | @Column() 19 | @RelationId((app: App) => app.account) 20 | accountId: number; 21 | 22 | @ManyToOne(() => Account, (account) => account.apps) 23 | account: number; 24 | 25 | @Column() 26 | name: string; 27 | 28 | @Column() 29 | @Generated('uuid') 30 | clientId: string; 31 | 32 | @Column() 33 | originUrl: string; 34 | 35 | // @Column() 36 | // secretToken: string; 37 | } 38 | -------------------------------------------------------------------------------- /gateway/src/webhooks/entities/webhook.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | Column, 4 | PrimaryGeneratedColumn, 5 | Index, 6 | BaseEntity, 7 | RelationId, 8 | ManyToOne, 9 | } from 'typeorm'; 10 | 11 | import { Account } from '@/accounts/entities/account.entity'; 12 | 13 | export type WebhookName = 'user.insert' 14 | | 'user.update' 15 | | 'user.remove' 16 | 17 | @Entity() 18 | export class Webhook extends BaseEntity { 19 | @PrimaryGeneratedColumn() 20 | id: number; 21 | 22 | @Index() 23 | @Column() 24 | name: WebhookName; 25 | 26 | @Column() 27 | callbackUrl: string; 28 | 29 | @Column() 30 | @RelationId((webhook: Webhook) => webhook.account) 31 | accountId: number; 32 | 33 | @ManyToOne(() => Account, (account) => account.webhooks) 34 | account: Account; 35 | } 36 | -------------------------------------------------------------------------------- /gateway/src/accounts/entities/account.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | Column, 4 | PrimaryGeneratedColumn, 5 | OneToMany, 6 | BaseEntity, 7 | } from 'typeorm'; 8 | 9 | import { App } from '@/apps/entities/app.entity'; 10 | import { User } from '@/users/entities/user.entity'; 11 | import { Webhook } from '@/webhooks/entities/webhook.entity'; 12 | 13 | @Entity() 14 | export class Account extends BaseEntity { 15 | @PrimaryGeneratedColumn() 16 | id: number; 17 | 18 | @Column() 19 | name: string; 20 | 21 | @Column() 22 | description: string; 23 | 24 | @OneToMany(() => User, (user) => user.account) 25 | users: User[]; 26 | 27 | @OneToMany(() => App, (app) => app.account) 28 | apps: App[]; 29 | 30 | @OneToMany(() => Webhook, (webhook) => webhook.account) 31 | webhooks: Webhook[]; 32 | } 33 | -------------------------------------------------------------------------------- /.env-template: -------------------------------------------------------------------------------- 1 | # GATEWAY MICROSERVICE 2 | SERVER_PORT=3000 3 | DB_PASSWORD=pass 4 | DB_USERNAME=nestjsboilerplate 5 | DB_DATABASE_NAME=nestjsboilerplate 6 | JWT_SECRET=myjwtsecret 7 | JWT_EXPIRES=60s 8 | TWO_FACTOR_AUTHENTICATION_APP_NAME=nestjsboilerplate 9 | EMAIL_CONFIRMATION_URL=http://localhost:3000/api/auth/register/confirm 10 | EMAIL_CONFIRMATION_SUCCESS_URL=https://example.com 11 | 12 | # MAILER MICROSERVICE 13 | # MAILER_SERVICE_HOST=localhost 14 | # MAILER_SERVICE_PORT=8004 15 | MAILER_TRANSPORT= 16 | 17 | # SCHEDULER MICROSERVICE 18 | SCHEDULER_SERVICE_HOST=localhost 19 | SCHEDULER_SERVICE_PORT=8005 20 | 21 | # SENTIMENT ANALYSIS MICROSERVICE 22 | SENTIMENT_SERVICE_HOST=localhost 23 | SENTIMENT_SERVICE_PORT=8006 24 | 25 | # REDIS 26 | REDIS_HOST=localhost 27 | REDIS_PORT=6379 28 | 29 | # RMQ (CloudAMQP) 30 | RMQ_URL= 31 | -------------------------------------------------------------------------------- /gateway/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "project": "tsconfig.json", 5 | "sourceType": "module", 6 | "createDefaultProgram": true 7 | }, 8 | "plugins": ["@typescript-eslint/eslint-plugin"], 9 | "extends": [ 10 | "plugin:@typescript-eslint/recommended", 11 | "prettier", 12 | "plugin:prettier/recommended" 13 | ], 14 | "root": true, 15 | "env": { 16 | "node": true, 17 | "jest": true 18 | }, 19 | "ignorePatterns": [".eslintrc.js"], 20 | "rules": { 21 | "@typescript-eslint/interface-name-prefix": "off", 22 | "@typescript-eslint/explicit-function-return-type": "off", 23 | "@typescript-eslint/explicit-module-boundary-types": "off", 24 | "@typescript-eslint/no-explicit-any": "off" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /mailer/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "project": "tsconfig.json", 5 | "sourceType": "module", 6 | "createDefaultProgram": true 7 | }, 8 | "plugins": ["@typescript-eslint/eslint-plugin"], 9 | "extends": [ 10 | "plugin:@typescript-eslint/recommended", 11 | "prettier", 12 | "plugin:prettier/recommended" 13 | ], 14 | "root": true, 15 | "env": { 16 | "node": true, 17 | "jest": true 18 | }, 19 | "ignorePatterns": [".eslintrc.js"], 20 | "rules": { 21 | "@typescript-eslint/interface-name-prefix": "off", 22 | "@typescript-eslint/explicit-function-return-type": "off", 23 | "@typescript-eslint/explicit-module-boundary-types": "off", 24 | "@typescript-eslint/no-explicit-any": "off" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scheduler/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "project": "tsconfig.json", 5 | "sourceType": "module", 6 | "createDefaultProgram": true 7 | }, 8 | "plugins": ["@typescript-eslint/eslint-plugin"], 9 | "extends": [ 10 | "plugin:@typescript-eslint/recommended", 11 | "prettier", 12 | "plugin:prettier/recommended" 13 | ], 14 | "root": true, 15 | "env": { 16 | "node": true, 17 | "jest": true 18 | }, 19 | "ignorePatterns": [".eslintrc.js"], 20 | "rules": { 21 | "@typescript-eslint/interface-name-prefix": "off", 22 | "@typescript-eslint/explicit-function-return-type": "off", 23 | "@typescript-eslint/explicit-module-boundary-types": "off", 24 | "@typescript-eslint/no-explicit-any": "off" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sentiment/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "project": "tsconfig.json", 5 | "sourceType": "module", 6 | "createDefaultProgram": true 7 | }, 8 | "plugins": ["@typescript-eslint/eslint-plugin"], 9 | "extends": [ 10 | "plugin:@typescript-eslint/recommended", 11 | "prettier", 12 | "plugin:prettier/recommended" 13 | ], 14 | "root": true, 15 | "env": { 16 | "node": true, 17 | "jest": true 18 | }, 19 | "ignorePatterns": [".eslintrc.js"], 20 | "rules": { 21 | "@typescript-eslint/interface-name-prefix": "off", 22 | "@typescript-eslint/explicit-function-return-type": "off", 23 | "@typescript-eslint/explicit-module-boundary-types": "off", 24 | "@typescript-eslint/no-explicit-any": "off" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /gateway/src/auth/strategies/jwt.strategy.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { ConfigService } from '@nestjs/config'; 3 | import { PassportStrategy } from '@nestjs/passport'; 4 | import { ExtractJwt, Strategy } from 'passport-jwt'; 5 | 6 | import { UsersService } from '@/users/users.service'; 7 | 8 | @Injectable() 9 | export class JwtStrategy extends PassportStrategy(Strategy) { 10 | constructor( 11 | private readonly usersService: UsersService, 12 | private readonly configService: ConfigService, 13 | ) { 14 | super({ 15 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 16 | ignoreExpiration: false, 17 | secretOrKey: configService.get('JWT_SECRET'), 18 | }); 19 | } 20 | 21 | async validate(payload: any) { 22 | return this.usersService.find(payload.id); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mailer/src/mailer.module.ts: -------------------------------------------------------------------------------- 1 | import { MailerModule } from '@nestjs-modules/mailer'; 2 | import { Module } from '@nestjs/common'; 3 | import { ConfigModule, ConfigService } from '@nestjs/config'; 4 | import { MailerController } from './mailer.controller'; 5 | 6 | @Module({ 7 | imports: [ 8 | ConfigModule.forRoot({ 9 | isGlobal: true, 10 | envFilePath: `../.env`, 11 | }), 12 | MailerModule.forRootAsync({ 13 | imports: [ConfigModule], 14 | inject: [ConfigService], 15 | useFactory: (configService: ConfigService) => ({ 16 | transport: configService.get('MAILER_TRANSPORT'), 17 | defaults: { 18 | from: '"nestjsboilerplate" ', 19 | }, 20 | }), 21 | }), 22 | ], 23 | controllers: [MailerController], 24 | }) 25 | export class AppModule {} 26 | -------------------------------------------------------------------------------- /gateway/src/admin/resources/user.resource.ts: -------------------------------------------------------------------------------- 1 | import passwordFeature from '@adminjs/passwords'; 2 | import { ResourceWithOptions } from 'adminjs'; 3 | import * as bcrypt from 'bcrypt'; 4 | 5 | import { User } from '@/users/entities/user.entity'; 6 | 7 | import { mainNav } from '../navigation'; 8 | 9 | const UserResource: ResourceWithOptions = { 10 | resource: User, 11 | options: { 12 | properties: { 13 | encryptedPassword: { isVisible: false }, 14 | twoFactorAuthenticationSecret: { isVisible: false }, 15 | }, 16 | navigation: mainNav, 17 | }, 18 | features: [ 19 | passwordFeature({ 20 | properties: { 21 | password: 'password', 22 | encryptedPassword: 'password', 23 | }, 24 | hash: (pass) => bcrypt.hash(pass, 10), 25 | }), 26 | ], 27 | }; 28 | 29 | export default UserResource; 30 | -------------------------------------------------------------------------------- /gateway/src/auth/roles.guard.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | CanActivate, 4 | ExecutionContext, 5 | UnauthorizedException, 6 | } from '@nestjs/common'; 7 | import { Reflector } from '@nestjs/core'; 8 | 9 | @Injectable() 10 | export class RolesGuard implements CanActivate { 11 | constructor(private reflector: Reflector) {} 12 | 13 | canActivate(context: ExecutionContext): boolean { 14 | const roles = this.reflector.get('roles', context.getHandler()); 15 | const request = context.switchToHttp().getRequest(); 16 | const user = request.user; 17 | 18 | if (!roles) { 19 | return true; 20 | } else if (roles.includes('admin') && user.email === 'admin@toto.com') { 21 | return true; 22 | } else if (roles.includes('user')) { 23 | return true; 24 | } else { 25 | throw new UnauthorizedException(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /gateway/src/sentiment/sentiment.controller.ts: -------------------------------------------------------------------------------- 1 | import { Body, Controller, Inject, Post, UseGuards } from '@nestjs/common'; 2 | import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; 3 | import { ClientProxy } from '@nestjs/microservices'; 4 | 5 | import { AuthGuard } from '@/auth/guards/auth.guard'; 6 | import { SentimentDto } from './dto/sentiment.dto'; 7 | // import { EmailConfirmationGuard } from '@/auth/guards/email-confirmation.guard'; 8 | 9 | @ApiTags('Sentiment') 10 | @ApiBearerAuth() 11 | @UseGuards(AuthGuard) 12 | // @UseGuards(EmailConfirmationGuard) 13 | @Controller('sentiment') 14 | export class SentimentController { 15 | constructor( 16 | @Inject('SENTIMENT_SERVICE') private sentimentService: ClientProxy, 17 | ) {} 18 | 19 | @Post() 20 | async triggerWebhook(@Body() body: SentimentDto) { 21 | return this.sentimentService.send('sentiment.analyse', body); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /gateway/src/users/users.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { User, UsersService } from './users.service'; 4 | 5 | describe('UsersService', () => { 6 | let service: UsersService; 7 | 8 | beforeEach(async () => { 9 | const moduleRef: TestingModule = await Test.createTestingModule({ 10 | providers: [UsersService], 11 | }).compile(); 12 | 13 | service = moduleRef.get(UsersService); 14 | }); 15 | 16 | it('should be defined', () => { 17 | expect(service).toBeDefined(); 18 | }); 19 | it.each` 20 | name | returnVal 21 | ${'john'} | ${{ userId: 1, username: 'john', password: 'changeme' }} 22 | `( 23 | 'should call findOne for $name and return $returnVal', 24 | async ({ name, returnVal }: { name: string; returnVal: User }) => { 25 | expect(await service.findOne(name)).toEqual(returnVal); 26 | }, 27 | ); 28 | }); 29 | -------------------------------------------------------------------------------- /mailer/src/mailer.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common'; 2 | import { MessagePattern } from '@nestjs/microservices'; 3 | import { ISendMailOptions, MailerService } from '@nestjs-modules/mailer'; 4 | import { compile } from 'handlebars'; 5 | import * as mjml2html from 'mjml'; 6 | import { readFileSync } from 'fs'; 7 | 8 | @Controller() 9 | export class MailerController { 10 | constructor(private readonly mailerService: MailerService) {} 11 | 12 | @MessagePattern('mailer.mailSend') 13 | mailSend(options: ISendMailOptions) { 14 | const mjmlFile = readFileSync( 15 | `${process.cwd()}/templates/${options.template}.mjml`, 16 | { encoding: 'utf8' }, 17 | ); 18 | const htmlString = mjml2html(mjmlFile); 19 | 20 | return this.mailerService.sendMail({ 21 | ...options, 22 | template: null, 23 | context: null, 24 | html: compile(htmlString.html)(options.context), 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /gateway/src/apps/apps.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, UseGuards } from '@nestjs/common'; 2 | import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; 3 | import { Crud, CrudController } from '@nestjsx/crud'; 4 | 5 | import { AuthGuard } from '@/auth/guards/auth.guard'; 6 | 7 | import { AppsService } from './apps.service'; 8 | import { CreateAppDto } from './dto/create-app.dto'; 9 | import { UpdateAppDto } from './dto/update-app.dto'; 10 | import { App } from './entities/app.entity'; 11 | 12 | @ApiTags('Apps') 13 | @ApiBearerAuth() 14 | @UseGuards(AuthGuard) 15 | @Crud({ 16 | model: { 17 | type: App, 18 | }, 19 | dto: { 20 | create: CreateAppDto, 21 | update: UpdateAppDto, 22 | replace: UpdateAppDto, 23 | }, 24 | query: { 25 | join: { 26 | account: { 27 | eager: true, 28 | }, 29 | }, 30 | }, 31 | }) 32 | @Controller('apps') 33 | export class AppsController implements CrudController { 34 | constructor(public service: AppsService) {} 35 | } 36 | -------------------------------------------------------------------------------- /gateway/src/forms/forms.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, UseGuards } from '@nestjs/common'; 2 | import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; 3 | import { Crud, CrudController } from '@nestjsx/crud'; 4 | 5 | import { AuthGuard } from '@/auth/guards/auth.guard'; 6 | 7 | import { FormsService } from './forms.service'; 8 | import { CreateFormDto } from './dto/create-form.dto'; 9 | import { UpdateFormDto } from './dto/update-form.dto'; 10 | import { Form } from './entities/form.entity'; 11 | 12 | @ApiTags('Forms') 13 | @ApiBearerAuth() 14 | @UseGuards(AuthGuard) 15 | @Crud({ 16 | model: { 17 | type: Form, 18 | }, 19 | dto: { 20 | create: CreateFormDto, 21 | update: UpdateFormDto, 22 | replace: UpdateFormDto, 23 | }, 24 | query: { 25 | join: { 26 | account: { 27 | eager: true, 28 | }, 29 | }, 30 | }, 31 | }) 32 | @Controller('forms') 33 | export class FormsController implements CrudController { 34 | constructor(public service: FormsService) {} 35 | } 36 | -------------------------------------------------------------------------------- /gateway/src/plans/plans.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, UseGuards } from '@nestjs/common'; 2 | import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; 3 | import { Crud, CrudController } from '@nestjsx/crud'; 4 | 5 | import { AuthGuard } from '@/auth/guards/auth.guard'; 6 | 7 | import { CreatePlanDto } from './dto/create-plan.dto'; 8 | import { UpdatePlanDto } from './dto/update-plan.dto'; 9 | import { Plan } from './entities/plan.entity'; 10 | import { PlansService } from './plans.service'; 11 | 12 | @ApiTags('Plans') 13 | @ApiBearerAuth() 14 | @UseGuards(AuthGuard) 15 | @Crud({ 16 | model: { 17 | type: Plan, 18 | }, 19 | dto: { 20 | create: CreatePlanDto, 21 | update: UpdatePlanDto, 22 | replace: UpdatePlanDto, 23 | }, 24 | query: { 25 | join: { 26 | account: { 27 | eager: true, 28 | }, 29 | }, 30 | }, 31 | }) 32 | @Controller('plans') 33 | export class PlansController implements CrudController { 34 | constructor(public service: PlansService) {} 35 | } 36 | -------------------------------------------------------------------------------- /gateway/src/sentiment/sentiment.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigService } from '@nestjs/config'; 3 | import { ClientProxyFactory, Transport } from '@nestjs/microservices'; 4 | // import { TypeOrmModule } from '@nestjs/typeorm'; 5 | 6 | import { SentimentController } from './sentiment.controller'; 7 | 8 | @Module({ 9 | imports: [ 10 | // TypeOrmModule.forFeature([Webhook]) 11 | ], 12 | // exports: [TypeOrmModule], 13 | controllers: [SentimentController], 14 | providers: [ 15 | { 16 | provide: 'SENTIMENT_SERVICE', 17 | useFactory: (configService: ConfigService) => { 18 | return ClientProxyFactory.create({ 19 | transport: Transport.RMQ, 20 | options: { 21 | urls: [configService.get('RMQ_URL')], 22 | queue: 'sentiment_queue', 23 | queueOptions: { 24 | durable: false, 25 | }, 26 | }, 27 | }); 28 | }, 29 | inject: [ConfigService], 30 | }, 31 | ], 32 | }) 33 | export class SentimentModule {} 34 | -------------------------------------------------------------------------------- /gateway/src/webhooks/webhooks.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpService } from '@nestjs/axios'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { InjectRepository } from '@nestjs/typeorm'; 4 | import { TypeOrmCrudService } from '@nestjsx/crud-typeorm'; 5 | import { lastValueFrom } from 'rxjs'; 6 | 7 | import { Webhook } from './entities/webhook.entity'; 8 | import { WebhookEvent } from './events/webhook.event'; 9 | 10 | @Injectable() 11 | export class WebhooksService extends TypeOrmCrudService { 12 | constructor( 13 | @InjectRepository(Webhook) repo, 14 | private httpService: HttpService, 15 | ) { 16 | super(repo); 17 | } 18 | 19 | async callWebhook({ name, accountId, payload }: WebhookEvent) { 20 | const webhook = await this.repo.findOne({ 21 | where: { name, accountId }, 22 | }); 23 | 24 | if (webhook && webhook.callbackUrl) { 25 | const res = this.httpService.post(webhook.callbackUrl, { 26 | webhook: name, 27 | payload, 28 | }); 29 | return lastValueFrom(res); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /gateway/src/features/features.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, UseGuards } from '@nestjs/common'; 2 | import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; 3 | import { Crud, CrudController } from '@nestjsx/crud'; 4 | 5 | import { AuthGuard } from '@/auth/guards/auth.guard'; 6 | 7 | import { CreateFeatureDto } from './dto/create-feature.dto'; 8 | import { UpdateFeatureDto } from './dto/update-feature.dto'; 9 | import { Feature } from './entities/feature.entity'; 10 | import { FeaturesService } from './features.service'; 11 | 12 | @ApiTags('Features') 13 | @ApiBearerAuth() 14 | @UseGuards(AuthGuard) 15 | @Crud({ 16 | model: { 17 | type: Feature, 18 | }, 19 | dto: { 20 | create: CreateFeatureDto, 21 | update: UpdateFeatureDto, 22 | replace: UpdateFeatureDto, 23 | }, 24 | query: { 25 | join: { 26 | account: { 27 | eager: true, 28 | }, 29 | }, 30 | }, 31 | }) 32 | @Controller('features') 33 | export class FeaturesController implements CrudController { 34 | constructor(public service: FeaturesService) {} 35 | } 36 | -------------------------------------------------------------------------------- /gateway/src/roles/roles.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, UseGuards } from '@nestjs/common'; 2 | import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; 3 | import { Crud, CrudController } from '@nestjsx/crud'; 4 | 5 | import { AuthGuard } from '@/auth/guards/auth.guard'; 6 | 7 | import { CreateRoleDto } from './dto/create-role.dto'; 8 | import { UpdateRoleDto } from './dto/update-role.dto'; 9 | import { Role } from './entities/role.entity'; 10 | import { RolesService } from './roles.service'; 11 | 12 | @ApiTags('Roles') 13 | @ApiBearerAuth() 14 | @UseGuards(AuthGuard) 15 | @Crud({ 16 | model: { 17 | type: Role, 18 | }, 19 | dto: { 20 | create: CreateRoleDto, 21 | update: UpdateRoleDto, 22 | replace: UpdateRoleDto, 23 | }, 24 | query: { 25 | join: { 26 | role: { 27 | eager: true, 28 | }, 29 | user: { 30 | eager: true, 31 | }, 32 | }, 33 | }, 34 | }) 35 | @Controller('roles') 36 | export class RolesController implements CrudController { 37 | constructor(public service: RolesService) {} 38 | } 39 | -------------------------------------------------------------------------------- /gateway/src/users/users.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, UseGuards } from '@nestjs/common'; 2 | import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; 3 | import { Crud, CrudController } from '@nestjsx/crud'; 4 | 5 | import { AuthGuard } from '@/auth/guards/auth.guard'; 6 | 7 | import { UserCreateDto } from './dto/user-create.dto'; 8 | import { User } from './entities/user.entity'; 9 | import { UsersService } from './users.service'; 10 | 11 | @ApiTags('Users') 12 | @ApiBearerAuth() 13 | @UseGuards(AuthGuard) 14 | @Crud({ 15 | model: { 16 | type: User, 17 | }, 18 | dto: { 19 | create: UserCreateDto, 20 | update: UserCreateDto, 21 | replace: UserCreateDto, 22 | }, 23 | query: { 24 | join: { 25 | account: { 26 | eager: true, 27 | }, 28 | roles: { 29 | eager: true, 30 | }, 31 | 'roles.role': { 32 | eager: true, 33 | }, 34 | }, 35 | }, 36 | }) 37 | @Controller('users') 38 | export class UsersController implements CrudController { 39 | constructor(public service: UsersService) {} 40 | } 41 | -------------------------------------------------------------------------------- /gateway/src/auth/guards/auth.guard.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CanActivate, 3 | ExecutionContext, 4 | Inject, 5 | Injectable, 6 | } from '@nestjs/common'; 7 | import { Observable } from 'rxjs'; 8 | 9 | import { AppsService } from '@/apps/apps.service'; 10 | 11 | import { JwtAuthGuard } from './jwt-auth.guard'; 12 | 13 | @Injectable() 14 | export class AuthGuard extends JwtAuthGuard implements CanActivate { 15 | constructor(@Inject(AppsService) private readonly appsService: AppsService) { 16 | super(); 17 | } 18 | 19 | canActivate( 20 | context: ExecutionContext, 21 | ): boolean | Promise | Observable { 22 | const request = context.switchToHttp().getRequest(); 23 | 24 | if (request.query.clientId) { 25 | return this.appsService 26 | .findOne({ 27 | where: { 28 | clientId: request.query.clientId, 29 | originUrl: request.hostname.split('/')[0], 30 | }, 31 | }) 32 | .then((res) => Boolean(res)); 33 | } else { 34 | return super.canActivate(context); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /gateway/src/userRoles/userRoles.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, UseGuards } from '@nestjs/common'; 2 | import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; 3 | import { Crud, CrudController } from '@nestjsx/crud'; 4 | 5 | import { AuthGuard } from '@/auth/guards/auth.guard'; 6 | 7 | import { CreateRoleDto } from './dto/create-userRole.dto'; 8 | import { UpdateRoleDto } from './dto/update-userRoles.dto'; 9 | import { UserRole } from './entities/userRole.entity'; 10 | import { UserRolesService } from './userRoles.service'; 11 | 12 | @ApiTags('Roles') 13 | @ApiBearerAuth() 14 | @UseGuards(AuthGuard) 15 | @Crud({ 16 | model: { 17 | type: UserRole, 18 | }, 19 | dto: { 20 | create: CreateRoleDto, 21 | update: UpdateRoleDto, 22 | replace: UpdateRoleDto, 23 | }, 24 | query: { 25 | join: { 26 | role: { 27 | eager: true, 28 | }, 29 | user: { 30 | eager: true, 31 | }, 32 | }, 33 | }, 34 | }) 35 | @Controller('user-roles') 36 | export class UserRolesController implements CrudController { 37 | constructor(public service: UserRolesService) {} 38 | } 39 | -------------------------------------------------------------------------------- /gateway/src/accounts/accounts.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, UseGuards } from '@nestjs/common'; 2 | import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; 3 | import { Crud, CrudController } from '@nestjsx/crud'; 4 | 5 | import { AuthGuard } from '@/auth/guards/auth.guard'; 6 | 7 | import { AccountsService } from './accounts.service'; 8 | import { CreateAccountDto } from './dto/create-account.dto'; 9 | import { UpdateAccountDto } from './dto/update-account.dto'; 10 | import { Account } from './entities/account.entity'; 11 | 12 | @ApiTags('Accounts') 13 | @ApiBearerAuth() 14 | @UseGuards(AuthGuard) 15 | @Crud({ 16 | model: { 17 | type: Account, 18 | }, 19 | dto: { 20 | create: CreateAccountDto, 21 | update: UpdateAccountDto, 22 | replace: UpdateAccountDto, 23 | }, 24 | query: { 25 | join: { 26 | users: { 27 | eager: true, 28 | }, 29 | apps: { 30 | eager: true, 31 | }, 32 | webhooks: { 33 | eager: true, 34 | }, 35 | }, 36 | }, 37 | }) 38 | @Controller('accounts') 39 | export class AccountsController implements CrudController { 40 | constructor(public service: AccountsService) {} 41 | } 42 | -------------------------------------------------------------------------------- /gateway/src/users/users.service.ts: -------------------------------------------------------------------------------- 1 | import { ConflictException, Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { TypeOrmCrudService } from '@nestjsx/crud-typeorm'; 4 | import * as bcrypt from 'bcrypt'; 5 | 6 | import { RegisterDto } from '@/auth/dto/register.dto'; 7 | 8 | import { User } from './entities/user.entity'; 9 | 10 | @Injectable() 11 | export class UsersService extends TypeOrmCrudService { 12 | constructor(@InjectRepository(User) repo) { 13 | super(repo); 14 | } 15 | 16 | async registerUser(registerDto: RegisterDto) { 17 | const result = await this.findOne({ where: { email: registerDto.email } }); 18 | 19 | if (result) { 20 | throw new ConflictException(); 21 | } else { 22 | const newUser = await this.repo.create({ 23 | email: registerDto.email, 24 | password: await bcrypt.hash(registerDto.password, 10), 25 | }); 26 | 27 | return this.repo.save(newUser); 28 | } 29 | } 30 | 31 | set2FASecret(userId: number, secret: string) { 32 | return this.repo.update(userId, { twoFactorAuthenticationSecret: secret }); 33 | } 34 | 35 | confirmUserEMail(userId: number) { 36 | return this.repo.update(userId, { isEmailConfirmed: true }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /gateway/src/database/database.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule, ConfigService } from '@nestjs/config'; 3 | import { TypeOrmModule } from '@nestjs/typeorm'; 4 | 5 | import { Account } from '@/accounts/entities/account.entity'; 6 | import { App } from '@/apps/entities/app.entity'; 7 | import { Role } from '@/roles/entities/role.entity'; 8 | import { UserRole } from '@/userRoles/entities/userRole.entity'; 9 | import { User } from '@/users/entities/user.entity'; 10 | import { Webhook } from '@/webhooks/entities/webhook.entity'; 11 | import { Form } from '@/forms/entities/form.entity'; 12 | 13 | @Module({ 14 | imports: [ 15 | TypeOrmModule.forRootAsync({ 16 | imports: [ConfigModule], 17 | inject: [ConfigService], 18 | useFactory: (configService: ConfigService) => ({ 19 | type: 'postgres', 20 | host: 'localhost', 21 | port: 5432, 22 | username: configService.get('DB_USERNAME'), 23 | password: configService.get('DB_PASSWORD'), 24 | database: configService.get('DB_DATABASE_NAME'), 25 | entities: [App, Account, User, Webhook, Role, UserRole, Form], 26 | synchronize: true, 27 | logging: true, 28 | }), 29 | }), 30 | ], 31 | }) 32 | export class DatabaseModule {} 33 | -------------------------------------------------------------------------------- /gateway/src/webhooks/webhooks.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, UseGuards } from '@nestjs/common'; 2 | import { OnEvent } from '@nestjs/event-emitter'; 3 | import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; 4 | import { Crud } from '@nestjsx/crud'; 5 | 6 | import { AuthGuard } from '@/auth/guards/auth.guard'; 7 | import { EmailConfirmationGuard } from '@/auth/guards/email-confirmation.guard'; 8 | 9 | import { CreateWebhookDto } from './dto/create-webhook.dto'; 10 | import { Webhook } from './entities/webhook.entity'; 11 | import { WebhookEvent } from './events/webhook.event'; 12 | import { WebhooksService } from './webhooks.service'; 13 | 14 | /** 15 | * App are used for developers 16 | */ 17 | @ApiTags('Webhooks') 18 | @ApiBearerAuth() 19 | @UseGuards(AuthGuard) 20 | @UseGuards(EmailConfirmationGuard) 21 | @Crud({ 22 | model: { 23 | type: Webhook, 24 | }, 25 | dto: { 26 | create: CreateWebhookDto, 27 | update: CreateWebhookDto, 28 | replace: CreateWebhookDto, 29 | }, 30 | query: { 31 | join: { 32 | account: { 33 | eager: true, 34 | }, 35 | }, 36 | }, 37 | }) 38 | @Controller('webhooks') 39 | export class WebhooksController { 40 | constructor(public service: WebhooksService) {} 41 | 42 | /** 43 | * Trigger webhook callback 44 | */ 45 | @OnEvent('webhook') 46 | async triggerWebhook(event: WebhookEvent) { 47 | return this.service.callWebhook(event); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /gateway/src/users/user.subscriber.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter2 } from 'eventemitter2'; 2 | import { 3 | Connection, 4 | EntitySubscriberInterface, 5 | EventSubscriber, 6 | InsertEvent, 7 | RemoveEvent, 8 | UpdateEvent, 9 | } from 'typeorm'; 10 | 11 | import { WebhookEvent } from '@/webhooks/events/webhook.event'; 12 | 13 | import { User } from './entities/user.entity'; 14 | 15 | @EventSubscriber() 16 | export class UserSubscriber implements EntitySubscriberInterface { 17 | constructor(connection: Connection, private eventEmitter: EventEmitter2) { 18 | connection.subscribers.push(this); 19 | } 20 | 21 | listenTo() { 22 | return User; 23 | } 24 | 25 | afterInsert(event: InsertEvent) { 26 | this.eventEmitter.emit( 27 | 'webhook', 28 | new WebhookEvent({ 29 | name: 'user.insert', 30 | accountId: event.entity.accountId, 31 | payload: event.entity, 32 | }), 33 | ); 34 | } 35 | 36 | afterUpdate(event: UpdateEvent) { 37 | this.eventEmitter.emit( 38 | 'webhook', 39 | new WebhookEvent({ 40 | name: 'user.update', 41 | accountId: event.entity.accountId, 42 | payload: event.entity, 43 | }), 44 | ); 45 | } 46 | 47 | afterRemove(event: RemoveEvent) { 48 | this.eventEmitter.emit( 49 | 'webhook', 50 | new WebhookEvent({ 51 | name: 'user.remove', 52 | accountId: event.entity.accountId, 53 | payload: event.entity, 54 | }), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /gateway/views/health.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Health Status 8 | 9 | 17 | 18 | 19 |
20 |

21 | NestJs Microservices Boilerplate 22 |

23 | 24 |

25 | Health Status 26 |

27 | 28 |
29 |
 
30 | Global status 31 |
32 | 33 |
34 |

Services

35 | 36 |
    37 | {{#each details}} 38 |
  • 39 |
     
    40 |
    41 | {{@key}}
    42 | {{this.message}} 43 |
    44 |
  • 45 | {{/each}} 46 |
47 |
48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /mailer/templates/confirm-account.mjml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | Welcome aboard 14 | 15 | 16 | 17 | 18 | Dear {{email}} 20 | One last step before using your account! 22 | Confirm my email 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /gateway/src/admin/admin.module.ts: -------------------------------------------------------------------------------- 1 | import { AdminModule } from '@adminjs/nestjs'; 2 | import { Database, Resource } from '@adminjs/typeorm'; 3 | import { Module } from '@nestjs/common'; 4 | import AdminJs from 'adminjs'; 5 | import * as bcrypt from 'bcrypt'; 6 | 7 | import { User } from '@/users/entities/user.entity'; 8 | 9 | import AccountResource from './resources/account.resource'; 10 | import AppsResource from './resources/apps.resource'; 11 | import UserResource from './resources/user.resource'; 12 | import WebhookResource from './resources/webhook.resource'; 13 | 14 | AdminJs.registerAdapter({ Database, Resource }); 15 | 16 | @Module({ 17 | imports: [ 18 | AdminModule.createAdmin({ 19 | adminJsOptions: { 20 | rootPath: '/admin', 21 | branding: { 22 | companyName: 'My Company', 23 | softwareBrothers: false, 24 | }, 25 | resources: [ 26 | AccountResource, 27 | UserResource, 28 | AppsResource, 29 | WebhookResource, 30 | ], 31 | dashboard: { 32 | component: AdminJs.bundle('./frontend/home.tsx'), 33 | }, 34 | }, 35 | auth: { 36 | authenticate: async (email, password) => 37 | new Promise(async (resolve, reject) => { 38 | const user = await User.findOne({ email }); 39 | 40 | if (user && (await bcrypt.compare(password, user.password))) { 41 | return resolve({ email: user.email, avatarUrl: user.avatar }); 42 | } else { 43 | return reject(false); 44 | } 45 | }), 46 | cookieName: 'adminAuth', 47 | cookiePassword: 'adminPass', 48 | }, 49 | }), 50 | ], 51 | }) 52 | export class AppAdminModule {} 53 | -------------------------------------------------------------------------------- /gateway/src/users/entities/user.entity.ts: -------------------------------------------------------------------------------- 1 | import { Exclude, Transform } from 'class-transformer'; 2 | import { 3 | Entity, 4 | Column, 5 | PrimaryGeneratedColumn, 6 | Index, 7 | ManyToOne, 8 | BaseEntity, 9 | RelationId, 10 | OneToMany, 11 | } from 'typeorm'; 12 | 13 | import { Account } from '@/accounts/entities/account.entity'; 14 | import { UserRole } from '@/userRoles/entities/userRole.entity'; 15 | 16 | @Entity() 17 | export class User extends BaseEntity { 18 | @PrimaryGeneratedColumn() 19 | id: number; 20 | 21 | @Column({ nullable: true }) 22 | username: string; 23 | 24 | @Exclude() 25 | @Column({ nullable: true }) 26 | password: string; 27 | 28 | @Index({ unique: true }) 29 | @Column() 30 | email: string; 31 | 32 | @Column({ nullable: true }) 33 | avatar: string; 34 | 35 | @Column({ default: false }) 36 | twoFactor: boolean; 37 | 38 | @Exclude() 39 | @Column({ nullable: true }) 40 | twoFactorAuthenticationSecret: string; 41 | 42 | @Column({ default: true }) 43 | active: boolean; 44 | 45 | @Column({ type: 'timestamptz', default: () => 'now()' }) 46 | createdAt: Date; 47 | 48 | @Column({ type: 'timestamptz', default: () => 'now()' }) 49 | updatedAt: Date; 50 | 51 | @Column({ type: 'timestamptz', default: () => 'now()' }) 52 | lastLogin: Date; 53 | 54 | @Column({ default: false }) 55 | isEmailConfirmed: boolean; 56 | 57 | @RelationId((user: User) => user.account) 58 | @Column({ nullable: true }) 59 | accountId: number; 60 | 61 | @ManyToOne(() => Account, (account) => account.users) 62 | account: Account; 63 | 64 | @OneToMany(() => UserRole, (userRole) => userRole.user) 65 | @Transform(({ value }) => value.map((row) => row.role.name)) 66 | roles: UserRole[]; 67 | } 68 | -------------------------------------------------------------------------------- /gateway/src/sentiment/dto/sentiment.dto.ts: -------------------------------------------------------------------------------- 1 | export class SentimentDto { 2 | sentence: string; 3 | lang?: 4 | | 'af' 5 | | 'am' 6 | | 'ar' 7 | | 'az' 8 | | 'be' 9 | | 'bg' 10 | | 'bn' 11 | | 'bs' 12 | | 'ca' 13 | | 'ceb' 14 | | 'co' 15 | | 'cs' 16 | | 'cy' 17 | | 'da' 18 | | 'de' 19 | | 'el' 20 | | 'en' 21 | | 'eo' 22 | | 'es' 23 | | 'et' 24 | | 'eu' 25 | | 'fa' 26 | | 'fi' 27 | | 'fr' 28 | | 'fy' 29 | | 'ga' 30 | | 'gd' 31 | | 'gl' 32 | | 'gu' 33 | | 'ha' 34 | | 'haw' 35 | | 'hi' 36 | | 'hmn' 37 | | 'hr' 38 | | 'ht' 39 | | 'hu' 40 | | 'hy' 41 | | 'id' 42 | | 'ig' 43 | | 'is' 44 | | 'it' 45 | | 'iw' 46 | | 'ja' 47 | | 'jw' 48 | | 'ka' 49 | | 'kk' 50 | | 'km' 51 | | 'kn' 52 | | 'ko' 53 | | 'ku' 54 | | 'ky' 55 | | 'la' 56 | | 'lb' 57 | | 'lo' 58 | | 'lt' 59 | | 'lv' 60 | | 'mg' 61 | | 'mi' 62 | | 'mk' 63 | | 'ml' 64 | | 'mn' 65 | | 'mr' 66 | | 'ms' 67 | | 'mt' 68 | | 'my' 69 | | 'ne' 70 | | 'nl' 71 | | 'no' 72 | | 'ny' 73 | | 'pa' 74 | | 'pl' 75 | | 'ps' 76 | | 'pt' 77 | | 'ro' 78 | | 'ru' 79 | | 'sd' 80 | | 'si' 81 | | 'sk' 82 | | 'sl' 83 | | 'sm' 84 | | 'sn' 85 | | 'so' 86 | | 'sq' 87 | | 'sr' 88 | | 'st' 89 | | 'su' 90 | | 'sv' 91 | | 'sw' 92 | | 'ta' 93 | | 'te' 94 | | 'tg' 95 | | 'th' 96 | | 'tl' 97 | | 'tr' 98 | | 'uk' 99 | | 'ur' 100 | | 'uz' 101 | | 'vi' 102 | | 'xh' 103 | | 'yi' 104 | | 'yo' 105 | | 'zh-tw' 106 | | 'zh' 107 | | 'zu'; 108 | } 109 | -------------------------------------------------------------------------------- /gateway/src/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule, ConfigService } from '@nestjs/config'; 3 | import { JwtModule } from '@nestjs/jwt'; 4 | import { PassportModule } from '@nestjs/passport'; 5 | import { ClientProxyFactory, Transport } from '@nestjs/microservices'; 6 | 7 | import { AppsModule } from '@/apps/apps.module'; 8 | import { UsersModule } from '@/users/users.module'; 9 | 10 | import { AuthController } from './auth.controller'; 11 | import { AuthService } from './auth.service'; 12 | import { JwtStrategy } from './strategies/jwt.strategy'; 13 | import { LocalStrategy } from './strategies/local.strategy'; 14 | 15 | @Module({ 16 | imports: [ 17 | ConfigModule.forRoot({ 18 | isGlobal: true, 19 | envFilePath: `../.env`, 20 | }), 21 | UsersModule, 22 | PassportModule, 23 | AppsModule, 24 | JwtModule.registerAsync({ 25 | imports: [ConfigModule], 26 | useFactory: async (configService: ConfigService) => ({ 27 | secret: configService.get('JWT_SECRET'), 28 | signOptions: { expiresIn: configService.get('JWT_EXPIRES') }, 29 | }), 30 | inject: [ConfigService], 31 | }), 32 | ], 33 | controllers: [AuthController], 34 | providers: [ 35 | AuthService, 36 | LocalStrategy, 37 | JwtStrategy, 38 | { 39 | provide: 'MAILER_SERVICE', 40 | useFactory: (configService: ConfigService) => { 41 | return ClientProxyFactory.create({ 42 | transport: Transport.RMQ, 43 | options: { 44 | urls: [configService.get('RMQ_URL')], 45 | queue: 'mailer_queue', 46 | queueOptions: { 47 | durable: false, 48 | }, 49 | }, 50 | }); 51 | }, 52 | inject: [ConfigService], 53 | }, 54 | ], 55 | exports: [AuthService], 56 | }) 57 | export class AuthModule {} 58 | -------------------------------------------------------------------------------- /gateway/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { NestExpressApplication } from '@nestjs/platform-express'; 3 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; 4 | import { RedocModule } from 'nestjs-redoc'; 5 | import { join } from 'path'; 6 | 7 | import { AppModule } from './app.module'; 8 | 9 | require('module-alias/register'); 10 | 11 | async function bootstrap() { 12 | const app = await NestFactory.create(AppModule); 13 | 14 | const config = new DocumentBuilder() 15 | .setTitle('My API') 16 | .setDescription('The API description') 17 | .addBearerAuth() 18 | .setVersion('1.0') 19 | .build(); 20 | 21 | const document = SwaggerModule.createDocument(app, config); 22 | 23 | const redocOptions = { 24 | title: 'My API', 25 | sortPropsAlphabetically: true, 26 | hideDownloadButton: false, 27 | hideHostname: false, 28 | // auth: { 29 | // enabled: true, 30 | // user: 'admin', 31 | // password: '123', 32 | // }, 33 | // tagGroups: [ 34 | // { 35 | // name: 'Auth & User', 36 | // tags: ['Auth', 'Users', 'UserRoles', 'Roles', 'Features'], 37 | // }, 38 | // { 39 | // name: 'Billing', 40 | // tags: ['Accounts', 'Plans'], 41 | // }, 42 | // { 43 | // name: 'Tools', 44 | // tags: ['Sentiment', 'Forms'], 45 | // }, 46 | // { 47 | // name: 'Developers', 48 | // tags: ['Apps', 'Webhooks', 'Health'], 49 | // }, 50 | // ], 51 | }; 52 | 53 | await RedocModule.setup('/api', app, document, redocOptions); 54 | 55 | app.setGlobalPrefix('api'); 56 | 57 | app.useStaticAssets(join(__dirname, '..', 'public')); 58 | app.setBaseViewsDir(join(__dirname, '..', 'views')); 59 | app.setViewEngine('hbs'); 60 | 61 | await app.listen(3000); 62 | } 63 | bootstrap(); 64 | -------------------------------------------------------------------------------- /gateway/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { ClassSerializerInterceptor, Module } from '@nestjs/common'; 2 | import { ConfigModule } from '@nestjs/config'; 3 | import { APP_INTERCEPTOR } from '@nestjs/core'; 4 | import { EventEmitterModule } from '@nestjs/event-emitter'; 5 | import { ScheduleModule } from '@nestjs/schedule'; 6 | 7 | import { AppAdminModule } from '@/admin/admin.module'; 8 | import { AccountsModule } from '@/accounts/accounts.module'; 9 | import { AppsModule } from '@/apps/apps.module'; 10 | import { AuthModule } from '@/auth/auth.module'; 11 | import { DatabaseModule } from '@/database/database.module'; 12 | import { RolesModule } from '@/roles/roles.module'; 13 | import { UserRolesModule } from '@/userRoles/userRoles.module'; 14 | import { UsersModule } from '@/users/users.module'; 15 | import { WebhooksModule } from '@/webhooks/webhooks.module'; 16 | import { SentimentModule } from './sentiment/sentiment.module'; 17 | import { FormsModule } from './forms/forms.module'; 18 | import { HealthModule } from './health/health.module'; 19 | 20 | import { AppController } from './app.controller'; 21 | import { AppService } from './app.service'; 22 | 23 | @Module({ 24 | imports: [ 25 | ConfigModule.forRoot({ 26 | isGlobal: true, 27 | envFilePath: `../.env`, 28 | }), 29 | EventEmitterModule.forRoot({ 30 | wildcard: true, 31 | }), 32 | ScheduleModule.forRoot(), 33 | HealthModule, 34 | DatabaseModule, 35 | AppAdminModule, 36 | AccountsModule, 37 | AppsModule, 38 | AuthModule, 39 | UsersModule, 40 | WebhooksModule, 41 | RolesModule, 42 | UserRolesModule, 43 | SentimentModule, 44 | FormsModule, 45 | ], 46 | controllers: [AppController], 47 | providers: [ 48 | AppService, 49 | { 50 | provide: APP_INTERCEPTOR, 51 | useClass: ClassSerializerInterceptor, 52 | }, 53 | ], 54 | }) 55 | export class AppModule {} 56 | -------------------------------------------------------------------------------- /scheduler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scheduler", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "prebuild": "rimraf dist", 10 | "build": "nest build", 11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 12 | "start": "nest start", 13 | "start:dev": "nest start --watch", 14 | "start:debug": "nest start --debug --watch", 15 | "start:prod": "node dist/main", 16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 17 | "test": "jest", 18 | "test:watch": "jest --watch", 19 | "test:cov": "jest --coverage", 20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 21 | "test:e2e": "jest --config ./test/jest-e2e.json" 22 | }, 23 | "dependencies": { 24 | "@nestjs/common": "8.1.2", 25 | "@nestjs/config": "^1.0.3", 26 | "@nestjs/core": "8.1.2", 27 | "@nestjs/microservices": "^8.1.2", 28 | "@nestjs/platform-express": "8.1.2", 29 | "amqp-connection-manager": "^3.7.0", 30 | "amqplib": "^0.8.0", 31 | "reflect-metadata": "^0.1.13", 32 | "rimraf": "^3.0.2", 33 | "rxjs": "7.4.0" 34 | }, 35 | "devDependencies": { 36 | "@nestjs/cli": "8.1.4", 37 | "@nestjs/schematics": "8.0.4", 38 | "@nestjs/testing": "8.1.2", 39 | "@types/express": "^4.17.8", 40 | "@types/jest": "27.0.2", 41 | "@types/node": "16.11.6", 42 | "@types/supertest": "^2.0.10", 43 | "@typescript-eslint/eslint-plugin": "5.3.0", 44 | "@typescript-eslint/parser": "5.3.0", 45 | "eslint": "8.1.0", 46 | "eslint-config-prettier": "8.3.0", 47 | "eslint-plugin-prettier": "4.0.0", 48 | "jest": "27.3.1", 49 | "prettier": "^2.1.2", 50 | "supertest": "^6.0.0", 51 | "ts-jest": "27.0.7", 52 | "ts-loader": "9.2.6", 53 | "ts-node": "10.4.0", 54 | "tsconfig-paths": "^3.9.0", 55 | "typescript": "^4.0.5" 56 | }, 57 | "jest": { 58 | "moduleFileExtensions": [ 59 | "js", 60 | "json", 61 | "ts" 62 | ], 63 | "rootDir": "src", 64 | "testRegex": ".*\\.spec\\.ts$", 65 | "transform": { 66 | "^.+\\.(t|j)s$": "ts-jest" 67 | }, 68 | "collectCoverageFrom": [ 69 | "**/*.(t|j)s" 70 | ], 71 | "coverageDirectory": "../coverage", 72 | "testEnvironment": "node" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /sentiment/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sentiment", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "prebuild": "rimraf dist", 10 | "build": "nest build", 11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 12 | "start": "nest start", 13 | "start:dev": "nest start --watch", 14 | "start:debug": "nest start --debug --watch", 15 | "start:prod": "node dist/main", 16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 17 | "test": "jest", 18 | "test:watch": "jest --watch", 19 | "test:cov": "jest --coverage", 20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 21 | "test:e2e": "jest --config ./test/jest-e2e.json" 22 | }, 23 | "dependencies": { 24 | "@nestjs/common": "8.1.2", 25 | "@nestjs/config": "^1.0.3", 26 | "@nestjs/core": "8.1.2", 27 | "@nestjs/microservices": "^8.1.2", 28 | "@nestjs/platform-express": "8.1.2", 29 | "amqp-connection-manager": "^3.7.0", 30 | "amqplib": "^0.8.0", 31 | "multilang-sentiment": "^2.0.0", 32 | "reflect-metadata": "^0.1.13", 33 | "rimraf": "^3.0.2", 34 | "rxjs": "7.4.0" 35 | }, 36 | "devDependencies": { 37 | "@nestjs/cli": "8.1.4", 38 | "@nestjs/schematics": "8.0.4", 39 | "@nestjs/testing": "8.1.2", 40 | "@types/express": "^4.17.8", 41 | "@types/jest": "27.0.2", 42 | "@types/node": "16.11.6", 43 | "@types/supertest": "^2.0.10", 44 | "@typescript-eslint/eslint-plugin": "5.3.0", 45 | "@typescript-eslint/parser": "5.3.0", 46 | "eslint": "8.1.0", 47 | "eslint-config-prettier": "8.3.0", 48 | "eslint-plugin-prettier": "4.0.0", 49 | "jest": "27.3.1", 50 | "prettier": "^2.1.2", 51 | "supertest": "^6.0.0", 52 | "ts-jest": "27.0.7", 53 | "ts-loader": "9.2.6", 54 | "ts-node": "10.4.0", 55 | "tsconfig-paths": "^3.9.0", 56 | "typescript": "^4.0.5" 57 | }, 58 | "jest": { 59 | "moduleFileExtensions": [ 60 | "js", 61 | "json", 62 | "ts" 63 | ], 64 | "rootDir": "src", 65 | "testRegex": ".*\\.spec\\.ts$", 66 | "transform": { 67 | "^.+\\.(t|j)s$": "ts-jest" 68 | }, 69 | "collectCoverageFrom": [ 70 | "**/*.(t|j)s" 71 | ], 72 | "coverageDirectory": "../coverage", 73 | "testEnvironment": "node" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /mailer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mailer", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "prebuild": "rimraf dist", 10 | "build": "nest build", 11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 12 | "start": "nest start", 13 | "start:dev": "nest start --watch", 14 | "start:debug": "nest start --debug --watch", 15 | "start:prod": "node dist/main", 16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 17 | "test": "jest", 18 | "test:watch": "jest --watch", 19 | "test:cov": "jest --coverage", 20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 21 | "test:e2e": "jest --config ./test/jest-e2e.json" 22 | }, 23 | "dependencies": { 24 | "@nestjs-modules/mailer": "^1.6.0", 25 | "@nestjs/common": "8.1.2", 26 | "@nestjs/config": "^1.0.3", 27 | "@nestjs/core": "8.1.2", 28 | "@nestjs/microservices": "^8.1.2", 29 | "@nestjs/platform-express": "8.1.2", 30 | "amqp-connection-manager": "^3.7.0", 31 | "amqplib": "^0.8.0", 32 | "handlebars": "^4.7.7", 33 | "mjml": "^4.10.4", 34 | "nodemailer": "^6.7.0", 35 | "reflect-metadata": "^0.1.13", 36 | "rimraf": "^3.0.2", 37 | "rxjs": "7.4.0" 38 | }, 39 | "devDependencies": { 40 | "@nestjs/cli": "8.1.4", 41 | "@nestjs/schematics": "8.0.4", 42 | "@nestjs/testing": "8.1.2", 43 | "@types/express": "^4.17.8", 44 | "@types/jest": "27.0.2", 45 | "@types/node": "16.11.6", 46 | "@types/nodemailer": "^6.4.4", 47 | "@types/supertest": "^2.0.10", 48 | "@typescript-eslint/eslint-plugin": "5.3.0", 49 | "@typescript-eslint/parser": "5.3.0", 50 | "eslint": "8.1.0", 51 | "eslint-config-prettier": "8.3.0", 52 | "eslint-plugin-prettier": "4.0.0", 53 | "jest": "27.3.1", 54 | "prettier": "^2.1.2", 55 | "supertest": "^6.0.0", 56 | "ts-jest": "27.0.7", 57 | "ts-loader": "9.2.6", 58 | "ts-node": "10.4.0", 59 | "tsconfig-paths": "^3.9.0", 60 | "typescript": "^4.0.5" 61 | }, 62 | "jest": { 63 | "moduleFileExtensions": [ 64 | "js", 65 | "json", 66 | "ts" 67 | ], 68 | "rootDir": "src", 69 | "testRegex": ".*\\.spec\\.ts$", 70 | "transform": { 71 | "^.+\\.(t|j)s$": "ts-jest" 72 | }, 73 | "collectCoverageFrom": [ 74 | "**/*.(t|j)s" 75 | ], 76 | "coverageDirectory": "../coverage", 77 | "testEnvironment": "node" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /gateway/src/auth/auth.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { JwtModule } from '@nestjs/jwt'; 2 | import { PassportModule } from '@nestjs/passport'; 3 | import { Test, TestingModule } from '@nestjs/testing'; 4 | 5 | import { UsersModule } from '@/users/users.module'; 6 | 7 | import { AuthService } from './auth.service'; 8 | import { JwtStrategy } from './strategies/jwt.strategy'; 9 | import { LocalStrategy } from './strategies/local.strategy'; 10 | 11 | describe('AuthService', () => { 12 | let service: AuthService; 13 | 14 | beforeEach(async () => { 15 | const moduleRef: TestingModule = await Test.createTestingModule({ 16 | imports: [ 17 | UsersModule, 18 | PassportModule, 19 | JwtModule.register({ 20 | secret: '', 21 | signOptions: { expiresIn: '60s' }, 22 | }), 23 | ], 24 | providers: [AuthService, LocalStrategy, JwtStrategy], 25 | }).compile(); 26 | 27 | service = moduleRef.get(AuthService); 28 | }); 29 | 30 | it('should be defined', () => { 31 | expect(service).toBeDefined(); 32 | }); 33 | }); 34 | 35 | describe('validateUser', () => { 36 | let service: AuthService; 37 | 38 | beforeEach(async () => { 39 | const moduleRef: TestingModule = await Test.createTestingModule({ 40 | imports: [ 41 | UsersModule, 42 | PassportModule, 43 | JwtModule.register({ 44 | secret: '', 45 | signOptions: { expiresIn: '60s' }, 46 | }), 47 | ], 48 | providers: [AuthService, LocalStrategy, JwtStrategy], 49 | }).compile(); 50 | 51 | service = moduleRef.get(AuthService); 52 | }); 53 | 54 | it('should return a user object when credentials are valid', async () => { 55 | const res = await service.validateUser('maria', 'guess'); 56 | expect(res.userId).toEqual(3); 57 | }); 58 | 59 | it('should return null when credentials are invalid', async () => { 60 | const res = await service.validateUser('xxx', 'xxx'); 61 | expect(res).toBeNull(); 62 | }); 63 | }); 64 | 65 | describe('validateLogin', () => { 66 | let service: AuthService; 67 | 68 | beforeEach(async () => { 69 | const moduleRef: TestingModule = await Test.createTestingModule({ 70 | imports: [ 71 | UsersModule, 72 | PassportModule, 73 | JwtModule.register({ 74 | secret: '', 75 | signOptions: { expiresIn: '60s' }, 76 | }), 77 | ], 78 | providers: [AuthService, LocalStrategy, JwtStrategy], 79 | }).compile(); 80 | 81 | service = moduleRef.get(AuthService); 82 | }); 83 | 84 | it('should return JWT object when credentials are valid', async () => { 85 | const res = await service.login({ email: 'maria', id: 3 }); 86 | expect(res.access_token).toBeDefined(); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /gateway/src/auth/auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BadRequestException, 3 | Body, 4 | Controller, 5 | Get, 6 | Post, 7 | Query, 8 | Req, 9 | Request, 10 | Res, 11 | UseGuards, 12 | } from '@nestjs/common'; 13 | import { ConfigService } from '@nestjs/config'; 14 | import { ApiTags } from '@nestjs/swagger'; 15 | import { CrudRequest, ParsedRequest } from '@nestjsx/crud'; 16 | import { Response } from 'express'; 17 | import { User } from 'src/users/entities/user.entity'; 18 | import { UsersService } from 'src/users/users.service'; 19 | 20 | import { AuthService } from './auth.service'; 21 | import { RegisterDto } from './dto/register.dto'; 22 | import { JwtAuthGuard } from './guards/jwt-auth.guard'; 23 | import { LocalAuthGuard } from './guards/local-auth.guard'; 24 | 25 | @ApiTags('Auth') 26 | @Controller() 27 | export class AuthController { 28 | constructor( 29 | private readonly configService: ConfigService, 30 | private readonly authService: AuthService, 31 | private readonly usersService: UsersService, 32 | ) {} 33 | 34 | /** 35 | * Register a User 36 | */ 37 | @Post('auth/register') 38 | async register(@Body() registerDto: RegisterDto) { 39 | const user = await this.usersService.registerUser(registerDto); 40 | await this.authService.sendVerificationLink(user.email); 41 | return user; 42 | } 43 | 44 | /** 45 | * Confirm registration 46 | */ 47 | @Get('auth/register/confirm') 48 | async registerConfirm( 49 | @ParsedRequest() req: CrudRequest, 50 | @Query('token') token: string, 51 | @Res() res: Response, 52 | ) { 53 | const email = await this.authService.decodeConfirmationToken(token); 54 | await this.authService.confirmEmail(req, email); 55 | res.redirect(this.configService.get('EMAIL_CONFIRMATION_SUCCESS_URL')); 56 | } 57 | 58 | /** 59 | * Resend registration confirmation email 60 | */ 61 | @Post('auth/register/resend-confirm') 62 | @UseGuards(JwtAuthGuard) 63 | async resendConfirmationLink(@Req() request: any) { 64 | const user = await this.usersService.findOne(request.user.id); 65 | 66 | if (user.isEmailConfirmed) { 67 | throw new BadRequestException('Email already confirmed'); 68 | } 69 | 70 | await this.authService.sendVerificationLink(user.email); 71 | } 72 | 73 | /** 74 | * Authenticate a User 75 | */ 76 | @UseGuards(LocalAuthGuard) 77 | @Post('auth/login') 78 | async login(@Request() req) { 79 | return this.authService.login(req.user); 80 | } 81 | 82 | /** 83 | * Generate 2FA Qr Code 84 | */ 85 | @UseGuards(JwtAuthGuard) 86 | @Post('auth/2fa/generate') 87 | async genQrCode( 88 | @Res() response: Response, 89 | @Req() request: Request & { user: User }, 90 | ) { 91 | const { otpauthUrl } = await this.authService.generate2FASecret( 92 | request.user, 93 | ); 94 | 95 | return this.authService.pipeQrCodeStream(response, otpauthUrl); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /gateway/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 6 | [circleci-url]: https://circleci.com/gh/nestjs/nest 7 | 8 |

A progressive Node.js framework for building efficient and scalable server-side applications.

9 |

10 | NPM Version 11 | Package License 12 | NPM Downloads 13 | CircleCI 14 | Coverage 15 | Discord 16 | Backers on Open Collective 17 | Sponsors on Open Collective 18 | 19 | Support us 20 | 21 |

22 | 24 | 25 | ## Description 26 | 27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 28 | 29 | ## Installation 30 | 31 | ```bash 32 | $ npm install 33 | ``` 34 | 35 | ## Running the app 36 | 37 | ```bash 38 | # development 39 | $ npm run start 40 | 41 | # watch mode 42 | $ npm run start:dev 43 | 44 | # production mode 45 | $ npm run start:prod 46 | ``` 47 | 48 | ## Test 49 | 50 | ```bash 51 | # unit tests 52 | $ npm run test 53 | 54 | # e2e tests 55 | $ npm run test:e2e 56 | 57 | # test coverage 58 | $ npm run test:cov 59 | ``` 60 | 61 | ## Support 62 | 63 | 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). 64 | 65 | ## Stay in touch 66 | 67 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) 68 | - Website - [https://nestjs.com](https://nestjs.com/) 69 | - Twitter - [@nestframework](https://twitter.com/nestframework) 70 | 71 | ## License 72 | 73 | Nest is [MIT licensed](LICENSE). 74 | -------------------------------------------------------------------------------- /mailer/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 6 | [circleci-url]: https://circleci.com/gh/nestjs/nest 7 | 8 |

A progressive Node.js framework for building efficient and scalable server-side applications.

9 |

10 | NPM Version 11 | Package License 12 | NPM Downloads 13 | CircleCI 14 | Coverage 15 | Discord 16 | Backers on Open Collective 17 | Sponsors on Open Collective 18 | 19 | Support us 20 | 21 |

22 | 24 | 25 | ## Description 26 | 27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 28 | 29 | ## Installation 30 | 31 | ```bash 32 | $ npm install 33 | ``` 34 | 35 | ## Running the app 36 | 37 | ```bash 38 | # development 39 | $ npm run start 40 | 41 | # watch mode 42 | $ npm run start:dev 43 | 44 | # production mode 45 | $ npm run start:prod 46 | ``` 47 | 48 | ## Test 49 | 50 | ```bash 51 | # unit tests 52 | $ npm run test 53 | 54 | # e2e tests 55 | $ npm run test:e2e 56 | 57 | # test coverage 58 | $ npm run test:cov 59 | ``` 60 | 61 | ## Support 62 | 63 | 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). 64 | 65 | ## Stay in touch 66 | 67 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) 68 | - Website - [https://nestjs.com](https://nestjs.com/) 69 | - Twitter - [@nestframework](https://twitter.com/nestframework) 70 | 71 | ## License 72 | 73 | Nest is [MIT licensed](LICENSE). 74 | -------------------------------------------------------------------------------- /scheduler/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 6 | [circleci-url]: https://circleci.com/gh/nestjs/nest 7 | 8 |

A progressive Node.js framework for building efficient and scalable server-side applications.

9 |

10 | NPM Version 11 | Package License 12 | NPM Downloads 13 | CircleCI 14 | Coverage 15 | Discord 16 | Backers on Open Collective 17 | Sponsors on Open Collective 18 | 19 | Support us 20 | 21 |

22 | 24 | 25 | ## Description 26 | 27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 28 | 29 | ## Installation 30 | 31 | ```bash 32 | $ npm install 33 | ``` 34 | 35 | ## Running the app 36 | 37 | ```bash 38 | # development 39 | $ npm run start 40 | 41 | # watch mode 42 | $ npm run start:dev 43 | 44 | # production mode 45 | $ npm run start:prod 46 | ``` 47 | 48 | ## Test 49 | 50 | ```bash 51 | # unit tests 52 | $ npm run test 53 | 54 | # e2e tests 55 | $ npm run test:e2e 56 | 57 | # test coverage 58 | $ npm run test:cov 59 | ``` 60 | 61 | ## Support 62 | 63 | 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). 64 | 65 | ## Stay in touch 66 | 67 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) 68 | - Website - [https://nestjs.com](https://nestjs.com/) 69 | - Twitter - [@nestframework](https://twitter.com/nestframework) 70 | 71 | ## License 72 | 73 | Nest is [MIT licensed](LICENSE). 74 | -------------------------------------------------------------------------------- /sentiment/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 6 | [circleci-url]: https://circleci.com/gh/nestjs/nest 7 | 8 |

A progressive Node.js framework for building efficient and scalable server-side applications.

9 |

10 | NPM Version 11 | Package License 12 | NPM Downloads 13 | CircleCI 14 | Coverage 15 | Discord 16 | Backers on Open Collective 17 | Sponsors on Open Collective 18 | 19 | Support us 20 | 21 |

22 | 24 | 25 | ## Description 26 | 27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 28 | 29 | ## Installation 30 | 31 | ```bash 32 | $ npm install 33 | ``` 34 | 35 | ## Running the app 36 | 37 | ```bash 38 | # development 39 | $ npm run start 40 | 41 | # watch mode 42 | $ npm run start:dev 43 | 44 | # production mode 45 | $ npm run start:prod 46 | ``` 47 | 48 | ## Test 49 | 50 | ```bash 51 | # unit tests 52 | $ npm run test 53 | 54 | # e2e tests 55 | $ npm run test:e2e 56 | 57 | # test coverage 58 | $ npm run test:cov 59 | ``` 60 | 61 | ## Support 62 | 63 | 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). 64 | 65 | ## Stay in touch 66 | 67 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) 68 | - Website - [https://nestjs.com](https://nestjs.com/) 69 | - Twitter - [@nestframework](https://twitter.com/nestframework) 70 | 71 | ## License 72 | 73 | Nest is [MIT licensed](LICENSE). 74 | -------------------------------------------------------------------------------- /gateway/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gateway", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "prebuild": "rimraf dist", 10 | "build": "nest build", 11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 12 | "start": "nest start", 13 | "start:dev": "nest start --watch", 14 | "start:debug": "nest start --debug --watch", 15 | "start:prod": "node dist/main", 16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 17 | "test": "jest", 18 | "test:watch": "jest --watch", 19 | "test:cov": "jest --coverage", 20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 21 | "test:e2e": "jest --config ./test/jest-e2e.json" 22 | }, 23 | "dependencies": { 24 | "@adminjs/express": "^4.0.0", 25 | "@adminjs/nestjs": "^3.0.0", 26 | "@adminjs/passwords": "^2.0.0", 27 | "@adminjs/typeorm": "^2.0.0", 28 | "@nestjs/axios": "^0.0.3", 29 | "@nestjs/common": "8.1.2", 30 | "@nestjs/config": "^1.0.3", 31 | "@nestjs/core": "8.1.2", 32 | "@nestjs/event-emitter": "^1.0.0", 33 | "@nestjs/jwt": "^8.0.0", 34 | "@nestjs/microservices": "^8.1.2", 35 | "@nestjs/passport": "^8.0.1", 36 | "@nestjs/platform-express": "8.1.2", 37 | "@nestjs/schedule": "^1.0.1", 38 | "@nestjs/swagger": "^5.1.4", 39 | "@nestjs/terminus": "^8.0.1", 40 | "@nestjs/typeorm": "^8.0.2", 41 | "@nestjs/websockets": "^8.1.2", 42 | "@nestjsx/crud": "^5.0.0-alpha.3", 43 | "@nestjsx/crud-typeorm": "^5.0.0-alpha.3", 44 | "adminjs": "^5.2.2", 45 | "amqp-connection-manager": "^3.7.0", 46 | "amqplib": "^0.8.0", 47 | "bcrypt": "^5.0.1", 48 | "class-transformer": "^0.4.0", 49 | "express-formidable": "^1.2.0", 50 | "express-session": "^1.17.2", 51 | "hbs": "^4.1.2", 52 | "module-alias": "^2.2.2", 53 | "nestjs-redoc": "^2.2.2", 54 | "otplib": "^12.0.1", 55 | "passport": "^0.5.0", 56 | "passport-jwt": "^4.0.0", 57 | "passport-local": "^1.0.0", 58 | "pg": "^8.7.1", 59 | "qrcode": "^1.4.4", 60 | "redis": "^3.1.2", 61 | "reflect-metadata": "^0.1.13", 62 | "rimraf": "^3.0.2", 63 | "rxjs": "7.4.0", 64 | "socket.io": "^4.3.1", 65 | "swagger-ui-express": "^4.1.6", 66 | "typeorm": "^0.2.38" 67 | }, 68 | "devDependencies": { 69 | "@nestjs/cli": "8.1.4", 70 | "@nestjs/schematics": "8.0.4", 71 | "@nestjs/testing": "8.1.2", 72 | "@types/express": "^4.17.8", 73 | "@types/jest": "27.0.2", 74 | "@types/node": "16.11.6", 75 | "@types/supertest": "^2.0.10", 76 | "@typescript-eslint/eslint-plugin": "5.3.0", 77 | "@typescript-eslint/parser": "5.3.0", 78 | "eslint": "8.1.0", 79 | "eslint-config-prettier": "8.3.0", 80 | "eslint-plugin-prettier": "4.0.0", 81 | "jest": "27.3.1", 82 | "prettier": "^2.1.2", 83 | "supertest": "^6.0.0", 84 | "ts-jest": "27.0.7", 85 | "ts-loader": "9.2.6", 86 | "ts-node": "10.4.0", 87 | "tsconfig-paths": "^3.9.0", 88 | "typescript": "^4.0.5" 89 | }, 90 | "jest": { 91 | "moduleFileExtensions": [ 92 | "js", 93 | "json", 94 | "ts" 95 | ], 96 | "rootDir": "src", 97 | "testRegex": ".*\\.spec\\.ts$", 98 | "transform": { 99 | "^.+\\.(t|j)s$": "ts-jest" 100 | }, 101 | "collectCoverageFrom": [ 102 | "**/*.(t|j)s" 103 | ], 104 | "coverageDirectory": "../coverage", 105 | "testEnvironment": "node" 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /gateway/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Render } from '@nestjs/common'; 2 | import { ConfigService } from '@nestjs/config'; 3 | import { RedisOptions, RmqOptions, Transport } from '@nestjs/microservices'; 4 | import { ApiTags } from '@nestjs/swagger'; 5 | import { 6 | HealthCheckService, 7 | HealthCheck, 8 | TypeOrmHealthIndicator, 9 | MemoryHealthIndicator, 10 | DiskHealthIndicator, 11 | MicroserviceHealthIndicator, 12 | } from '@nestjs/terminus'; 13 | 14 | @ApiTags('Health') 15 | @Controller('health') 16 | export class HealthController { 17 | constructor( 18 | private readonly configService: ConfigService, 19 | private healthCheckService: HealthCheckService, 20 | private typeOrmHealthIndicator: TypeOrmHealthIndicator, 21 | private microserviceHealthIndicator: MicroserviceHealthIndicator, 22 | private memoryHealthIndicator: MemoryHealthIndicator, 23 | private diskHealthIndicator: DiskHealthIndicator, 24 | ) {} 25 | 26 | @Get() 27 | @HealthCheck() 28 | check() { 29 | const database = () => 30 | this.typeOrmHealthIndicator.pingCheck('Postgres Database'); 31 | 32 | const redis = () => 33 | this.microserviceHealthIndicator.pingCheck('Redis', { 34 | transport: Transport.REDIS, 35 | options: { 36 | url: `redis://${this.configService.get( 37 | 'REDIS_HOST', 38 | )}:${this.configService.get('REDIS_PORT')}`, 39 | }, 40 | }); 41 | 42 | // Mailer microservice 43 | const mailer = () => 44 | this.microserviceHealthIndicator.pingCheck( 45 | 'Mailer Microservice', 46 | { 47 | transport: Transport.RMQ, 48 | options: { 49 | urls: [this.configService.get('RMQ_URL') as string], 50 | queue: 'mailer_queue', 51 | queueOptions: { 52 | durable: false, 53 | }, 54 | }, 55 | }, 56 | ); 57 | 58 | // Scheduler microservice 59 | const scheduler = () => 60 | this.microserviceHealthIndicator.pingCheck( 61 | 'Scheduler Microservice', 62 | { 63 | transport: Transport.RMQ, 64 | options: { 65 | urls: [this.configService.get('RMQ_URL') as string], 66 | queue: 'scheduler_queue', 67 | queueOptions: { 68 | durable: false, 69 | }, 70 | }, 71 | }, 72 | ); 73 | 74 | // Sentiment microservice 75 | const sentiment = () => 76 | this.microserviceHealthIndicator.pingCheck( 77 | 'Sentiment Analysis Microservice', 78 | { 79 | transport: Transport.RMQ, 80 | options: { 81 | urls: [this.configService.get('RMQ_URL') as string], 82 | queue: 'sentiment_queue', 83 | queueOptions: { 84 | durable: false, 85 | }, 86 | }, 87 | }, 88 | ); 89 | 90 | // the process should not use more than 300MB memory 91 | const memory = () => 92 | this.memoryHealthIndicator.checkHeap('Memory heap', 300 * 1024 * 1024); 93 | 94 | // The process should not have more than 300MB RSS memory allocated 95 | const rss = () => 96 | this.memoryHealthIndicator.checkRSS('Memory RSS', 300 * 1024 * 1024); 97 | 98 | // the used disk storage should not exceed the 50% of the available space 99 | const disk = () => 100 | this.diskHealthIndicator.checkStorage('Disk health', { 101 | thresholdPercent: 0.5, 102 | path: '/', 103 | }); 104 | 105 | return this.healthCheckService.check([ 106 | database, 107 | redis, 108 | mailer, 109 | scheduler, 110 | sentiment, 111 | memory, 112 | rss, 113 | // disk, 114 | ]); 115 | } 116 | 117 | @Get('/status') 118 | @Render('health') 119 | render() { 120 | return this.check().catch((res) => res.response); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /gateway/src/auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException, Inject, Injectable } from '@nestjs/common'; 2 | import { ConfigService } from '@nestjs/config'; 3 | import { JwtService } from '@nestjs/jwt'; 4 | import { CrudRequest } from '@nestjsx/crud'; 5 | import * as bcrypt from 'bcrypt'; 6 | import { authenticator } from 'otplib'; 7 | import { toFileStream } from 'qrcode'; 8 | import { Writable } from 'stream'; 9 | 10 | import { User } from '@/users/entities/user.entity'; 11 | import { UsersService } from '@/users/users.service'; 12 | import { ClientProxy } from '@nestjs/microservices'; 13 | import { lastValueFrom } from 'rxjs'; 14 | 15 | @Injectable() 16 | export class AuthService { 17 | constructor( 18 | private readonly usersService: UsersService, 19 | private readonly jwtService: JwtService, 20 | private readonly configService: ConfigService, 21 | @Inject('MAILER_SERVICE') private mailerService: ClientProxy, 22 | ) {} 23 | 24 | /** 25 | * Validate user on classic login 26 | */ 27 | async validateUser(email: string, pass: string) { 28 | const user = await this.usersService.findOne({ where: { email } }); 29 | 30 | if (user && (await bcrypt.compare(pass, user.password))) { 31 | const { password, ...result } = user; 32 | return result; 33 | } else { 34 | return null; 35 | } 36 | } 37 | 38 | /** 39 | * Generate JWT token from basic login 40 | */ 41 | login(user: User) { 42 | return { 43 | access_token: this.jwtService.sign({ 44 | email: user.email, 45 | id: user.id, 46 | }), 47 | }; 48 | } 49 | 50 | public async generate2FASecret(user: User) { 51 | const secret = authenticator.generateSecret(); 52 | 53 | const otpauthUrl = authenticator.keyuri( 54 | user.email, 55 | this.configService.get('TWO_FACTOR_AUTHENTICATION_APP_NAME'), 56 | secret, 57 | ); 58 | 59 | await this.usersService.set2FASecret(user.id, secret); 60 | 61 | return { 62 | secret, 63 | otpauthUrl, 64 | }; 65 | } 66 | 67 | public async pipeQrCodeStream(stream: any, otpauthUrl: string) { 68 | return toFileStream(stream as Writable, otpauthUrl); 69 | } 70 | 71 | public check2FACode(twoFACode: string, user: User) { 72 | return authenticator.verify({ 73 | token: twoFACode, 74 | secret: user.twoFactorAuthenticationSecret, 75 | }); 76 | } 77 | 78 | public async decodeConfirmationToken(token: string) { 79 | try { 80 | const payload = await this.jwtService.verify(token, { 81 | secret: this.configService.get('JWT_SECRET'), 82 | }); 83 | 84 | if (typeof payload === 'object' && 'email' in payload) { 85 | return payload.email; 86 | } else { 87 | throw new BadRequestException(); 88 | } 89 | } catch (error) { 90 | if (error?.name === 'TokenExpiredError') { 91 | throw new BadRequestException('Email confirmation token expired'); 92 | } else { 93 | throw new BadRequestException('Bad confirmation token'); 94 | } 95 | } 96 | } 97 | 98 | public sendVerificationLink(email: string) { 99 | const token = this.jwtService.sign( 100 | { email }, 101 | { 102 | secret: this.configService.get('JWT_SECRET'), 103 | expiresIn: this.configService.get('JWT_EXPIRES'), 104 | }, 105 | ); 106 | 107 | const url = `${this.configService.get( 108 | 'EMAIL_CONFIRMATION_URL', 109 | )}?token=${token}`; 110 | 111 | const req = this.mailerService.send('mailer.mailSend', { 112 | to: email, 113 | subject: 'Email confirmation', 114 | template: 'confirm-account', 115 | context: { 116 | email, 117 | url, 118 | }, 119 | }); 120 | 121 | return lastValueFrom(req); 122 | } 123 | 124 | public async confirmEmail(req: CrudRequest, email: string) { 125 | const user = await this.usersService.findOne({ where: { email } }); 126 | 127 | if (user.isEmailConfirmed) { 128 | throw new BadRequestException('Email already confirmed'); 129 | } 130 | 131 | await this.usersService.confirmUserEMail(user.id); 132 | } 133 | } 134 | --------------------------------------------------------------------------------