├── README.md ├── frontend ├── .prettierignore ├── src │ ├── components │ │ ├── ui │ │ │ └── index.tsx │ │ ├── working.tsx │ │ ├── shared │ │ │ ├── theme-provider.tsx │ │ │ └── theme-toogle.tsx │ │ └── layout │ │ │ ├── header │ │ │ ├── navbar.tsx │ │ │ ├── index.tsx │ │ │ └── banner.tsx │ │ │ └── footer │ │ │ └── index.tsx │ ├── app │ │ ├── globals.css │ │ ├── icon.jpg │ │ ├── gre │ │ │ └── page.tsx │ │ ├── contact │ │ │ └── page.tsx │ │ ├── ielts │ │ │ └── page.tsx │ │ ├── schools │ │ │ └── page.tsx │ │ ├── vocabulary │ │ │ └── page.tsx │ │ ├── materials │ │ │ └── page.tsx │ │ ├── not-found.tsx │ │ ├── page.tsx │ │ ├── error.tsx │ │ ├── about │ │ │ └── page.tsx │ │ └── layout.tsx │ ├── lib │ │ └── utils.ts │ ├── types │ │ ├── index.ts │ │ └── next-auth.d.ts │ ├── hooks │ │ └── use-scroll.ts │ └── config │ │ └── site.ts ├── .env.example ├── .eslintrc.json ├── public │ ├── logo.jpg │ ├── biprodas.jpg │ ├── favicon.jpg │ ├── biprodas-roy.jpg │ ├── vercel.svg │ └── next.svg ├── next.config.js ├── postcss.config.js ├── .prettierrc.js ├── .gitignore ├── tailwind.config.ts ├── tsconfig.json ├── package.json └── README.md ├── server ├── src │ ├── modules │ │ ├── iam │ │ │ └── iam.module.ts │ │ ├── admin │ │ │ └── admin.module.ts │ │ ├── report │ │ │ └── report.module.ts │ │ ├── faq │ │ │ └── faq.module.ts │ │ ├── school │ │ │ └── school.module.ts │ │ ├── word │ │ │ ├── dtos │ │ │ │ ├── filter-word.dto.ts │ │ │ │ ├── index.ts │ │ │ │ ├── word.dto.ts │ │ │ │ ├── update-word.dto.ts │ │ │ │ └── create-word.dto.ts │ │ │ ├── word.http │ │ │ ├── word.module.ts │ │ │ ├── services │ │ │ │ └── word.service.ts │ │ │ ├── schemas │ │ │ │ └── word.schema.ts │ │ │ └── controllers │ │ │ │ └── word.controller.ts │ │ ├── dictionary │ │ │ ├── dtos │ │ │ │ ├── filter-dictionary.dto.ts │ │ │ │ ├── update-dictionary.dto.ts │ │ │ │ ├── dictionary.dto.ts │ │ │ │ └── create-dictionary.dto.ts │ │ │ ├── dictionary.http │ │ │ ├── schemas │ │ │ │ └── dictionary.schema.ts │ │ │ ├── dictionary.module.ts │ │ │ ├── services │ │ │ │ └── dictionary.service.ts │ │ │ └── controllers │ │ │ │ └── dictionary.controller.ts │ │ └── task │ │ │ ├── task.controller.ts │ │ │ ├── task.schema.ts │ │ │ ├── task.service.ts │ │ │ └── task.module.ts │ ├── shared │ │ └── shared.module.ts │ ├── app │ │ ├── constants │ │ │ ├── app.constant.ts │ │ │ └── app.enum.constant.ts │ │ ├── services │ │ │ └── app.service.ts │ │ ├── controllers │ │ │ ├── app.controller.ts │ │ │ └── app.controller.spec.ts │ │ └── app.module.ts │ ├── common │ │ ├── constants │ │ │ ├── index.ts │ │ │ ├── constraint-errors.ts │ │ │ └── http-error-type.ts │ │ ├── helper │ │ │ ├── constants │ │ │ │ ├── helper.function.ts │ │ │ │ └── helper.enum.constant.ts │ │ │ ├── helper.module.ts │ │ │ ├── interfaces │ │ │ │ ├── helper.interface.ts │ │ │ │ └── helper.date-service.interface.ts │ │ │ └── services │ │ │ │ └── helper.date.service.ts │ │ ├── enums │ │ │ ├── environment.enum.ts │ │ │ └── error-type.enum.ts │ │ ├── interfaces │ │ │ └── IFile.ts │ │ ├── database │ │ │ ├── interfaces │ │ │ │ └── database.options-service.interface.ts │ │ │ ├── constants │ │ │ │ ├── database.function.constant.ts │ │ │ │ └── database.constant.ts │ │ │ ├── database.options.module.ts │ │ │ └── services │ │ │ │ └── database.options.service.ts │ │ ├── decorators │ │ │ └── http.decorator.ts │ │ ├── pipes │ │ │ └── parse-object-id.pipe.ts │ │ └── common.module.ts │ ├── job │ │ ├── router │ │ │ └── jobs.router.module.ts │ │ └── jobs.module.ts │ ├── database │ │ ├── database.module.ts │ │ └── database.providers.ts │ ├── configs │ │ ├── aws.config.ts │ │ ├── index.ts │ │ ├── app.config.ts │ │ ├── database.config.ts │ │ ├── config-validation.schema.ts │ │ ├── auth.config.ts │ │ └── config.type.ts │ ├── router │ │ ├── routes │ │ │ ├── routes.auth.module.ts │ │ │ ├── routes.user.module.ts │ │ │ ├── routes.admin.module.ts │ │ │ └── routes.public.module.ts │ │ └── router.module.ts │ └── main.ts ├── tsconfig.build.json ├── .prettierrc ├── nest-cli.json ├── test │ ├── jest-e2e.json │ └── app.e2e-spec.ts ├── .gitignore ├── .eslintrc.js ├── Dockerfile ├── tsconfig.json ├── .env.example ├── docker-compose.yml ├── package.json └── README.md ├── .vscode └── settings.json └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # wordbook -------------------------------------------------------------------------------- /frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /frontend/src/components/ui/index.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/src/modules/iam/iam.module.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/src/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/src/modules/admin/admin.module.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/src/modules/report/report.module.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/.env.example: -------------------------------------------------------------------------------- 1 | PORT=3100 2 | API_URL=http://localhost:3900 -------------------------------------------------------------------------------- /frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /server/src/app/constants/app.constant.ts: -------------------------------------------------------------------------------- 1 | export const APP_LANGUAGE = 'en'; 2 | -------------------------------------------------------------------------------- /server/src/modules/faq/faq.module.ts: -------------------------------------------------------------------------------- 1 | // id 2 | // question 3 | // answer 4 | // isActive 5 | -------------------------------------------------------------------------------- /frontend/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /frontend/public/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masud-pervez/wordbook/HEAD/frontend/public/logo.jpg -------------------------------------------------------------------------------- /frontend/src/app/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masud-pervez/wordbook/HEAD/frontend/src/app/icon.jpg -------------------------------------------------------------------------------- /frontend/public/biprodas.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masud-pervez/wordbook/HEAD/frontend/public/biprodas.jpg -------------------------------------------------------------------------------- /frontend/public/favicon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masud-pervez/wordbook/HEAD/frontend/public/favicon.jpg -------------------------------------------------------------------------------- /server/src/common/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constraint-errors'; 2 | export * from './http-error-type'; 3 | -------------------------------------------------------------------------------- /frontend/public/biprodas-roy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masud-pervez/wordbook/HEAD/frontend/public/biprodas-roy.jpg -------------------------------------------------------------------------------- /frontend/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /server/src/modules/school/school.module.ts: -------------------------------------------------------------------------------- 1 | // id; 2 | // name; 3 | // country; 4 | // city / region; 5 | // details; 6 | // notes; 7 | -------------------------------------------------------------------------------- /server/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/app/gre/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Gre() { 4 | return ( 5 |
Gre
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /frontend/src/app/contact/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function ContactPage() { 4 | return
ContactPage
5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/app/ielts/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Ielts() { 4 | return ( 5 |
Ielts
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /server/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 100, 5 | "tabWidth": 2, 6 | "bracketSpacing": true 7 | } -------------------------------------------------------------------------------- /frontend/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | trailingComma: "all", 4 | singleQuote: true, 5 | printWidth: 100, 6 | tabWidth: 2 7 | }; -------------------------------------------------------------------------------- /frontend/src/app/schools/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function School() { 4 | return ( 5 |
School
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /frontend/src/components/working.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const working = () => { 4 | return
working
5 | } 6 | 7 | export default working 8 | -------------------------------------------------------------------------------- /frontend/src/app/vocabulary/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Vocabulary() { 4 | return ( 5 |
Vocabulary
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /server/src/common/helper/constants/helper.function.ts: -------------------------------------------------------------------------------- 1 | import ms from 'ms'; 2 | 3 | export function seconds(msValue: string): number { 4 | return ms(msValue) / 1000; 5 | } 6 | -------------------------------------------------------------------------------- /server/src/modules/word/dtos/filter-word.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString } from 'class-validator'; 2 | 3 | export class FilterWordDto { 4 | @IsString() 5 | word: string; 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/app/materials/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function StudyMaterials() { 4 | return ( 5 |
StudyMaterials
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /server/src/app/constants/app.enum.constant.ts: -------------------------------------------------------------------------------- 1 | export enum ENUM_APP_ENVIRONMENT { 2 | PRODUCTION = 'production', 3 | STAGING = 'staging', 4 | DEVELOPMENT = 'development', 5 | } 6 | -------------------------------------------------------------------------------- /server/src/modules/word/dtos/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-word.dto'; 2 | export * from './update-word.dto'; 3 | export * from './filter-word.dto'; 4 | export * from './word.dto'; 5 | -------------------------------------------------------------------------------- /server/src/modules/dictionary/dtos/filter-dictionary.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString } from 'class-validator'; 2 | 3 | export class FilterDictionaryDto { 4 | @IsString() 5 | title: string; 6 | } 7 | -------------------------------------------------------------------------------- /server/src/common/enums/environment.enum.ts: -------------------------------------------------------------------------------- 1 | export enum Environment { 2 | Development = 'development', 3 | Production = 'production', 4 | Test = 'test', 5 | Provision = 'provision', 6 | } 7 | -------------------------------------------------------------------------------- /server/src/app/services/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /server/src/common/interfaces/IFile.ts: -------------------------------------------------------------------------------- 1 | export interface IFile { 2 | encoding: string; 3 | buffer: Buffer; 4 | fieldname: string; 5 | mimetype: string; 6 | originalname: string; 7 | size: number; 8 | } 9 | -------------------------------------------------------------------------------- /server/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /server/src/modules/dictionary/dtos/update-dictionary.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString } from 'class-validator'; 2 | 3 | export class UpdateDictionaryDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | title: string; 7 | } 8 | -------------------------------------------------------------------------------- /server/src/modules/dictionary/dtos/dictionary.dto.ts: -------------------------------------------------------------------------------- 1 | import { Expose } from 'class-transformer'; 2 | 3 | export class DictionaryDto { 4 | @Expose() 5 | id: string; 6 | 7 | @Expose() 8 | title: string; 9 | } 10 | -------------------------------------------------------------------------------- /server/src/common/database/interfaces/database.options-service.interface.ts: -------------------------------------------------------------------------------- 1 | import { MongooseModuleOptions } from '@nestjs/mongoose'; 2 | 3 | export interface IDatabaseOptionsService { 4 | createOptions(): MongooseModuleOptions; 5 | } 6 | -------------------------------------------------------------------------------- /server/src/job/router/jobs.router.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | 3 | @Module({ 4 | providers: [], 5 | exports: [], 6 | imports: [], 7 | controllers: [], 8 | }) 9 | export class JobsRouterModule {} 10 | -------------------------------------------------------------------------------- /server/src/database/database.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { databaseProviders } from './database.providers'; 3 | 4 | @Module({ 5 | imports: [...databaseProviders], 6 | }) 7 | export class DatabaseModule {} 8 | -------------------------------------------------------------------------------- /server/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 | -------------------------------------------------------------------------------- /server/src/modules/word/word.http: -------------------------------------------------------------------------------- 1 | ### Get all 2 | GET http://localhost:3900/api/v1/words 3 | 4 | 5 | ### Create 6 | POST http://localhost:3900/api/v1/words 7 | Content-Type: application/json 8 | 9 | { 10 | "word": "root", 11 | "definition": "" 12 | } -------------------------------------------------------------------------------- /server/src/common/database/constants/database.function.constant.ts: -------------------------------------------------------------------------------- 1 | import { Types } from 'mongoose'; 2 | import { v4 as uuidV4 } from 'uuid'; 3 | 4 | export const DatabaseDefaultUUID = uuidV4; 5 | 6 | export const DatabaseDefaultObjectId = () => new Types.ObjectId(); 7 | -------------------------------------------------------------------------------- /server/src/modules/dictionary/dictionary.http: -------------------------------------------------------------------------------- 1 | ### Get all 2 | GET http://localhost:3900/api/v1/dictionaries 3 | 4 | 5 | ### Create 6 | POST http://localhost:3900/api/v1/dictionaries 7 | Content-Type: application/json 8 | 9 | { 10 | "word": "root", 11 | "definition": "" 12 | } -------------------------------------------------------------------------------- /server/src/common/database/constants/database.constant.ts: -------------------------------------------------------------------------------- 1 | export const DATABASE_CONNECTION_NAME = 'PrimaryConnectionDatabase'; 2 | 3 | export const DATABASE_CREATED_AT_FIELD_NAME = 'createdAt'; 4 | export const DATABASE_UPDATED_AT_FIELD_NAME = 'updatedAt'; 5 | export const DATABASE_DELETED_AT_FIELD_NAME = 'deletedAt'; 6 | -------------------------------------------------------------------------------- /frontend/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export type CurrentUser = { 2 | id: string 3 | name: string 4 | username: string 5 | email: string 6 | shortBio: string 7 | image: string 8 | } 9 | 10 | export interface payload { 11 | name: string 12 | email: string 13 | shortBio?: string 14 | image?: string 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const NotFound = () => { 4 | return ( 5 |
6 |

404, Page Not Found!

7 |
8 | ) 9 | } 10 | 11 | export default NotFound 12 | -------------------------------------------------------------------------------- /frontend/src/types/next-auth.d.ts: -------------------------------------------------------------------------------- 1 | import { type User } from 'next-auth' 2 | import 'next-auth/jwt' 3 | 4 | type UserId = string 5 | 6 | declare module 'next-auth/jwt' { 7 | interface JWT { 8 | id: UserId 9 | } 10 | } 11 | 12 | declare module 'next-auth' { 13 | interface Session { 14 | user: User & { 15 | id: UserId 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/components/shared/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { ThemeProvider as NextThemesProvider } from 'next-themes' 4 | import { type ThemeProviderProps } from 'next-themes/dist/types' 5 | 6 | export default function ThemeProvider({ children, ...props }: ThemeProviderProps) { 7 | return {children} 8 | } 9 | -------------------------------------------------------------------------------- /server/src/common/database/database.options.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { DatabaseOptionsService } from './services/database.options.service'; 3 | 4 | @Module({ 5 | providers: [DatabaseOptionsService], 6 | exports: [DatabaseOptionsService], 7 | imports: [], 8 | controllers: [], 9 | }) 10 | export class DatabaseOptionsModule {} 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "[javascript]": { 5 | "editor.defaultFormatter": "esbenp.prettier-vscode", 6 | "editor.formatOnSave": true 7 | }, 8 | "[javascriptreact]": { 9 | "editor.defaultFormatter": "esbenp.prettier-vscode", 10 | "editor.formatOnSave": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/src/modules/task/task.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { TasksService } from './task.service'; 3 | 4 | @Controller({ path: 'tasks', version: '1' }) 5 | export class TaskController { 6 | constructor(private readonly taskService: TasksService) {} 7 | 8 | @Get('/') 9 | getHello() { 10 | return this.taskService.findAll(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/src/common/decorators/http.decorator.ts: -------------------------------------------------------------------------------- 1 | import { PipeTransform, ParseUUIDPipe, Param } from '@nestjs/common'; 2 | import type { Type } from '@nestjs/common/interfaces'; 3 | 4 | export function UUIDParam( 5 | property: string, 6 | ...pipes: Array | PipeTransform> 7 | ): ParameterDecorator { 8 | return Param(property, new ParseUUIDPipe({ version: '4' }), ...pipes); 9 | } 10 | -------------------------------------------------------------------------------- /server/src/modules/task/task.schema.ts: -------------------------------------------------------------------------------- 1 | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; 2 | import { HydratedDocument } from 'mongoose'; 3 | 4 | export type TaskDocument = HydratedDocument; 5 | 6 | @Schema() 7 | export class Task { 8 | @Prop() 9 | title: string; 10 | 11 | @Prop() 12 | description: string; 13 | } 14 | 15 | export const TaskSchema = SchemaFactory.createForClass(Task); 16 | -------------------------------------------------------------------------------- /server/src/modules/dictionary/dtos/create-dictionary.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsBoolean, IsDefined, IsNotEmpty, IsOptional, IsString } from 'class-validator'; 2 | 3 | export class CreateDictionaryDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | @IsDefined() 7 | title: string; 8 | 9 | @IsString() 10 | @IsOptional() 11 | description: string; 12 | 13 | @IsBoolean() 14 | @IsOptional() 15 | isPrivate: boolean; 16 | 17 | // forks 18 | } 19 | -------------------------------------------------------------------------------- /server/src/modules/task/task.service.ts: -------------------------------------------------------------------------------- 1 | import { Model } from 'mongoose'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { InjectModel } from '@nestjs/mongoose'; 4 | import { Task } from './task.schema'; 5 | 6 | @Injectable() 7 | export class TasksService { 8 | constructor(@InjectModel(Task.name) private taskModel: Model) {} 9 | 10 | async findAll(): Promise { 11 | return this.taskModel.find().exec(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import ThemeToggle from '@/components/shared/theme-toogle' 2 | import { Button, Flex, Text } from '@radix-ui/themes' 3 | 4 | export default function Home() { 5 | return ( 6 |
7 | 8 |

Biprodas R.

9 | Hello from Radix Themes 10 | 11 |
12 | 13 |
14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /server/src/modules/task/task.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | import { Task, TaskSchema } from './task.schema'; 4 | import { TasksService } from './task.service'; 5 | import { TaskController } from './task.controller'; 6 | 7 | @Module({ 8 | imports: [MongooseModule.forFeature([{ name: Task.name, schema: TaskSchema }])], 9 | controllers: [TaskController], 10 | providers: [TasksService], 11 | }) 12 | export class TaskModule {} 13 | -------------------------------------------------------------------------------- /frontend/src/components/layout/header/navbar.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import Link from 'next/link' 4 | import { CurrentUser } from '@/types' 5 | import Banner from './banner' 6 | 7 | const Navbar = ({ currentUser }: { currentUser: CurrentUser }) => { 8 | return ( 9 | 15 | ) 16 | } 17 | 18 | export default Navbar 19 | -------------------------------------------------------------------------------- /server/src/configs/aws.config.ts: -------------------------------------------------------------------------------- 1 | import { registerAs } from '@nestjs/config'; 2 | 3 | export default registerAs( 4 | 'aws', 5 | (): Record => ({ 6 | credential: { 7 | key: process.env.AWS_CREDENTIAL_KEY, 8 | secret: process.env.AWS_CREDENTIAL_SECRET, 9 | }, 10 | s3: { 11 | bucket: process.env.AWS_S3_BUCKET ?? 'bucket', 12 | region: process.env.AWS_S3_REGION, 13 | baseUrl: `https://${process.env.AWS_S3_BUCKET}.s3.${process.env.AWS_S3_REGION}.amazonaws.com`, 14 | }, 15 | }), 16 | ); 17 | -------------------------------------------------------------------------------- /server/src/modules/word/word.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | import { Word, WordSchema } from './schemas/word.schema'; 4 | import { WordController } from './controllers/word.controller'; 5 | import { WordService } from './services/word.service'; 6 | 7 | @Module({ 8 | imports: [MongooseModule.forFeature([{ name: Word.name, schema: WordSchema }])], 9 | controllers: [WordController], 10 | providers: [WordService], 11 | exports: [], 12 | }) 13 | export class WordModule {} 14 | -------------------------------------------------------------------------------- /server/src/common/pipes/parse-object-id.pipe.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; 2 | import { Types } from 'mongoose'; 3 | 4 | @Injectable() 5 | export class ParseObjectIdPipe implements PipeTransform { 6 | transform(value: any): Types.ObjectId { 7 | const validObjectId = Types.ObjectId.isValid(value); 8 | 9 | if (!validObjectId) { 10 | throw new BadRequestException('Invalid ObjectId'); 11 | } 12 | 13 | return Types.ObjectId.createFromHexString(value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/hooks/use-scroll.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react' 2 | 3 | export default function useScroll(threshold: number) { 4 | const [scrolled, setScrolled] = useState(false) 5 | 6 | const onScroll = useCallback(() => { 7 | setScrolled(window.scrollY > threshold) 8 | }, [threshold]) 9 | 10 | useEffect(() => { 11 | onScroll() 12 | }, [onScroll]) 13 | 14 | useEffect(() => { 15 | window.addEventListener('scroll', onScroll) 16 | return () => window.removeEventListener('scroll', onScroll) 17 | }, [onScroll]) 18 | 19 | return scrolled 20 | } 21 | -------------------------------------------------------------------------------- /server/src/common/enums/error-type.enum.ts: -------------------------------------------------------------------------------- 1 | export enum ErrorType { 2 | InvalidToken = 'INVALID_TOKEN', 3 | AccessTokenExpired = 'ACCESS_TOKEN_EXPIRED', 4 | RefreshTokenExpired = 'REFRESH_TOKEN_EXPIRED', 5 | PermissionExists = 'PERMISSION_EXISTS', 6 | RoleExists = 'ROLE_EXISTS', 7 | UserExists = 'USER_EXISTS', 8 | InvalidCurrentPassword = 'INVALID_CURRENT_PASSWORD', 9 | InvalidCredentials = 'INVALID_CREDENTIALS', 10 | BlockedUser = 'BLOCKED_USER', 11 | InactiveUser = 'INACTIVE_USER', 12 | ForeignKeyConflict = 'FOREIGN_KEY_CONFLICT', 13 | ValidationError = 'VALIDATION_ERROR', 14 | } 15 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | 37 | # Environment varibales 38 | .env* 39 | !.env*.example 40 | -------------------------------------------------------------------------------- /server/src/router/routes/routes.auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | // import { AuthModule } from 'src/common/auth/auth.module'; 3 | // import { AwsModule } from 'src/common/aws/aws.module'; 4 | // import { UserAuthController } from 'src/modules/user/controllers/user.auth.controller'; 5 | // import { UserModule } from 'src/modules/user/user.module'; 6 | 7 | @Module({ 8 | controllers: [ 9 | // UserAuthController 10 | ], 11 | providers: [], 12 | exports: [], 13 | imports: [ 14 | // UserModule, AuthModule, AwsModule 15 | ], 16 | }) 17 | export class RoutesAuthModule {} 18 | -------------------------------------------------------------------------------- /server/src/app/controllers/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Version } from '@nestjs/common'; 2 | import { AppService } from '../services/app.service'; 3 | 4 | @Controller({ version: '1' }) 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get('/') 9 | getHello(): string { 10 | console.log('Get Hello Version 1'); 11 | return this.appService.getHello(); 12 | } 13 | 14 | @Version('2') 15 | @Get('/') 16 | getHelloV2(): string { 17 | console.log('Get Hello Version 2'); 18 | return this.appService.getHello(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/modules/dictionary/schemas/dictionary.schema.ts: -------------------------------------------------------------------------------- 1 | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; 2 | import { HydratedDocument } from 'mongoose'; 3 | 4 | @Schema({ timestamps: true }) 5 | export class Dictionary { 6 | @Prop({ required: true }) 7 | title: string; 8 | 9 | @Prop() 10 | description: string; 11 | 12 | @Prop() 13 | isPrivate: boolean; 14 | } 15 | 16 | export type DictionaryDocument = HydratedDocument; 17 | export const DictionarySchema = SchemaFactory.createForClass(Dictionary); 18 | 19 | // DictionarySchema.index({ documentId: 1 }, { unique: true }); 20 | -------------------------------------------------------------------------------- /frontend/src/app/error.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { Button } from '@radix-ui/themes' 4 | import { useEffect } from 'react' 5 | 6 | export default function GlobalError({ error, reset }: { error: Error; reset: () => void }) { 7 | useEffect(() => { 8 | console.error('Error: ', error) 9 | }, [error]) 10 | 11 | return ( 12 |
13 |

Oops, Something Went Wrong!

14 | 15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /frontend/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | const config: Config = { 4 | darkMode: ['class'], 5 | content: ['./src/**/*.{ts,tsx}'], 6 | theme: { 7 | extend: { 8 | backgroundImage: { 9 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 10 | 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 11 | }, 12 | }, 13 | }, 14 | plugins: [ 15 | require('tailwindcss-animate'), 16 | require('@tailwindcss/typography'), 17 | require('@tailwindcss/forms'), 18 | ], 19 | } 20 | export default config 21 | -------------------------------------------------------------------------------- /server/src/router/routes/routes.user.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | // import { ApiKeyModule } from 'src/common/api-key/api-key.module'; 3 | // import { RoleModule } from 'src/modules/role/role.module'; 4 | // import { UserUserController } from 'src/modules/user/controllers/user.user.controller'; 5 | // import { UserModule } from 'src/modules/user/user.module'; 6 | 7 | @Module({ 8 | controllers: [ 9 | // UserUserController 10 | ], 11 | providers: [], 12 | exports: [], 13 | imports: [ 14 | // UserModule, ApiKeyModule, RoleModule 15 | ], 16 | }) 17 | export class RoutesUserModule {} 18 | -------------------------------------------------------------------------------- /frontend/src/config/site.ts: -------------------------------------------------------------------------------- 1 | const siteUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' 2 | const apiBaseUrl = process.env.NEXT_PUBLIC_BASE_API_URL || 'http://localhost:3900' 3 | 4 | export const siteConfig = { 5 | name: 'WordBook', 6 | description: 'Easy vocabulary builder app', 7 | siteUrl, 8 | apiBaseUrl, 9 | author: { 10 | name: 'Biprodas Roy', 11 | website: 'https://biprodas.me', 12 | }, 13 | links: { 14 | linkedIn: 'https://www.linkedin.com/in/biprodas-roy', 15 | github: 'https://github.com/biprodas/wordbook', 16 | }, 17 | } 18 | 19 | export type SiteConfig = typeof siteConfig 20 | -------------------------------------------------------------------------------- /frontend/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | pnpm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # OS 15 | .DS_Store 16 | 17 | # Tests 18 | /coverage 19 | /.nyc_output 20 | 21 | # IDEs and editors 22 | /.idea 23 | .project 24 | .classpath 25 | .c9/ 26 | *.launch 27 | .settings/ 28 | *.sublime-workspace 29 | 30 | # IDE - VSCode 31 | .vscode/* 32 | !.vscode/settings.json 33 | !.vscode/tasks.json 34 | !.vscode/launch.json 35 | !.vscode/extensions.json 36 | 37 | 38 | # Environment varibales 39 | .env* 40 | !.env*.example 41 | -------------------------------------------------------------------------------- /server/src/modules/dictionary/dictionary.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | import { Dictionary, DictionarySchema } from './schemas/dictionary.schema'; 4 | import { DictionaryController } from './controllers/dictionary.controller'; 5 | import { DictionaryService } from './services/dictionary.service'; 6 | 7 | @Module({ 8 | imports: [MongooseModule.forFeature([{ name: Dictionary.name, schema: DictionarySchema }])], 9 | controllers: [DictionaryController], 10 | providers: [DictionaryService], 11 | exports: [], 12 | }) 13 | export class DictionaryModule {} 14 | -------------------------------------------------------------------------------- /server/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/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()).get('/').expect(200).expect('Hello World!'); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /server/src/job/jobs.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, ForwardReference, Module, Type } from '@nestjs/common'; 2 | import { ScheduleModule } from '@nestjs/schedule'; 3 | import { JobsRouterModule } from './router/jobs.router.module'; 4 | 5 | @Module({}) 6 | export class JobsModule { 7 | static forRoot(): DynamicModule { 8 | const imports: (DynamicModule | Type | Promise | ForwardReference)[] = 9 | []; 10 | 11 | if (process.env.JOB_ENABLE === 'true') { 12 | imports.push(ScheduleModule.forRoot(), JobsRouterModule); 13 | } 14 | 15 | return { 16 | module: JobsModule, 17 | providers: [], 18 | exports: [], 19 | controllers: [], 20 | imports, 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/src/app/controllers/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from '../services/app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /frontend/src/components/layout/header/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Navbar from './navbar' 3 | 4 | const Header = () => { 5 | const currentUser = { 6 | id: '1', 7 | name: 'Biprodas Roy', 8 | username: 'biprodas', 9 | email: 'email', 10 | shortBio: 'Sr. Software Engineer', 11 | image: '', 12 | } 13 | return ( 14 |
15 | {/* */} 16 |
17 |
WordBook
18 | 19 |
20 |
21 | ) 22 | } 23 | 24 | export default Header 25 | -------------------------------------------------------------------------------- /server/src/common/constants/constraint-errors.ts: -------------------------------------------------------------------------------- 1 | export const constraintErrors: Record = { 2 | // UQ_97672ac88f789774dd47f7c8be3: 'error.unique.email', 3 | // UQ_fe0bb3f6520ee0469504521e710: 'error.unique.username', 4 | UQ_fe0bb3f6520ee0469504521e710: 'Username already exists', 5 | UQ_97672ac88f789774dd47f7c8be3: 'User email already exists', 6 | UQ_1ea41502c6dddcec44ad9fcbbb3: 'Office id exists', 7 | UQ_e6da8701cf28df2e88a041daa67: 'Office email exists', 8 | 9 | // Members 10 | UQ_04221b29f19253f9ba048588dc3: 'Member mobile_no exists', 11 | UQ_05d635debffb9a6f7fcbbb543c9: 'Member email exists', 12 | UQ_7164e6a76b90875016ee3f3bac0: 'Member id_no exists', 13 | UQ_75f38e68ae1592713dc4ef079bc: 'Member id_prev exists', 14 | UQ_abf49a2990fc5865dbfeaf17c3c: 'Member applicaiton_no exists', 15 | }; 16 | -------------------------------------------------------------------------------- /frontend/src/components/layout/footer/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import dayjs from 'dayjs' 3 | import { Link } from '@radix-ui/themes' 4 | import { siteConfig } from '@/config/site' 5 | 6 | export default function Footer() { 7 | return ( 8 |
9 |
10 | 11 | WordBook 12 | © {dayjs().format('YYYY')} all right reserved 13 | 14 | 15 | Developed with ♥ by 16 | 17 | Biprodas R. 18 | 19 | 20 |
21 |
22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /server/src/common/common.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | import { DatabaseOptionsService } from 'src/common/database/services/database.options.service'; 4 | import { DatabaseOptionsModule } from 'src/common/database/database.options.module'; 5 | import { DATABASE_CONNECTION_NAME } from 'src/common/database/constants/database.constant'; 6 | 7 | @Module({ 8 | controllers: [], 9 | providers: [], 10 | imports: [ 11 | MongooseModule.forRootAsync({ 12 | connectionName: DATABASE_CONNECTION_NAME, 13 | imports: [DatabaseOptionsModule], 14 | inject: [DatabaseOptionsService], 15 | useFactory: (databaseOptionsService: DatabaseOptionsService) => 16 | databaseOptionsService.createOptions(), 17 | }), 18 | ], 19 | }) 20 | export class CommonModule {} 21 | -------------------------------------------------------------------------------- /server/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | tsconfigRootDir: __dirname, 6 | sourceType: 'module', 7 | }, 8 | plugins: ['@typescript-eslint/eslint-plugin'], 9 | extends: [ 10 | 'plugin:@typescript-eslint/recommended', 11 | 'plugin:prettier/recommended', 12 | ], 13 | root: true, 14 | env: { 15 | node: true, 16 | jest: true, 17 | }, 18 | ignorePatterns: ['.eslintrc.js'], 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/explicit-module-boundary-types': 'off', 23 | '@typescript-eslint/no-explicit-any': 'off', 24 | '@typescript-eslint/no-explicit-any': 'off', 25 | // 'no-unused-vars': 'off', 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts AS dist 2 | COPY package.json ./ 3 | 4 | RUN npm install 5 | 6 | COPY . ./ 7 | 8 | RUN npm run build:prod 9 | 10 | FROM node:lts AS node_modules 11 | COPY package.json ./ 12 | 13 | RUN npm install --prod 14 | 15 | FROM node:lts 16 | 17 | ARG PORT=3000 18 | 19 | RUN mkdir -p /usr/src/app 20 | 21 | WORKDIR /usr/src/app 22 | 23 | COPY --from=dist dist /usr/src/app/dist 24 | COPY --from=node_modules node_modules /usr/src/app/node_modules 25 | 26 | COPY . /usr/src/app 27 | 28 | EXPOSE $PORT 29 | 30 | CMD ["npm", "run", "start:prod"] 31 | 32 | 33 | # FROM node:14.16.0-alpine3.13 34 | 35 | # RUN addgroup app && adduser -S -G app app 36 | # RUN mkdir /app && chown app:app /app 37 | # USER app 38 | 39 | # WORKDIR /app 40 | # COPY package*.json ./ 41 | # RUN npm install 42 | # COPY . ./ 43 | 44 | # EXPOSE 3900 45 | 46 | # CMD ["npm", "start"] -------------------------------------------------------------------------------- /server/src/common/helper/constants/helper.enum.constant.ts: -------------------------------------------------------------------------------- 1 | export enum ENUM_HELPER_DATE_FORMAT { 2 | DATE = 'YYYY-MM-DD', 3 | FRIENDLY_DATE = 'MMM, DD YYYY', 4 | FRIENDLY_DATE_TIME = 'MMM, DD YYYY HH:MM:SS', 5 | YEAR_MONTH = 'YYYY-MM', 6 | MONTH_DATE = 'MM-DD', 7 | ONLY_YEAR = 'YYYY', 8 | ONLY_MONTH = 'MM', 9 | ONLY_DATE = 'DD', 10 | ISO_DATE = 'YYYY-MM-DDTHH:MM:SSZ', 11 | DAY_LONG = 'dddd', 12 | DAY_SHORT = 'ddd', 13 | HOUR_LONG = 'HH', 14 | HOUR_SHORT = 'H', 15 | MINUTE_LONG = 'mm', 16 | MINUTE_SHORT = 'm', 17 | SECOND_LONG = 'ss', 18 | SECOND_SHORT = 's', 19 | } 20 | 21 | export enum ENUM_HELPER_DATE_DIFF { 22 | MILIS = 'milis', 23 | SECONDS = 'seconds', 24 | HOURS = 'hours', 25 | DAYS = 'days', 26 | MINUTES = 'minutes', 27 | } 28 | 29 | export enum ENUM_HELPER_FILE_TYPE { 30 | XLSX = 'xlsx', 31 | XLS = 'xls', 32 | CSV = 'csv', 33 | } 34 | -------------------------------------------------------------------------------- /server/src/configs/index.ts: -------------------------------------------------------------------------------- 1 | import AppConfig from './app.config'; 2 | import databaseConfig from './database.config'; 3 | // import AuthConfig from './auth.config'; 4 | // import DatabaseConfig from './database.config'; 5 | // import HelperConfig from './helper.config'; 6 | // import AwsConfig from './aws.config'; 7 | // import UserConfig from './user.config'; 8 | // import FileConfig from './file.config'; 9 | // import RequestConfig from './request.config'; 10 | // import DocConfig from './doc.config'; 11 | // import DebuggerConfig from './debugger.config'; 12 | // import MessageConfig from './message.config'; 13 | 14 | export default [ 15 | AppConfig, 16 | // AuthConfig, 17 | databaseConfig, 18 | // HelperConfig, 19 | // AwsConfig, 20 | // UserConfig, 21 | // RequestConfig, 22 | // FileConfig, 23 | // DocConfig, 24 | // DebuggerConfig, 25 | // MessageConfig, 26 | ]; 27 | -------------------------------------------------------------------------------- /frontend/src/components/shared/theme-toogle.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { LaptopIcon, MoonIcon, SunIcon } from '@radix-ui/react-icons' 4 | import { Button, DropdownMenu } from '@radix-ui/themes' 5 | import { useTheme } from 'next-themes' 6 | 7 | export default function ThemeToggle() { 8 | const { setTheme, theme } = useTheme() 9 | 10 | console.log(theme) 11 | 12 | return ( 13 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /server/src/modules/word/dtos/word.dto.ts: -------------------------------------------------------------------------------- 1 | import { Expose } from 'class-transformer'; 2 | import { LangOpt } from '../schemas/word.schema'; 3 | 4 | export class WordDto { 5 | @Expose() 6 | id: string; 7 | 8 | @Expose() 9 | word: string; 10 | 11 | @Expose() 12 | details: string; 13 | 14 | @Expose() 15 | pronounce: string; 16 | 17 | @Expose() 18 | partOfSpeech: [string]; 19 | 20 | @Expose() 21 | definition: LangOpt[]; 22 | 23 | @Expose() 24 | examples: LangOpt[]; 25 | 26 | @Expose() 27 | mnemonics: string[]; 28 | 29 | @Expose() 30 | audios: string[]; 31 | 32 | @Expose() 33 | videos: string[]; 34 | 35 | @Expose() 36 | level: number; 37 | 38 | @Expose() 39 | tags: string[]; 40 | 41 | @Expose() 42 | isFavorite: boolean; 43 | 44 | @Expose() 45 | isKnown: boolean; 46 | 47 | @Expose() 48 | synonyms: string[]; 49 | 50 | @Expose() 51 | antonyms: string[]; 52 | 53 | @Expose() 54 | similarWords: string[]; 55 | } 56 | -------------------------------------------------------------------------------- /server/src/common/constants/http-error-type.ts: -------------------------------------------------------------------------------- 1 | export const HttpErrorType = { 2 | 400: 'BAD_REQUEST', 3 | 401: 'UNAUTHORIZED', 4 | 402: 'PAYMENT_REQUIRED', 5 | 403: 'FORBIDDEN', 6 | 404: 'NOT_FOUND', 7 | 405: 'METHOD_NOT_ALLOWED', 8 | 406: 'NOT_ACCEPTABLE', 9 | 407: 'PROXY_AUTHENTICATION_REQUIRED', 10 | 408: 'REQUEST_TIMEOUT', 11 | 409: 'CONFLICT', 12 | 410: 'GONE', 13 | 411: 'LENGTH_REQUIRED', 14 | 412: 'PRECONDITION_FAILED', 15 | 413: 'PAYLOAD_TOO_LARGE', 16 | 414: 'URI_TOO_LONG', 17 | 415: 'UNSUPPORTED_MEDIA_TYPE', 18 | 416: 'REQUESTED_RANGE_NOT_SATISFIABLE', 19 | 417: 'EXPECTATION_FAILED', 20 | 418: 'I_AM_A_TEAPOT', 21 | 421: 'MISDIRECTED', 22 | 422: 'UNPROCESSABLE_ENTITY', 23 | 424: 'FAILED_DEPENDENCY', 24 | 429: 'TOO_MANY_REQUESTS', 25 | 500: 'INTERNAL_SERVER_ERROR', 26 | 501: 'NOT_IMPLEMENTED', 27 | 502: 'BAD_GATEWAY', 28 | 503: 'SERVICE_UNAVAILABLE', 29 | 504: 'GATEWAY_TIMEOUT', 30 | 505: 'HTTP_VERSION_NOT_SUPPORTED', 31 | }; 32 | -------------------------------------------------------------------------------- /server/src/router/routes/routes.admin.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | // import { ApiKeyModule } from 'src/common/api-key/api-key.module'; 3 | // import { ApiKeyAdminController } from 'src/common/api-key/controllers/api-key.admin.controller'; 4 | // import { AuthModule } from 'src/common/auth/auth.module'; 5 | // import { RoleAdminController } from 'src/modules/role/controllers/role.admin.controller'; 6 | // import { RoleModule } from 'src/modules/role/role.module'; 7 | // import { SettingAdminController } from 'src/common/setting/controllers/setting.admin.controller'; 8 | // import { UserAdminController } from 'src/modules/user/controllers/user.admin.controller'; 9 | // import { UserModule } from 'src/modules/user/user.module'; 10 | 11 | @Module({ 12 | controllers: [ 13 | // SettingAdminController, 14 | // ApiKeyAdminController, 15 | // RoleAdminController, 16 | // UserAdminController, 17 | ], 18 | providers: [], 19 | exports: [], 20 | imports: [ 21 | // ApiKeyModule, RoleModule, UserModule, AuthModule 22 | ], 23 | }) 24 | export class RoutesAdminModule {} 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Biprodas Roy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@headlessui/react": "^1.7.17", 13 | "@radix-ui/react-icons": "^1.3.0", 14 | "@radix-ui/react-toast": "^1.1.4", 15 | "@radix-ui/themes": "^1.1.2", 16 | "@tailwindcss/forms": "^0.5.5", 17 | "@tailwindcss/typography": "^0.5.9", 18 | "@types/node": "20.5.6", 19 | "@types/react": "18.2.21", 20 | "@types/react-dom": "18.2.7", 21 | "@vercel/analytics": "^1.0.2", 22 | "autoprefixer": "10.4.15", 23 | "clsx": "^2.0.0", 24 | "dayjs": "^1.11.9", 25 | "eslint": "8.48.0", 26 | "eslint-config-next": "13.4.19", 27 | "next": "13.4.19", 28 | "next-themes": "^0.2.1", 29 | "postcss": "8.4.28", 30 | "react": "18.2.0", 31 | "react-dom": "18.2.0", 32 | "react-icons": "^4.10.1", 33 | "tailwind-merge": "^1.14.0", 34 | "tailwindcss": "3.3.3", 35 | "tailwindcss-animate": "^1.0.6", 36 | "typescript": "5.2.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/src/router/routes/routes.public.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | // import { TerminusModule } from '@nestjs/terminus'; 3 | // import { AuthModule } from 'src/common/auth/auth.module'; 4 | // import { HealthModule } from 'src/health/health.module'; 5 | // import { HealthPublicController } from 'src/health/controllers/health.public.controller'; 6 | // import { MessagePublicController } from 'src/common/message/controllers/message.public.controller'; 7 | // import { SettingPublicController } from 'src/common/setting/controllers/setting.public.controller'; 8 | // import { UserPublicController } from 'src/modules/user/controllers/user.public.controller'; 9 | // import { UserModule } from 'src/modules/user/user.module'; 10 | // import { RoleModule } from 'src/modules/role/role.module'; 11 | 12 | @Module({ 13 | controllers: [ 14 | // HealthPublicController, 15 | // MessagePublicController, 16 | // SettingPublicController, 17 | // UserPublicController, 18 | ], 19 | providers: [], 20 | exports: [], 21 | imports: [ 22 | // TerminusModule, HealthModule, UserModule, AuthModule, RoleModule 23 | ], 24 | }) 25 | export class RoutesPublicModule {} 26 | -------------------------------------------------------------------------------- /server/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": "ES2021", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "paths": { 15 | "@common/*":["src/common/*"], 16 | // "@shared/*":["src/shared/*"], 17 | // "@database/*":["src/database/*"], 18 | "@config":["src/configs"], 19 | // "@helpers":["src/utils/helpers"], 20 | // "@subscribers/*":["src/entity-subscribers/*"], 21 | // "@auth":["src/modules/admin/auth"], 22 | // "@admin/*":["src/modules/admin/*"], 23 | // "@log":["src/modules/other/log"], 24 | "@modules/*":["src/modules/*"], 25 | }, 26 | "skipLibCheck": true, 27 | "strictNullChecks": false, 28 | "noImplicitAny": false, 29 | "strictBindCallApply": false, 30 | "forceConsistentCasingInFileNames": false, 31 | "noFallthroughCasesInSwitch": false, 32 | "resolveJsonModule": true, 33 | "esModuleInterop": true, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /server/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule } from '@nestjs/config'; 3 | import configs from '../configs'; 4 | import { configValidationSchema } from '../configs/config-validation.schema'; 5 | import { JobsModule } from '../job/jobs.module'; 6 | import { AppService } from './services/app.service'; 7 | import { AppController } from './controllers/app.controller'; 8 | import { TaskModule } from '@modules/task/task.module'; 9 | import { DatabaseModule } from 'src/database/database.module'; 10 | import { DictionaryModule } from '@modules/dictionary/dictionary.module'; 11 | import { WordModule } from '@modules/word/word.module'; 12 | 13 | @Module({ 14 | imports: [ 15 | ConfigModule.forRoot({ 16 | load: configs, 17 | isGlobal: true, 18 | cache: true, 19 | // envFilePath: ['.env'], 20 | envFilePath: `.env.${process.env.NODE_ENV}`, 21 | validationSchema: configValidationSchema, 22 | validationOptions: { 23 | allowUnknown: true, 24 | abortEarly: true, 25 | }, 26 | }), 27 | DatabaseModule, 28 | // CommonModule, 29 | DictionaryModule, 30 | WordModule, 31 | TaskModule, 32 | JobsModule.forRoot(), 33 | ], 34 | controllers: [AppController], 35 | providers: [AppService], 36 | }) 37 | export class AppModule {} 38 | -------------------------------------------------------------------------------- /frontend/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/src/modules/word/services/word.service.ts: -------------------------------------------------------------------------------- 1 | import { Model, ObjectId } from 'mongoose'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { InjectModel } from '@nestjs/mongoose'; 4 | import { Word } from '../schemas/word.schema'; 5 | import { FilterWordDto } from '../dtos/filter-word.dto'; 6 | import { UpdateWordDto } from '../dtos/update-word.dto'; 7 | 8 | @Injectable() 9 | export class WordService { 10 | constructor(@InjectModel(Word.name) private wordModel: Model) {} 11 | 12 | async getAll(filterWordDto: FilterWordDto): Promise { 13 | console.log(filterWordDto); 14 | return this.wordModel.find(); 15 | } 16 | 17 | async getOne(id: ObjectId): Promise { 18 | return this.wordModel.findById(id); 19 | } 20 | 21 | async findOne(filterWordDto: FilterWordDto): Promise { 22 | const { word } = filterWordDto; 23 | return this.wordModel.findOne({ word }); 24 | } 25 | 26 | async create(createWordDto: FilterWordDto): Promise { 27 | console.log(createWordDto); 28 | // through error for duplicate word 29 | return this.wordModel.create(createWordDto); 30 | } 31 | 32 | async update(id: string, updateWordDto: UpdateWordDto): Promise { 33 | return this.wordModel.findByIdAndUpdate(id, updateWordDto); 34 | } 35 | 36 | delete(id: string): Promise { 37 | return this.wordModel.findByIdAndRemove(id); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /server/src/configs/app.config.ts: -------------------------------------------------------------------------------- 1 | import { registerAs } from '@nestjs/config'; 2 | import { version } from 'package.json'; 3 | import { Environment } from '@common/enums/environment.enum'; 4 | import { AppConfig } from './config.type'; 5 | 6 | export default registerAs('app', () => { 7 | // validateConfig(process.env, EnvironmentVariablesValidator); 8 | 9 | return { 10 | name: process.env.APP_NAME ?? 'app', 11 | nodeEnv: process.env.APP_ENV ?? Environment.Development, 12 | 13 | repoVersion: version, 14 | versioning: { 15 | enable: process.env.HTTP_VERSIONING_ENABLE === 'true' ?? false, 16 | prefix: 'v', 17 | version: process.env.HTTP_VERSION ?? '1', 18 | }, 19 | apiPrefix: process.env.API_PREFIX || '/api', 20 | http: { 21 | enable: process.env.HTTP_ENABLE === 'true' ?? false, 22 | host: process.env.HTTP_HOST ?? 'localhost', 23 | port: process.env.HTTP_PORT ? Number.parseInt(process.env.HTTP_PORT) : 5000, 24 | }, 25 | 26 | jobEnable: process.env.JOB_ENABLE === 'true' ?? false, 27 | 28 | // workingDirectory: process.env.PWD || process.cwd(), 29 | // frontendDomain: process.env.FRONTEND_DOMAIN, 30 | // backendDomain: process.env.BACKEND_DOMAIN ?? 'http://localhost', 31 | 32 | fallbackLanguage: process.env.APP_FALLBACK_LANGUAGE || 'en', 33 | // headerLanguage: process.env.APP_HEADER_LANGUAGE || 'x-custom-lang', 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /frontend/src/app/about/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function AboutPage() { 4 | return ( 5 |
6 |

about

7 |

about

8 |

about

9 |

abouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuut

10 |

about

11 |

about

12 |

about

13 |

about

14 |

about

15 |

about

16 |

about

17 |

about

18 |

about

19 |

about

20 |

about

21 |

abouttttttttttttttttttttttttttttttttttttttttttttttttttttttt

22 |

about

23 |

about

24 |

about

25 |

about

26 |

about

27 |

about

28 |

about

29 |

about

30 |

about

31 |

about

32 |

about

33 |

about

34 |

about

35 |

about

36 |

about

37 |

about

38 |

about

39 |

about

40 |

about

41 |

about

42 |

about

43 |

about

44 |

about

45 |

about

46 |

about

47 |

about

48 |

about

49 |

about

50 |

about

51 |

about

52 |

about

53 |

about

54 |
55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /server/src/modules/word/schemas/word.schema.ts: -------------------------------------------------------------------------------- 1 | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; 2 | import { HydratedDocument } from 'mongoose'; 3 | 4 | export type LangOpt = { 5 | en: string; 6 | bn: string; 7 | }; 8 | 9 | @Schema({ timestamps: true }) 10 | export class Word { 11 | @Prop({ required: true }) 12 | word: string; 13 | 14 | // dictionaryId 15 | 16 | @Prop() 17 | slug: string; 18 | 19 | @Prop() 20 | details: string; 21 | 22 | // [{ opt: 'en'; text: '', audio: ''; video: '' }]; 23 | @Prop() 24 | pronounce: string; 25 | 26 | @Prop() 27 | partOfSpeech: [string]; 28 | 29 | @Prop({ type: [{ en: String, bn: String }] }) 30 | definition: LangOpt[]; 31 | 32 | @Prop({ type: [{ en: String, bn: String }] }) 33 | examples: LangOpt[]; 34 | 35 | @Prop([String]) 36 | mnemonics: string[]; 37 | 38 | @Prop([String]) 39 | audios: string[]; 40 | 41 | @Prop([String]) 42 | videos: string[]; 43 | 44 | @Prop() 45 | level: number; 46 | 47 | @Prop([String]) 48 | tags: string[]; 49 | 50 | @Prop() 51 | isFavorite: boolean; 52 | 53 | @Prop() 54 | isKnown: boolean; 55 | 56 | @Prop([String]) 57 | synonyms: string[]; 58 | 59 | @Prop([String]) 60 | antonyms: string[]; 61 | 62 | @Prop([String]) 63 | similarWords: string[]; 64 | } 65 | 66 | export type WordDocument = HydratedDocument; 67 | export const WordSchema = SchemaFactory.createForClass(Word); 68 | 69 | // WordSchema.index({ documentId: 1 }, { unique: true }); 70 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 18 | 19 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /frontend/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import './globals.css' 2 | import '@radix-ui/themes/styles.css' 3 | import type { Metadata } from 'next' 4 | import { Inter } from 'next/font/google' 5 | import { Analytics } from '@vercel/analytics/react' 6 | import { Theme } from '@radix-ui/themes' 7 | import ThemeProvider from '@/components/shared/theme-provider' 8 | import Header from '@/components/layout/header' 9 | import Footer from '@/components/layout/footer' 10 | 11 | const inter = Inter({ subsets: ['latin'] }) 12 | 13 | export const metadata: Metadata = { 14 | title: 'WordBook', 15 | description: 'Easy vocabulary builder app', 16 | } 17 | 18 | export default function RootLayout({ children }: { children: React.ReactNode }) { 19 | return ( 20 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |
{children}
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /server/src/modules/dictionary/services/dictionary.service.ts: -------------------------------------------------------------------------------- 1 | import { Model } from 'mongoose'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { InjectModel } from '@nestjs/mongoose'; 4 | import { Dictionary } from '../schemas/dictionary.schema'; 5 | import { FilterDictionaryDto } from '../dtos/filter-dictionary.dto'; 6 | import { UpdateDictionaryDto } from '../dtos/update-dictionary.dto'; 7 | 8 | @Injectable() 9 | export class DictionaryService { 10 | constructor(@InjectModel(Dictionary.name) private dictionaryModel: Model) {} 11 | 12 | async getAll(filterDictionaryDto: FilterDictionaryDto): Promise { 13 | console.log(filterDictionaryDto); 14 | return this.dictionaryModel.find().exec(); 15 | } 16 | 17 | async getOne(id: string): Promise { 18 | return this.dictionaryModel.findById(id).exec(); 19 | } 20 | 21 | async findOne(filterDictionaryDto: FilterDictionaryDto): Promise { 22 | const { title } = filterDictionaryDto; 23 | return this.dictionaryModel.findOne({ title }).exec(); 24 | } 25 | 26 | async create(createDictionaryDto: FilterDictionaryDto): Promise { 27 | console.log(createDictionaryDto); 28 | return this.dictionaryModel.create(createDictionaryDto); 29 | } 30 | 31 | async update(id: string, updateDictionaryDto: UpdateDictionaryDto): Promise { 32 | return this.dictionaryModel.findByIdAndUpdate(id, updateDictionaryDto).exec(); 33 | } 34 | 35 | delete(id: string): Promise { 36 | return this.dictionaryModel.findByIdAndRemove(id).exec(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME="NestJS Boilerplate" 2 | APP_ENV=development 3 | APP_LANGUAGE=en 4 | 5 | HTTP_ENABLE=true 6 | HTTP_HOST=localhost 7 | HTTP_PORT= 3900 8 | HTTP_VERSIONING_ENABLE=true 9 | HTTP_VERSION=1 10 | 11 | DEBUGGER_HTTP_WRITE_INTO_FILE=false 12 | DEBUGGER_HTTP_WRITE_INTO_CONSOLE=false 13 | DEBUGGER_SYSTEM_WRITE_INTO_FILE=false 14 | DEBUGGER_SYSTEM_WRITE_INTO_CONSOLE=false 15 | 16 | JOB_ENABLE=false 17 | 18 | MONGODB_URI= 19 | DATABASE_HOST=mongodb://localhost:30001,localhost:30002,localhost:30003 20 | DATABASE_NAME=test_db 21 | DATABASE_USER= 22 | DATABASE_PASSWORD= 23 | DATABASE_DEBUG=false 24 | DATABASE_OPTIONS=replicaSet=rs0&retryWrites=true&w=majority 25 | 26 | AUTH_JWT_SUBJECT=RyDevelopment 27 | AUTH_JWT_ISSUER=ry 28 | AUTH_JWT_AUDIENCE=https://example.com 29 | 30 | AUTH_JWT_ACCESS_TOKEN_SECRET_KEY=1234567890 31 | AUTH_JWT_ACCESS_TOKEN_EXPIRED=1h 32 | 33 | AUTH_JWT_REFRESH_TOKEN_SECRET_KEY=0987654321 34 | AUTH_JWT_REFRESH_TOKEN_EXPIRED=14d 35 | AUTH_JWT_REFRESH_TOKEN_NOT_BEFORE_EXPIRATION=1h 36 | 37 | AUTH_JWT_PAYLOAD_ENCRYPT=false 38 | AUTH_JWT_PAYLOAD_ACCESS_TOKEN_ENCRYPT_KEY=qwerty 39 | AUTH_JWT_PAYLOAD_ACCESS_TOKEN_ENCRYPT_IV=123456 40 | AUTH_JWT_PAYLOAD_REFRESH_TOKEN_ENCRYPT_KEY=ytrewq 41 | AUTH_JWT_PAYLOAD_REFRESH_TOKEN_ENCRYPT_IV=654321 42 | 43 | AWS_CREDENTIAL_KEY= 44 | AWS_CREDENTIAL_SECRET= 45 | AWS_S3_REGION=ap-southeast-3 46 | AWS_S3_BUCKET= 47 | 48 | SSO_GOOGLE_CLIENT_ID= 49 | SSO_GOOGLE_CLIENT_SECRET= 50 | SSO_GOOGLE_CALLBACK_URL_LOGIN=http://localhost:3000/api/v1/public/user/login/google/callback 51 | SSO_GOOGLE_CALLBACK_URL_SIGN_UP=http://localhost:3000/api/v1/public/user/sign-up/google/callback -------------------------------------------------------------------------------- /server/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | volumes: 4 | db: # mount it internally instead of a folder. it avoids file lock problems 5 | 6 | services: 7 | app: 8 | env_file: 9 | - .env 10 | container_name: backend 11 | restart: always 12 | build: . 13 | ports: 14 | - '$PORT:$PORT' 15 | links: 16 | - postgres 17 | 18 | mysql: 19 | image: mysql:8.0.23 20 | command: --default-authentication-plugin=mysql_native_password 21 | restart: always 22 | environment: 23 | MYSQL_ROOT_PASSWORD: example 24 | ports: 25 | - 3306:3306 26 | 27 | postgres: 28 | image: postgres:13.1 29 | container_name: postgres 30 | restart: always 31 | environment: 32 | POSTGRES_DB: matir_bank 33 | POSTGRES_USER: postgres 34 | POSTGRES_PASSWORD: 101 35 | TZ: 'GMT' 36 | PGTZ: 'GMT' 37 | ports: 38 | - 5432:5432 39 | volumes: 40 | - db:/var/lib/postgresql/data 41 | 42 | mongodb_ecbe: 43 | image: sathyajith/mongo-replica-set:5.0.6 44 | container_name: mongodb 45 | ports: 46 | - 27017:27017 47 | - 27018:27018 48 | - 27019:27019 49 | 50 | redis: 51 | image: 'redis:alpine' 52 | container_name: redis 53 | ports: 54 | - '6379:6379' 55 | 56 | rabbitmq: 57 | image: rabbitmq:3-management-alpine 58 | container_name: rabbitmq 59 | ports: 60 | - 5672:5672 61 | - 15672:15672 62 | 63 | adminer: 64 | image: adminer 65 | restart: always 66 | links: 67 | - postgres 68 | ports: 69 | - 8081:8080 70 | 71 | networks: 72 | default: 73 | name: backend 74 | -------------------------------------------------------------------------------- /server/src/router/router.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, ForwardReference, Module, Type } from '@nestjs/common'; 2 | import { RouterModule as NestJsRouterModule } from '@nestjs/core'; 3 | import { RoutesPublicModule } from './routes/routes.public.module'; 4 | import { RoutesUserModule } from './routes/routes.user.module'; 5 | import { RoutesAuthModule } from './routes/routes.auth.module'; 6 | import { RoutesAdminModule } from './routes/routes.admin.module'; 7 | import { AppController } from 'src/app/controllers/app.controller'; 8 | 9 | @Module({}) 10 | export class RouterModule { 11 | static forRoot(): DynamicModule { 12 | const imports: (DynamicModule | Type | Promise | ForwardReference)[] = 13 | []; 14 | 15 | if (process.env.HTTP_ENABLE === 'true') { 16 | imports.push( 17 | RoutesPublicModule, 18 | RoutesUserModule, 19 | RoutesAdminModule, 20 | RoutesAuthModule, 21 | NestJsRouterModule.register([ 22 | { 23 | path: '/public', 24 | module: RoutesPublicModule, 25 | }, 26 | { 27 | path: '/admin', 28 | module: RoutesAdminModule, 29 | }, 30 | { 31 | path: '/user', 32 | module: RoutesUserModule, 33 | }, 34 | { 35 | path: '/auth', 36 | module: RoutesAuthModule, 37 | }, 38 | ]), 39 | ); 40 | } 41 | 42 | return { 43 | module: RouterModule, 44 | providers: [], 45 | exports: [], 46 | controllers: [AppController], 47 | imports, 48 | }; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /server/src/common/database/services/database.options.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { MongooseModuleOptions } from '@nestjs/mongoose'; 3 | import mongoose from 'mongoose'; 4 | import { ConfigService } from '@nestjs/config'; 5 | import { IDatabaseOptionsService } from '../interfaces/database.options-service.interface'; 6 | import { ENUM_APP_ENVIRONMENT } from 'src/app/constants/app.enum.constant'; 7 | import { AppConfig, DatabaseConfig } from 'src/configs/config.type'; 8 | 9 | @Injectable() 10 | export class DatabaseOptionsService implements IDatabaseOptionsService { 11 | constructor(private readonly configService: ConfigService) {} 12 | 13 | createOptions(): MongooseModuleOptions { 14 | const appConfig = this.configService.get('app'); 15 | const dbConfig = this.configService.get('database'); 16 | 17 | // console.log(appConfig.nodeEnv, dbConfig); 18 | 19 | const options = dbConfig.options ? `?${dbConfig.options}` : ''; 20 | 21 | let uri = `${dbConfig.host}`; 22 | 23 | if (dbConfig.name) { 24 | uri = `${uri}/${dbConfig.name}${options}`; 25 | } 26 | 27 | if (appConfig.nodeEnv !== ENUM_APP_ENVIRONMENT.PRODUCTION) { 28 | mongoose.set('debug', dbConfig.debug); 29 | } 30 | 31 | const mongooseOptions: MongooseModuleOptions = { 32 | uri, 33 | serverSelectionTimeoutMS: 5000, 34 | autoCreate: true, 35 | }; 36 | 37 | if (dbConfig.username && dbConfig.password) { 38 | mongooseOptions.auth = { 39 | username: dbConfig.username, 40 | password: dbConfig.password, 41 | }; 42 | } 43 | 44 | return mongooseOptions; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /server/src/common/helper/helper.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from '@nestjs/common'; 2 | import { JwtModule } from '@nestjs/jwt'; 3 | import { ConfigModule, ConfigService } from '@nestjs/config'; 4 | // import { HelperArrayService } from './services/helper.array.service'; 5 | import { HelperDateService } from './services/helper.date.service'; 6 | // import { HelperEncryptionService } from './services/helper.encryption.service'; 7 | // import { HelperHashService } from './services/helper.hash.service'; 8 | // import { HelperNumberService } from './services/helper.number.service'; 9 | // import { HelperStringService } from './services/helper.string.service'; 10 | // import { HelperFileService } from './services/helper.file.service'; 11 | 12 | @Global() 13 | @Module({ 14 | providers: [ 15 | // HelperArrayService, 16 | HelperDateService, 17 | // HelperEncryptionService, 18 | // HelperHashService, 19 | // HelperNumberService, 20 | // HelperStringService, 21 | // HelperFileService, 22 | ], 23 | exports: [ 24 | // HelperArrayService, 25 | HelperDateService, 26 | // HelperEncryptionService, 27 | // HelperHashService, 28 | // HelperNumberService, 29 | // HelperStringService, 30 | // HelperFileService, 31 | ], 32 | controllers: [], 33 | imports: [ 34 | JwtModule.registerAsync({ 35 | inject: [ConfigService], 36 | imports: [ConfigModule], 37 | useFactory: (configService: ConfigService) => ({ 38 | secret: configService.get('helper.jwt.defaultSecretKey'), 39 | signOptions: { 40 | expiresIn: configService.get('helper.jwt.defaultExpirationTime'), 41 | }, 42 | }), 43 | }), 44 | ], 45 | }) 46 | export class HelperModule {} 47 | -------------------------------------------------------------------------------- /server/src/modules/word/dtos/update-word.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ArrayNotEmpty, 3 | IsArray, 4 | IsBoolean, 5 | IsNotEmpty, 6 | IsNumber, 7 | IsOptional, 8 | IsString, 9 | } from 'class-validator'; 10 | import { LangOpt } from '../schemas/word.schema'; 11 | 12 | export class UpdateWordDto { 13 | @IsString() 14 | @IsNotEmpty() 15 | word: string; 16 | 17 | @IsString() 18 | @IsOptional() 19 | details: string; 20 | 21 | @IsString() 22 | @IsOptional() 23 | pronounce: string; 24 | 25 | @IsArray() 26 | @IsString({ each: true }) 27 | @IsOptional() 28 | partOfSpeech: [string]; 29 | 30 | @IsArray() 31 | @ArrayNotEmpty({ each: true }) 32 | @IsOptional() 33 | definition: LangOpt[]; 34 | 35 | @IsArray() 36 | @ArrayNotEmpty({ each: true }) 37 | @IsOptional() 38 | examples: LangOpt[]; 39 | 40 | @IsArray() 41 | @IsString({ each: true }) 42 | @IsOptional() 43 | mnemonics: string[]; 44 | 45 | @IsArray() 46 | @IsString({ each: true }) 47 | @IsOptional() 48 | audios: string[]; 49 | 50 | @IsArray() 51 | @IsString({ each: true }) 52 | @IsOptional() 53 | videos: string[]; 54 | 55 | @IsNumber() 56 | @IsOptional() 57 | level: number; 58 | 59 | @IsArray() 60 | @IsString({ each: true }) 61 | @IsOptional() 62 | tags: string[]; 63 | 64 | @IsBoolean() 65 | @IsOptional() 66 | isFavorite: boolean; 67 | 68 | @IsBoolean() 69 | @IsOptional() 70 | isKnown: boolean; 71 | 72 | @IsArray() 73 | @IsString({ each: true }) 74 | @IsOptional() 75 | synonyms: string[]; 76 | 77 | @IsArray() 78 | @IsString({ each: true }) 79 | @IsOptional() 80 | antonyms: string[]; 81 | 82 | @IsArray() 83 | @IsString({ each: true }) 84 | @IsOptional() 85 | similarWords: string[]; 86 | } 87 | -------------------------------------------------------------------------------- /server/src/modules/word/dtos/create-word.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ArrayNotEmpty, 3 | IsArray, 4 | IsBoolean, 5 | IsDefined, 6 | IsNotEmpty, 7 | IsNumber, 8 | IsOptional, 9 | IsString, 10 | } from 'class-validator'; 11 | import { LangOpt } from '../schemas/word.schema'; 12 | 13 | export class CreateWordDto { 14 | @IsString() 15 | @IsNotEmpty() 16 | @IsDefined() 17 | word: string; 18 | 19 | @IsString() 20 | @IsOptional() 21 | details: string; 22 | 23 | @IsString() 24 | @IsOptional() 25 | pronounce: string; 26 | 27 | @IsArray() 28 | @IsString({ each: true }) 29 | @IsOptional() 30 | partOfSpeech: [string]; 31 | 32 | @IsArray() 33 | @ArrayNotEmpty({ each: true }) 34 | @IsOptional() 35 | definition: LangOpt[]; 36 | 37 | @IsArray() 38 | @ArrayNotEmpty({ each: true }) 39 | @IsOptional() 40 | examples: LangOpt[]; 41 | 42 | @IsArray() 43 | @IsString({ each: true }) 44 | @IsOptional() 45 | mnemonics: string[]; 46 | 47 | @IsArray() 48 | @IsString({ each: true }) 49 | @IsOptional() 50 | audios: string[]; 51 | 52 | @IsArray() 53 | @IsString({ each: true }) 54 | @IsOptional() 55 | videos: string[]; 56 | 57 | @IsNumber() 58 | @IsOptional() 59 | level: number; 60 | 61 | @IsArray() 62 | @IsString({ each: true }) 63 | @IsOptional() 64 | tags: string[]; 65 | 66 | @IsBoolean() 67 | @IsOptional() 68 | isFavorite: boolean; 69 | 70 | @IsBoolean() 71 | @IsOptional() 72 | isKnown: boolean; 73 | 74 | @IsArray() 75 | @IsString({ each: true }) 76 | @IsOptional() 77 | synonyms: string[]; 78 | 79 | @IsArray() 80 | @IsString({ each: true }) 81 | @IsOptional() 82 | antonyms: string[]; 83 | 84 | @IsArray() 85 | @IsString({ each: true }) 86 | @IsOptional() 87 | similarWords: string[]; 88 | } 89 | -------------------------------------------------------------------------------- /server/src/configs/database.config.ts: -------------------------------------------------------------------------------- 1 | import { registerAs } from '@nestjs/config'; 2 | import { DatabaseConfig } from './config.type'; 3 | 4 | export default registerAs('database', () => ({ 5 | host: process.env?.DATABASE_HOST ?? 'mongodb://localhost:27017,localhost:27018,localhost:27019', 6 | port: process.env.DATABASE_PORT ? parseInt(process.env.DATABASE_PORT, 10) : 5432, 7 | name: process.env?.DATABASE_NAME ?? 'ry', 8 | username: process.env?.DATABASE_USERNAME, 9 | password: process?.env.DATABASE_PASSWORD, 10 | debug: process.env.DATABASE_DEBUG === 'true', 11 | options: process.env?.DATABASE_OPTIONS, 12 | })); 13 | 14 | // export default registerAs('database', () => { 15 | // // validateConfig(process.env, EnvironmentVariablesValidator); 16 | 17 | // return { 18 | // url: process.env.DATABASE_URL, 19 | // type: process.env.DATABASE_TYPE, 20 | // host: process.env.DATABASE_HOST, 21 | // port: process.env.DATABASE_PORT ? parseInt(process.env.DATABASE_PORT, 10) : 5432, 22 | // name: process.env.DATABASE_NAME, 23 | // password: process.env.DATABASE_PASSWORD, 24 | // debug: process.env.DATABASE_DEBUG === 'true', 25 | // options: process.env?.DATABASE_OPTIONS, 26 | // username: process.env.DATABASE_USERNAME, 27 | // synchronize: process.env.DATABASE_SYNCHRONIZE === 'true', 28 | // maxConnections: process.env.DATABASE_MAX_CONNECTIONS 29 | // ? parseInt(process.env.DATABASE_MAX_CONNECTIONS, 10) 30 | // : 100, 31 | // sslEnabled: process.env.DATABASE_SSL_ENABLED === 'true', 32 | // rejectUnauthorized: process.env.DATABASE_REJECT_UNAUTHORIZED === 'true', 33 | // ca: process.env.DATABASE_CA, 34 | // key: process.env.DATABASE_KEY, 35 | // cert: process.env.DATABASE_CERT, 36 | // }; 37 | // }); 38 | -------------------------------------------------------------------------------- /server/src/database/database.providers.ts: -------------------------------------------------------------------------------- 1 | import { ConfigModule, ConfigService } from '@nestjs/config'; 2 | import { MongooseModule, MongooseModuleOptions } from '@nestjs/mongoose'; 3 | import mongoose from 'mongoose'; 4 | import { ENUM_APP_ENVIRONMENT } from 'src/app/constants/app.enum.constant'; 5 | import { AppConfig, DatabaseConfig } from 'src/configs/config.type'; 6 | 7 | const defaultConnection = (configService: ConfigService): MongooseModuleOptions => { 8 | const appConfig = configService.get('app'); 9 | const dbConfig = configService.get('database'); 10 | 11 | // console.log(appConfig.nodeEnv, dbConfig); 12 | 13 | let uri = `${dbConfig.host}`; 14 | // const options = dbConfig.options ? `?${dbConfig.options}` : ''; 15 | 16 | if (dbConfig.name) { 17 | uri = `${uri}/${dbConfig.name}`; 18 | } 19 | 20 | if (appConfig.nodeEnv !== ENUM_APP_ENVIRONMENT.PRODUCTION) { 21 | mongoose.set('debug', dbConfig.debug); 22 | } 23 | 24 | const mongooseOptions: MongooseModuleOptions = { 25 | uri, 26 | serverSelectionTimeoutMS: 5000, 27 | autoCreate: true, 28 | }; 29 | 30 | if (dbConfig.username && dbConfig.password) { 31 | mongooseOptions.auth = { 32 | username: dbConfig.username, 33 | password: dbConfig.password, 34 | }; 35 | } 36 | 37 | return mongooseOptions; 38 | }; 39 | 40 | export const databaseProviders = [ 41 | MongooseModule.forRootAsync({ 42 | imports: [ConfigModule], 43 | inject: [ConfigService], 44 | useFactory: defaultConnection, 45 | // useFactory: (configService: ConfigService) => ({ 46 | // uri: configService.get('MONGODB_URI'), 47 | // connectionFactory: (connection) => { 48 | // connection.plugin(mongooseAutopopulate); 49 | // connection.plugin(slug); 50 | // return connection; 51 | // }, 52 | // }), 53 | }), 54 | ]; 55 | -------------------------------------------------------------------------------- /server/src/configs/config-validation.schema.ts: -------------------------------------------------------------------------------- 1 | import Joi from 'joi'; 2 | import { Environment } from '@common/enums/environment.enum'; 3 | 4 | export const configValidationSchema = Joi.object({ 5 | APP_NAME: Joi.string().required(), 6 | APP_ENV: Joi.string() 7 | .valid(...Object.values(Environment)) 8 | .default(Environment.Development) 9 | .required(), 10 | APP_LANGUAGE: Joi.string().default('en').required(), 11 | 12 | HTTP_ENABLE: Joi.boolean().default(true).required(), 13 | HTTP_HOST: [Joi.string().ip({ version: 'ipv4' }).required(), Joi.valid('localhost').required()], 14 | HTTP_PORT: Joi.number().default(4000).required(), 15 | HTTP_VERSIONING_ENABLE: Joi.boolean().default(true).required(), 16 | HTTP_VERSION: Joi.number().required(), 17 | 18 | DATABASE_HOST: Joi.string(), 19 | DATABASE_NAME: Joi.string(), 20 | DATABASE_USERNAME: Joi.string().allow(null, ''), 21 | DATABASE_PASSWORD: Joi.string().allow(null, ''), 22 | DATABASE_DEBUG: Joi.boolean(), 23 | DATABASE_OPTIONS: Joi.string(), 24 | 25 | // // STAGE: Joi.string().required(), 26 | // UPLOAD_PATH: Joi.string().required(), 27 | // MAX_FILE_SIZE: Joi.number().required(), 28 | // MAX_FILE_COUNTS: Joi.number().required(), 29 | // DB_HOST: Joi.string().required(), 30 | // DB_PORT: Joi.number().default(5432).required(), 31 | // DB_USERNAME: Joi.string().required(), 32 | // DB_PASSWORD: Joi.string().required(), 33 | // DB_DATABASE: Joi.string().required(), 34 | // JWT_ACCESS_TOKEN_SECRET: Joi.string().required(), 35 | // JWT_REFRESH_TOKEN_SECRET: Joi.string().required(), 36 | // JWT_ACCESS_TOKEN_EXPIRES: Joi.number().required(), 37 | // JWT_REFRESH_TOKEN_EXPIRES: Joi.number().required(), 38 | // // SMS_API_URL: Joi.string().allow(null, ''), 39 | // // SMS_API_KEY: Joi.string().allow(null, ''), 40 | // // TWILIO_ACCOUNT_SID: Joi.string().required(), 41 | // // TWILIO_AUTH_TOKEN: Joi.string().required(), 42 | }); 43 | -------------------------------------------------------------------------------- /server/src/configs/auth.config.ts: -------------------------------------------------------------------------------- 1 | import { registerAs } from '@nestjs/config'; 2 | import { seconds } from '@common/helper/constants/helper.function'; 3 | 4 | export default registerAs( 5 | 'auth', 6 | (): Record => ({ 7 | accessToken: { 8 | secretKey: process.env.AUTH_JWT_ACCESS_TOKEN_SECRET_KEY ?? '123456', 9 | expirationTime: seconds(process.env.AUTH_JWT_ACCESS_TOKEN_EXPIRED ?? '1h'), // 1 hours 10 | notBeforeExpirationTime: seconds('0'), // immediately 11 | 12 | encryptKey: process.env.AUTH_JWT_PAYLOAD_ACCESS_TOKEN_ENCRYPT_KEY, 13 | encryptIv: process.env.AUTH_JWT_PAYLOAD_ACCESS_TOKEN_ENCRYPT_IV, 14 | }, 15 | 16 | refreshToken: { 17 | secretKey: process.env.AUTH_JWT_REFRESH_TOKEN_SECRET_KEY ?? '123456000', 18 | expirationTime: seconds(process.env.AUTH_JWT_REFRESH_TOKEN_EXPIRED ?? '14d'), // 14 days 19 | notBeforeExpirationTime: seconds( 20 | process.env.AUTH_JWT_REFRESH_TOKEN_NOT_BEFORE_EXPIRATION ?? '1h', 21 | ), // 1 hours 22 | 23 | encryptKey: process.env.AUTH_JWT_PAYLOAD_REFRESH_TOKEN_ENCRYPT_KEY, 24 | encryptIv: process.env.AUTH_JWT_PAYLOAD_REFRESH_TOKEN_ENCRYPT_IV, 25 | }, 26 | 27 | subject: process.env.AUTH_JWT_SUBJECT ?? 'ryDevelopment', 28 | audience: process.env.AUTH_JWT_AUDIENCE ?? 'https://example.com', 29 | issuer: process.env.AUTH_JWT_ISSUER ?? 'ry', 30 | prefixAuthorization: 'Bearer', 31 | payloadEncryption: process.env.AUTH_JWT_PAYLOAD_ENCRYPT === 'true' ? true : false, 32 | 33 | password: { 34 | attempt: true, 35 | maxAttempt: 5, 36 | saltLength: 8, 37 | expiredIn: seconds('182d'), // 182 days 38 | }, 39 | 40 | googleOAuth2: { 41 | clientId: process.env.SSO_GOOGLE_CLIENT_ID, 42 | clientSecret: process.env.SSO_GOOGLE_CLIENT_SECRET, 43 | callbackUrlLogin: process.env.SSO_GOOGLE_CALLBACK_URL_LOGIN, 44 | callbackUrlSignUp: process.env.SSO_GOOGLE_CALLBACK_URL_SIGN_UP, 45 | }, 46 | }), 47 | ); 48 | -------------------------------------------------------------------------------- /server/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestApplication, NestFactory } from '@nestjs/core'; 2 | import { Logger, VersioningType } from '@nestjs/common'; 3 | import { ConfigService } from '@nestjs/config'; 4 | import { AppModule } from './app/app.module'; 5 | import { useContainer } from 'class-validator'; 6 | import { AppConfig } from './configs/config.type'; 7 | // import swaggerInit from './swagger'; 8 | 9 | async function bootstrap() { 10 | const app: NestApplication = await NestFactory.create(AppModule); 11 | 12 | const configService = app.get(ConfigService); 13 | const appConfig = configService.get('app'); 14 | // console.log('Main', appConfig); 15 | 16 | const logger = new Logger(); 17 | 18 | // Global 19 | app.setGlobalPrefix(appConfig.apiPrefix); 20 | useContainer(app.select(AppModule), { fallbackOnErrors: true }); 21 | 22 | // Versioning 23 | if (appConfig.versioning.enable) { 24 | app.enableVersioning({ 25 | type: VersioningType.URI, 26 | defaultVersion: appConfig.versioning.version, 27 | prefix: appConfig.versioning.prefix, 28 | }); 29 | } 30 | 31 | // Swagger 32 | // await swaggerInit(app); 33 | 34 | // Listen 35 | await app.listen(appConfig.http.port, appConfig.http.host); 36 | 37 | // logger.log(`==========================================================`); 38 | 39 | // logger.log(`Environment Variable`, 'NestApplication'); 40 | // logger.log(JSON.parse(JSON.stringify(process.env)), 'NestApplication'); 41 | 42 | logger.log(`======================================================================`); 43 | 44 | logger.log(`Job is ${appConfig.jobEnable}`, 'NestApplication'); 45 | logger.log( 46 | `Http is ${appConfig.http.enable}, ${ 47 | appConfig.http.enable ? 'routes registered' : 'no routes registered' 48 | }`, 49 | 'NestApplication', 50 | ); 51 | logger.log(`Http versioning is ${appConfig.versioning.enable}`, 'NestApplication'); 52 | 53 | logger.log(`Http Server running on ${await app.getUrl()}`, 'NestApplication'); 54 | // logger.log(`Database uri ${databaseUri}`, 'NestApplication'); 55 | 56 | logger.log(`======================================================================`); 57 | } 58 | bootstrap(); 59 | -------------------------------------------------------------------------------- /server/src/common/helper/interfaces/helper.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ENUM_HELPER_DATE_DIFF, 3 | ENUM_HELPER_DATE_FORMAT, 4 | ENUM_HELPER_FILE_TYPE, 5 | } from 'src/common/helper/constants/helper.enum.constant'; 6 | 7 | // Helper Array 8 | export interface IHelperArrayRemove { 9 | removed: T[]; 10 | arrays: T[]; 11 | } 12 | 13 | // Helper Encryption 14 | export interface IHelperJwtVerifyOptions { 15 | audience: string; 16 | issuer: string; 17 | subject: string; 18 | secretKey: string; 19 | } 20 | 21 | export interface IHelperJwtOptions extends IHelperJwtVerifyOptions { 22 | expiredIn: number | string; 23 | notBefore?: number | string; 24 | } 25 | 26 | // Helper String 27 | export interface IHelperStringRandomOptions { 28 | upperCase?: boolean; 29 | safe?: boolean; 30 | prefix?: string; 31 | } 32 | 33 | // Helper Date 34 | export interface IHelperDateStartAndEnd { 35 | month?: number; 36 | year?: number; 37 | } 38 | 39 | export interface IHelperDateStartAndEndDate { 40 | startDate: Date; 41 | endDate: Date; 42 | } 43 | 44 | export interface IHelperDateExtractDate { 45 | date: Date; 46 | day: string; 47 | month: string; 48 | year: string; 49 | } 50 | 51 | export interface IHelperDateOptionsDiff { 52 | format?: ENUM_HELPER_DATE_DIFF; 53 | } 54 | 55 | export interface IHelperDateOptionsCreate { 56 | startOfDay?: boolean; 57 | } 58 | 59 | export interface IHelperDateOptionsFormat { 60 | format?: ENUM_HELPER_DATE_FORMAT | string; 61 | } 62 | 63 | export interface IHelperDateOptionsForward { 64 | fromDate?: Date; 65 | } 66 | 67 | export type IHelperDateOptionsBackward = IHelperDateOptionsForward; 68 | 69 | export interface IHelperDateOptionsRoundDown { 70 | hour: boolean; 71 | minute: boolean; 72 | second: boolean; 73 | } 74 | 75 | // Helper File 76 | 77 | export type IHelperFileRows = Record; 78 | 79 | export interface IHelperFileWriteExcelOptions { 80 | password?: string; 81 | type?: ENUM_HELPER_FILE_TYPE; 82 | } 83 | 84 | export interface IHelperFileCreateExcelWorkbookOptions { 85 | sheetName?: string; 86 | } 87 | 88 | export interface IHelperFileReadExcelOptions { 89 | sheet?: string | number; 90 | password?: string; 91 | } 92 | -------------------------------------------------------------------------------- /server/src/common/helper/interfaces/helper.date-service.interface.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IHelperDateOptionsBackward, 3 | IHelperDateOptionsCreate, 4 | IHelperDateOptionsDiff, 5 | IHelperDateOptionsFormat, 6 | IHelperDateOptionsForward, 7 | IHelperDateStartAndEnd, 8 | IHelperDateStartAndEndDate, 9 | } from './helper.interface'; 10 | import { IHelperDateExtractDate } from './helper.interface'; 11 | 12 | export interface IHelperDateService { 13 | calculateAge(dateOfBirth: Date, year?: number): number; 14 | diff(dateOne: Date, dateTwoMoreThanDateOne: Date, options?: IHelperDateOptionsDiff): number; 15 | check(date: string | Date | number): boolean; 16 | checkDateTime(date: string | Date | number): boolean; 17 | checkTimestamp(timestamp: number): boolean; 18 | create(date?: string | number | Date, options?: IHelperDateOptionsCreate): Date; 19 | timestamp(date?: string | number | Date, options?: IHelperDateOptionsCreate): number; 20 | format(date: Date, options?: IHelperDateOptionsFormat): string; 21 | forwardInMilliseconds(milliseconds: number, options?: IHelperDateOptionsForward): Date; 22 | backwardInMilliseconds(milliseconds: number, options?: IHelperDateOptionsBackward): Date; 23 | forwardInSeconds(seconds: number, options?: IHelperDateOptionsForward): Date; 24 | backwardInSeconds(seconds: number, options?: IHelperDateOptionsBackward): Date; 25 | forwardInMinutes(minutes: number, options?: IHelperDateOptionsForward): Date; 26 | backwardInMinutes(minutes: number, options?: IHelperDateOptionsBackward): Date; 27 | forwardInHours(hours: number, options?: IHelperDateOptionsForward): Date; 28 | backwardInHours(hours: number, options?: IHelperDateOptionsBackward): Date; 29 | forwardInDays(days: number, options?: IHelperDateOptionsForward): Date; 30 | backwardInDays(days: number, options?: IHelperDateOptionsBackward): Date; 31 | forwardInMonths(months: number, options?: IHelperDateOptionsForward): Date; 32 | backwardInMonths(months: number, options?: IHelperDateOptionsBackward): Date; 33 | endOfMonth(date?: Date): Date; 34 | startOfMonth(date?: Date): Date; 35 | endOfYear(date?: Date): Date; 36 | startOfYear(date?: Date): Date; 37 | endOfDay(date?: Date): Date; 38 | startOfDay(date?: Date): Date; 39 | extractDate(date: string | Date | number): IHelperDateExtractDate; 40 | getStartAndEndDate(options?: IHelperDateStartAndEnd): IHelperDateStartAndEndDate; 41 | } 42 | -------------------------------------------------------------------------------- /server/src/configs/config.type.ts: -------------------------------------------------------------------------------- 1 | export type AppConfig = { 2 | nodeEnv: string; 3 | name: string; 4 | // workingDirectory: string; 5 | // frontendDomain?: string; 6 | // backendDomain: string; 7 | repoVersion: string; 8 | apiPrefix: string; // globalPrefix 9 | versioning: { 10 | enable: boolean; 11 | prefix: string; 12 | version: string; 13 | }; 14 | http: { 15 | enable: boolean; 16 | host: string; 17 | port: number; 18 | }; 19 | jobEnable: boolean; 20 | 21 | fallbackLanguage: string; 22 | // headerLanguage: string; 23 | }; 24 | 25 | export type AppleConfig = { 26 | appAudience: string[]; 27 | }; 28 | 29 | export type AuthConfig = { 30 | secret?: string; 31 | expires?: string; 32 | refreshSecret?: string; 33 | refreshExpires?: string; 34 | }; 35 | 36 | export type DatabaseConfig = { 37 | url?: string; 38 | type?: string; 39 | host?: string; 40 | port?: number; 41 | name?: string; 42 | username?: string; 43 | password?: string; 44 | debug?: boolean; 45 | options?: string; 46 | synchronize?: boolean; 47 | maxConnections?: number; 48 | sslEnabled?: boolean; 49 | rejectUnauthorized?: boolean; 50 | ca?: string; 51 | key?: string; 52 | cert?: string; 53 | }; 54 | 55 | export type FacebookConfig = { 56 | appId?: string; 57 | appSecret?: string; 58 | }; 59 | 60 | export type FileConfig = { 61 | driver: string; 62 | accessKeyId?: string; 63 | secretAccessKey?: string; 64 | awsDefaultS3Bucket?: string; 65 | awsDefaultS3Url?: string; 66 | awsS3Region?: string; 67 | maxFileSize: number; 68 | }; 69 | 70 | export type GoogleConfig = { 71 | clientId?: string; 72 | clientSecret?: string; 73 | }; 74 | 75 | export type MailConfig = { 76 | port: number; 77 | host?: string; 78 | user?: string; 79 | password?: string; 80 | defaultEmail?: string; 81 | defaultName?: string; 82 | ignoreTLS: boolean; 83 | secure: boolean; 84 | requireTLS: boolean; 85 | }; 86 | 87 | export type TwitterConfig = { 88 | consumerKey?: string; 89 | consumerSecret?: string; 90 | }; 91 | 92 | export type AllConfigType = { 93 | app: AppConfig; 94 | apple: AppleConfig; 95 | auth: AuthConfig; 96 | database: DatabaseConfig; 97 | facebook: FacebookConfig; 98 | file: FileConfig; 99 | google: GoogleConfig; 100 | mail: MailConfig; 101 | twitter: TwitterConfig; 102 | }; 103 | -------------------------------------------------------------------------------- /frontend/src/components/layout/header/banner.tsx: -------------------------------------------------------------------------------- 1 | import { HiXMark } from 'react-icons/hi2' 2 | 3 | export default function Banner() { 4 | return ( 5 |
6 |