├── 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 |
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 |
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 |
18 |
30 |
31 |
32 | WordbookCon 2023
33 |
40 | Join us in Wordbook from June 7 – 9 to see what’s coming next.
41 |
42 |
46 | Register now →
47 |
48 |
49 |
50 |
54 |
55 |
56 | )
57 | }
58 |
--------------------------------------------------------------------------------
/server/src/modules/word/controllers/word.controller.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Body,
3 | Controller,
4 | Delete,
5 | Get,
6 | Logger,
7 | Param,
8 | ParseUUIDPipe,
9 | Post,
10 | Put,
11 | Query,
12 | } from '@nestjs/common';
13 | import { WordService } from '../services/word.service';
14 | import { FilterWordDto } from '../dtos/filter-word.dto';
15 | import { CreateWordDto } from '../dtos/create-word.dto';
16 | import { UpdateWordDto } from '../dtos/update-word.dto';
17 | import { ParseObjectIdPipe } from '@common/pipes/parse-object-id.pipe';
18 | import { ObjectId } from 'mongoose';
19 |
20 | @Controller({ path: 'words', version: '1' })
21 | export class WordController {
22 | private logger = new Logger(WordController.name);
23 |
24 | constructor(private readonly wordService: WordService) {}
25 |
26 | @Get('/')
27 | async getAll(@Query() filterWordDto: FilterWordDto) {
28 | this.logger.verbose(`User "unknown" retieving all words`);
29 |
30 | const results = await this.wordService.getAll(filterWordDto);
31 |
32 | return {
33 | success: true,
34 | statusCode: 200,
35 | message: `List of words`,
36 | data: results,
37 | };
38 | }
39 |
40 | @Get('/:id')
41 | async getOne(@Param('id', ParseObjectIdPipe) id: ObjectId) {
42 | this.logger.verbose(`User "unknown" retieving word of id: ${id}`);
43 |
44 | const result = await this.wordService.getOne(id);
45 |
46 | return {
47 | success: true,
48 | statusCode: 200,
49 | message: `Details of word of id: ${id}`,
50 | data: result,
51 | };
52 | }
53 |
54 | @Post('/')
55 | async create(@Body() createWordDto: CreateWordDto) {
56 | this.logger.verbose(`User "unknown" creating new word`);
57 |
58 | const result = await this.wordService.create(createWordDto);
59 |
60 | return {
61 | success: true,
62 | statusCode: 201,
63 | message: `New word "${result.word}" created`,
64 | data: result,
65 | };
66 | }
67 |
68 | @Put('/:id')
69 | async update(@Param('id', ParseUUIDPipe) id: string, @Body() updateWordDto: UpdateWordDto) {
70 | this.logger.verbose(`User "unknown" updating word of id: ${id}`);
71 |
72 | const result = await this.wordService.update(id, updateWordDto);
73 |
74 | return {
75 | success: true,
76 | statusCode: 201,
77 | message: `Word "${result.word}" updated`,
78 | data: result,
79 | };
80 | }
81 |
82 | @Delete('/:id')
83 | async delete(@Param('id', ParseUUIDPipe) id: string) {
84 | this.logger.verbose(`User "unknown" deleting word of id: ${id}`);
85 |
86 | const result = await this.wordService.delete(id);
87 |
88 | return {
89 | success: true,
90 | statusCode: 201,
91 | message: `Word "${result.word}" deleted`,
92 | data: result,
93 | };
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/server/src/modules/dictionary/controllers/dictionary.controller.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Body,
3 | Controller,
4 | Delete,
5 | Get,
6 | Logger,
7 | Param,
8 | ParseUUIDPipe,
9 | Post,
10 | Put,
11 | Query,
12 | } from '@nestjs/common';
13 | import { DictionaryService } from '../services/dictionary.service';
14 | import { FilterDictionaryDto } from '../dtos/filter-dictionary.dto';
15 | import { CreateDictionaryDto } from '../dtos/create-dictionary.dto';
16 | import { UpdateDictionaryDto } from '../dtos/update-dictionary.dto';
17 |
18 | // Dictionarybook
19 | @Controller({ path: 'Dictionarys', version: '1' })
20 | export class DictionaryController {
21 | private logger = new Logger(DictionaryController.name);
22 |
23 | constructor(private readonly dictionaryService: DictionaryService) {}
24 |
25 | @Get('/')
26 | async getAll(@Query() filterDictionaryDto: FilterDictionaryDto) {
27 | this.logger.verbose(`User "unknown" retieving all Dictionarys`);
28 |
29 | const results = await this.dictionaryService.getAll(filterDictionaryDto);
30 |
31 | return {
32 | success: true,
33 | statusCode: 200,
34 | message: `List of Dictionarys`,
35 | data: results,
36 | };
37 | }
38 |
39 | @Get('/:id')
40 | async getOne(@Param('id', ParseUUIDPipe) id: string) {
41 | this.logger.verbose(`User "unknown" retieving Dictionary of id: ${id}`);
42 |
43 | const result = await this.dictionaryService.getOne(id);
44 |
45 | return {
46 | success: true,
47 | statusCode: 200,
48 | message: `Details of Dictionary of id: ${id}`,
49 | data: result,
50 | };
51 | }
52 |
53 | @Post('/')
54 | async create(@Body() createDictionaryDto: CreateDictionaryDto) {
55 | this.logger.verbose(`User "unknown" creating new Dictionary`);
56 |
57 | const result = await this.dictionaryService.create(createDictionaryDto);
58 |
59 | return {
60 | success: true,
61 | statusCode: 201,
62 | message: `New Dictionary "${result.title}" created`,
63 | data: result,
64 | };
65 | }
66 |
67 | @Put('/:id')
68 | async update(
69 | @Param('id', ParseUUIDPipe) id: string,
70 | @Body() updateDictionaryDto: UpdateDictionaryDto,
71 | ) {
72 | this.logger.verbose(`User "unknown" updating Dictionary of id: ${id}`);
73 |
74 | const result = await this.dictionaryService.update(id, updateDictionaryDto);
75 |
76 | return {
77 | success: true,
78 | statusCode: 201,
79 | message: `Dictionary "${result.title}" updated`,
80 | data: result,
81 | };
82 | }
83 |
84 | @Delete('/:id')
85 | async delete(@Param('id', ParseUUIDPipe) id: string) {
86 | this.logger.verbose(`User "unknown" deleting Dictionary of id: ${id}`);
87 |
88 | const result = await this.dictionaryService.delete(id);
89 |
90 | return {
91 | success: true,
92 | statusCode: 201,
93 | message: `Dictionary "${result.title}" deleted`,
94 | data: result,
95 | };
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "0.0.1",
4 | "description": "",
5 | "author": {
6 | "name": "Biprodas Roy",
7 | "email": "biprodas.cse@gmail.com"
8 | },
9 | "private": true,
10 | "license": "UNLICENSED",
11 | "scripts": {
12 | "prebuild": "rimraf dist",
13 | "build": "nest build",
14 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
15 | "start": "cross-env NODE_ENV=development nest start",
16 | "dev": "cross-env NODE_ENV=development nest start --watch",
17 | "start:dev": "cross-env NODE_ENV=development nest start --watch",
18 | "start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
19 | "start:prod": "cross-env NODE_ENV=production node dist/main",
20 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
21 | "test": "cross-env NODE_ENV=test jest",
22 | "test:watch": "cross-env NODE_ENV=test jest --watch",
23 | "test:cov": "cross-env NODE_ENV=test jest --coverage",
24 | "test:debug": "cross-env NODE_ENV=test node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
25 | "test:e2e": "cross-env NODE_ENV=test jest --config ./test/jest-e2e.json"
26 | },
27 | "dependencies": {
28 | "@nestjs/common": "^10.0.0",
29 | "@nestjs/config": "^3.0.0",
30 | "@nestjs/core": "^10.0.0",
31 | "@nestjs/jwt": "^10.1.0",
32 | "@nestjs/mongoose": "^10.0.1",
33 | "@nestjs/platform-express": "^10.0.0",
34 | "@nestjs/schedule": "^3.0.3",
35 | "@nestjs/swagger": "^7.1.8",
36 | "class-transformer": "^0.5.1",
37 | "class-validator": "^0.14.0",
38 | "cross-env": "^7.0.3",
39 | "joi": "^17.9.2",
40 | "moment": "^2.29.4",
41 | "mongoose": "^7.4.4",
42 | "ms": "^2.1.3",
43 | "reflect-metadata": "^0.1.13",
44 | "rxjs": "^7.8.1",
45 | "swagger-themes": "^1.2.30"
46 | },
47 | "devDependencies": {
48 | "@nestjs/cli": "^10.0.0",
49 | "@nestjs/schematics": "^10.0.0",
50 | "@nestjs/testing": "^10.0.0",
51 | "@types/express": "^4.17.17",
52 | "@types/jest": "^29.5.2",
53 | "@types/ms": "^0.7.31",
54 | "@types/node": "^20.3.1",
55 | "@types/supertest": "^2.0.12",
56 | "@types/uuid": "^9.0.2",
57 | "@typescript-eslint/eslint-plugin": "^6.0.0",
58 | "@typescript-eslint/parser": "^6.0.0",
59 | "eslint": "^8.42.0",
60 | "eslint-config-prettier": "^9.0.0",
61 | "eslint-plugin-prettier": "^5.0.0",
62 | "jest": "^29.5.0",
63 | "prettier": "^3.0.0",
64 | "source-map-support": "^0.5.21",
65 | "supertest": "^6.3.3",
66 | "ts-jest": "^29.1.0",
67 | "ts-loader": "^9.4.3",
68 | "ts-node": "^10.9.1",
69 | "tsconfig-paths": "^4.2.0",
70 | "typescript": "^5.1.3"
71 | },
72 | "jest": {
73 | "moduleFileExtensions": [
74 | "js",
75 | "json",
76 | "ts"
77 | ],
78 | "rootDir": "src",
79 | "testRegex": ".*\\.spec\\.ts$",
80 | "transform": {
81 | "^.+\\.(t|j)s$": "ts-jest"
82 | },
83 | "collectCoverageFrom": [
84 | "**/*.(t|j)s"
85 | ],
86 | "coverageDirectory": "../coverage",
87 | "testEnvironment": "node"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/server/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
6 | [circleci-url]: https://circleci.com/gh/nestjs/nest
7 |
8 | A progressive Node.js framework for building efficient and scalable server-side applications.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
24 |
25 | ## Description
26 |
27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
28 |
29 | ## Installation
30 |
31 | ```bash
32 | $ npm install
33 | ```
34 |
35 | ## Running the app
36 |
37 | ```bash
38 | # development
39 | $ npm run start
40 |
41 | # watch mode
42 | $ npm run start:dev
43 |
44 | # production mode
45 | $ npm run start:prod
46 | ```
47 |
48 | ## Test
49 |
50 | ```bash
51 | # unit tests
52 | $ npm run test
53 |
54 | # e2e tests
55 | $ npm run test:e2e
56 |
57 | # test coverage
58 | $ npm run test:cov
59 | ```
60 |
61 | ## Support
62 |
63 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
64 |
65 | ## Stay in touch
66 |
67 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
68 | - Website - [https://nestjs.com](https://nestjs.com/)
69 | - Twitter - [@nestframework](https://twitter.com/nestframework)
70 |
71 | ## License
72 |
73 | Nest is [MIT licensed](LICENSE).
74 |
--------------------------------------------------------------------------------
/server/src/common/helper/services/helper.date.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import moment, { ISO_8601 } from 'moment';
3 | import { ENUM_HELPER_DATE_DIFF, ENUM_HELPER_DATE_FORMAT } from '../constants/helper.enum.constant';
4 | import { IHelperDateService } from '../interfaces/helper.date-service.interface';
5 | import {
6 | IHelperDateExtractDate,
7 | IHelperDateOptionsBackward,
8 | IHelperDateOptionsCreate,
9 | IHelperDateOptionsDiff,
10 | IHelperDateOptionsFormat,
11 | IHelperDateOptionsForward,
12 | IHelperDateOptionsRoundDown,
13 | IHelperDateStartAndEnd,
14 | IHelperDateStartAndEndDate,
15 | } from '../interfaces/helper.interface';
16 |
17 | @Injectable()
18 | export class HelperDateService implements IHelperDateService {
19 | calculateAge(dateOfBirth: Date, year?: number): number {
20 | const m = moment();
21 |
22 | if (year) {
23 | m.set('year', year);
24 | }
25 |
26 | return m.diff(dateOfBirth, 'years');
27 | }
28 |
29 | diff(dateOne: Date, dateTwoMoreThanDateOne: Date, options?: IHelperDateOptionsDiff): number {
30 | const mDateOne = moment(dateOne);
31 | const mDateTwo = moment(dateTwoMoreThanDateOne);
32 | const diff = moment.duration(mDateTwo.diff(mDateOne));
33 |
34 | if (options?.format === ENUM_HELPER_DATE_DIFF.MILIS) {
35 | return diff.asMilliseconds();
36 | } else if (options?.format === ENUM_HELPER_DATE_DIFF.SECONDS) {
37 | return diff.asSeconds();
38 | } else if (options?.format === ENUM_HELPER_DATE_DIFF.HOURS) {
39 | return diff.asHours();
40 | } else if (options?.format === ENUM_HELPER_DATE_DIFF.MINUTES) {
41 | return diff.asMinutes();
42 | } else {
43 | return diff.asDays();
44 | }
45 | }
46 |
47 | check(date: string | Date | number): boolean {
48 | return moment(date, 'YYYY-MM-DD', true).isValid();
49 | }
50 |
51 | checkDateTime(date: string | Date | number): boolean {
52 | return moment(date, ISO_8601, true).isValid();
53 | }
54 |
55 | checkTimestamp(timestamp: number): boolean {
56 | return moment(timestamp, true).isValid();
57 | }
58 |
59 | create(date?: string | number | Date, options?: IHelperDateOptionsCreate): Date {
60 | const mDate = moment(date ?? undefined);
61 |
62 | if (options?.startOfDay) {
63 | mDate.startOf('day');
64 | }
65 |
66 | return mDate.toDate();
67 | }
68 |
69 | timestamp(date?: string | number | Date, options?: IHelperDateOptionsCreate): number {
70 | const mDate = moment(date ?? undefined);
71 |
72 | if (options?.startOfDay) {
73 | mDate.startOf('day');
74 | }
75 |
76 | return mDate.valueOf();
77 | }
78 |
79 | format(date: Date, options?: IHelperDateOptionsFormat): string {
80 | return moment(date).format(options?.format ?? ENUM_HELPER_DATE_FORMAT.DATE);
81 | }
82 |
83 | forwardInMilliseconds(milliseconds: number, options?: IHelperDateOptionsForward): Date {
84 | return moment(options?.fromDate)
85 | .add(milliseconds, 'ms')
86 | .toDate();
87 | }
88 |
89 | backwardInMilliseconds(milliseconds: number, options?: IHelperDateOptionsBackward): Date {
90 | return moment(options?.fromDate)
91 | .subtract(milliseconds, 'ms')
92 | .toDate();
93 | }
94 |
95 | forwardInSeconds(seconds: number, options?: IHelperDateOptionsForward): Date {
96 | return moment(options?.fromDate)
97 | .add(seconds, 's')
98 | .toDate();
99 | }
100 |
101 | backwardInSeconds(seconds: number, options?: IHelperDateOptionsBackward): Date {
102 | return moment(options?.fromDate)
103 | .subtract(seconds, 's')
104 | .toDate();
105 | }
106 |
107 | forwardInMinutes(minutes: number, options?: IHelperDateOptionsForward): Date {
108 | return moment(options?.fromDate)
109 | .add(minutes, 'm')
110 | .toDate();
111 | }
112 |
113 | backwardInMinutes(minutes: number, options?: IHelperDateOptionsBackward): Date {
114 | return moment(options?.fromDate)
115 | .subtract(minutes, 'm')
116 | .toDate();
117 | }
118 |
119 | forwardInHours(hours: number, options?: IHelperDateOptionsForward): Date {
120 | return moment(options?.fromDate)
121 | .add(hours, 'h')
122 | .toDate();
123 | }
124 |
125 | backwardInHours(hours: number, options?: IHelperDateOptionsBackward): Date {
126 | return moment(options?.fromDate)
127 | .subtract(hours, 'h')
128 | .toDate();
129 | }
130 |
131 | forwardInDays(days: number, options?: IHelperDateOptionsForward): Date {
132 | return moment(options?.fromDate)
133 | .add(days, 'd')
134 | .toDate();
135 | }
136 |
137 | backwardInDays(days: number, options?: IHelperDateOptionsBackward): Date {
138 | return moment(options?.fromDate)
139 | .subtract(days, 'd')
140 | .toDate();
141 | }
142 |
143 | forwardInMonths(months: number, options?: IHelperDateOptionsForward): Date {
144 | return moment(options?.fromDate)
145 | .add(months, 'M')
146 | .toDate();
147 | }
148 |
149 | backwardInMonths(months: number, options?: IHelperDateOptionsBackward): Date {
150 | return moment(options?.fromDate)
151 | .subtract(months, 'M')
152 | .toDate();
153 | }
154 |
155 | endOfMonth(date?: Date): Date {
156 | return moment(date).endOf('month').toDate();
157 | }
158 |
159 | startOfMonth(date?: Date): Date {
160 | return moment(date).startOf('month').toDate();
161 | }
162 |
163 | endOfYear(date?: Date): Date {
164 | return moment(date).endOf('year').toDate();
165 | }
166 |
167 | startOfYear(date?: Date): Date {
168 | return moment(date).startOf('year').toDate();
169 | }
170 |
171 | endOfDay(date?: Date): Date {
172 | return moment(date).endOf('day').toDate();
173 | }
174 |
175 | startOfDay(date?: Date): Date {
176 | return moment(date).startOf('day').toDate();
177 | }
178 |
179 | extractDate(date: string | Date | number): IHelperDateExtractDate {
180 | const newDate = this.create(date);
181 | const day: string = this.format(newDate, {
182 | format: ENUM_HELPER_DATE_FORMAT.ONLY_DATE,
183 | });
184 | const month: string = this.format(newDate, {
185 | format: ENUM_HELPER_DATE_FORMAT.ONLY_MONTH,
186 | });
187 | const year: string = this.format(newDate, {
188 | format: ENUM_HELPER_DATE_FORMAT.ONLY_YEAR,
189 | });
190 |
191 | return {
192 | date: newDate,
193 | day,
194 | month,
195 | year,
196 | };
197 | }
198 |
199 | roundDown(date: Date, options?: IHelperDateOptionsRoundDown): Date {
200 | const mDate = moment(date).set({ millisecond: 0 });
201 |
202 | if (options?.hour) {
203 | mDate.set({ hour: 0 });
204 | }
205 |
206 | if (options?.minute) {
207 | mDate.set({ minute: 0 });
208 | }
209 |
210 | if (options?.second) {
211 | mDate.set({ second: 0 });
212 | }
213 |
214 | return mDate.toDate();
215 | }
216 |
217 | getStartAndEndDate(options?: IHelperDateStartAndEnd): IHelperDateStartAndEndDate {
218 | const today = moment();
219 | const todayMonth = today.format(ENUM_HELPER_DATE_FORMAT.ONLY_MONTH);
220 | const todayYear = today.format(ENUM_HELPER_DATE_FORMAT.ONLY_YEAR);
221 | // set month and year
222 | const year = options?.year ?? todayYear;
223 | const month = options?.month ?? todayMonth;
224 |
225 | const date = moment(`${year}-${month}-02`, 'YYYY-MM-DD');
226 | let startDate: Date = date.startOf('month').toDate();
227 | let endDate: Date = date.endOf('month').toDate();
228 |
229 | if (options?.month) {
230 | const date = moment(`${year}-${month}-02`, 'YYYY-MM-DD');
231 | startDate = date.startOf('month').toDate();
232 | endDate = date.endOf('month').toDate();
233 | }
234 |
235 | return {
236 | startDate,
237 | endDate,
238 | };
239 | }
240 | }
241 |
--------------------------------------------------------------------------------