From 48a0a2076a575f81f923309ac0e7b6dfda9cd515 Mon Sep 17 00:00:00 2001 From: Javier Ruiz Date: Tue, 17 Dec 2024 17:02:02 +0100 Subject: [PATCH 1/4] custom router basic lib --- apps/login-ui/src/app/AppRoutes.tsx | 22 ++++++++++++++++ apps/login-ui/src/app/AppStarter.tsx | 7 +++-- libs/i18n/data/project.json | 2 +- libs/i18n/feature/project.json | 2 +- libs/react-query/feature/.babelrc | 12 +++++++++ libs/react-query/feature/README.md | 7 +++++ libs/react-query/feature/project.json | 9 +++++++ libs/react-query/feature/src/index.ts | 1 + .../feature/src/lib/react-query-provider.tsx | 26 +++++++++++++++++++ libs/react-query/feature/tsconfig.json | 17 ++++++++++++ libs/react-query/feature/tsconfig.lib.json | 23 ++++++++++++++++ libs/router/data/.babelrc | 12 +++++++++ libs/router/data/README.md | 7 +++++ libs/router/data/eslint.config.js | 12 +++++++++ libs/router/data/project.json | 9 +++++++ libs/router/data/src/index.ts | 1 + libs/router/data/src/lib/model.ts | 6 +++++ libs/router/data/tsconfig.json | 17 ++++++++++++ libs/router/data/tsconfig.lib.json | 23 ++++++++++++++++ libs/router/feature/.babelrc | 12 +++++++++ libs/router/feature/README.md | 7 +++++ libs/router/feature/eslint.config.js | 12 +++++++++ libs/router/feature/project.json | 9 +++++++ libs/router/feature/src/index.ts | 1 + libs/router/feature/src/lib/router.tsx | 19 ++++++++++++++ libs/router/feature/tsconfig.json | 17 ++++++++++++ libs/router/feature/tsconfig.lib.json | 23 ++++++++++++++++ tsconfig.base.json | 7 ++++- 28 files changed, 317 insertions(+), 5 deletions(-) create mode 100644 apps/login-ui/src/app/AppRoutes.tsx create mode 100644 libs/react-query/feature/.babelrc create mode 100644 libs/react-query/feature/README.md create mode 100644 libs/react-query/feature/project.json create mode 100644 libs/react-query/feature/src/index.ts create mode 100644 libs/react-query/feature/src/lib/react-query-provider.tsx create mode 100644 libs/react-query/feature/tsconfig.json create mode 100644 libs/react-query/feature/tsconfig.lib.json create mode 100644 libs/router/data/.babelrc create mode 100644 libs/router/data/README.md create mode 100644 libs/router/data/eslint.config.js create mode 100644 libs/router/data/project.json create mode 100644 libs/router/data/src/index.ts create mode 100644 libs/router/data/src/lib/model.ts create mode 100644 libs/router/data/tsconfig.json create mode 100644 libs/router/data/tsconfig.lib.json create mode 100644 libs/router/feature/.babelrc create mode 100644 libs/router/feature/README.md create mode 100644 libs/router/feature/eslint.config.js create mode 100644 libs/router/feature/project.json create mode 100644 libs/router/feature/src/index.ts create mode 100644 libs/router/feature/src/lib/router.tsx create mode 100644 libs/router/feature/tsconfig.json create mode 100644 libs/router/feature/tsconfig.lib.json diff --git a/apps/login-ui/src/app/AppRoutes.tsx b/apps/login-ui/src/app/AppRoutes.tsx new file mode 100644 index 0000000..7d64eaa --- /dev/null +++ b/apps/login-ui/src/app/AppRoutes.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import App from './App'; +import { Route } from '@onboarding/router/data'; +import { CustomRouter } from '@onboarding/router/feature'; + +const routes: Route[] = [ + { + path: '/', + element: , + }, + { + path: '/login', + element:
hola
, + }, +]; + +const AppRoutes: React.FC = () => { + console.log('hola'); + return ; +}; + +export default AppRoutes; diff --git a/apps/login-ui/src/app/AppStarter.tsx b/apps/login-ui/src/app/AppStarter.tsx index 6cd0188..d8a67fe 100644 --- a/apps/login-ui/src/app/AppStarter.tsx +++ b/apps/login-ui/src/app/AppStarter.tsx @@ -1,8 +1,9 @@ import React from 'react'; import { ThemeProvider } from '@mui/material'; import { theme } from '@onboarding/themes'; -import App from './App'; import { I18nProvider } from '@onboarding/i18n/feature'; +import { ReactQueryProvider } from '@onboarding/react-query/feature'; +import AppRoutes from './AppRoutes'; const translations = { //cant import of external file. eslint error @@ -22,7 +23,9 @@ export const AppStarter: React.FC = () => { const children = ( - + + + ); diff --git a/libs/i18n/data/project.json b/libs/i18n/data/project.json index d09dd38..46ddfae 100644 --- a/libs/i18n/data/project.json +++ b/libs/i18n/data/project.json @@ -1,5 +1,5 @@ { - "name": "data", + "name": "data-i18n", "$schema": "../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "libs/i18n/data/src", "projectType": "library", diff --git a/libs/i18n/feature/project.json b/libs/i18n/feature/project.json index a632ee4..d050b60 100644 --- a/libs/i18n/feature/project.json +++ b/libs/i18n/feature/project.json @@ -1,5 +1,5 @@ { - "name": "feature", + "name": "feature-i18n", "$schema": "../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "libs/i18n/feature/src", "projectType": "library", diff --git a/libs/react-query/feature/.babelrc b/libs/react-query/feature/.babelrc new file mode 100644 index 0000000..1ea870e --- /dev/null +++ b/libs/react-query/feature/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/react-query/feature/README.md b/libs/react-query/feature/README.md new file mode 100644 index 0000000..a520852 --- /dev/null +++ b/libs/react-query/feature/README.md @@ -0,0 +1,7 @@ +# feature + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test feature` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/react-query/feature/project.json b/libs/react-query/feature/project.json new file mode 100644 index 0000000..8e1573f --- /dev/null +++ b/libs/react-query/feature/project.json @@ -0,0 +1,9 @@ +{ + "name": "feature-react-query", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/react-query/feature/src", + "projectType": "library", + "tags": [], + "// targets": "to see all targets run: nx show project feature --web", + "targets": {} +} diff --git a/libs/react-query/feature/src/index.ts b/libs/react-query/feature/src/index.ts new file mode 100644 index 0000000..af348fa --- /dev/null +++ b/libs/react-query/feature/src/index.ts @@ -0,0 +1 @@ +export * from './lib/react-query-provider'; diff --git a/libs/react-query/feature/src/lib/react-query-provider.tsx b/libs/react-query/feature/src/lib/react-query-provider.tsx new file mode 100644 index 0000000..efadd15 --- /dev/null +++ b/libs/react-query/feature/src/lib/react-query-provider.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + +export const customReactQueryOptions = { + queries: { + retry: false, + refetchOnWindowFocus: false, + refetchOnReconnect: true, + staleTime: 0, + cacheTime: 0, + }, +}; + +const queryClient = new QueryClient({ + defaultOptions: customReactQueryOptions, +}); + +interface Props { + children: React.ReactNode; +} +export const ReactQueryProvider: React.FC = ({ children }) => { + return ( + {children} + ); +}; diff --git a/libs/react-query/feature/tsconfig.json b/libs/react-query/feature/tsconfig.json new file mode 100644 index 0000000..89f8ac0 --- /dev/null +++ b/libs/react-query/feature/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ], + "extends": "../../../tsconfig.base.json" +} diff --git a/libs/react-query/feature/tsconfig.lib.json b/libs/react-query/feature/tsconfig.lib.json new file mode 100644 index 0000000..aa683fa --- /dev/null +++ b/libs/react-query/feature/tsconfig.lib.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [ + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts" + ] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/libs/router/data/.babelrc b/libs/router/data/.babelrc new file mode 100644 index 0000000..1ea870e --- /dev/null +++ b/libs/router/data/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/router/data/README.md b/libs/router/data/README.md new file mode 100644 index 0000000..20ede1f --- /dev/null +++ b/libs/router/data/README.md @@ -0,0 +1,7 @@ +# data + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test data` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/router/data/eslint.config.js b/libs/router/data/eslint.config.js new file mode 100644 index 0000000..2016bab --- /dev/null +++ b/libs/router/data/eslint.config.js @@ -0,0 +1,12 @@ +const nx = require('@nx/eslint-plugin'); +const baseConfig = require('../../../eslint.config.js'); + +module.exports = [ + ...baseConfig, + ...nx.configs['flat/react'], + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + // Override or add rules here + rules: {}, + }, +]; diff --git a/libs/router/data/project.json b/libs/router/data/project.json new file mode 100644 index 0000000..bb07570 --- /dev/null +++ b/libs/router/data/project.json @@ -0,0 +1,9 @@ +{ + "name": "data-router", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/router/data/src", + "projectType": "library", + "tags": [], + "// targets": "to see all targets run: nx show project data --web", + "targets": {} +} diff --git a/libs/router/data/src/index.ts b/libs/router/data/src/index.ts new file mode 100644 index 0000000..1f2da0b --- /dev/null +++ b/libs/router/data/src/index.ts @@ -0,0 +1 @@ +export * from './lib/model'; diff --git a/libs/router/data/src/lib/model.ts b/libs/router/data/src/lib/model.ts new file mode 100644 index 0000000..3c0d012 --- /dev/null +++ b/libs/router/data/src/lib/model.ts @@ -0,0 +1,6 @@ +import { ReactNode } from "react"; + +export interface Route { + path: string; + element: ReactNode; +} diff --git a/libs/router/data/tsconfig.json b/libs/router/data/tsconfig.json new file mode 100644 index 0000000..89f8ac0 --- /dev/null +++ b/libs/router/data/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ], + "extends": "../../../tsconfig.base.json" +} diff --git a/libs/router/data/tsconfig.lib.json b/libs/router/data/tsconfig.lib.json new file mode 100644 index 0000000..aa683fa --- /dev/null +++ b/libs/router/data/tsconfig.lib.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [ + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts" + ] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/libs/router/feature/.babelrc b/libs/router/feature/.babelrc new file mode 100644 index 0000000..1ea870e --- /dev/null +++ b/libs/router/feature/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/router/feature/README.md b/libs/router/feature/README.md new file mode 100644 index 0000000..a520852 --- /dev/null +++ b/libs/router/feature/README.md @@ -0,0 +1,7 @@ +# feature + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test feature` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/router/feature/eslint.config.js b/libs/router/feature/eslint.config.js new file mode 100644 index 0000000..2016bab --- /dev/null +++ b/libs/router/feature/eslint.config.js @@ -0,0 +1,12 @@ +const nx = require('@nx/eslint-plugin'); +const baseConfig = require('../../../eslint.config.js'); + +module.exports = [ + ...baseConfig, + ...nx.configs['flat/react'], + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + // Override or add rules here + rules: {}, + }, +]; diff --git a/libs/router/feature/project.json b/libs/router/feature/project.json new file mode 100644 index 0000000..ff5363c --- /dev/null +++ b/libs/router/feature/project.json @@ -0,0 +1,9 @@ +{ + "name": "feature-router", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/router/feature/src", + "projectType": "library", + "tags": [], + "// targets": "to see all targets run: nx show project feature --web", + "targets": {} +} diff --git a/libs/router/feature/src/index.ts b/libs/router/feature/src/index.ts new file mode 100644 index 0000000..b068ca4 --- /dev/null +++ b/libs/router/feature/src/index.ts @@ -0,0 +1 @@ +export * from './lib/router'; diff --git a/libs/router/feature/src/lib/router.tsx b/libs/router/feature/src/lib/router.tsx new file mode 100644 index 0000000..1030753 --- /dev/null +++ b/libs/router/feature/src/lib/router.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Route as IRoute } from '@onboarding/router/data'; +import { Routes, Route } from 'react-router-dom'; + +interface Props { + routes: IRoute[]; +} + +export const CustomRouter: React.FC = ({ routes }) => { + return ( + + {routes.map((e, index: number) => { + return ( + + ); + })} + + ); +}; diff --git a/libs/router/feature/tsconfig.json b/libs/router/feature/tsconfig.json new file mode 100644 index 0000000..89f8ac0 --- /dev/null +++ b/libs/router/feature/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": false, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ], + "extends": "../../../tsconfig.base.json" +} diff --git a/libs/router/feature/tsconfig.lib.json b/libs/router/feature/tsconfig.lib.json new file mode 100644 index 0000000..aa683fa --- /dev/null +++ b/libs/router/feature/tsconfig.lib.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [ + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts" + ] + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx" + ], + "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 3a7d187..b0cef22 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -15,10 +15,15 @@ "skipDefaultLibCheck": true, "baseUrl": ".", "paths": { + "@onboarding/router/data": ["libs/router/data/src/index.ts"], "@onboarding/i18n/data": ["libs/i18n/data/src/index.ts"], "@onboarding/i18n/feature": ["libs/i18n/feature/src/index.ts"], + "@onboarding/react-query/feature": [ + "libs/react-query/feature/src/index.ts" + ], + "@onboarding/router/feature": ["libs/router/feature/src/index.ts"], "@onboarding/themes": ["libs/themes/src/index.ts"], - "@onboarding/ui": ["libs/i18n/ui/src/index.ts"], + "@onboarding/ui": ["libs/i18n/ui/src/index.ts"] } }, "exclude": ["node_modules", "tmp"] From 68d91ae289ad51799ac27d62d94b91a4c9a8010e Mon Sep 17 00:00:00 2001 From: Javier Ruiz Date: Wed, 18 Dec 2024 13:19:14 +0100 Subject: [PATCH 2/4] feat: send otp token via email --- apps/auth/src/routes.ts | 18 +- apps/login-ui/src/app/AppRoutes.tsx | 4 +- apps/otp/index.html | 22 - apps/otp/jest.config.ts | 10 - apps/otp/project.json | 9 - apps/otp/public/favicon.ico | Bin 15086 -> 0 bytes apps/otp/src/app/app.module.css | 0 apps/otp/src/app/app.spec.tsx | 15 - apps/otp/src/assets/.gitkeep | 0 apps/otp/src/main.tsx | 26 - apps/otp/src/styles.css | 1 - apps/otp/tsconfig.app.json | 24 - apps/otp/tsconfig.json | 21 - libs/i18n/ui/README.md | 7 - libs/i18n/ui/project.json | 9 - libs/i18n/ui/src/index.ts | 1 - libs/i18n/ui/src/lib/ui.tsx | 9 - libs/{i18n/ui => otp-ui/feature}/.babelrc | 10 +- libs/otp-ui/feature/README.md | 7 + .../otp-ui/feature}/eslint.config.js | 2 +- libs/otp-ui/feature/project.json | 9 + libs/otp-ui/feature/src/components/index.ts | 3 + .../src}/components/otp-input/otp-input.tsx | 0 .../request-token-form/request-token-form.tsx | 54 +- .../validate-token-form.tsx | 2 +- .../src}/context/authentication-context.ts | 0 .../src}/context/authentication-provider.tsx | 0 libs/otp-ui/feature/src/hooks/index.ts | 2 + .../feature/src}/hooks/use-request-token.ts | 3 +- .../feature/src}/hooks/use-validate-token.ts | 0 libs/otp-ui/feature/src/index.ts | 3 + libs/otp-ui/feature/src/pages/index.ts | 1 + .../feature/src/pages/otp-page-auth.tsx | 12 +- .../otp-ui/feature/src}/services/index.ts | 0 .../src}/services/otp-authentication.ts | 2 +- .../{i18n/ui => otp-ui/feature}/tsconfig.json | 3 + .../ui => otp-ui/feature}/tsconfig.lib.json | 23 +- .../otp-ui/feature}/tsconfig.spec.json | 17 +- .../otp-ui/feature}/vite.config.ts | 26 +- libs/otp/feature/README.md | 7 + libs/otp/feature/eslint.config.js | 3 + libs/otp/feature/jest.config.ts | 10 + libs/otp/feature/project.json | 9 + libs/otp/feature/src/crypto.ts | 5 + libs/otp/feature/src/index.ts | 2 + libs/otp/feature/src/nodemailer.ts | 20 + libs/otp/feature/tsconfig.json | 16 + libs/otp/feature/tsconfig.lib.json | 11 + libs/otp/feature/tsconfig.spec.json | 15 + package-lock.json | 627 ++++++++++++++++++ package.json | 2 + tsconfig.base.json | 7 +- 52 files changed, 864 insertions(+), 225 deletions(-) delete mode 100644 apps/otp/index.html delete mode 100644 apps/otp/jest.config.ts delete mode 100644 apps/otp/project.json delete mode 100644 apps/otp/public/favicon.ico delete mode 100644 apps/otp/src/app/app.module.css delete mode 100644 apps/otp/src/app/app.spec.tsx delete mode 100644 apps/otp/src/assets/.gitkeep delete mode 100644 apps/otp/src/main.tsx delete mode 100644 apps/otp/src/styles.css delete mode 100644 apps/otp/tsconfig.app.json delete mode 100644 apps/otp/tsconfig.json delete mode 100644 libs/i18n/ui/README.md delete mode 100644 libs/i18n/ui/project.json delete mode 100644 libs/i18n/ui/src/index.ts delete mode 100644 libs/i18n/ui/src/lib/ui.tsx rename libs/{i18n/ui => otp-ui/feature}/.babelrc (54%) create mode 100644 libs/otp-ui/feature/README.md rename {apps/otp => libs/otp-ui/feature}/eslint.config.js (80%) create mode 100644 libs/otp-ui/feature/project.json create mode 100644 libs/otp-ui/feature/src/components/index.ts rename {apps/otp/src/app => libs/otp-ui/feature/src}/components/otp-input/otp-input.tsx (100%) rename {apps/otp/src/app => libs/otp-ui/feature/src}/components/request-token-form/request-token-form.tsx (55%) rename {apps/otp/src/app => libs/otp-ui/feature/src}/components/validate-token-form/validate-token-form.tsx (100%) rename {apps/otp/src/app => libs/otp-ui/feature/src}/context/authentication-context.ts (100%) rename {apps/otp/src/app => libs/otp-ui/feature/src}/context/authentication-provider.tsx (100%) create mode 100644 libs/otp-ui/feature/src/hooks/index.ts rename {apps/otp/src/app => libs/otp-ui/feature/src}/hooks/use-request-token.ts (85%) rename {apps/otp/src/app => libs/otp-ui/feature/src}/hooks/use-validate-token.ts (100%) create mode 100644 libs/otp-ui/feature/src/index.ts create mode 100644 libs/otp-ui/feature/src/pages/index.ts rename apps/otp/src/app/app.tsx => libs/otp-ui/feature/src/pages/otp-page-auth.tsx (65%) rename {apps/otp/src/app => libs/otp-ui/feature/src}/services/index.ts (100%) rename {apps/otp/src/app => libs/otp-ui/feature/src}/services/otp-authentication.ts (99%) rename libs/{i18n/ui => otp-ui/feature}/tsconfig.json (86%) rename libs/{i18n/ui => otp-ui/feature}/tsconfig.lib.json (59%) rename {apps/otp => libs/otp-ui/feature}/tsconfig.spec.json (58%) rename {apps/otp => libs/otp-ui/feature}/vite.config.ts (57%) create mode 100644 libs/otp/feature/README.md create mode 100644 libs/otp/feature/eslint.config.js create mode 100644 libs/otp/feature/jest.config.ts create mode 100644 libs/otp/feature/project.json create mode 100644 libs/otp/feature/src/crypto.ts create mode 100644 libs/otp/feature/src/index.ts create mode 100644 libs/otp/feature/src/nodemailer.ts create mode 100644 libs/otp/feature/tsconfig.json create mode 100644 libs/otp/feature/tsconfig.lib.json create mode 100644 libs/otp/feature/tsconfig.spec.json diff --git a/apps/auth/src/routes.ts b/apps/auth/src/routes.ts index b851c64..c69e7ad 100644 --- a/apps/auth/src/routes.ts +++ b/apps/auth/src/routes.ts @@ -1,6 +1,6 @@ import koaRouter from 'koa-router'; -import crypto from 'crypto'; import jwt from 'jsonwebtoken'; +import { generateOTP, sendOTPEmail } from "@onboarding/otp/feature" const router = new koaRouter(); @@ -9,13 +9,15 @@ router.get('hello', '/', (ctx) => { }); router.post('/otp/generate', async (ctx, next) => { - const otp = crypto.randomBytes(2).toString('hex'); - const email = ctx.request.body; - - ctx.app.user = { email: email.email, otp }; - - ctx.body = { otp }; - + const otp = generateOTP(2); + const { email } = ctx.request.body; + + ctx.app.user = { email: email, otp }; //save temporally in memory + + await sendOTPEmail(email, otp); + ctx.body = { message: "OTP sent to your email" } + + next(); }); diff --git a/apps/login-ui/src/app/AppRoutes.tsx b/apps/login-ui/src/app/AppRoutes.tsx index 7d64eaa..f3f9d14 100644 --- a/apps/login-ui/src/app/AppRoutes.tsx +++ b/apps/login-ui/src/app/AppRoutes.tsx @@ -2,6 +2,7 @@ import React from 'react'; import App from './App'; import { Route } from '@onboarding/router/data'; import { CustomRouter } from '@onboarding/router/feature'; +import { OtpPageAuth } from '@onboarding/otp-ui/feature'; const routes: Route[] = [ { @@ -10,12 +11,11 @@ const routes: Route[] = [ }, { path: '/login', - element:
hola
, + element: , }, ]; const AppRoutes: React.FC = () => { - console.log('hola'); return ; }; diff --git a/apps/otp/index.html b/apps/otp/index.html deleted file mode 100644 index d5f8695..0000000 --- a/apps/otp/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Ui - - - - - - - - - - -
- - - diff --git a/apps/otp/jest.config.ts b/apps/otp/jest.config.ts deleted file mode 100644 index 7117fe1..0000000 --- a/apps/otp/jest.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default { - displayName: 'ui', - preset: '../../jest.preset.js', - transform: { - '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest', - '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }], - }, - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], - coverageDirectory: '../../coverage/apps/otp', -}; diff --git a/apps/otp/project.json b/apps/otp/project.json deleted file mode 100644 index 35fc38b..0000000 --- a/apps/otp/project.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "otp", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/otp/src", - "projectType": "application", - "tags": [], - "// targets": "to see all targets run: nx show project otp --web", - "targets": {} -} diff --git a/apps/otp/public/favicon.ico b/apps/otp/public/favicon.ico deleted file mode 100644 index 317ebcb2336e0833a22dddf0ab287849f26fda57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 zcmeI332;U^%p|z7g|#(P)qFEA@4f!_@qOK2 z_lJl}!lhL!VT_U|uN7%8B2iKH??xhDa;*`g{yjTFWHvXn;2s{4R7kH|pKGdy(7z!K zgftM+Ku7~24TLlh(!g)gz|foI94G^t2^IO$uvX$3(OR0<_5L2sB)lMAMy|+`xodJ{ z_Uh_1m)~h?a;2W{dmhM;u!YGo=)OdmId_B<%^V^{ovI@y`7^g1_V9G}*f# zNzAtvou}I!W1#{M^@ROc(BZ! z+F!!_aR&Px3_reO(EW+TwlW~tv*2zr?iP7(d~a~yA|@*a89IUke+c472NXM0wiX{- zl`UrZC^1XYyf%1u)-Y)jj9;MZ!SLfd2Hl?o|80Su%Z?To_=^g_Jt0oa#CT*tjx>BI z16wec&AOWNK<#i0Qd=1O$fymLRoUR*%;h@*@v7}wApDl^w*h}!sYq%kw+DKDY)@&A z@9$ULEB3qkR#85`lb8#WZw=@})#kQig9oqy^I$dj&k4jU&^2(M3q{n1AKeGUKPFbr z1^<)aH;VsG@J|B&l>UtU#Ejv3GIqERzYgL@UOAWtW<{p#zy`WyJgpCy8$c_e%wYJL zyGHRRx38)HyjU3y{-4z6)pzb>&Q1pR)B&u01F-|&Gx4EZWK$nkUkOI|(D4UHOXg_- zw{OBf!oWQUn)Pe(=f=nt=zkmdjpO^o8ZZ9o_|4tW1ni+Un9iCW47*-ut$KQOww!;u z`0q)$s6IZO!~9$e_P9X!hqLxu`fpcL|2f^I5d4*a@Dq28;@2271v_N+5HqYZ>x;&O z05*7JT)mUe&%S0@UD)@&8SmQrMtsDfZT;fkdA!r(S=}Oz>iP)w=W508=Rc#nNn7ym z1;42c|8($ALY8#a({%1#IXbWn9-Y|0eDY$_L&j{63?{?AH{);EzcqfydD$@-B`Y3<%IIj7S7rK_N}je^=dEk%JQ4c z!tBdTPE3Tse;oYF>cnrapWq*o)m47X1`~6@(!Y29#>-#8zm&LXrXa(3=7Z)ElaQqj z-#0JJy3Fi(C#Rx(`=VXtJ63E2_bZGCz+QRa{W0e2(m3sI?LOcUBx)~^YCqZ{XEPX)C>G>U4tfqeH8L(3|pQR*zbL1 zT9e~4Tb5p9_G}$y4t`i*4t_Mr9QYvL9C&Ah*}t`q*}S+VYh0M6GxTTSXI)hMpMpIq zD1ImYqJLzbj0}~EpE-aH#VCH_udYEW#`P2zYmi&xSPs_{n6tBj=MY|-XrA;SGA_>y zGtU$?HXm$gYj*!N)_nQ59%lQdXtQZS3*#PC-{iB_sm+ytD*7j`D*k(P&IH2GHT}Eh z5697eQECVIGQAUe#eU2I!yI&%0CP#>%6MWV z@zS!p@+Y1i1b^QuuEF*13CuB zu69dve5k7&Wgb+^s|UB08Dr3u`h@yM0NTj4h7MnHo-4@xmyr7(*4$rpPwsCDZ@2be zRz9V^GnV;;?^Lk%ynzq&K(Aix`mWmW`^152Hoy$CTYVehpD-S1-W^#k#{0^L`V6CN+E z!w+xte;2vu4AmVNEFUOBmrBL>6MK@!O2*N|2=d|Y;oN&A&qv=qKn73lDD zI(+oJAdgv>Yr}8(&@ZuAZE%XUXmX(U!N+Z_sjL<1vjy1R+1IeHt`79fnYdOL{$ci7 z%3f0A*;Zt@ED&Gjm|OFTYBDe%bbo*xXAQsFz+Q`fVBH!N2)kaxN8P$c>sp~QXnv>b zwq=W3&Mtmih7xkR$YA)1Yi?avHNR6C99!u6fh=cL|KQ&PwF!n@ud^n(HNIImHD!h87!i*t?G|p0o+eelJ?B@A64_9%SBhNaJ64EvKgD&%LjLCYnNfc; znj?%*p@*?dq#NqcQFmmX($wms@CSAr9#>hUR^=I+=0B)vvGX%T&#h$kmX*s=^M2E!@N9#m?LhMvz}YB+kd zG~mbP|D(;{s_#;hsKK9lbVK&Lo734x7SIFJ9V_}2$@q?zm^7?*XH94w5Qae{7zOMUF z^?%F%)c1Y)Q?Iy?I>knw*8gYW#ok|2gdS=YYZLiD=CW|Nj;n^x!=S#iJ#`~Ld79+xXpVmUK^B(xO_vO!btA9y7w3L3-0j-y4 z?M-V{%z;JI`bk7yFDcP}OcCd*{Q9S5$iGA7*E1@tfkyjAi!;wP^O71cZ^Ep)qrQ)N z#wqw0_HS;T7x3y|`P==i3hEwK%|>fZ)c&@kgKO1~5<5xBSk?iZV?KI6&i72H6S9A* z=U(*e)EqEs?Oc04)V-~K5AUmh|62H4*`UAtItO$O(q5?6jj+K^oD!04r=6#dsxp?~}{`?&sXn#q2 zGuY~7>O2=!u@@Kfu7q=W*4egu@qPMRM>(eyYyaIE<|j%d=iWNdGsx%c!902v#ngNg z@#U-O_4xN$s_9?(`{>{>7~-6FgWpBpqXb`Ydc3OFL#&I}Irse9F_8R@4zSS*Y*o*B zXL?6*Aw!AfkNCgcr#*yj&p3ZDe2y>v$>FUdKIy_2N~}6AbHc7gA3`6$g@1o|dE>vz z4pl(j9;kyMsjaw}lO?(?Xg%4k!5%^t#@5n=WVc&JRa+XT$~#@rldvN3S1rEpU$;XgxVny7mki3 z-Hh|jUCHrUXuLr!)`w>wgO0N%KTB-1di>cj(x3Bav`7v z3G7EIbU$z>`Nad7Rk_&OT-W{;qg)-GXV-aJT#(ozdmnA~Rq3GQ_3mby(>q6Ocb-RgTUhTN)))x>m&eD;$J5Bg zo&DhY36Yg=J=$Z>t}RJ>o|@hAcwWzN#r(WJ52^g$lh^!63@hh+dR$&_dEGu&^CR*< z!oFqSqO@>xZ*nC2oiOd0eS*F^IL~W-rsrO`J`ej{=ou_q^_(<$&-3f^J z&L^MSYWIe{&pYq&9eGaArA~*kA { - it('should render successfully', () => { - const { baseElement } = render(); - expect(baseElement).toBeTruthy(); - }); - - it('should have a greeting as the title', () => { - const { getByText } = render(); - expect(getByText(/Welcome ui/gi)).toBeTruthy(); - }); -}); diff --git a/apps/otp/src/assets/.gitkeep b/apps/otp/src/assets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/apps/otp/src/main.tsx b/apps/otp/src/main.tsx deleted file mode 100644 index c2df5d8..0000000 --- a/apps/otp/src/main.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { StrictMode } from 'react'; -import * as ReactDOM from 'react-dom/client'; -import App from './app/app'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; - -import '@fontsource/roboto/300.css'; -import '@fontsource/roboto/400.css'; -import '@fontsource/roboto/500.css'; -import '@fontsource/roboto/700.css'; -import { AuthenticationProvider } from './app/context/authentication-provider'; - -const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement -); - -const queryClient = new QueryClient(); - -root.render( - - - - - - - -); diff --git a/apps/otp/src/styles.css b/apps/otp/src/styles.css deleted file mode 100644 index 90d4ee0..0000000 --- a/apps/otp/src/styles.css +++ /dev/null @@ -1 +0,0 @@ -/* You can add global styles to this file, and also import other style files */ diff --git a/apps/otp/tsconfig.app.json b/apps/otp/tsconfig.app.json deleted file mode 100644 index db420c0..0000000 --- a/apps/otp/tsconfig.app.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "types": [ - "node", - "@nx/react/typings/cssmodule.d.ts", - "@nx/react/typings/image.d.ts", - "vite/client" - ] - }, - "exclude": [ - "src/**/*.spec.ts", - "src/**/*.test.ts", - "src/**/*.spec.tsx", - "src/**/*.test.tsx", - "src/**/*.spec.js", - "src/**/*.test.js", - "src/**/*.spec.jsx", - "src/**/*.test.jsx", - "jest.config.ts" - ], - "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] -} diff --git a/apps/otp/tsconfig.json b/apps/otp/tsconfig.json deleted file mode 100644 index b1de769..0000000 --- a/apps/otp/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "jsx": "react-jsx", - "allowJs": false, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": true, - "types": ["vite/client"] - }, - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.spec.json" - } - ], - "extends": "../../tsconfig.base.json" -} diff --git a/libs/i18n/ui/README.md b/libs/i18n/ui/README.md deleted file mode 100644 index f46c5c2..0000000 --- a/libs/i18n/ui/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# ui - -This library was generated with [Nx](https://nx.dev). - -## Running unit tests - -Run `nx test ui` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/i18n/ui/project.json b/libs/i18n/ui/project.json deleted file mode 100644 index 4a17be3..0000000 --- a/libs/i18n/ui/project.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "ui", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "libs/i18n/ui/src", - "projectType": "library", - "tags": [], - "// targets": "to see all targets run: nx show project ui --web", - "targets": {} -} diff --git a/libs/i18n/ui/src/index.ts b/libs/i18n/ui/src/index.ts deleted file mode 100644 index 48da4fd..0000000 --- a/libs/i18n/ui/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './lib/ui'; diff --git a/libs/i18n/ui/src/lib/ui.tsx b/libs/i18n/ui/src/lib/ui.tsx deleted file mode 100644 index 9ce5949..0000000 --- a/libs/i18n/ui/src/lib/ui.tsx +++ /dev/null @@ -1,9 +0,0 @@ -export function Ui() { - return ( -
-

Welcome to Ui!

-
- ); -} - -export default Ui; diff --git a/libs/i18n/ui/.babelrc b/libs/otp-ui/feature/.babelrc similarity index 54% rename from libs/i18n/ui/.babelrc rename to libs/otp-ui/feature/.babelrc index 1ea870e..ef4889c 100644 --- a/libs/i18n/ui/.babelrc +++ b/libs/otp-ui/feature/.babelrc @@ -8,5 +8,13 @@ } ] ], - "plugins": [] + "plugins": [ + [ + "styled-components", + { + "pure": true, + "ssr": true + } + ] + ] } diff --git a/libs/otp-ui/feature/README.md b/libs/otp-ui/feature/README.md new file mode 100644 index 0000000..5302c8e --- /dev/null +++ b/libs/otp-ui/feature/README.md @@ -0,0 +1,7 @@ +# feature + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test feature` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/apps/otp/eslint.config.js b/libs/otp-ui/feature/eslint.config.js similarity index 80% rename from apps/otp/eslint.config.js rename to libs/otp-ui/feature/eslint.config.js index 2f6e3f0..2016bab 100644 --- a/apps/otp/eslint.config.js +++ b/libs/otp-ui/feature/eslint.config.js @@ -1,5 +1,5 @@ const nx = require('@nx/eslint-plugin'); -const baseConfig = require('../../eslint.config.js'); +const baseConfig = require('../../../eslint.config.js'); module.exports = [ ...baseConfig, diff --git a/libs/otp-ui/feature/project.json b/libs/otp-ui/feature/project.json new file mode 100644 index 0000000..2422f26 --- /dev/null +++ b/libs/otp-ui/feature/project.json @@ -0,0 +1,9 @@ +{ + "name": "feature-otp", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/otp/feature/src", + "projectType": "library", + "tags": [], + "// targets": "to see all targets run: nx show project feature --web", + "targets": {} +} diff --git a/libs/otp-ui/feature/src/components/index.ts b/libs/otp-ui/feature/src/components/index.ts new file mode 100644 index 0000000..a65f621 --- /dev/null +++ b/libs/otp-ui/feature/src/components/index.ts @@ -0,0 +1,3 @@ +export * from "./otp-input/otp-input"; +export * from "./request-token-form/request-token-form"; +export * from "./validate-token-form/validate-token-form"; \ No newline at end of file diff --git a/apps/otp/src/app/components/otp-input/otp-input.tsx b/libs/otp-ui/feature/src/components/otp-input/otp-input.tsx similarity index 100% rename from apps/otp/src/app/components/otp-input/otp-input.tsx rename to libs/otp-ui/feature/src/components/otp-input/otp-input.tsx diff --git a/apps/otp/src/app/components/request-token-form/request-token-form.tsx b/libs/otp-ui/feature/src/components/request-token-form/request-token-form.tsx similarity index 55% rename from apps/otp/src/app/components/request-token-form/request-token-form.tsx rename to libs/otp-ui/feature/src/components/request-token-form/request-token-form.tsx index 481e737..9851239 100644 --- a/apps/otp/src/app/components/request-token-form/request-token-form.tsx +++ b/libs/otp-ui/feature/src/components/request-token-form/request-token-form.tsx @@ -9,6 +9,7 @@ import { } from '@mui/material'; import useRequestToken from '../../hooks/use-request-token'; import { AuthenticationContext } from '../../context/authentication-context'; +import CircularProgress from '@mui/material/CircularProgress'; interface IRequestTokenForm { testId?: string; @@ -32,11 +33,16 @@ const RequestTokenForm: React.FC = (props) => { const { setIsWaitingForValidation, setEmail } = useContext( AuthenticationContext ); - const { mutate: fetchNewToken, isSuccess, data } = useRequestToken(); + const { + mutate: fetchNewToken, + isSuccess, + data, + isPending, + } = useRequestToken(); - const onSubmit: SubmitHandler = (data, e) => { + const onSubmit: SubmitHandler = (d, e) => { e?.preventDefault(); - fetchNewToken(data.emailRequired); + fetchNewToken(d.emailRequired); }; useEffect(() => { @@ -46,21 +52,37 @@ const RequestTokenForm: React.FC = (props) => { } }, [isSuccess, data, setIsWaitingForValidation, setEmail]); + if (isPending) { + return ; + } + return (
- - Request a otp token - Email - + + Request a otp token +
+ Email + +
{errors.emailRequired && errors.emailRequired.message}
diff --git a/apps/otp/src/app/components/validate-token-form/validate-token-form.tsx b/libs/otp-ui/feature/src/components/validate-token-form/validate-token-form.tsx similarity index 100% rename from apps/otp/src/app/components/validate-token-form/validate-token-form.tsx rename to libs/otp-ui/feature/src/components/validate-token-form/validate-token-form.tsx index d3deae0..c616df0 100644 --- a/apps/otp/src/app/components/validate-token-form/validate-token-form.tsx +++ b/libs/otp-ui/feature/src/components/validate-token-form/validate-token-form.tsx @@ -2,8 +2,8 @@ import { Button, Typography } from '@mui/material'; import { useContext, useEffect, useState } from 'react'; import { OtpInputComp } from '../otp-input/otp-input'; import useValidateToken from '../../hooks/use-validate-token'; -import { AuthenticationContext } from '../../context/authentication-context'; import { Snackbar } from '@mui/material'; +import { AuthenticationContext } from '../../context/authentication-context'; const ValidateTokenForm = () => { const [userToken, setUserToken] = useState(''); diff --git a/apps/otp/src/app/context/authentication-context.ts b/libs/otp-ui/feature/src/context/authentication-context.ts similarity index 100% rename from apps/otp/src/app/context/authentication-context.ts rename to libs/otp-ui/feature/src/context/authentication-context.ts diff --git a/apps/otp/src/app/context/authentication-provider.tsx b/libs/otp-ui/feature/src/context/authentication-provider.tsx similarity index 100% rename from apps/otp/src/app/context/authentication-provider.tsx rename to libs/otp-ui/feature/src/context/authentication-provider.tsx diff --git a/libs/otp-ui/feature/src/hooks/index.ts b/libs/otp-ui/feature/src/hooks/index.ts new file mode 100644 index 0000000..447e842 --- /dev/null +++ b/libs/otp-ui/feature/src/hooks/index.ts @@ -0,0 +1,2 @@ +export * from "./use-request-token"; +export * from "./use-validate-token"; \ No newline at end of file diff --git a/apps/otp/src/app/hooks/use-request-token.ts b/libs/otp-ui/feature/src/hooks/use-request-token.ts similarity index 85% rename from apps/otp/src/app/hooks/use-request-token.ts rename to libs/otp-ui/feature/src/hooks/use-request-token.ts index 427c663..76c8387 100644 --- a/apps/otp/src/app/hooks/use-request-token.ts +++ b/libs/otp-ui/feature/src/hooks/use-request-token.ts @@ -8,7 +8,7 @@ export interface TokenResponse { const REQUEST_TOKEN_KEY = 'request_token'; const useRequestToken = () => { - const { data, mutate, error, isSuccess } = useMutation({ + const { data, mutate, error, isSuccess, isPending } = useMutation({ mutationKey: [REQUEST_TOKEN_KEY], mutationFn: async (email: string) => { const response = await postRequestToken(email); @@ -21,6 +21,7 @@ const useRequestToken = () => { mutate, error, isSuccess, + isPending }; }; diff --git a/apps/otp/src/app/hooks/use-validate-token.ts b/libs/otp-ui/feature/src/hooks/use-validate-token.ts similarity index 100% rename from apps/otp/src/app/hooks/use-validate-token.ts rename to libs/otp-ui/feature/src/hooks/use-validate-token.ts diff --git a/libs/otp-ui/feature/src/index.ts b/libs/otp-ui/feature/src/index.ts new file mode 100644 index 0000000..04a1278 --- /dev/null +++ b/libs/otp-ui/feature/src/index.ts @@ -0,0 +1,3 @@ +export * from "./components/index"; +export * from "./hooks/index"; +export * from "./pages/index"; \ No newline at end of file diff --git a/libs/otp-ui/feature/src/pages/index.ts b/libs/otp-ui/feature/src/pages/index.ts new file mode 100644 index 0000000..1670881 --- /dev/null +++ b/libs/otp-ui/feature/src/pages/index.ts @@ -0,0 +1 @@ +export * from "./otp-page-auth"; \ No newline at end of file diff --git a/apps/otp/src/app/app.tsx b/libs/otp-ui/feature/src/pages/otp-page-auth.tsx similarity index 65% rename from apps/otp/src/app/app.tsx rename to libs/otp-ui/feature/src/pages/otp-page-auth.tsx index 4ab8c3a..17cdda7 100644 --- a/apps/otp/src/app/app.tsx +++ b/libs/otp-ui/feature/src/pages/otp-page-auth.tsx @@ -1,9 +1,9 @@ import { useContext } from 'react'; -import RequestTokenForm from './components/request-token-form/request-token-form'; -import { AuthenticationContext } from './context/authentication-context'; -import ValidateTokenForm from './components/validate-token-form/validate-token-form'; +import { AuthenticationContext } from '../context/authentication-context'; +import ValidateTokenForm from '../components/validate-token-form/validate-token-form'; +import RequestTokenForm from '../components/request-token-form/request-token-form'; -export function App() { +export const OtpPageAuth = () => { const { isWaitingForValidation } = useContext(AuthenticationContext); const appContainerStyles = { @@ -26,6 +26,4 @@ export function App() { )} ); -} - -export default App; +}; diff --git a/apps/otp/src/app/services/index.ts b/libs/otp-ui/feature/src/services/index.ts similarity index 100% rename from apps/otp/src/app/services/index.ts rename to libs/otp-ui/feature/src/services/index.ts diff --git a/apps/otp/src/app/services/otp-authentication.ts b/libs/otp-ui/feature/src/services/otp-authentication.ts similarity index 99% rename from apps/otp/src/app/services/otp-authentication.ts rename to libs/otp-ui/feature/src/services/otp-authentication.ts index b68c4a8..78d48a4 100644 --- a/apps/otp/src/app/services/otp-authentication.ts +++ b/libs/otp-ui/feature/src/services/otp-authentication.ts @@ -7,7 +7,6 @@ export const postRequestToken = (email: string) => { const body = { email: email, }; - return axios .post(apiUrlGenerateToken, body) .then((response) => { @@ -17,6 +16,7 @@ export const postRequestToken = (email: string) => { .catch((error) => { console.error(error); }); + }; export const postValidateToken = (email: string, otp: string) => { diff --git a/libs/i18n/ui/tsconfig.json b/libs/otp-ui/feature/tsconfig.json similarity index 86% rename from libs/i18n/ui/tsconfig.json rename to libs/otp-ui/feature/tsconfig.json index 89f8ac0..4daaf45 100644 --- a/libs/i18n/ui/tsconfig.json +++ b/libs/otp-ui/feature/tsconfig.json @@ -11,6 +11,9 @@ "references": [ { "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" } ], "extends": "../../../tsconfig.base.json" diff --git a/libs/i18n/ui/tsconfig.lib.json b/libs/otp-ui/feature/tsconfig.lib.json similarity index 59% rename from libs/i18n/ui/tsconfig.lib.json rename to libs/otp-ui/feature/tsconfig.lib.json index aa683fa..49dd657 100644 --- a/libs/i18n/ui/tsconfig.lib.json +++ b/libs/otp-ui/feature/tsconfig.lib.json @@ -9,15 +9,26 @@ ] }, "exclude": [ - "jest.config.ts", - "src/**/*.spec.ts", + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", "src/**/*.test.ts", - "src/**/*.spec.tsx", + "src/**/*.spec.ts", "src/**/*.test.tsx", - "src/**/*.spec.js", + "src/**/*.spec.tsx", "src/**/*.test.js", - "src/**/*.spec.jsx", - "src/**/*.test.jsx" + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx" ], "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] } diff --git a/apps/otp/tsconfig.spec.json b/libs/otp-ui/feature/tsconfig.spec.json similarity index 58% rename from apps/otp/tsconfig.spec.json rename to libs/otp-ui/feature/tsconfig.spec.json index 9c28e60..69d6af2 100644 --- a/apps/otp/tsconfig.spec.json +++ b/libs/otp-ui/feature/tsconfig.spec.json @@ -1,19 +1,20 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "moduleResolution": "node10", - "jsx": "react-jsx", + "outDir": "../../../dist/out-tsc", "types": [ - "jest", + "vitest/globals", + "vitest/importMeta", + "vite/client", "node", - "@nx/react/typings/cssmodule.d.ts", - "@nx/react/typings/image.d.ts" + "vitest" ] }, "include": [ - "jest.config.ts", + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.test.tsx", diff --git a/apps/otp/vite.config.ts b/libs/otp-ui/feature/vite.config.ts similarity index 57% rename from apps/otp/vite.config.ts rename to libs/otp-ui/feature/vite.config.ts index e24019c..726c6af 100644 --- a/apps/otp/vite.config.ts +++ b/libs/otp-ui/feature/vite.config.ts @@ -1,4 +1,3 @@ -/// import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; @@ -6,26 +5,21 @@ import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin'; export default defineConfig({ root: __dirname, - cacheDir: '../../node_modules/.vite/apps/ui', - server: { - port: 4200, - host: 'localhost', - }, - preview: { - port: 4300, - host: 'localhost', - }, + cacheDir: '../../../node_modules/.vite/libs/otp/feature', plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])], // Uncomment this if you are using workers. // worker: { // plugins: [ nxViteTsPaths() ], // }, - build: { - outDir: '../../dist/apps/ui', - emptyOutDir: true, - reportCompressedSize: true, - commonjsOptions: { - transformMixedEsModules: true, + test: { + watch: false, + globals: true, + environment: 'jsdom', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + reporters: ['default'], + coverage: { + reportsDirectory: '../../../coverage/libs/otp/feature', + provider: 'v8', }, }, }); diff --git a/libs/otp/feature/README.md b/libs/otp/feature/README.md new file mode 100644 index 0000000..a520852 --- /dev/null +++ b/libs/otp/feature/README.md @@ -0,0 +1,7 @@ +# feature + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test feature` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/otp/feature/eslint.config.js b/libs/otp/feature/eslint.config.js new file mode 100644 index 0000000..07e518f --- /dev/null +++ b/libs/otp/feature/eslint.config.js @@ -0,0 +1,3 @@ +const baseConfig = require('../../../eslint.config.js'); + +module.exports = [...baseConfig]; diff --git a/libs/otp/feature/jest.config.ts b/libs/otp/feature/jest.config.ts new file mode 100644 index 0000000..44ea6ff --- /dev/null +++ b/libs/otp/feature/jest.config.ts @@ -0,0 +1,10 @@ +export default { + displayName: 'feature', + preset: '../../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/libs/otp/feature', +}; diff --git a/libs/otp/feature/project.json b/libs/otp/feature/project.json new file mode 100644 index 0000000..5a2ad3f --- /dev/null +++ b/libs/otp/feature/project.json @@ -0,0 +1,9 @@ +{ + "name": "feature", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/otp/feature/src", + "projectType": "library", + "tags": [], + "// targets": "to see all targets run: nx show project feature --web", + "targets": {} +} diff --git a/libs/otp/feature/src/crypto.ts b/libs/otp/feature/src/crypto.ts new file mode 100644 index 0000000..59253e4 --- /dev/null +++ b/libs/otp/feature/src/crypto.ts @@ -0,0 +1,5 @@ +import {randomBytes} from 'crypto'; + +export const generateOTP = (numberBytes: number) => { + return randomBytes(numberBytes).toString('hex'); // Generates a 6-character OTP +}; \ No newline at end of file diff --git a/libs/otp/feature/src/index.ts b/libs/otp/feature/src/index.ts new file mode 100644 index 0000000..664e545 --- /dev/null +++ b/libs/otp/feature/src/index.ts @@ -0,0 +1,2 @@ +export * from './crypto'; +export * from "./nodemailer"; diff --git a/libs/otp/feature/src/nodemailer.ts b/libs/otp/feature/src/nodemailer.ts new file mode 100644 index 0000000..1ccd8c2 --- /dev/null +++ b/libs/otp/feature/src/nodemailer.ts @@ -0,0 +1,20 @@ +import { createTransport } from "nodemailer"; + +export const sendOTPEmail = async (email, otp) => { + const transporter = createTransport({ + service: 'gmail', + auth: { + user: 'javiertestnode@gmail.com', + pass: 'your password', + }, + }); + + const mailOptions = { + from: 'javiertestnode@gmail.com', + to: email, + subject: 'Your OTP Code', + text: `Your OTP code is ${otp}`, + }; + + await transporter.sendMail(mailOptions); + }; \ No newline at end of file diff --git a/libs/otp/feature/tsconfig.json b/libs/otp/feature/tsconfig.json new file mode 100644 index 0000000..25f7201 --- /dev/null +++ b/libs/otp/feature/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs" + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/otp/feature/tsconfig.lib.json b/libs/otp/feature/tsconfig.lib.json new file mode 100644 index 0000000..e583571 --- /dev/null +++ b/libs/otp/feature/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/libs/otp/feature/tsconfig.spec.json b/libs/otp/feature/tsconfig.spec.json new file mode 100644 index 0000000..ab55b7c --- /dev/null +++ b/libs/otp/feature/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "moduleResolution": "node10", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/package-lock.json b/package-lock.json index f99d9da..0365bf5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "koa-cors": "^0.0.16", "koa-jwt": "4.0.4", "koa-router": "13.0.1", + "nodemailer": "^6.9.16", "react": "18.3.1", "react-dom": "18.3.1", "react-hook-form": "^7.54.1", @@ -81,6 +82,7 @@ "typescript": "~5.6.2", "typescript-eslint": "^8.13.0", "vite": "^5.0.0", + "vite-plugin-dts": "~3.8.1", "vitest": "^1.3.1" } }, @@ -3389,6 +3391,151 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@microsoft/api-extractor": { + "version": "7.43.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.43.0.tgz", + "integrity": "sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor-model": "7.28.13", + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "4.0.2", + "@rushstack/rig-package": "0.5.2", + "@rushstack/terminal": "0.10.0", + "@rushstack/ts-command-line": "4.19.1", + "lodash": "~4.17.15", + "minimatch": "~3.0.3", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.4.2" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.28.13", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.13.tgz", + "integrity": "sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "~0.16.1", + "@rushstack/node-core-library": "4.0.2" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/@module-federation/bridge-react-webpack-plugin": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.7.6.tgz", @@ -7744,6 +7891,36 @@ "node": ">=14" } }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", @@ -8234,6 +8411,169 @@ "dev": true, "license": "MIT" }, + "node_modules/@rushstack/node-core-library": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", + "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "z-schema": "~5.0.2" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@rushstack/rig-package": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.2.tgz", + "integrity": "sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", + "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "4.0.2", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", + "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/terminal": "0.10.0", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, + "node_modules/@rushstack/ts-command-line/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -9062,6 +9402,13 @@ "@types/node": "*" } }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -9934,6 +10281,102 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@volar/language-core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz", + "integrity": "sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "1.11.1" + } + }, + "node_modules/@volar/source-map": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.11.1.tgz", + "integrity": "sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "muggle-string": "^0.3.1" + } + }, + "node_modules/@volar/typescript": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.11.1.tgz", + "integrity": "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "1.11.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/language-core": { + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz", + "integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~1.11.1", + "@volar/source-map": "~1.11.1", + "@vue/compiler-dom": "^3.3.0", + "@vue/shared": "^3.3.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.3.1", + "path-browserify": "^1.0.1", + "vue-template-compiler": "^2.7.14" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/shared": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -11744,6 +12187,13 @@ "node": ">= 10" } }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "devOptional": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -12191,6 +12641,13 @@ "node": ">=4.0" } }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -15095,6 +15552,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -16728,6 +17195,13 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true, + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -17178,6 +17652,13 @@ "node": ">= 0.6" } }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -17301,6 +17782,13 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -17313,6 +17801,13 @@ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", "license": "MIT" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -17716,6 +18211,13 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/muggle-string": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", + "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -17820,6 +18322,15 @@ "node": ">=6" } }, + "node_modules/nodemailer": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz", + "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -18382,6 +18893,13 @@ "node": ">= 0.8" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "devOptional": true, + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -19977,6 +20495,16 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -21250,6 +21778,16 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -21342,6 +21880,34 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite-plugin-dts": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-3.8.3.tgz", + "integrity": "sha512-yRHiRosQw7MXdOhmcrVI+kRiB8YEShbSxnADNteK4eZGdEoyOkMHihvO5XOAVlOq8ng9sIqu8vVefDK1zcj3qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor": "7.43.0", + "@rollup/pluginutils": "^5.1.0", + "@vue/language-core": "^1.8.27", + "debug": "^4.3.4", + "kolorist": "^1.8.0", + "magic-string": "^0.30.8", + "vue-tsc": "^1.8.27" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "typescript": "*", + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -21982,6 +22548,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz", + "integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "~1.11.1", + "@vue/language-core": "1.8.27", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", @@ -22406,6 +23001,38 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } } } } diff --git a/package.json b/package.json index b5ea258..8899c26 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "koa-cors": "^0.0.16", "koa-jwt": "4.0.4", "koa-router": "13.0.1", + "nodemailer": "^6.9.16", "react": "18.3.1", "react-dom": "18.3.1", "react-hook-form": "^7.54.1", @@ -77,6 +78,7 @@ "typescript": "~5.6.2", "typescript-eslint": "^8.13.0", "vite": "^5.0.0", + "vite-plugin-dts": "~3.8.1", "vitest": "^1.3.1" } } diff --git a/tsconfig.base.json b/tsconfig.base.json index b0cef22..c5e2071 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -15,15 +15,16 @@ "skipDefaultLibCheck": true, "baseUrl": ".", "paths": { - "@onboarding/router/data": ["libs/router/data/src/index.ts"], + "@onboarding/otp/feature": ["libs/otp/feature/src/index.ts"], "@onboarding/i18n/data": ["libs/i18n/data/src/index.ts"], "@onboarding/i18n/feature": ["libs/i18n/feature/src/index.ts"], + "@onboarding/otp-ui/feature": ["libs/otp-ui/feature/src/index.ts"], "@onboarding/react-query/feature": [ "libs/react-query/feature/src/index.ts" ], + "@onboarding/router/data": ["libs/router/data/src/index.ts"], "@onboarding/router/feature": ["libs/router/feature/src/index.ts"], - "@onboarding/themes": ["libs/themes/src/index.ts"], - "@onboarding/ui": ["libs/i18n/ui/src/index.ts"] + "@onboarding/themes": ["libs/themes/src/index.ts"] } }, "exclude": ["node_modules", "tmp"] From 1999934e6b17c6aa4c97207570bb426847ef6ccf Mon Sep 17 00:00:00 2001 From: Javier Ruiz Date: Wed, 18 Dec 2024 15:03:38 +0100 Subject: [PATCH 3/4] feat: otp authentication --- apps/auth/src/routes.ts | 11 +++---- apps/login-ui/src/app/AppStarter.tsx | 5 +++- .../request-token-form/request-token-form.tsx | 17 ++++++----- .../validate-token-form.tsx | 9 +++++- .../validate-token/validate-token.tsx | 30 +++++++++++++++++++ .../feature/src/hooks/use-request-token.ts | 5 ++-- libs/otp-ui/feature/src/index.ts | 3 +- .../feature/src/pages/otp-page-auth.tsx | 8 +++-- .../src/services/otp-authentication.ts | 2 ++ libs/otp/feature/src/nodemailer.ts | 2 +- 10 files changed, 68 insertions(+), 24 deletions(-) create mode 100644 libs/otp-ui/feature/src/components/validate-token/validate-token.tsx diff --git a/apps/auth/src/routes.ts b/apps/auth/src/routes.ts index c69e7ad..ad7b7ca 100644 --- a/apps/auth/src/routes.ts +++ b/apps/auth/src/routes.ts @@ -1,6 +1,6 @@ import koaRouter from 'koa-router'; import jwt from 'jsonwebtoken'; -import { generateOTP, sendOTPEmail } from "@onboarding/otp/feature" +import { generateOTP, sendOTPEmail } from '@onboarding/otp/feature'; const router = new koaRouter(); @@ -11,20 +11,17 @@ router.get('hello', '/', (ctx) => { router.post('/otp/generate', async (ctx, next) => { const otp = generateOTP(2); const { email } = ctx.request.body; - + ctx.app.user = { email: email, otp }; //save temporally in memory - + await sendOTPEmail(email, otp); - ctx.body = { message: "OTP sent to your email" } + ctx.body = { message: 'OTP sent to your email' }; - next(); }); router.post('/otp/verify', async (ctx, next) => { const { otp: userOtp, email } = ctx.request.body; - console.log('users', ctx.app.user); - const storedOtp = ctx.app.user?.otp ?? ''; // retrieve stored OTP if (userOtp === storedOtp) { diff --git a/apps/login-ui/src/app/AppStarter.tsx b/apps/login-ui/src/app/AppStarter.tsx index d8a67fe..ab773fb 100644 --- a/apps/login-ui/src/app/AppStarter.tsx +++ b/apps/login-ui/src/app/AppStarter.tsx @@ -4,6 +4,7 @@ import { theme } from '@onboarding/themes'; import { I18nProvider } from '@onboarding/i18n/feature'; import { ReactQueryProvider } from '@onboarding/react-query/feature'; import AppRoutes from './AppRoutes'; +import { AuthenticationProvider } from '@onboarding/otp-ui/feature'; const translations = { //cant import of external file. eslint error @@ -24,7 +25,9 @@ export const AppStarter: React.FC = () => { - + + + diff --git a/libs/otp-ui/feature/src/components/request-token-form/request-token-form.tsx b/libs/otp-ui/feature/src/components/request-token-form/request-token-form.tsx index 9851239..fab80d0 100644 --- a/libs/otp-ui/feature/src/components/request-token-form/request-token-form.tsx +++ b/libs/otp-ui/feature/src/components/request-token-form/request-token-form.tsx @@ -6,6 +6,7 @@ import { Button, FormLabel, Typography, + Snackbar, } from '@mui/material'; import useRequestToken from '../../hooks/use-request-token'; import { AuthenticationContext } from '../../context/authentication-context'; @@ -33,12 +34,7 @@ const RequestTokenForm: React.FC = (props) => { const { setIsWaitingForValidation, setEmail } = useContext( AuthenticationContext ); - const { - mutate: fetchNewToken, - isSuccess, - data, - isPending, - } = useRequestToken(); + const { mutate: fetchNewToken, status, data, isPending } = useRequestToken(); const onSubmit: SubmitHandler = (d, e) => { e?.preventDefault(); @@ -46,11 +42,11 @@ const RequestTokenForm: React.FC = (props) => { }; useEffect(() => { - if (data?.otp) { + if (status === 'success') { setIsWaitingForValidation(true); setEmail(data.emailRequired); } - }, [isSuccess, data, setIsWaitingForValidation, setEmail]); + }, [status, data, setIsWaitingForValidation, setEmail]); if (isPending) { return ; @@ -86,6 +82,11 @@ const RequestTokenForm: React.FC = (props) => { {errors.emailRequired && errors.emailRequired.message}
+ ); }; diff --git a/libs/otp-ui/feature/src/components/validate-token-form/validate-token-form.tsx b/libs/otp-ui/feature/src/components/validate-token-form/validate-token-form.tsx index c616df0..0ac259c 100644 --- a/libs/otp-ui/feature/src/components/validate-token-form/validate-token-form.tsx +++ b/libs/otp-ui/feature/src/components/validate-token-form/validate-token-form.tsx @@ -11,9 +11,9 @@ const ValidateTokenForm = () => { const { mutate: fetchValidation, - isSuccess, data: jwtToken, error, + isSuccess, } = useValidateToken(); const { email } = useContext(AuthenticationContext); @@ -34,6 +34,7 @@ const ValidateTokenForm = () => { flexDirection: 'column', justifyContent: 'center', gap: '8px', + textAlign: 'center', }} > Introduce your token @@ -41,10 +42,16 @@ const ValidateTokenForm = () => { + setOpenErrorMessage(false)} + autoHideDuration={5000} /> ); diff --git a/libs/otp-ui/feature/src/components/validate-token/validate-token.tsx b/libs/otp-ui/feature/src/components/validate-token/validate-token.tsx new file mode 100644 index 0000000..f93f8cf --- /dev/null +++ b/libs/otp-ui/feature/src/components/validate-token/validate-token.tsx @@ -0,0 +1,30 @@ +import React, { useState } from 'react'; +import ValidateTokenForm from '../validate-token-form/validate-token-form'; +import { Button, Typography } from '@mui/material'; + +const ValidateToken = () => { + const [isValidatingForm, setIsValidatingForm] = useState(false); + + if (isValidatingForm) { + return ; + } + + return ( +
+ + Your token is sent to your email, please check and introduce + + +
+ ); +}; + +export default ValidateToken; diff --git a/libs/otp-ui/feature/src/hooks/use-request-token.ts b/libs/otp-ui/feature/src/hooks/use-request-token.ts index 76c8387..ef8d563 100644 --- a/libs/otp-ui/feature/src/hooks/use-request-token.ts +++ b/libs/otp-ui/feature/src/hooks/use-request-token.ts @@ -8,7 +8,7 @@ export interface TokenResponse { const REQUEST_TOKEN_KEY = 'request_token'; const useRequestToken = () => { - const { data, mutate, error, isSuccess, isPending } = useMutation({ + const { data, mutate, isPending, status } = useMutation({ mutationKey: [REQUEST_TOKEN_KEY], mutationFn: async (email: string) => { const response = await postRequestToken(email); @@ -19,8 +19,7 @@ const useRequestToken = () => { return { data, mutate, - error, - isSuccess, + status, isPending }; }; diff --git a/libs/otp-ui/feature/src/index.ts b/libs/otp-ui/feature/src/index.ts index 04a1278..dcf0fa0 100644 --- a/libs/otp-ui/feature/src/index.ts +++ b/libs/otp-ui/feature/src/index.ts @@ -1,3 +1,4 @@ export * from "./components/index"; export * from "./hooks/index"; -export * from "./pages/index"; \ No newline at end of file +export * from "./pages/index"; +export * from "./context/authentication-provider" \ No newline at end of file diff --git a/libs/otp-ui/feature/src/pages/otp-page-auth.tsx b/libs/otp-ui/feature/src/pages/otp-page-auth.tsx index 17cdda7..2203012 100644 --- a/libs/otp-ui/feature/src/pages/otp-page-auth.tsx +++ b/libs/otp-ui/feature/src/pages/otp-page-auth.tsx @@ -1,10 +1,14 @@ import { useContext } from 'react'; import { AuthenticationContext } from '../context/authentication-context'; -import ValidateTokenForm from '../components/validate-token-form/validate-token-form'; import RequestTokenForm from '../components/request-token-form/request-token-form'; +import ValidateToken from '../components/validate-token/validate-token'; export const OtpPageAuth = () => { const { isWaitingForValidation } = useContext(AuthenticationContext); + console.log( + '🚀 ~ OtpPageAuth ~ isWaitingForValidation:', + isWaitingForValidation + ); const appContainerStyles = { height: '100vh', @@ -17,7 +21,7 @@ export const OtpPageAuth = () => {
{isWaitingForValidation ? (
- +
) : (
diff --git a/libs/otp-ui/feature/src/services/otp-authentication.ts b/libs/otp-ui/feature/src/services/otp-authentication.ts index 78d48a4..de91d0c 100644 --- a/libs/otp-ui/feature/src/services/otp-authentication.ts +++ b/libs/otp-ui/feature/src/services/otp-authentication.ts @@ -15,6 +15,7 @@ export const postRequestToken = (email: string) => { }) .catch((error) => { console.error(error); + throw error; }); }; @@ -31,5 +32,6 @@ export const postValidateToken = (email: string, otp: string) => { }) .catch((error) => { console.error(error); + throw error; }); }; diff --git a/libs/otp/feature/src/nodemailer.ts b/libs/otp/feature/src/nodemailer.ts index 1ccd8c2..1b2f840 100644 --- a/libs/otp/feature/src/nodemailer.ts +++ b/libs/otp/feature/src/nodemailer.ts @@ -5,7 +5,7 @@ export const sendOTPEmail = async (email, otp) => { service: 'gmail', auth: { user: 'javiertestnode@gmail.com', - pass: 'your password', + pass: 'tlrq fspt ecvu rcmi', }, }); From 365d68dfc3196f7c93248a1d6b2ed74fd246494d Mon Sep 17 00:00:00 2001 From: Javier Ruiz Date: Wed, 18 Dec 2024 16:00:50 +0100 Subject: [PATCH 4/4] feat: jwt authentication --- apps/auth/src/main.ts | 28 +++++++++-------------- apps/auth/src/middlewares/index.ts | 1 + apps/auth/src/middlewares/jwt.ts | 6 +++++ apps/auth/src/routes.ts | 9 ++++++-- apps/auth/src/utils/config.ts | 1 + apps/auth/src/utils/index.ts | 1 + libs/otp/feature/src/nodemailer.ts | 36 +++++++++++++++--------------- 7 files changed, 45 insertions(+), 37 deletions(-) create mode 100644 apps/auth/src/middlewares/index.ts create mode 100644 apps/auth/src/middlewares/jwt.ts create mode 100644 apps/auth/src/utils/config.ts create mode 100644 apps/auth/src/utils/index.ts diff --git a/apps/auth/src/main.ts b/apps/auth/src/main.ts index 897df0a..108911a 100644 --- a/apps/auth/src/main.ts +++ b/apps/auth/src/main.ts @@ -1,29 +1,23 @@ import koa from 'koa'; -import router from "./routes"; +import router from './routes'; import bodyParser from 'koa-bodyparser'; -import cors from "koa-cors" -import jwtMiddleware from 'koa-jwt'; - +import cors from 'koa-cors'; const host = process.env.HOST ?? 'localhost'; const port = process.env.PORT ? Number(process.env.PORT) : 8080; -const secretKey = 'your-secret-key'; const app = new koa(); -app.use(cors({ - origin: '*', - credentials: true, - headers: ['Content-Type', 'Authorization'], - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'] -})); -app.use(jwtMiddleware({ - secret: secretKey, - passthrough: true, -})); -app.use(bodyParser()) +app.use( + cors({ + origin: '*', + credentials: true, + headers: ['Content-Type', 'Authorization'], + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + }) +); +app.use(bodyParser()); app.use(router.routes()).use(router.allowedMethods()); app.listen(port, host, () => { console.log(`[ ready ] http://${host}:${port}`); }); - diff --git a/apps/auth/src/middlewares/index.ts b/apps/auth/src/middlewares/index.ts new file mode 100644 index 0000000..511b15a --- /dev/null +++ b/apps/auth/src/middlewares/index.ts @@ -0,0 +1 @@ +export * from './jwt'; diff --git a/apps/auth/src/middlewares/jwt.ts b/apps/auth/src/middlewares/jwt.ts new file mode 100644 index 0000000..fd7c771 --- /dev/null +++ b/apps/auth/src/middlewares/jwt.ts @@ -0,0 +1,6 @@ +import koaJwt from 'koa-jwt'; +import { jwtSecret } from '../utils'; + +export const jwtVal = koaJwt({ + secret: jwtSecret, // Should not be hardcoded +}); diff --git a/apps/auth/src/routes.ts b/apps/auth/src/routes.ts index ad7b7ca..d8adef0 100644 --- a/apps/auth/src/routes.ts +++ b/apps/auth/src/routes.ts @@ -1,6 +1,8 @@ import koaRouter from 'koa-router'; import jwt from 'jsonwebtoken'; import { generateOTP, sendOTPEmail } from '@onboarding/otp/feature'; +import { jwtSecret } from './utils'; +import { jwtVal } from './middlewares/index'; const router = new koaRouter(); @@ -8,6 +10,10 @@ router.get('hello', '/', (ctx) => { ctx.body = '

Hello

'; }); +router.get('/data', jwtVal, async (ctx) => { + ctx.body = '

hh

'; +}); + router.post('/otp/generate', async (ctx, next) => { const otp = generateOTP(2); const { email } = ctx.request.body; @@ -25,7 +31,7 @@ router.post('/otp/verify', async (ctx, next) => { const storedOtp = ctx.app.user?.otp ?? ''; // retrieve stored OTP if (userOtp === storedOtp) { - const token = jwt.sign({ user: email }, 'your-secret-key', { + const token = jwt.sign({ user: email }, jwtSecret, { expiresIn: '1h', }); ctx.body = { token }; @@ -33,7 +39,6 @@ router.post('/otp/verify', async (ctx, next) => { ctx.status = 401; ctx.body = { error: 'Invalid OTP' }; } - next(); }); diff --git a/apps/auth/src/utils/config.ts b/apps/auth/src/utils/config.ts new file mode 100644 index 0000000..add078b --- /dev/null +++ b/apps/auth/src/utils/config.ts @@ -0,0 +1 @@ +export const jwtSecret = 'n6dHKHspDp'; diff --git a/apps/auth/src/utils/index.ts b/apps/auth/src/utils/index.ts new file mode 100644 index 0000000..f03c228 --- /dev/null +++ b/apps/auth/src/utils/index.ts @@ -0,0 +1 @@ +export * from './config'; diff --git a/libs/otp/feature/src/nodemailer.ts b/libs/otp/feature/src/nodemailer.ts index 1b2f840..4351bba 100644 --- a/libs/otp/feature/src/nodemailer.ts +++ b/libs/otp/feature/src/nodemailer.ts @@ -1,20 +1,20 @@ -import { createTransport } from "nodemailer"; +import { createTransport } from 'nodemailer'; export const sendOTPEmail = async (email, otp) => { - const transporter = createTransport({ - service: 'gmail', - auth: { - user: 'javiertestnode@gmail.com', - pass: 'tlrq fspt ecvu rcmi', - }, - }); - - const mailOptions = { - from: 'javiertestnode@gmail.com', - to: email, - subject: 'Your OTP Code', - text: `Your OTP code is ${otp}`, - }; - - await transporter.sendMail(mailOptions); - }; \ No newline at end of file + const transporter = createTransport({ + service: 'gmail', + auth: { + user: 'javiertestnode@gmail.com', + pass: '', + }, + }); + + const mailOptions = { + from: 'javiertestnode@gmail.com', + to: email, + subject: 'Your OTP Code', + text: `Your OTP code is ${otp}`, + }; + + await transporter.sendMail(mailOptions); +};